Overview

Request 6066 (accepted)

New stable branch release

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

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,14 @@
2
 -------------------------------------------------------------------
3
+Sun Jun 30 17:34:39 UTC 2024 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 1.2.0
6
+
7
+-------------------------------------------------------------------
8
+Fri Jun 28 13:42:21 UTC 2024 - Bjørn Lie <zaitor@opensuse.org>
9
+
10
+- Update to version 1.0.7
11
+
12
+-------------------------------------------------------------------
13
 Wed Jan 24 22:21:37 UTC 2024 - Mariusz Fik <fisiu@opensuse.org>
14
 
15
 - Update to version 1.0.1
16
pipewire-aptx.spec Changed
16
 
1
@@ -7,12 +7,12 @@
2
 %define soversion 0_2
3
 
4
 Name:           pipewire-aptx
5
-Version:        1.0.1
6
+Version:        1.2.0
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
 URL:            https://gitlab.freedesktop.org/pipewire/pipewire
11
-Source:         %{url}/-/archive/%{version}/pipewire-%{version}.tar.bz2
12
+Source:         %{url}/-/archive/%{version}/pipewire-%{version}.tar.gz
13
 
14
 BuildRequires:  c++_compiler
15
 BuildRequires:  c_compiler
16
pipewire-1.0.1.tar.bz2/doc/dox/programs/pipewire-pulse-modules.7.md Deleted
34
 
1
@@ -1,32 +0,0 @@
2
-\page page_man_pipewire-pulse-modules_7 pipewire-pulse-modules
3
-
4
-PipeWire Pulseaudio modules
5
-
6
-# DESCRIPTION
7
-
8
-PipeWire's Pulseaudio emulation implements several Pulseaudio modules.
9
-It only supports its own built-in modules, and cannot load external
10
-modules written for Pulseaudio.
11
-
12
-The built-in modules can be loaded using Pulseaudio client programs, for
13
-example `pactl load-module \<module-name\> \<module-options\>`.
14
-They can also added to `pipewire-pulse.conf`, typically by a
15
-drop-in file in `~/.config/pipewire/pipewire-pulse.conf.d/`
16
-containing the module name and its arguments
17
-
18
-    pulse.cmd = 
19
-       { cmd = "load-module" args = "module-null-sink sink_name=foo" flags =   }
20
-    
21
-
22
-# KNOWN MODULES
23
-
24
-$(PIPEWIRE_PULSE_MODULES)
25
-
26
-# AUTHORS
27
-
28
-The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
29
-PipeWire is available from <$(PACKAGE_URL)>
30
-
31
-# SEE ALSO
32
-
33
-\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)"
34
pipewire-1.0.1.tar.bz2/doc/dox/programs/pipewire-pulse.conf.5.md Deleted
58
 
1
@@ -1,56 +0,0 @@
2
-\page page_man_pipewire-pulse_conf_5 pipewire-pulse.conf
3
-
4
-The PipeWire Pulseaudio server configuration file
5
-
6
-# SYNOPSIS
7
-
8
-*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf*
9
-
10
-*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf*
11
-
12
-*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf*
13
-
14
-*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf.d/*
15
-
16
-*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf.d/*
17
-
18
-*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf.d/*
19
-
20
-# DESCRIPTION
21
-
22
-Configuration for PipeWire's PulseAudio-compatible daemon.
23
-
24
-The configuration file format is the same as for `pipewire.conf(5)`.
25
-There are additional sections for configuring `pipewire-pulse(1)`
26
-settings.
27
-
28
-# CONFIGURATION FILE SECTIONS
29
-
30
-\par pulse.properties
31
-Dictionary. These properties configure the PipeWire Pulseaudio server
32
-properties.
33
-
34
-\par pulse.cmd
35
-Array of dictionaries. A set of commands to be executed on startup.
36
-
37
-\par pulse.rules
38
-Array of dictionaries. A set of match rules and actions to apply to
39
-clients.
40
-
41
-See \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)"
42
-for the detailed description.
43
-
44
-In addition, the general PipeWire daemon configuration sections apply,
45
-see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
46
-
47
-# AUTHORS
48
-
49
-The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
50
-PipeWire is available from <$(PACKAGE_URL)>
51
-
52
-# SEE ALSO
53
-
54
-\ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)",
55
-\ref page_man_pipewire_conf_5 "pipewire.conf(5)",
56
-\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)",
57
-\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
58
pipewire-1.0.1.tar.bz2/doc/dox/programs/pipewire.conf.5.md Deleted
104
 
1
@@ -1,102 +0,0 @@
2
-\page page_man_pipewire_conf_5 pipewire.conf
3
-
4
-The PipeWire server configuration file
5
-
6
-# SYNOPSIS
7
-
8
-*$XDG_CONFIG_HOME/pipewire/pipewire.conf*
9
-
10
-*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf*
11
-
12
-*$(PIPEWIRE_CONFDATADIR)/pipewire.conf*
13
-
14
-*$(PIPEWIRE_CONFDATADIR)/pipewire.conf.d/*
15
-
16
-*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf.d/*
17
-
18
-*$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/*
19
-
20
-# DESCRIPTION
21
-
22
-PipeWire is a service that facilitates sharing of multimedia content
23
-between devices and applications.
24
-
25
-On startup, the daemon reads a main configuration file to configure
26
-itself. It executes a series of commands listed in the config file.
27
-
28
-The config files are loaded in the order listed in the
29
-SYNOPSIS(#synopsis). The environment variables `PIPEWIRE_CONFIG_DIR`,
30
-`PIPEWIRE_CONFIG_PREFIX` and `PIPEWIRE_CONFIG_NAME` can be used to
31
-specify an alternative config directory, subdirectory and file
32
-respectively.
33
-
34
-Next to the configuration file can be a directory with the same name as
35
-the file with a `.d/` suffix. All directories in the
36
-SYNOPSIS(#synopsis) directory search paths are traversed in the listed
37
-order and the contents of the `*.conf` files inside them are appended to
38
-the main configuration file as overrides. Object sections are merged and
39
-array sections are appended.
40
-
41
-# CONFIGURATION FILE FORMAT
42
-
43
-The configuration file format is grouped into sections. A section is
44
-either a dictionary, {}, or an array, \\. Dictionary and array entries
45
-are separated by whitespace and may be simple value assignment, an array
46
-or a dictionary. For example:
47
-```
48
-    name = value # simple assignment
49
-
50
-    name = { key1 = value1 key2 = value2 } # a dictionary with two entries
51
-
52
-    name =  value1 value2  # an array with two entries
53
-
54
-    name =  { k = v1 } { k = v2 }  # an array of dictionaries
55
-```
56
-
57
-The configuration files can be expressed in full JSON syntax but for
58
-ease of use, a relaxed format may be used where:
59
-
60
-- `:` to delimit keys and values can be substuted by `=` or a space.
61
-- <tt>\"</tt> around keys and string can be omitted as long as no special
62
-  characters are used in the strings.
63
-- `,` to separate objects can be replaced with a whitespace character.
64
-- `#` can be used to start a comment until the line end
65
-
66
-# CONFIGURATION FILE SECTIONS
67
-
68
-\par context.properties
69
-Dictionary. These properties configure the PipeWire instance.
70
-
71
-\par context.spa-libs
72
-Dictionary. Maps plugin features with globs to a spa library.
73
-
74
-\par context.modules
75
-Array of dictionaries. Each entry in the array is a dictionary with the
76
-*name* of the module to load, including optional *args* and *flags*.
77
-Most modules support being loaded multiple times.
78
-
79
-\par context.objects
80
-Array of dictionaries. Each entry in the array is a dictionary
81
-containing the *factory* to create an object from and optional extra
82
-arguments specific to that factory.
83
-
84
-\par context.exec
85
-\parblock
86
-Array of dictionaries. Each entry in the array is dictionary containing
87
-the *path* of a program to execute on startup and optional *args*.
88
-
89
-This array used to contain an entry to start the session manager but
90
-this mode of operation has since been demoted to development aid. Avoid
91
-starting a session manager in this way in production environment.
92
-\endparblock
93
-
94
-# AUTHORS
95
-
96
-The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
97
-PipeWire is available from <$(PACKAGE_URL)>
98
-
99
-# SEE ALSO
100
-
101
-\ref page_man_pipewire_1 "pipewire(1)",
102
-\ref page_man_pw-mon_1 "pw-mon(1)",
103
-\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)"
104
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/dbus-helpers.h Deleted
74
 
1
@@ -1,72 +0,0 @@
2
-/* Spa Bluez5 DBus helpers */
3
-/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
4
-/* SPDX-License-Identifier: MIT */
5
-
6
-#ifndef SPA_BLUEZ5_DBUS_HELPERS_H
7
-#define SPA_BLUEZ5_DBUS_HELPERS_H
8
-
9
-#include <stdbool.h>
10
-
11
-#include <dbus/dbus.h>
12
-
13
-#include <spa/utils/cleanup.h>
14
-
15
-static inline void cancel_and_unref(DBusPendingCall **pp)
16
-{
17
-   DBusPendingCall *pending_call = spa_steal_ptr(*pp);
18
-
19
-   if (pending_call) {
20
-       dbus_pending_call_cancel(pending_call);
21
-       dbus_pending_call_unref(pending_call);
22
-   }
23
-}
24
-
25
-static inline DBusMessage *steal_reply_and_unref(DBusPendingCall **pp)
26
-{
27
-   DBusPendingCall *pending_call = spa_steal_ptr(*pp);
28
-
29
-   DBusMessage *reply = dbus_pending_call_steal_reply(pending_call);
30
-   dbus_pending_call_unref(pending_call);
31
-
32
-   return reply;
33
-}
34
-
35
-SPA_DEFINE_AUTOPTR_CLEANUP(DBusMessage, DBusMessage, {
36
-   spa_clear_ptr(*thing, dbus_message_unref);
37
-})
38
-
39
-static inline bool reply_with_error(DBusConnection *conn,
40
-                   DBusMessage *reply_to,
41
-                   const char *error_name, const char *error_message)
42
-{
43
-   spa_autoptr(DBusMessage) reply = dbus_message_new_error(reply_to, error_name, error_message);
44
-
45
-   return reply && dbus_connection_send(conn, reply, NULL);
46
-}
47
-
48
-static inline DBusPendingCall *send_with_reply(DBusConnection *conn,
49
-                          DBusMessage *m,
50
-                          DBusPendingCallNotifyFunction callback, void *user_data)
51
-{
52
-   DBusPendingCall *pending_call;
53
-
54
-   if (!dbus_connection_send_with_reply(conn, m, &pending_call, DBUS_TIMEOUT_USE_DEFAULT))
55
-       return NULL;
56
-
57
-   if (!pending_call)
58
-       return NULL;
59
-
60
-   if (!dbus_pending_call_set_notify(pending_call, callback, user_data, NULL)) {
61
-       dbus_pending_call_cancel(pending_call);
62
-       dbus_pending_call_unref(pending_call);
63
-       return NULL;
64
-   }
65
-
66
-   return pending_call;
67
-}
68
-
69
-SPA_DEFINE_AUTO_CLEANUP(DBusError, DBusError, {
70
-   dbus_error_free(thing);
71
-})
72
-
73
-#endif /* SPA_BLUEZ5_DBUS_HELPERS_H */
74
pipewire-1.0.1.tar.bz2/spa/plugins/support/log-patterns.c Deleted
92
 
1
@@ -1,90 +0,0 @@
2
-/* Spa */
3
-/* SPDX-FileCopyrightText: Copyright © 2021 Red Hat, Inc. */
4
-/* SPDX-License-Identifier: MIT */
5
-
6
-#include <errno.h>
7
-#include <fnmatch.h>
8
-
9
-#include <spa/support/log.h>
10
-#include <spa/utils/list.h>
11
-#include <spa/utils/json.h>
12
-
13
-#include "log-patterns.h"
14
-
15
-struct support_log_pattern {
16
-   struct spa_list link;
17
-   enum spa_log_level level;
18
-   char pattern;
19
-};
20
-
21
-void
22
-support_log_topic_init(struct spa_list *patterns, enum spa_log_level default_level,
23
-              struct spa_log_topic *t)
24
-{
25
-   enum spa_log_level level = default_level;
26
-   bool has_custom_level = false;
27
-   const char *topic = t->topic;
28
-   struct support_log_pattern *pattern;
29
-
30
-   spa_list_for_each(pattern, patterns, link) {
31
-       if (fnmatch(pattern->pattern, topic, 0) != 0)
32
-           continue;
33
-       level = pattern->level;
34
-       has_custom_level = true;
35
-   }
36
-
37
-   t->level = level;
38
-   t->has_custom_level = has_custom_level;
39
-}
40
-
41
-int
42
-support_log_parse_patterns(struct spa_list *patterns, const char *jsonstr)
43
-{
44
-   struct spa_json iter, array, elem;
45
-   int res = 0;
46
-
47
-   spa_json_init(&iter, jsonstr, strlen(jsonstr));
48
-
49
-   if (spa_json_enter_array(&iter, &array) < 0)
50
-       return -EINVAL;
51
-
52
-   while (spa_json_enter_object(&array, &elem) > 0) {
53
-       char pattern512 = {0};
54
-
55
-       while (spa_json_get_string(&elem, pattern, sizeof(pattern)) > 0) {
56
-           struct support_log_pattern *p;
57
-           const char *val;
58
-           int len;
59
-           int lvl;
60
-
61
-           if ((len = spa_json_next(&elem, &val)) <= 0)
62
-               break;
63
-
64
-           if (!spa_json_is_int(val, len))
65
-               break;
66
-
67
-           if ((res = spa_json_parse_int(val, len, &lvl)) < 0)
68
-               break;
69
-
70
-           SPA_CLAMP(lvl, SPA_LOG_LEVEL_NONE, SPA_LOG_LEVEL_TRACE);
71
-
72
-           p = calloc(1, sizeof(*p) + strlen(pattern) + 1);
73
-           p->level = lvl;
74
-           strcpy(p->pattern, pattern);
75
-           spa_list_append(patterns, &p->link);
76
-       }
77
-   }
78
-
79
-   return res;
80
-}
81
-
82
-void
83
-support_log_free_patterns(struct spa_list *patterns)
84
-{
85
-   struct support_log_pattern *p;
86
-
87
-   spa_list_consume(p, patterns, link) {
88
-       spa_list_remove(&p->link);
89
-       free(p);
90
-   }
91
-}
92
pipewire-1.0.1.tar.bz2/spa/plugins/support/log-patterns.h Deleted
15
 
1
@@ -1,13 +0,0 @@
2
-#ifndef LOG_PATTERNS_H
3
-#define LOG_PATTERNS_H
4
-
5
-#include <spa/support/log.h>
6
-
7
-struct spa_list;
8
-
9
-void support_log_topic_init(struct spa_list *patterns, enum spa_log_level default_level,
10
-               struct spa_log_topic *t);
11
-int support_log_parse_patterns(struct spa_list *patterns, const char *jsonstr);
12
-void support_log_free_patterns(struct spa_list *patterns);
13
-
14
-#endif /* LOG_PATTERNS_H */
15
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extensions Deleted
2
 
1
-(directory)
2
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extensions/ext-device-manager.c Deleted
10
 
1
@@ -1,8 +0,0 @@
2
-#include <errno.h>
3
-
4
-#include "registry.h"
5
-
6
-int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m)
7
-{
8
-   return -ENOTSUP;
9
-}
10
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extensions/ext-device-restore.c Deleted
318
 
1
@@ -1,316 +0,0 @@
2
-/* PipeWire */
3
-/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
4
-/* SPDX-License-Identifier: MIT */
5
-
6
-#define EXT_DEVICE_RESTORE_VERSION 1
7
-
8
-#include <stdbool.h>
9
-#include <stdint.h>
10
-#include <stdio.h>
11
-#include <stdlib.h>
12
-#include <string.h>
13
-
14
-#include <spa/pod/builder.h>
15
-#include <spa/utils/defs.h>
16
-#include <spa/utils/dict.h>
17
-#include <spa/utils/string.h>
18
-#include <spa/utils/json.h>
19
-#include <spa/param/audio/format.h>
20
-#include <spa/param/props.h>
21
-
22
-#include <pipewire/log.h>
23
-#include <pipewire/properties.h>
24
-
25
-#include "../client.h"
26
-#include "../collect.h"
27
-#include "../defs.h"
28
-#include "../extension.h"
29
-#include "../format.h"
30
-#include "../manager.h"
31
-#include "../message.h"
32
-#include "../reply.h"
33
-#include "../volume.h"
34
-#include "registry.h"
35
-
36
-PW_LOG_TOPIC_EXTERN(pulse_ext_dev_restore);
37
-#undef PW_LOG_TOPIC_DEFAULT
38
-#define PW_LOG_TOPIC_DEFAULT pulse_ext_dev_restore
39
-
40
-#define DEVICE_TYPE_SINK   0
41
-#define DEVICE_TYPE_SOURCE 1
42
-
43
-static int do_extension_device_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m)
44
-{
45
-   struct message *reply;
46
-
47
-   reply = reply_new(client, tag);
48
-   message_put(reply,
49
-           TAG_U32, EXT_DEVICE_RESTORE_VERSION,
50
-           TAG_INVALID);
51
-
52
-   return client_queue_message(client, reply);
53
-}
54
-
55
-static int do_extension_device_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m)
56
-{
57
-   return reply_simple_ack(client, tag);
58
-}
59
-
60
-
61
-struct format_data {
62
-        struct client *client;
63
-        struct message *reply;
64
-};
65
-
66
-static int do_sink_read_format(void *data, struct pw_manager_object *o)
67
-{
68
-   struct format_data *d = data;
69
-   struct pw_manager_param *p;
70
-   struct format_info info32;
71
-   uint32_t i, n_info = 0;
72
-
73
-   if (!pw_manager_object_is_sink(o))
74
-       return 0;
75
-
76
-   spa_list_for_each(p, &o->param_list, link) {
77
-       uint32_t index = 0;
78
-
79
-       if (p->id != SPA_PARAM_EnumFormat)
80
-           continue;
81
-
82
-       while (n_info < SPA_N_ELEMENTS(info)) {
83
-           spa_zero(infon_info);
84
-           if (format_info_from_param(&infon_info, p->param, index++) < 0)
85
-               break;
86
-           if (infon_info.encoding == ENCODING_ANY) {
87
-               format_info_clear(&infon_info);
88
-               continue;
89
-           }
90
-           n_info++;
91
-       }
92
-   }
93
-   message_put(d->reply,
94
-       TAG_U32, DEVICE_TYPE_SINK,
95
-       TAG_U32, o->index,          /* sink index */
96
-       TAG_U8, n_info,             /* n_formats */
97
-       TAG_INVALID);
98
-   for (i = 0; i < n_info; i++) {
99
-       message_put(d->reply,
100
-           TAG_FORMAT_INFO, &infoi,
101
-           TAG_INVALID);
102
-       format_info_clear(&infoi);
103
-   }
104
-   return 0;
105
-}
106
-
107
-static int do_extension_device_restore_read_formats_all(struct client *client,
108
-       uint32_t command, uint32_t tag, struct message *m)
109
-{
110
-   struct pw_manager *manager = client->manager;
111
-   struct format_data data;
112
-
113
-   spa_zero(data);
114
-   data.client = client;
115
-   data.reply = reply_new(client, tag);
116
-
117
-   pw_manager_for_each_object(manager, do_sink_read_format, &data);
118
-
119
-   return client_queue_message(client, data.reply);
120
-}
121
-
122
-static int do_extension_device_restore_read_formats(struct client *client,
123
-       uint32_t command, uint32_t tag, struct message *m)
124
-{
125
-   struct pw_manager *manager = client->manager;
126
-   struct format_data data;
127
-   uint32_t type, sink_index;
128
-   struct selector sel;
129
-   struct pw_manager_object *o;
130
-   int res;
131
-
132
-   if ((res = message_get(m,
133
-           TAG_U32, &type,
134
-           TAG_U32, &sink_index,
135
-           TAG_INVALID)) < 0)
136
-       return -EPROTO;
137
-
138
-   if (type != DEVICE_TYPE_SINK) {
139
-       pw_log_info("Device format reading is only supported on sinks");
140
-       return -ENOTSUP;
141
-   }
142
-
143
-   spa_zero(sel);
144
-   sel.index = sink_index;
145
-   sel.type = pw_manager_object_is_sink;
146
-
147
-   o = select_object(manager, &sel);
148
-        if (o == NULL)
149
-       return -ENOENT;
150
-
151
-   spa_zero(data);
152
-   data.client = client;
153
-   data.reply = reply_new(client, tag);
154
-
155
-   do_sink_read_format(&data, o);
156
-
157
-   return client_queue_message(client, data.reply);
158
-}
159
-
160
-static int set_card_codecs(struct pw_manager_object *o, uint32_t port_index,
161
-       uint32_t device_id, uint32_t n_codecs, uint32_t *codecs)
162
-{
163
-   char buf1024;
164
-   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
165
-   struct spa_pod_frame f2;
166
-   struct spa_pod *param;
167
-
168
-   if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
169
-       return -EACCES;
170
-
171
-   if (o->proxy == NULL)
172
-       return -ENOENT;
173
-
174
-   spa_pod_builder_push_object(&b, &f0,
175
-           SPA_TYPE_OBJECT_ParamRoute, SPA_PARAM_Route);
176
-   spa_pod_builder_add(&b,
177
-           SPA_PARAM_ROUTE_index, SPA_POD_Int(port_index),
178
-           SPA_PARAM_ROUTE_device, SPA_POD_Int(device_id),
179
-           0);
180
-   spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_props, 0);
181
-   spa_pod_builder_push_object(&b, &f1,
182
-           SPA_TYPE_OBJECT_Props,  SPA_PARAM_Props);
183
-   spa_pod_builder_add(&b,
184
-           SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t),
185
-               SPA_TYPE_Id, n_codecs, codecs), 0);
186
-   spa_pod_builder_pop(&b, &f1);
187
-   spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_save, 0);
188
-   spa_pod_builder_bool(&b, true);
189
-   param = spa_pod_builder_pop(&b, &f0);
190
-
191
-   pw_device_set_param((struct pw_device*)o->proxy,
192
-           SPA_PARAM_Route, 0, param);
193
-   return 0;
194
-}
195
-
196
-static int set_node_codecs(struct pw_manager_object *o, uint32_t n_codecs, uint32_t *codecs)
197
-{
198
-   char buf1024;
199
-   struct spa_pod_builder b;
200
-   struct spa_pod *param;
201
-
202
-   if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
203
-       return -EACCES;
204
-
205
-   if (o->proxy == NULL)
206
-       return -ENOENT;
207
-
208
-   spa_pod_builder_init(&b, buf, sizeof(buf));
209
-   param = spa_pod_builder_add_object(&b,
210
-           SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
211
-           SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t),
212
-               SPA_TYPE_Id, n_codecs, codecs));
213
-
214
-   pw_node_set_param((struct pw_node*)o->proxy,
215
-           SPA_PARAM_Props, 0, param);
216
-
217
-   return 0;
218
-}
219
-
220
-
221
-static int do_extension_device_restore_save_formats(struct client *client,
222
-       uint32_t command, uint32_t tag, struct message *m)
223
-{
224
-   struct pw_manager *manager = client->manager;
225
-   struct selector sel;
226
-   struct pw_manager_object *o, *card = NULL;
227
-   struct pw_node_info *info;
228
-   int res;
229
-   uint32_t type, sink_index;
230
-   uint8_t i, n_formats;
231
-   uint32_t n_codecs = 0, codec, iec958codecs32;
232
-   struct device_info dev_info;
233
-
234
-   if ((res = message_get(m,
235
-           TAG_U32, &type,
236
-           TAG_U32, &sink_index,
237
-           TAG_U8, &n_formats,
238
-           TAG_INVALID)) < 0)
239
-       return -EPROTO;
240
-   if (n_formats < 1)
241
-       return -EPROTO;
242
-
243
-   if (type != DEVICE_TYPE_SINK)
244
-       return -ENOTSUP;
245
-
246
-   for (i = 0; i < n_formats; ++i) {
247
-       struct format_info format;
248
-       spa_zero(format);
249
-       if (message_get(m,
250
-               TAG_FORMAT_INFO, &format,
251
-               TAG_INVALID) < 0)
252
-           return -EPROTO;
253
-
254
-       codec = format_encoding2id(format.encoding);
255
-       if (codec != SPA_ID_INVALID && n_codecs < SPA_N_ELEMENTS(iec958codecs))
256
-           iec958codecsn_codecs++ = codec;
257
-
258
-       format_info_clear(&format);
259
-   }
260
-   if (n_codecs == 0)
261
-       return -ENOTSUP;
262
-
263
-   spa_zero(sel);
264
-   sel.index = sink_index;
265
-   sel.type = pw_manager_object_is_sink;
266
-
267
-   o = select_object(manager, &sel);
268
-   if (o == NULL || (info = o->info) == NULL || info->props == NULL)
269
-       return -ENOENT;
270
-
271
-   get_device_info(o, &dev_info, SPA_DIRECTION_INPUT, false);
272
-
273
-   if (dev_info.card_id != SPA_ID_INVALID) {
274
-       struct selector sel = { .id = dev_info.card_id, .type = pw_manager_object_is_card, };
275
-       card = select_object(manager, &sel);
276
-   }
277
-   if (card != NULL && dev_info.active_port != SPA_ID_INVALID) {
278
-       res = set_card_codecs(card, dev_info.active_port,
279
-               dev_info.device, n_codecs, iec958codecs);
280
-   } else {
281
-       res = set_node_codecs(o, n_codecs, iec958codecs);
282
-   }
283
-   if (res < 0)
284
-       return res;
285
-
286
-   return reply_simple_ack(client, tag);
287
-}
288
-
289
-static const struct extension_sub ext_device_restore = {
290
-   { "TEST", 0, do_extension_device_restore_test, },
291
-   { "SUBSCRIBE", 1, do_extension_device_restore_subscribe, },
292
-   { "EVENT", 2, },
293
-   { "READ_FORMATS_ALL", 3, do_extension_device_restore_read_formats_all, },
294
-   { "READ_FORMATS", 4, do_extension_device_restore_read_formats, },
295
-   { "SAVE_FORMATS", 5, do_extension_device_restore_save_formats, },
296
-};
297
-
298
-int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m)
299
-{
300
-   uint32_t command;
301
-   int res;
302
-
303
-   if ((res = message_get(m,
304
-           TAG_U32, &command,
305
-           TAG_INVALID)) < 0)
306
-       return -EPROTO;
307
-
308
-   if (command >= SPA_N_ELEMENTS(ext_device_restore))
309
-       return -ENOTSUP;
310
-   if (ext_device_restorecommand.process == NULL)
311
-       return -EPROTO;
312
-
313
-   pw_log_info("client %p %s: EXT_DEVICE_RESTORE_%s tag:%u",
314
-           client, client->name, ext_device_restorecommand.name, tag);
315
-
316
-   return ext_device_restorecommand.process(client, command, tag, m);
317
-}
318
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c Deleted
312
 
1
@@ -1,310 +0,0 @@
2
-/* PipeWire */
3
-/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
4
-/* SPDX-License-Identifier: MIT */
5
-
6
-#define EXT_STREAM_RESTORE_VERSION 1
7
-
8
-#include <stdbool.h>
9
-#include <stdint.h>
10
-#include <stdio.h>
11
-#include <stdlib.h>
12
-#include <string.h>
13
-
14
-#include <spa/utils/defs.h>
15
-#include <spa/utils/dict.h>
16
-#include <spa/utils/string.h>
17
-#include <spa/utils/json.h>
18
-#include <pipewire/log.h>
19
-#include <pipewire/properties.h>
20
-
21
-#include "../client.h"
22
-#include "../defs.h"
23
-#include "../extension.h"
24
-#include "../format.h"
25
-#include "../manager.h"
26
-#include "../message.h"
27
-#include "../remap.h"
28
-#include "../reply.h"
29
-#include "../volume.h"
30
-#include "registry.h"
31
-
32
-PW_LOG_TOPIC_EXTERN(pulse_ext_stream_restore);
33
-#undef PW_LOG_TOPIC_DEFAULT
34
-#define PW_LOG_TOPIC_DEFAULT pulse_ext_stream_restore
35
-
36
-static int do_extension_stream_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m)
37
-{
38
-   struct message *reply;
39
-
40
-   reply = reply_new(client, tag);
41
-   message_put(reply,
42
-           TAG_U32, EXT_STREAM_RESTORE_VERSION,
43
-           TAG_INVALID);
44
-
45
-   return client_queue_message(client, reply);
46
-}
47
-
48
-static int key_from_name(const char *name, char *key, size_t maxlen)
49
-{
50
-   const char *media_class, *select, *str;
51
-
52
-   if (spa_strstartswith(name, "sink-input-"))
53
-       media_class = "Output/Audio";
54
-   else if (spa_strstartswith(name, "source-output-"))
55
-       media_class = "Input/Audio";
56
-   else
57
-       return -1;
58
-
59
-   if ((str = strstr(name, "-by-media-role:")) != NULL) {
60
-       const struct str_map *map;
61
-       str += strlen("-by-media-role:");
62
-       map = str_map_find(media_role_map, NULL, str);
63
-       str = map ? map->pw_str : str;
64
-       select = "media.role";
65
-   }
66
-   else if ((str = strstr(name, "-by-application-id:")) != NULL) {
67
-       str += strlen("-by-application-id:");
68
-       select = "application.id";
69
-   }
70
-   else if ((str = strstr(name, "-by-application-name:")) != NULL) {
71
-       str += strlen("-by-application-name:");
72
-       select = "application.name";
73
-   }
74
-   else if ((str = strstr(name, "-by-media-name:")) != NULL) {
75
-       str += strlen("-by-media-name:");
76
-       select = "media.name";
77
-   } else
78
-       return -1;
79
-
80
-   snprintf(key, maxlen, "restore.stream.%s.%s:%s",
81
-               media_class, select, str);
82
-   return 0;
83
-}
84
-
85
-static int key_to_name(const char *key, char *name, size_t maxlen)
86
-{
87
-   const char *type, *select, *str;
88
-
89
-   if (spa_strstartswith(key, "restore.stream.Output/Audio."))
90
-       type = "sink-input";
91
-   else if (spa_strstartswith(key, "restore.stream.Input/Audio."))
92
-       type = "source-output";
93
-   else
94
-       type = "stream";
95
-
96
-   if ((str = strstr(key, ".media.role:")) != NULL) {
97
-       const struct str_map *map;
98
-       str += strlen(".media.role:");
99
-       map = str_map_find(media_role_map, str, NULL);
100
-       select = "media-role";
101
-       str = map ? map->pa_str : str;
102
-   }
103
-   else if ((str = strstr(key, ".application.id:")) != NULL) {
104
-       str += strlen(".application.id:");
105
-       select = "application-id";
106
-   }
107
-   else if ((str = strstr(key, ".application.name:")) != NULL) {
108
-       str += strlen(".application.name:");
109
-       select = "application-name";
110
-   }
111
-   else if ((str = strstr(key, ".media.name:")) != NULL) {
112
-       str += strlen(".media.name:");
113
-       select = "media-name";
114
-   }
115
-   else
116
-       return -1;
117
-
118
-   snprintf(name, maxlen, "%s-by-%s:%s", type, select, str);
119
-   return 0;
120
-
121
-}
122
-
123
-static int do_extension_stream_restore_read(struct client *client, uint32_t command, uint32_t tag, struct message *m)
124
-{
125
-   struct message *reply;
126
-   const struct spa_dict_item *item;
127
-
128
-   reply = reply_new(client, tag);
129
-
130
-   spa_dict_for_each(item, &client->routes->dict) {
131
-       struct spa_json it3;
132
-       const char *value;
133
-       char name1024, key128;
134
-       char device_name1024 = "\0";
135
-       bool mute = false;
136
-       struct volume vol = VOLUME_INIT;
137
-       struct channel_map map = CHANNEL_MAP_INIT;
138
-       float volume = 0.0f;
139
-
140
-       if (key_to_name(item->key, name, sizeof(name)) < 0)
141
-           continue;
142
-
143
-       pw_log_debug("%s -> %s: %s", item->key, name, item->value);
144
-
145
-       spa_json_init(&it0, item->value, strlen(item->value));
146
-       if (spa_json_enter_object(&it0, &it1) <= 0)
147
-           continue;
148
-
149
-       while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
150
-           if (spa_streq(key, "volume")) {
151
-               if (spa_json_get_float(&it1, &volume) <= 0)
152
-                   continue;
153
-           }
154
-           else if (spa_streq(key, "mute")) {
155
-               if (spa_json_get_bool(&it1, &mute) <= 0)
156
-                   continue;
157
-           }
158
-           else if (spa_streq(key, "volumes")) {
159
-               vol = VOLUME_INIT;
160
-               if (spa_json_enter_array(&it1, &it2) <= 0)
161
-                   continue;
162
-
163
-               for (vol.channels = 0; vol.channels < CHANNELS_MAX; vol.channels++) {
164
-                   if (spa_json_get_float(&it2, &vol.valuesvol.channels) <= 0)
165
-                       break;
166
-               }
167
-           }
168
-           else if (spa_streq(key, "channels")) {
169
-               if (spa_json_enter_array(&it1, &it2) <= 0)
170
-                   continue;
171
-
172
-               for (map.channels = 0; map.channels < CHANNELS_MAX; map.channels++) {
173
-                   char chname16;
174
-                   if (spa_json_get_string(&it2, chname, sizeof(chname)) <= 0)
175
-                       break;
176
-                   map.mapmap.channels = channel_name2id(chname);
177
-               }
178
-           }
179
-           else if (spa_streq(key, "target-node")) {
180
-               if (spa_json_get_string(&it1, device_name, sizeof(device_name)) <= 0)
181
-                   continue;
182
-           }
183
-           else if (spa_json_next(&it1, &value) <= 0)
184
-               break;
185
-       }
186
-       message_put(reply,
187
-           TAG_STRING, name,
188
-           TAG_CHANNEL_MAP, &map,
189
-           TAG_CVOLUME, &vol,
190
-           TAG_STRING, device_name0 ? device_name : NULL,
191
-           TAG_BOOLEAN, mute,
192
-           TAG_INVALID);
193
-   }
194
-
195
-   return client_queue_message(client, reply);
196
-}
197
-
198
-static int do_extension_stream_restore_write(struct client *client, uint32_t command, uint32_t tag, struct message *m)
199
-{
200
-   int res;
201
-   uint32_t mode;
202
-   bool apply;
203
-
204
-   if ((res = message_get(m,
205
-           TAG_U32, &mode,
206
-           TAG_BOOLEAN, &apply,
207
-           TAG_INVALID)) < 0)
208
-       return -EPROTO;
209
-
210
-   while (m->offset < m->length) {
211
-       const char *name, *device_name = NULL;
212
-       struct channel_map map;
213
-       struct volume vol;
214
-       bool mute = false;
215
-       uint32_t i;
216
-       FILE *f;
217
-       char *ptr;
218
-       size_t size;
219
-       char key1024, buf128;
220
-
221
-       spa_zero(map);
222
-       spa_zero(vol);
223
-
224
-       if (message_get(m,
225
-               TAG_STRING, &name,
226
-               TAG_CHANNEL_MAP, &map,
227
-               TAG_CVOLUME, &vol,
228
-               TAG_STRING, &device_name,
229
-               TAG_BOOLEAN, &mute,
230
-               TAG_INVALID) < 0)
231
-           return -EPROTO;
232
-
233
-       if (name == NULL || name0 == '\0')
234
-           return -EPROTO;
235
-
236
-       if ((f = open_memstream(&ptr, &size)) == NULL)
237
-           return -errno;
238
-
239
-       fprintf(f, "{");
240
-       fprintf(f, " \"mute\": %s", mute ? "true" : "false");
241
-       if (vol.channels > 0) {
242
-           fprintf(f, ", \"volumes\": ");
243
-           for (i = 0; i < vol.channels; i++)
244
-               fprintf(f, "%s%s", (i == 0 ? " ":", "),
245
-                       spa_json_format_float(buf, sizeof(buf), vol.valuesi));
246
-           fprintf(f, " ");
247
-       }
248
-       if (map.channels > 0) {
249
-           fprintf(f, ", \"channels\": ");
250
-           for (i = 0; i < map.channels; i++)
251
-               fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), channel_id2name(map.mapi));
252
-           fprintf(f, " ");
253
-       }
254
-       if (device_name != NULL && device_name0 &&
255
-           (client->default_source == NULL || !spa_streq(device_name, client->default_source)) &&
256
-           (client->default_sink == NULL || !spa_streq(device_name, client->default_sink)))
257
-           fprintf(f, ", \"target-node\": \"%s\"", device_name);
258
-       fprintf(f, " }");
259
-       fclose(f);
260
-       if (key_from_name(name, key, sizeof(key)) >= 0) {
261
-           pw_log_debug("%s -> %s: %s", name, key, ptr);
262
-           if ((res = pw_manager_set_metadata(client->manager,
263
-                           client->metadata_routes,
264
-                           PW_ID_CORE, key, "Spa:String:JSON", "%s", ptr)) < 0)
265
-               pw_log_warn("failed to set metadata %s = %s, %s", key, ptr, strerror(-res));
266
-       }
267
-       free(ptr);
268
-   }
269
-
270
-   return reply_simple_ack(client, tag);
271
-}
272
-
273
-static int do_extension_stream_restore_delete(struct client *client, uint32_t command, uint32_t tag, struct message *m)
274
-{
275
-   return reply_simple_ack(client, tag);
276
-}
277
-
278
-static int do_extension_stream_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m)
279
-{
280
-   return reply_simple_ack(client, tag);
281
-}
282
-
283
-static const struct extension_sub ext_stream_restore = {
284
-   { "TEST", 0, do_extension_stream_restore_test, },
285
-   { "READ", 1, do_extension_stream_restore_read, },
286
-   { "WRITE", 2, do_extension_stream_restore_write, },
287
-   { "DELETE", 3, do_extension_stream_restore_delete, },
288
-   { "SUBSCRIBE", 4, do_extension_stream_restore_subscribe, },
289
-   { "EVENT", 5, },
290
-};
291
-
292
-int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m)
293
-{
294
-   uint32_t command;
295
-   int res;
296
-
297
-   if ((res = message_get(m,
298
-           TAG_U32, &command,
299
-           TAG_INVALID)) < 0)
300
-       return -EPROTO;
301
-
302
-   if (command >= SPA_N_ELEMENTS(ext_stream_restore))
303
-       return -ENOTSUP;
304
-   if (ext_stream_restorecommand.process == NULL)
305
-       return -EPROTO;
306
-
307
-   pw_log_info("client %p %s: EXT_STREAM_RESTORE_%s tag:%u",
308
-           client, client->name, ext_stream_restorecommand.name, tag);
309
-
310
-   return ext_stream_restorecommand.process(client, command, tag, m);
311
-}
312
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extensions/registry.h Deleted
15
 
1
@@ -1,13 +0,0 @@
2
-#ifndef PIPEWIRE_PULSE_EXTENSION_REGISTRY_H
3
-#define PIPEWIRE_PULSE_EXTENSION_REGISTRY_H
4
-
5
-#include <stdint.h>
6
-
7
-struct client;
8
-struct message;
9
-
10
-int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m);
11
-int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m);
12
-int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m);
13
-
14
-#endif /* PIPEWIRE_PULSE_EXTENSION_REGISTRY_H */
15
pipewire-1.0.1.tar.bz2/src/pipewire/cleanup.h Deleted
23
 
1
@@ -1,21 +0,0 @@
2
-/* PipeWire */
3
-/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
4
-/* SPDX-License-Identifier: MIT */
5
-
6
-#ifndef PIPEWIRE_CLEANUP_H
7
-#define PIPEWIRE_CLEANUP_H
8
-
9
-#include <spa/utils/cleanup.h>
10
-
11
-#include <pipewire/properties.h>
12
-#include <pipewire/utils.h>
13
-
14
-SPA_DEFINE_AUTOPTR_CLEANUP(pw_properties, struct pw_properties, {
15
-   spa_clear_ptr(*thing, pw_properties_free);
16
-})
17
-
18
-SPA_DEFINE_AUTO_CLEANUP(pw_strv, char **, {
19
-   spa_clear_ptr(*thing, pw_free_strv);
20
-})
21
-
22
-#endif /* PIPEWIRE_CLEANUP_H */
23
pipewire-1.2.0.tar.gz/.gitattributes Added
3
 
1
@@ -0,0 +1,1 @@
2
+test/data/*.txt        diff
3
pipewire-1.0.1.tar.bz2/.gitlab-ci.yml -> pipewire-1.2.0.tar.gz/.gitlab-ci.yml Changed
233
 
1
@@ -25,8 +25,8 @@
2
 .fedora:
3
   variables:
4
     # Update this tag when you want to trigger a rebuild
5
-    FDO_DISTRIBUTION_TAG: '2023-11-22.0'
6
-    FDO_DISTRIBUTION_VERSION: '39'
7
+    FDO_DISTRIBUTION_TAG: '2024-05-30.0'
8
+    FDO_DISTRIBUTION_VERSION: '40'
9
     FDO_DISTRIBUTION_PACKAGES: >-
10
       alsa-lib-devel
11
       avahi-devel
12
@@ -90,12 +90,13 @@
13
 .ubuntu:
14
   variables:
15
     # Update this tag when you want to trigger a rebuild
16
-    FDO_DISTRIBUTION_TAG: '2023-04-18.0'
17
+    FDO_DISTRIBUTION_TAG: '2024-01-12.0'
18
     FDO_DISTRIBUTION_VERSION: '22.04'
19
     FDO_DISTRIBUTION_PACKAGES: >-
20
       debhelper-compat
21
       findutils
22
       git
23
+      libapparmor-dev
24
       libasound2-dev
25
       libavcodec-dev
26
       libavfilter-dev
27
@@ -107,6 +108,7 @@
28
       libgstreamer-plugins-base1.0-dev
29
       libsbc-dev
30
       libsdl2-dev
31
+      libsnapd-glib-dev
32
       libudev-dev
33
       libva-dev
34
       libv4l-dev
35
@@ -128,8 +130,8 @@
36
 .alpine:
37
   variables:
38
     # Update this tag when you want to trigger a rebuild
39
-    FDO_DISTRIBUTION_TAG: '2023-04-17.0'
40
-    FDO_DISTRIBUTION_VERSION: '3.17'
41
+    FDO_DISTRIBUTION_TAG: '2024-05-30.0'
42
+    FDO_DISTRIBUTION_VERSION: '3.20'
43
     FDO_DISTRIBUTION_PACKAGES: >-
44
       alsa-lib-dev
45
       avahi-dev
46
@@ -173,14 +175,12 @@
47
       tar xf /tmp/cov-analysis-linux64.tgz ;
48
       mv cov-analysis-linux64-* coverity ;
49
       rm /tmp/cov-analysis-linux64.tgz
50
-  only:
51
-    variables:
52
-      - $COVERITY
53
+  rules:
54
+    - if: $COVERITY != null
55
 
56
 .not_coverity:
57
-  except:
58
-    variables:
59
-      - $COVERITY
60
+  rules:
61
+    - if: $COVERITY == null
62
 
63
 .build:
64
   before_script:
65
@@ -247,7 +247,7 @@
66
     - .build
67
   stage: build
68
   variables:
69
-    MESON_OPTIONS: "-Dsession-managers="
70
+    MESON_OPTIONS: "-Dsession-managers= -Dsnap=enabled"
71
 
72
 .build_on_fedora:
73
   extends:
74
@@ -263,6 +263,9 @@
75
   variables:
76
     MESON_OPTIONS: >-
77
         -Ddocs=enabled
78
+        -Dman=enabled
79
+        -Ddoc-prefix-value=/usr
80
+        -Ddoc-sysconfdir-value=/etc
81
         -Dinstalled_tests=enabled
82
         -Dsystemd-system-service=enabled
83
         -Dbluez5-backend-hsphfpd=enabled
84
@@ -274,6 +277,7 @@
85
         -Dsdl2=enabled
86
         -Dsndfile=enabled
87
         -Dsession-managers=
88
+        -Dsnap=disabled
89
   artifacts:
90
     name: pipewire-$CI_COMMIT_SHA
91
     when: always
92
@@ -281,6 +285,49 @@
93
       - build-*/meson-logs
94
       - prefix-*
95
 
96
+build_on_fedora_html_docs:
97
+  extends:
98
+    - .build_on_fedora
99
+  variables:
100
+    MESON_OPTIONS: >-
101
+        -Ddocs=enabled
102
+        -Dman=enabled
103
+        -Ddoc-prefix-value=/usr
104
+        -Ddoc-sysconfdir-value=/etc
105
+        -Dinstalled_tests=enabled
106
+        -Dsystemd-system-service=enabled
107
+        -Dbluez5-backend-hsphfpd=enabled
108
+        -Daudiotestsrc=enabled
109
+        -Dtest=enabled
110
+        -Dvideotestsrc=enabled
111
+        -Dvolume=enabled
112
+        -Dvulkan=enabled
113
+        -Dsdl2=enabled
114
+        -Dsndfile=enabled
115
+        -Dsession-managers=
116
+  before_script:
117
+    - git fetch origin 1.0 master
118
+    - git branch -f 1.0 origin/1.0
119
+    - git branch -f master origin/master
120
+    - git clone -b 1.0 . branch-1.0
121
+    - git clone -b master . branch-master
122
+    - !reference .build, before_script
123
+  script:
124
+    - cd branch-1.0
125
+    - meson setup builddir $MESON_OPTIONS
126
+    - meson compile -C builddir doc/pipewire-docs
127
+    - cd ../branch-master
128
+    - meson setup builddir $MESON_OPTIONS
129
+    - meson compile -C builddir doc/pipewire-docs
130
+  artifacts:
131
+    name: pipewire-$CI_COMMIT_SHA
132
+    when: always
133
+    paths:
134
+      - branch-*/builddir/meson-logs
135
+      - branch-*/builddir/doc/html
136
+  rules:
137
+    - !reference pages, rules
138
+
139
 build_on_alpine:
140
   extends:
141
     - .alpine
142
@@ -289,7 +336,7 @@
143
     - .build
144
   stage: build
145
   variables:
146
-    MESON_OPTIONS: "-Dsession-managers="
147
+    MESON_OPTIONS: "-Dsession-managers= -Dsnap=disabled"
148
 
149
 # build with all auto() options enabled
150
 build_all:
151
@@ -308,6 +355,7 @@
152
         -Dsession-managers=
153
         -Dc_args='-UFASTPATH'
154
         -Dcpp_args='-UFASTPATH'
155
+        -Dsnap=disabled
156
   parallel:
157
     matrix:
158
       - CC: gcc, clang
159
@@ -317,7 +365,7 @@
160
   extends:
161
     - .build_on_fedora
162
   variables:
163
-    MESON_OPTIONS: "-Dsession-managers="
164
+    MESON_OPTIONS: "-Dsession-managers= -Dsnap=disabled"
165
   parallel:
166
     matrix:
167
       - CC: gcc, clang
168
@@ -353,7 +401,7 @@
169
   extends:
170
     - .build_on_fedora
171
   variables:
172
-    MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers="
173
+    MESON_OPTIONS: "-Dtest=enabled -Dbuildtype=release -Db_ndebug=true -Dsession-managers= -Dsnap=disabled"
174
   parallel:
175
     matrix:
176
       - CC: gcc, clang
177
@@ -367,7 +415,7 @@
178
     - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
179
     - meson install -C "$BUILD_DIR" --no-rebuild
180
   variables:
181
-    MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS"
182
+    MESON_OPTIONS: "-Dsession-managers=$SESSION_MANAGERS -Dsnap=disabled"
183
   parallel:
184
     matrix:
185
       - SESSION_MANAGERS: "", "wireplumber", "media-session", "media-session,wireplumber", "wireplumber,media-session" 
186
@@ -384,7 +432,7 @@
187
     - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
188
     - meson install -C "$BUILD_DIR" --no-rebuild
189
   variables:
190
-    MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session"
191
+    MESON_OPTIONS: "-Dsession-managers=wireplumber,media-session -Dsnap=disabled"
192
   allow_failure: true
193
 
194
 build_meson_exact_release:
195
@@ -402,7 +450,7 @@
196
     - meson compile -C "$BUILD_DIR" $COMPILE_ARGS
197
     - meson install -C "$BUILD_DIR" --no-rebuild
198
   variables:
199
-    MESON_OPTIONS: "-Dsession-managers="
200
+    MESON_OPTIONS: "-Dsession-managers= -Dsnap=disabled"
201
 
202
 valgrind:
203
   extends:
204
@@ -483,7 +531,7 @@
205
   stage: analysis
206
   script:
207
     # Check that each pipewire module has a \subpage entry
208
-    - git grep -h -o -e "\\\page page_module_\w\+" | cut -f2 -d' ' > pipewire_module_pages
209
+    - git grep -h -o -e "\\\page page_module_\w\+" | cut -f2 -d ' ' > pipewire_module_pages
210
     - cat pipewire_module_pages
211
     - |
212
       for page in $(cat pipewire_module_pages); do
213
@@ -507,12 +555,14 @@
214
     - .not_coverity
215
   stage: pages
216
   dependencies:
217
-    - build_on_fedora
218
+    - build_on_fedora_html_docs
219
   script:
220
-    - mkdir public
221
-    - cp -R prefix-*/share/doc/pipewire/html/* public/
222
+    - mkdir public public/devel
223
+    - cp -R branch-1.0/builddir/doc/html/* public/
224
+    - cp -R branch-master/builddir/doc/html/* public/devel/
225
   artifacts:
226
     paths:
227
       - public
228
-  only:
229
-    - master
230
+  rules:
231
+    - if: $CI_COMMIT_BRANCH == 'master'
232
+    - if: $CI_COMMIT_BRANCH == '1.0'
233
pipewire-1.0.1.tar.bz2/.gitlab/ci/check_missing_headers.sh -> pipewire-1.2.0.tar.gz/.gitlab/ci/check_missing_headers.sh Changed
10
 
1
@@ -10,7 +10,7 @@
2
     -f "$PREFIX/include/spa-0.2/$i"  || LIST="$i $LIST"
3
 done
4
 
5
-for i in $(find src/pipewire -name '*.h' -a -not -name '*private.h' -a -not -name 'cleanup.h' | sed s#src/##);
6
+for i in $(find src/pipewire -name '*.h' -a -not -name '*private.h' | sed s#src/##);
7
 do
8
     -f "$PREFIX/include/pipewire-0.3/$i"  || LIST="$i $LIST"
9
 done
10
pipewire-1.0.1.tar.bz2/INSTALL.md -> pipewire-1.2.0.tar.gz/INSTALL.md Changed
17
 
1
@@ -71,12 +71,12 @@
2
 The default config will also start `pipewire-media-session`, a default
3
 example media session and `pipewire-pulse`, a PulseAudio compatible server.
4
 
5
-You can also enable more debugging with the `PIPEWIRE_DEBUG` environment
6
-variable like so:
7
+You can also enable more debugging with the `PIPEWIRE_DEBUG` and
8
+`WIREPLUMBER_DEBUG` environment variables like so:
9
 
10
 ```
11
 cd builddir/
12
-PIPEWIRE_DEBUG="D" make run
13
+PIPEWIRE_DEBUG="D" WIREPLUMBER_DEBUG="D" make run
14
 ```
15
 
16
 You might have to stop the pipewire service/socket that might have been
17
pipewire-1.0.1.tar.bz2/NEWS -> pipewire-1.2.0.tar.gz/NEWS Changed
840
 
1
@@ -1,3 +1,576 @@
2
+# PipeWire 1.2.0 (2024-06-27)
3
+
4
+This is the 1.2 release that is API and ABI compatible with previous
5
+1.1.x and 1.0.x releases.
6
+
7
+This release contains some of the bigger changes that happened since
8
+the 1.0 release last year, including:
9
+
10
+  * Support for asynchronous processing has been implemented. Nodes can choose
11
+    (or be forced) to be scheduled asynchronously. The graph will not wait for
12
+    the output of the node to continue processing but it will use the output
13
+    of the previous cycle (or silence) instead. This adds one cycle of latency
14
+    but it can avoid having some nodes blocking the processing graph. Non realtime
15
+    streams and filters now also use this asynchronous processing instead of
16
+    their own slightly broken version.
17
+  * The concept of node.sync-group was added. This groups nodes with overlapping
18
+    sync-group together when one of them sets the node.sync = true. This is now
19
+    used to make sure all nodes are scheduled together when JACK transport is
20
+    started so that they all see the same transport time.
21
+  * Config parsing errors are reported earlier and much better with line and
22
+    column numbers where the parsing started to fail.
23
+  * Add support for mandatory metadata when negotiating buffer parameters. This
24
+    can be used to only negotiate extra buffer planes when certain metadata is
25
+    negotiated. One use case is the explicit sync support that requires 2
26
+    extra fds for the timelines.
27
+  * Explicit sync metadata and support was added.
28
+  * Support was added for making and using multiple data-loops in the server
29
+    and clients. Support for CPU affinity and priorities was added to the
30
+    data-loops as well.
31
+  * The log topic debug levels can now be changed at runtime with metadata.
32
+    The log levels in the pulse server can be dynamically changed with a
33
+    /core message.
34
+  * The UCM conflicting devices patches were merged.
35
+  * Add snapcast-discover module to stream to snapcast servers.
36
+  * Rework how peers are linked and the counters are updated. Resume the
37
+    peers when a node is unlinked and not yet processed. This should cause
38
+    less occasional dropouts in the graph when reconnecting things.
39
+  * Many GStreamer element updates.
40
+  * Many more fixes and improvements.
41
+
42
+Enjoy the summer vacation!
43
+
44
+
45
+## Highlights (since the previous 1.1.83 release)
46
+  - Small fixes here and there.
47
+
48
+## PipeWire
49
+  - Compilation fixes after enabling -Werror=float-conversion
50
+
51
+## Modules
52
+  - The module-rtp-sap now propagates the cleanup.sec property to the
53
+    rtp-source and the rtp-source now sets a property with the receiving
54
+    status.
55
+  - Fix for ROC 0.3, explicitly specify sender encoding. (#4070)
56
+  - Some fixes to the RAOP sink module, including a format fix for 32 bit
57
+    machines.
58
+
59
+## Tools
60
+  - Fix pw-cli monitoring code.
61
+
62
+## SPA
63
+  - Revert peer_enum_params again because it was not used and flawed.
64
+  - Fix multichannel processing in webrtc AEC.
65
+
66
+## GStreamer
67
+  - Logging improvements.
68
+  - Fix a race in the bufferpool activation.
69
+
70
+## Bluetooth
71
+  - Improvements to BAP broadcast code parsing.
72
+
73
+Older versions:
74
+
75
+
76
+# PipeWire 1.1.83 (2024-06-17)
77
+
78
+This is the third and hopefully the last 1.2 release candidate that is
79
+API and ABI compatible with previous 1.0.x releases.
80
+
81
+Some last minute changes went in to clean up the node activation and
82
+scheduling that justify another pre-release.
83
+
84
+## Highlights
85
+  - Rework how peers are linked and the counters are updated. Resume the
86
+    peers when a node is unlinked and not yet processed. This should cause
87
+    less occasional dropouts in the graph when reconnecting things.
88
+  - Improve xruns in module-ffado.
89
+  - Many GStreamer element updates.
90
+  - More fixes and improvements.
91
+
92
+
93
+## PipeWire
94
+  - Rework how peers are linked and the counters are updated. Resume the
95
+    peers when a node is unlinked and not yet processed. This should cause
96
+    less dropouts in the graph when reconnecting. (#4026)
97
+  - Improve debug of xruns.
98
+  - Evaluate node.rules and device.rules before loading the plugin so that
99
+    extra properties can be passed to the plugin init function.
100
+
101
+## Modules
102
+  - Improve timing reporting in module-ffado some more.
103
+  - Prealloc less memory in the profiler by default.
104
+  - Improve xrun handling in module-ffado.
105
+
106
+## Tools
107
+  - Fix a crash in pw-link when a link fails.
108
+  - Fix pw-dump update for metadata. (#4053)
109
+
110
+## SPA
111
+  - Improve handling of controls. (#4028)
112
+  - Fix the string size in v4l2 to hold the device and vendor id.
113
+  - Support meta_videotransform on buffers in v4l2. This can be used to
114
+    signal that the buffer was rotated for example.
115
+  - Add HDMI/AC3 profile to ALSA when supported.
116
+  - Make it possible to disable the webrtc dependency
117
+
118
+## GStreamer
119
+  - Improve caps handling in the elements.
120
+  - Set buffer duration when we can.
121
+  - Post an element error when all the elements buffers are removed.
122
+    (#1980)
123
+  - Improve DMA_DRM caps selection.
124
+  - Some refactoring work.
125
+  - Improve state handling in the elements.
126
+
127
+## JACK
128
+  - Improve how links are activated.
129
+  - Fix some races when freeing memory.
130
+
131
+## Bluetooth
132
+  - Support multiple BIS in the broadcast source.
133
+
134
+# PipeWire 1.1.82 (2024-05-24)
135
+
136
+This is the second 1.2 release candidate that is API and ABI
137
+compatible with previous 1.0.x releases.
138
+
139
+Not so many things needed to be fixed so this might already be the
140
+last prerelease if everything goes well...
141
+
142
+## Highlights
143
+  - Fix problem when moving nodes that could cause nodes to be scheduled
144
+    wrongly and cause errors. (#4017)
145
+  - Add snapcast-discover module to stream to snapcast servers.
146
+  - Work around wrong kernel provided MTU for USB controllers.
147
+  - Fix some spelling mistakes all over the codebase.
148
+  - More small fixes and improvements.
149
+
150
+
151
+## PipeWire
152
+  - Remove the private cleanup.h header and use the public SPA version.
153
+  - Fix problem when moving nodes that could cause nodes to be scheduled
154
+    wrongly and cause errors. (#4017)
155
+
156
+## Modules
157
+  - Handle IPv6 in module-protocol-simple and support port allocation.
158
+  - Add snapcast-discover module to stream to snapcast servers.
159
+
160
+## Bluetooth
161
+  - Work around wrong kernel provided MTU for USB controllers.
162
+
163
+# PipeWire 1.0.7 (2024-05-24)
164
+
165
+This is a small bugfix release that is API and ABI compatible with previous
166
+1.0.x releases.
167
+
168
+## Highlights
169
+  - Fix a potential race/crash.
170
+  - Fix some problems with negotiation of large integers and floats.
171
+  - Fix JACK sysex MIDI event handling.
172
+  - Some more smaller fixes and improvements.
173
+
174
+## PipeWire
175
+  - Fix a potential race when adding/removing a port to be scheduled.
176
+
177
+## Modules
178
+  - Fix FFADO default device handling. (#4023)
179
+
180
+## SPA
181
+  - Fix in integer overflow and float/double compare in POD.
182
+
183
+## JACK
184
+  - Copy larger MIDI events correctly.
185
+
186
+# PipeWire 1.1.81 (2024-05-16)
187
+
188
+This is the first 1.2 release candidate that is API and ABI
189
+compatible with previous 1.0.x releases.
190
+
191
+In addition to all the changes backported to 1.0.x, this release
192
+also contains some new features:
193
+
194
+## Highlights
195
+  - Support for asynchronous processing has been implemented. Nodes can choose
196
+    (or be forced) to be scheduled asynchronously. The graph will not wait for
197
+    the output of the node to continue processing but it will use the output
198
+    of the previous cycle (or silence) instead. This adds one cycle of latency
199
+    but it can avoid having some nodes blocking the processing graph. Non realtime
200
+    streams and filters now also use this asynchronous processing instead of
201
+    their own slightly broken version.
202
+  - The concept of node.sync-group was added. This groups nodes with overlapping
203
+    sync-group together when one of them sets the node.sync = true. This is now
204
+    used to make sure all nodes are scheduled together when JACK transport is
205
+    started so that they all see the same time.
206
+  - Config parsing errors are reported earlier and much better with line and
207
+    column numbers where the parsing started to fail.
208
+  - Add support for mandatory metadata when negotiation buffer parameters. This
209
+    can be used to only negotiate extra buffer planes when certain metadata is
210
+    negotiated. One use case is the explicit sync support that requires 2
211
+    extra fds for the timelines.
212
+  - Support was added for making and using multiple data-loops in the server
213
+    and clients. Support for CPU affinity and priorities was added to the
214
+    data-loops as well.
215
+  - The log topic debug levels can now be changed at runtime with metadata.
216
+    The log levels in the pulse server can be dynamically changed with a
217
+    /core message.
218
+  - The UCM conflicting devices patches were merged.
219
+
220
+
221
+## PipeWire
222
+  - snap support has been added.
223
+  - Implement async processing. (#3509)
224
+  - Support for explicit sync was added.
225
+  - Config parsing errors are reported earlier and much better.
226
+  - A -P option was added to provide extra properties to the context. This can be
227
+    used to enable some features that use rules.
228
+  - properties.rules was added to enhance properties based on some rules.
229
+    This deprecates the vm.overrides.
230
+  - Support was added for security-context. This makes it possible for a flatpak
231
+    to request a socket with specific properties from pipewire to mount in the
232
+    flatpak. The session manager can then assign permissions based on the connection
233
+    properties.
234
+  - Support for fixed arrays in pw_array was improved.
235
+  - PipeWire server and clients can now use multiple threads to process the nodes
236
+    in parallel.
237
+  - device.rules and node.rules were added to update device and node properties
238
+    based on rules.
239
+  - device.param and node.param can now be used to configure params when devices
240
+    and nodes are created.
241
+  - Memory will now try to use MFD_NOEXEC_SEAL.
242
+  - The driver id of a node is now placed in the properties.
243
+  - A potential race was fixed when adding and removing ports to the scheduling
244
+    lists.
245
+
246
+## Modules
247
+  - Priorities for the FFADO threads can be configured now.
248
+  - The loopback module now has support for up and downmixing.
249
+  - Extra properties can now be configured per native-connection socket.
250
+  - The pulse-tunnel can now automatically reconnect when the connection is
251
+    broken.
252
+  - The RTP module now supports the PTP management protocol.
253
+  - The RTP sender can now use a timer to send out multiple packets per
254
+    quantum.
255
+  - A new module was added for loading Parametric EQ.
256
+  - The simple-protocol module now has per stream configurable properties
257
+    and can also be used to interface with a snapcast server.
258
+  - Support for local services was added to raop, rtp and pulse avahi
259
+    discoverers. Support for IPv6 on local services was added to RAOP.
260
+
261
+## SPA
262
+  - Support for reporting JSON parsing errors has been added.
263
+  - Some extra checks are added when iterating POD structures.
264
+  - Port and profiles can now be hidden from ALSA nodes with
265
+    api.acp.hidden-ports and api.acp.hidden-profiles properties.
266
+  - The UCM conflicting devices patches were merged.
267
+  - Profiles and Routes can now also be set by name.
268
+  - Hires timestamps are now used when possible in IRQ based scheduling to
269
+    get more accurate wakeup times.
270
+  - udev can now be an optional dependency.
271
+  - audioadapter now has an option to automatically configure its ports.
272
+  - Camera rotation was added to the libcamera node.
273
+  - invoke on loops can now be done from multiple threads at the same time.
274
+  - Make sure we use CLOCK_MONOTONIC everywhere in the io_clock.
275
+  - Vulkan bit and convert filters were added.
276
+  - ALSA will now always read the HW ringbuffer pointer when followers are
277
+    not on the same card.
278
+  - Support for larger MIDI sysex messages was improved. Configuration of
279
+    the client input and output pool was added. (#4005)
280
+
281
+## Bluetooth
282
+  - Support Google OPUS codec.
283
+  - Support the LC3-SWB codec.
284
+  - Support the AAC-ELD codec.
285
+  - Broadcast source configuration support was added.
286
+
287
+## pulseaudio-server
288
+  - The GSettings schemas are now optionally installed.
289
+  - Extensions were moved to the modules.
290
+  - The log level of the pulse server can dynamically be changed with
291
+    a core object message.
292
+  - snap access control was added to pulse-server.
293
+  - The old pacmd describe-module functionality is now implemented with
294
+    a core message pipewire-pulse:describe-module.
295
+  - An option was added to disable module loading and unloading.
296
+
297
+## JACK
298
+  - OSC messages can now also be placed in JACK MIDI and the translation
299
+    layer will detect and tag the right PipeWire control message types.
300
+  - A jack.other-connect-mode was added to limit the connections that an
301
+    app can do to ports it doesn't own.
302
+  - The way the transport is started and how the nodes are grouped together
303
+    in the transport was improved using the new sync groups. (#3850)
304
+  - Fix large MIDI messages handling. (#4005)
305
+
306
+## ALSA
307
+  - Fix format renegotiation. (#3858)
308
+  - Handle period events better. (#3676)
309
+  - Improve handling of the eventfd wakeups.
310
+
311
+## GStreamer
312
+  - The GStreamer elements can now negotiate and use DMABUF.
313
+
314
+## Tools
315
+  - The T flag is used in pw-top when the transport is running.
316
+  - A new pw-container tool was added to start a new security context and
317
+    run an application in it.
318
+  - pw-dot handles properties with quotes better. Nodes are grouped with the
319
+    node.link-group.
320
+  - pw-link has a --wait option to wait for all links to be created.
321
+
322
+# PipeWire 1.0.6 (2024-05-09)
323
+
324
+This is a bugfix release that is API and ABI compatible with previous
325
+1.0.x releases.
326
+
327
+## Highlights
328
+  - A bitfield race was fixed that could cause some crashes or undefined
329
+    behaviour when moving nodes between drivers.
330
+  - Fix to some invalid memory access in the pw-mon and pw-dump.
331
+  - A regression in kodi with IEC958 formats playback was fixed.
332
+  - A race in the ALSA plugin was fixed when updating the eventfd.
333
+  - Improvements and fixes to module-combine-stream.
334
+  - Negotiation was improved in pipewiresrc.
335
+  - Some more small fixes and improvements.
336
+
337
+## PipeWire
338
+  - Context properties are now set early so that client properties can be
339
+    matched with rules.
340
+  - A bitfield race was fixed that could cause some crashes or undefined
341
+    behaviour when moving nodes between drivers.
342
+
343
+## Tools
344
+  - Fix failure to hide properties in pw-mon. (#3997)
345
+  - Fix some memleaks and a crash in pw-dump. (#4001)
346
+
347
+## Modules
348
+  - The combine-stream module now prevents resampling to avoid broken
349
+    audio because of different samplerates.
350
+  - Fix a potential double free in module-loopback when calculating the
351
+    delay. (#3748)
352
+  - The FFADO module now only starts when ports are negotiated to avoid
353
+    startup races. (#3968)
354
+  - The combine-stream module will now forward tags.
355
+
356
+## SPA
357
+  - Monitor volumes are now also clamped to the min/max volumes. (#3962)
358
+  - V4l2 and libcamera now encodes the device ids into a JSON array. This
359
+    is part of the deduplication code of devices.
360
+  - A regression in kodi with IEC958 formats playback was fixed.
361
+
362
+## Bluetooth
363
+  - Improved buffer handling and queued data when stopping.
364
+
365
+## ALSA
366
+  - A race was fixed when updating the eventfd. (#3711)
367
+
368
+## GStreamer
369
+  - Handle some errors better instead of crashing. (#3994)
370
+  - Fix a memleak in the stream params handling.
371
+  - Negotiation was improved in pipewiresrc.
372
+
373
+# PipeWire 1.0.5 (2024-04-15)
374
+
375
+This is a bugfix release that is API and ABI compatible with previous
376
+1.0.x releases.
377
+
378
+## Highlights
379
+  - pw_stream can now report timestamps on buffers and the expected
380
+    amount of samples for the resampler.
381
+  - The GStreamer element now has more correct timestamps using the new
382
+    pw_stream timestamps as a fallback.
383
+  - The FFADO module now handles suspend and resume better.
384
+  - A regression in v4l2 was fixed when parsing malformed filters.
385
+  - A potential memory/fd leak was fixed in client-node.
386
+  - Many more small bugfixes and improvements.
387
+
388
+
389
+## PipeWire
390
+  - pw_stream now reports the expected resampler input or output size in
391
+    the pw_time structure. (#3750)
392
+  - pw_stream now also adds a time field to the buffer, which contains the
393
+    time of the graph when the buffer was received in the stream.
394
+  - Fix a compiler error when compiling with -Werror=shadow. (#3915)
395
+  - The config parser will warn when invalid config is detected.
396
+
397
+## Modules
398
+  - The FFADO module now opens and closes when suspending. This fixes some
399
+    problems when FFADO properties are changed while suspended. (#3558)
400
+  - Filter-chain will now warn when invalid config is detected.
401
+  - Echo-cancel will now handle manage the state of the echo-cancel plugin
402
+    better, making sure run() is not called after deactivate().
403
+  - Fix some potential memory/fd leaks in client-node.
404
+
405
+## SPA
406
+  - Improve reading the bound ALSA controls.
407
+  - The resampler can now also report the number of expected output samples.
408
+  - The ALSA ACP device objects have some more properties like the card.id
409
+    and alsa.components. (#3912)
410
+  - Fix a potential string corruption when parsing JSON strings.
411
+  - V4l2 now sets the latency on the port. (#3910)
412
+  - alsa-udev now has an option to expose the device even if busy. (#3914)
413
+  - Improve null-audio-sink channel handling. (#3931)
414
+  - v4l2 will now drop the first frame because it often contains wrong
415
+    timestamps or garbage. (#3910)
416
+  - A regression in v4l2 was fixed where invalid/empty properties in the
417
+    filter would make it error early. (#3959)
418
+
419
+## GStreamer
420
+  - The source now falls back to the new pw_buffer time for the timestamps.
421
+
422
+## Docs
423
+  - Sync with the master branch.
424
+
425
+# PipeWire 1.0.4 (2024-03-13)
426
+
427
+This is a bugfix release that is API and ABI compatible with previous
428
+1.0.x releases.
429
+
430
+## Highlights
431
+
432
+  - Track memfd better to avoid inconsistent memory. Also make sure the
433
+    mixer info is removed correctly in all cases on destroyed ports.
434
+  - Correctly handle removed objects in the metadata.
435
+  - Add an option to set the server and client priorities instead of using
436
+    a hardcoded value of 88.
437
+  - The FFADO module has been fixed. Audio and MIDI now works with
438
+    the same latency as the JACK driver. This has now also been
439
+    tested with a Focusrite Saffire Pro 14.
440
+  - The JACK library has seen some important fixes. Some ardour crackling
441
+    has been fixed when looping and multiple MIDI ports on a client should
442
+    now work.
443
+  - Small bugfixes and improvements.
444
+
445
+
446
+## PipeWire
447
+  - Track memfd better to avoid inconsistent memory. Also make sure the
448
+    mixer info is removed correctly in all cases on destroyed ports.
449
+  - Fix Props param emission again in pw_stream. (#3833)
450
+  - Add MAPPABLE flag to buffer data to indicate that the fd can be
451
+    mmapped directly. Use this on DMABUF from v4l2. (#3840)
452
+  - Correctly handle removed object in the metadata.
453
+  - FreeBSD build and compatibility fixes.
454
+  - Add an option to set the server and client priorities instead of using
455
+    a hardcoded value of 88.
456
+  - Read config overrides in the right order.
457
+  - Fix PIPEWIRE_QUANTUM rate handling in pw_stream and pw_filter.
458
+  - Fix pw_context_parse_conf_section(), actually use the conf argument.
459
+  - A new pw_stream_get_nsec() and pw_filter_get_nsec() function was added
460
+    to get the current time of the stream/filter without having to assume a
461
+    particular clock.
462
+  - A new default.clock.quantum-floor property was added to configure the
463
+    absolute lowest buffer-size. (#3908)
464
+
465
+## docs
466
+  - Many doc updates.
467
+
468
+## tools
469
+  - Make sure we always quit pw-cli when the server stops. (#3837)
470
+  - pw-top now prints all drivers in batch mode. (#3899)
471
+
472
+## modules
473
+  - Don't destroy the client in protocol-simple on EAGAIN.
474
+  - Handle IPv6 better in the RTP modules. Fix IPv6 SAP header
475
+    parsing. (#3851)
476
+  - The FFADO module has been fixed. Audio and MIDI now works with
477
+    the same latency as the JACK driver. This has now also been
478
+    tested with a Focusrite Saffire Pro 14. (#3558)
479
+
480
+## pulse-server
481
+  - Make sure the peer_name is filled to avoid protocol errors.
482
+
483
+## SPA
484
+  - Small resampler tweaks to improve stability of adaptive resampler.
485
+  - Add ALSA option to control htimestamp autodisable.
486
+  - Avoid some potential crashes in audioconvert when ports are removed.
487
+  - Improve HDMI jack detection on some SOCs.
488
+  - The audioconvert now has a monitor.passthrough option to pass the
489
+    latency information on the monitor ports. (#3888)
490
+
491
+## GStreamer
492
+  - Don't use timeouts when autoconnect=false in pipewiresrc. (#3884)
493
+  - pipewiresrc and pipewiresink can now be automatically selected as
494
+    audio source and sink.
495
+  - An invalid memory access was fixed when destroying the device
496
+    provider.
497
+
498
+## JACK
499
+  - Remove properties correctly with the object id, not serial.
500
+  - Improve sync with the data thread by pausing the core. Also improve
501
+    handling of port io to avoid invalid buffer access.
502
+  - Fix PIPEWIRE_QUANTUM rate handling.
503
+  - Support multiple MIDI input ports per client. (#3901)
504
+  - The output buffer size is now always correctly set. (#3892)
505
+
506
+## ALSA
507
+  - Handle errors from eventfd_create correctly.
508
+
509
+# PipeWire 1.0.3 (2024-02-02)
510
+
511
+This is a quick bugfix release that is API and ABI compatible with previous
512
+1.0.x releases.
513
+
514
+## Highlights
515
+  - Fix ALSA version check. This should allow the alsa plugin to work again.
516
+  - Some small fixes and improvements.
517
+
518
+## PipeWire
519
+  - Escape @DEFAULT_SINK@ in the conf files.
520
+
521
+## Modules
522
+  - Improve logging in module-pipe-tunnel.
523
+
524
+## SPA
525
+  - Always recheck rate matching in ALSA when moving drivers. This fixes a
526
+    potential issue where the adaptive resampler would not be activated in
527
+    some cases.
528
+
529
+## ALSA
530
+  - Fix version check. This should allow the alsa plugin to work again
531
+    with version 1.0.2.
532
+
533
+# PipeWire 1.0.2 (2024-01-31)
534
+
535
+This is a bugfix release that is API and ABI compatible with previous
536
+1.0.x releases.
537
+
538
+## Highlights
539
+  - Fix v4l2 enumeration with filter. This should fix negotiation in some
540
+    GStreamer pipelines with capsfilter. Also probe for EXPBUF support
541
+    before using it.
542
+  - Fix max-latency property and Buffer param when dealing with small
543
+    ALSA device buffers. This should fix stuttering with some AMD
544
+    based soundcards.
545
+  - More small cleanups an improvements.
546
+
547
+## Modules
548
+  - Improve netjack2 channel positions.
549
+  - Improve RAOP module state after suspend/resume. (#3778)
550
+  - Avoid crash in some LV2 plugins by configuring the Atom ports. (#3815)
551
+
552
+## SPA
553
+  - Bump libcamera requirements to 0.2.0.
554
+  - Try to avoid unaligned load exceptions. (#3790)
555
+  - Fix v4l2 enumeration with filter. (#1793)
556
+  - Fix max-latency property and Buffer param when dealing with small
557
+    ALSA device buffers. This should fix stuttering with some AMD
558
+    based soundcards. (#3744,#3622)
559
+  - Add a resync.ms option to node.driver to make it possible to resync
560
+    fast to clock jumps.
561
+  - Probe for EXPBUF support in v4l2 before using it. (#3821)
562
+
563
+## pulse-server
564
+  - Also emit change events when the port list change.
565
+
566
+## Bluetooth
567
+  - Log a more verbose explanation when other soundservers seem to be
568
+    interfering with bluetooth.
569
+  - Add quirks for Rockbox Brick. (#3786)
570
+  - Add quirks for SoundCore mini2. (#2927)
571
+
572
+## JACK
573
+  - Improve check for the running state of clients. (#3794)
574
+
575
 # PipeWire 1.0.1 (2024-01-11)
576
 
577
 This is a bugfix release that is API and ABI compatible with previous
578
@@ -49,9 +622,6 @@
579
   - Improve error handling while connecting.
580
   - Fix dts_offset.
581
 
582
-Older versions:
583
-
584
-
585
 # PipeWire 1.0.0 (2023-11-26)
586
 
587
 The PipeWire project is immensely proud to announce the 1.0 release
588
@@ -78,7 +648,7 @@
589
 
590
  "From the beginning of the libcamera project, we have always seen
591
  PipeWire as the solution to handle desktop and mobile integration and
592
- give a seemless multimedia integration to users while providing security
593
+ give a seamless multimedia integration to users while providing security
594
  features and resource sharing between applications."
595
     - Kieran Bingham, libcamera author
596
 
597
@@ -390,7 +960,7 @@
598
     nodes adapt to the new quantum. This is important for Pro-Audio nodes
599
     that need to reconfigure the hardware to a new period.
600
   - The stream now has an EARLY_PROCESS option that can be used to implement
601
-    custum buffer fill levels. (#3480)
602
+    custom buffer fill levels. (#3480)
603
   - Fix a regression in regex parsing. (#3528)
604
   - Fix a bug in position reporting in the driver node. (#3189) (#3544)
605
   - Destroying a link will now recalculate the graph correctly.
606
@@ -500,7 +1070,7 @@
607
   - Some fixes for LE audio support (#3479)
608
 
609
 ## JACK
610
-  - Also emit unregister notify even when supressed when creating the
611
+  - Also emit unregister notify even when suppressed when creating the
612
     client.
613
   - The notify callbacks now match JACK2 behaviour more.
614
   - The mixer io areas are updated and handled safely now to avoid
615
@@ -620,7 +1190,7 @@
616
   - Fix a bug in ALSA source where the available number of samples was miscaluclated
617
     and resulted in xruns in some cases.
618
   - A new L permission was added to make it possible to force a link between
619
-    nodes even when the nodes can't see eachother.
620
+    nodes even when the nodes can't see each other.
621
   - The VBAN module now supports midi send and receive as well.
622
   - Many cleanups and small fixes.
623
 
624
@@ -629,7 +1199,7 @@
625
   - Global objects now only show permissions that apply to them. The permissions
626
     required to perform various API calls are documented.
627
   - A new L permission was added to make it possible to force a link between
628
-    nodes even when the nodes can't see eachother.
629
+    nodes even when the nodes can't see each other.
630
   - Config files need to end with .conf.
631
   - The client.api is added the to global properties of a node.
632
 
633
@@ -718,7 +1288,7 @@
634
 
635
 ## PipeWire
636
   - Permissions for links between nodes are now enforced. The link will now check
637
-    that the owner clients of the nodes can see eachother before allowing the link.
638
+    that the owner clients of the nodes can see each other before allowing the link.
639
     This avoids screensharing clients to accidentally being linked to the camera
640
     nodes by the session manager. A side effect is that patchbay tools will no longer
641
     be able to link portal managed screencast nodes to the camera, for this we need
642
@@ -971,7 +1541,7 @@
643
     graph as a synchronous JACK client with no added latency.
644
   - Many performance improvements. Activation of remote nodes is more
645
     efficient, fewer eventfds are required on the clients, less callback
646
-    overhead in performence critical paths and an optimized poll function
647
+    overhead in performance critical paths and an optimized poll function
648
     was added. This was mainly driven by the jackdbus module to get the lowest
649
     possible overhead when running the graph.
650
   - The JACK notify callback implementation was reworked to emulate better what
651
@@ -996,7 +1566,7 @@
652
   - A potential out-of-buffers case was fixed in capture pw-streams where buffers
653
     were not moved to the recycle queue when the node suspended.
654
   - Nodes are now always woken up with the eventfd. Previously there were
655
-    some optimiztions in the server to directly call into the node process
656
+    some optimizations in the server to directly call into the node process
657
     function but that optimization is not necessary. Without this optimization
658
     it is now possible to run nodes in different threads.
659
   - pw-stream trigger is now implemented correctly in all cases.
660
@@ -1083,7 +1653,7 @@
661
   - Improve some error reporting.
662
   - The JACK headers were updated to a newer version.
663
   - JACK callbacks are now managed with an event queue to simulate
664
-    more what JACK does. This avoids emiting callbacks when a method is blocking
665
+    more what JACK does. This avoids emitting callbacks when a method is blocking
666
     for a reply and causing deadlocks. (#3183)
667
   - Assign unique names to JACK clients. (#2833)
668
   - Fix a potential crash when the thread_utils was used after free.
669
@@ -1149,7 +1719,7 @@
670
 ## SPA
671
   - One ALSA commit was not correctly reverted and might cause crashes.
672
   - The ALSA sink and source now calculate the ALSA ringbuffer memory
673
-    location more correctly wich might improve compatibility with some
674
+    location more correctly which might improve compatibility with some
675
     hardware.
676
   - v4l2 now sets the values of the controls in the Props param.
677
 
678
@@ -1179,7 +1749,7 @@
679
     again. (#3153)
680
   - ALSA rate matching for sources was fixed. It would previously wait too
681
     long for rate matching and then cause drift. This should reduce
682
-    crackling and stuttering whan capturing in low latency.
683
+    crackling and stuttering when capturing in low latency.
684
   - Fix the GStreamer clock to make cheese video recording work again. (#3149)
685
   - More fixes and improvements.
686
 
687
@@ -1205,7 +1775,7 @@
688
   - More ALSA IRQ based scheduling improvements.
689
   - ALSA rate matching for sources was fixed. It would previously wait too
690
     long for rate matching and then cause drift. This should reduce
691
-    crackling and stuttering whan capturing in low latency.
692
+    crackling and stuttering when capturing in low latency.
693
   - The echo-cancel plugin API has a new method to make it possible to have
694
     different channels for capture, source and playback.
695
   - Reverted the UCM changes, they seem to cause regressions causing audio
696
@@ -1463,7 +2033,7 @@
697
     example is the X11-bell module that can now be disabled with a custom
698
     property override.
699
   - Filter-chain has a new mysofa based spacializer plugin.
700
-  - Suport was added for different clocks that allow the RTP modules to work
701
+  - Support was added for different clocks that allow the RTP modules to work
702
     with a PTP clock, for example.
703
   - Many bugfixes and improvements.
704
 
705
@@ -1472,7 +2042,7 @@
706
   - Avoid rate switches when the graph is idle.
707
   - The rate selection algorithm was improved. This ensures minimal performance
708
     and quality loss when resampling.
709
-  - The default min.quantum was set to 32 again after it got erronously changed
710
+  - The default min.quantum was set to 32 again after it got erroneously changed
711
     to (the too low) 16 in version 0.3.45.
712
   - Fix compilation issues with rust bindings because of macros in defines.
713
     Work around it for now. (#2952)
714
@@ -1637,7 +2207,7 @@
715
     This can be set per application using the rules.
716
 
717
 ## GStreamer
718
-  - pipewiresrc will now advertize DMABUF support if the pipeline suports
719
+  - pipewiresrc will now advertise DMABUF support if the pipeline supports
720
     this.
721
   - pipewiresrc will now always be a live source unless told otherwise.
722
 
723
@@ -1983,7 +2553,7 @@
724
   - Codecs can now share endpoints. This reduces the amount of endpoints and
725
     avoids problems with devices that can't handle a large amount of
726
     codec endpoints.
727
-  - Report batery status to UPower for HFP AG.
728
+  - Report battery status to UPower for HFP AG.
729
   - Fix bitpool increase.
730
 
731
 ## SPA
732
@@ -2204,10 +2774,10 @@
733
   - Support masking of conf.d/ files. (#2629)
734
   - Use org.freedesktop.portal.Realtime when available. This does the
735
     correct PID/TID mappings to make realtime also work from flatpaks.
736
-  - Fix rate adjustement logic in pulse-tunnel. This would cause
737
+  - Fix rate adjustment logic in pulse-tunnel. This would cause
738
     increasing delays and hickups when using tunnels. (#2548)
739
   - Add OPUS as a new vendor codec. Add OPUS-A2DP spec. PipeWire can now
740
-    send and reveive OPUS data over bluetooth.
741
+    send and receive OPUS data over bluetooth.
742
   - An AAC decoder was added so that PipeWire can now also function as
743
     an A2DP AAC receiver.
744
   - Fix some issues where the wrong samplerate was used. (#2614)
745
@@ -2244,13 +2814,13 @@
746
   - Implement PIPEWIRE_NOTIFICATION_FD for notification when the socket
747
     is ready.
748
   - Try to use rtkit if set_nice() fails.
749
-  - Fix rate adjustement logic in pulse-tunnel. This would cause
750
+  - Fix rate adjustment logic in pulse-tunnel. This would cause
751
     increasing delays and hickups when using tunnels. (#2548)
752
   - Handle disconnect in pulse-tunnel.
753
 
754
 ## Bluetooth
755
   - Add OPUS as a new vendor codec. Add OPUS-A2DP spec. PipeWire can now
756
-    send and reveive OPUS data over bluetooth.
757
+    send and receive OPUS data over bluetooth.
758
   - An AAC decoder was added so that PipeWire can now also function as
759
     an A2DP AAC receiver.
760
 
761
@@ -2606,7 +3176,7 @@
762
 
763
 ## pulse-server
764
   - Add initial stream latency property so that devices can be started
765
-    with a resonably accurate latency.
766
+    with a reasonably accurate latency.
767
   - Fix ringbuffer underrun case. (#2366)
768
   - module-native-protocol-tcp now has a auth-anonymous option to give
769
     full access to the clients.
770
@@ -2725,7 +3295,7 @@
771
   - There is a new module-roc-sink-input module, the the PulseAudio equivalent.
772
   - The ROC source and sink-input module now have a much lower latency.
773
   - The ROC module now has an option to select FEC mode.
774
-  - Playback and record rate adjustements should work now. (#1159)
775
+  - Playback and record rate adjustments should work now. (#1159)
776
 
777
 ## JACK
778
   - Remove some useless pthread attributes. This makes JACK work in QEMU with
779
@@ -2799,13 +3369,13 @@
780
 ## SPA
781
   - The channelmixer now has PSD upmixing enabled again. We used the
782
     simple upmixing in the previous release but that just sounds too
783
-    aweful to be a good default. (#861) and (#2219)
784
+    awful to be a good default. (#861) and (#2219)
785
   - The channelmixer will not upmix FC and LFE anymore when upmixing is
786
     explicitly disabled. (#2266)
787
   - The channelmixer will only lowpass filter FC and LFE channels when
788
     they were upmixed. (#2280)
789
   - The defaults of the channelmixer were tweaked a little. There is now
790
-    a little bit more bass in the LFE channel and more high fequencies
791
+    a little bit more bass in the LFE channel and more high frequencies
792
     in the FC channel when upmixing. Also the channel widening has been
793
     disabled by default.
794
   - Locale independent float parsing now uses a more portable solution
795
@@ -2944,7 +3514,7 @@
796
   - The channelmix parameters can be changed at runtime now.
797
   - Many improvements to the upmixing algorithms. Rear channels are now
798
     constructed from the ambient sound and can have delay and phase shift
799
-    applied to them to improve spacialization. The stereo channels can
800
+    applied to them to improve specialization. The stereo channels can
801
     be filtered so that the dialog is more concentrated in the center
802
     channel. (#861)
803
 
804
@@ -3063,7 +3633,7 @@
805
     more quickly and accurately.
806
 
807
 ## JACK
808
-  - Add an option to name the defauld device as `system` to improve
809
+  - Add an option to name the default device as `system` to improve
810
     compatibility with some applications,
811
   - Use lock-quantum by default. This makes sure that all dynamic quantum
812
     changes are disabled while a JACK app is running. The only way to force
813
@@ -3111,7 +3681,7 @@
814
   - TI2902 chips as found in various Behringer cards should have inputs
815
     again.
816
   - Better handling of busy devices in udev, retry when the inotify close
817
-    event is emited.
818
+    event is emitted.
819
 
820
 ## SPA
821
   - plugins now handle alignment properly and only expect the max alignment
822
@@ -3174,7 +3744,7 @@
823
   - Fix a potential file descriptor leak in the connection.
824
   - A new minimal.conf file was added to demonstrate a static setup
825
     of a daemon that doesn't require a session manager and is able to
826
-    run JACK applicaions.
827
+    run JACK applications.
828
   - Nice levels are now only changed on the servers, not the clients.
829
   - Add an option to suspend nodes when idle.
830
   - Make it possible to avoid quantum and rate changes with
831
@@ -3409,7 +3979,7 @@
832
     that newer clients can now bind to older servers, which is
833
     a typical case for a flatpak.
834
   - A bug in the latency calculations was fixed where it would in
835
-    some cases report the wrong minumum latency.
836
+    some cases report the wrong minimum latency.
837
 
838
 ## modules
839
   - module-echo-cancel has voice-detection enabled now.
840
pipewire-1.0.1.tar.bz2/doc/Doxyfile.in -> pipewire-1.2.0.tar.gz/doc/Doxyfile.in Changed
24
 
1
@@ -14,17 +14,19 @@
2
 GENERATE_TODOLIST      = NO
3
 GENERATE_TESTLIST      = NO
4
 GENERATE_BUGLIST       = NO
5
+GENERATE_DEPRECATEDLIST= NO
6
 QUIET                  = YES
7
 WARN_NO_PARAMDOC       = YES
8
 HAVE_DOT               = @HAVE_DOT@
9
 INPUT                  = @inputs@
10
-FILTER_PATTERNS        = "*.c=@c_input_filter@" "*.h=@h_input_filter@"
11
+FILTER_PATTERNS        = "*.c=@c_input_filter@" "*.h=@h_input_filter@" "*.md=@md_input_filter@"
12
 FILE_PATTERNS          = "*.h" "*.c"
13
 RECURSIVE              = YES
14
 EXAMPLE_PATH           = "@top_srcdir@/src/examples" \
15
                          "@top_srcdir@/spa/examples" \
16
-                         "@top_srcdir@/doc"
17
-EXAMPLE_PATTERNS       = "*.c"
18
+                         "@top_srcdir@/doc/examples" \
19
+                         "@top_srcdir@/doc/dox"
20
+EXAMPLE_PATTERNS       = "*.c" "*.inc"
21
 
22
 GENERATE_MAN           = YES
23
 MAN_EXTENSION          = 3
24
pipewire-1.0.1.tar.bz2/doc/DoxygenLayout.xml -> pipewire-1.2.0.tar.gz/doc/DoxygenLayout.xml Changed
124
 
1
@@ -1,8 +1,15 @@
2
+<?xml version="1.0" encoding="UTF-8"?>
3
 <doxygenlayout version="1.0">
4
+  <!-- Generated by doxygen 1.10.0 -->
5
+  <!-- Navigation index tabs for HTML output -->
6
   <navindex>
7
     <tab type="mainpage" visible="yes" title=""/>
8
     <tab type="pages" visible="yes" title="Pages" intro=""/>
9
-    <tab type="modules" visible="yes" title="API Reference" intro="" />
10
+    <tab type="topics" visible="yes" title="API Reference" intro=""/>
11
+    <tab type="modules" visible="yes" title="API Reference" intro="">
12
+      <tab type="modulelist" visible="yes" title="" intro=""/>
13
+      <tab type="modulemembers" visible="yes" title="" intro=""/>
14
+    </tab>
15
     <tab type="namespaces" visible="no" title="">
16
       <tab type="namespacelist" visible="yes" title="" intro=""/>
17
       <tab type="namespacemembers" visible="yes" title="" intro=""/>
18
@@ -16,7 +23,7 @@
19
     </tab>
20
     <tab type="classes" visible="yes" title="">
21
       <tab type="classlist" visible="yes" title="" intro=""/>
22
-      <tab type="classindex" visible="yes" title=""/>
23
+      <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
24
       <tab type="hierarchy" visible="yes" title="" intro=""/>
25
       <tab type="classmembers" visible="no" title="" intro=""/>
26
     </tab>
27
@@ -40,8 +47,8 @@
28
   <class>
29
     <briefdescription visible="yes"/>
30
     <includes visible="$SHOW_HEADERFILE"/>
31
-    <inheritancegraph visible="$CLASS_GRAPH"/>
32
-    <collaborationgraph visible="$COLLABORATION_GRAPH"/>
33
+    <inheritancegraph visible="no"/>
34
+    <collaborationgraph visible="no"/>
35
     <memberdecl>
36
       <nestedclasses visible="yes" title=""/>
37
       <publictypes title=""/>
38
@@ -112,6 +119,7 @@
39
       <enums title=""/>
40
       <functions title=""/>
41
       <variables title=""/>
42
+      <properties title=""/>
43
       <membergroups visible="yes"/>
44
     </memberdecl>
45
     <detaileddescription title=""/>
46
@@ -123,6 +131,7 @@
47
       <enums title=""/>
48
       <functions title=""/>
49
       <variables title=""/>
50
+      <properties title=""/>
51
     </memberdef>
52
     <authorsection visible="yes"/>
53
   </namespace>
54
@@ -140,8 +149,8 @@
55
   <file>
56
     <briefdescription visible="yes"/>
57
     <includes visible="$SHOW_INCLUDE_FILES"/>
58
-    <includegraph visible="$INCLUDE_GRAPH"/>
59
-    <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
60
+    <includegraph visible="no"/>
61
+    <includedbygraph visible="no"/>
62
     <sourcelink visible="yes"/>
63
     <memberdecl>
64
       <interfaces visible="yes" title=""/>
65
@@ -158,6 +167,7 @@
66
       <enums title=""/>
67
       <functions title=""/>
68
       <variables title=""/>
69
+      <properties title=""/>
70
       <membergroups visible="yes"/>
71
     </memberdecl>
72
     <detaileddescription title=""/>
73
@@ -170,6 +180,7 @@
74
       <enums title=""/>
75
       <functions title=""/>
76
       <variables title=""/>
77
+      <properties title=""/>
78
     </memberdef>
79
     <authorsection/>
80
   </file>
81
@@ -177,9 +188,10 @@
82
   <!-- Layout definition for a group page -->
83
   <group>
84
     <briefdescription visible="yes"/>
85
-    <groupgraph visible="$GROUP_GRAPHS"/>
86
+    <groupgraph visible="no"/>
87
     <memberdecl>
88
       <nestedgroups visible="yes" title=""/>
89
+      <modules visible="yes" title=""/>
90
       <dirs visible="yes" title=""/>
91
       <files visible="yes" title=""/>
92
       <namespaces visible="yes" title=""/>
93
@@ -225,10 +237,29 @@
94
     <authorsection visible="yes"/>
95
   </group>
96
 
97
+  <!-- Layout definition for a C++20 module page -->
98
+  <module>
99
+    <briefdescription visible="yes"/>
100
+    <exportedmodules visible="yes"/>
101
+    <memberdecl>
102
+      <concepts visible="yes" title=""/>
103
+      <classes visible="yes" title=""/>
104
+      <enums title=""/>
105
+      <typedefs title=""/>
106
+      <functions title=""/>
107
+      <variables title=""/>
108
+      <membergroups title=""/>
109
+    </memberdecl>
110
+    <detaileddescription title=""/>
111
+    <memberdecl>
112
+      <files visible="yes"/>
113
+    </memberdecl>
114
+  </module>
115
+
116
   <!-- Layout definition for a directory page -->
117
   <directory>
118
     <briefdescription visible="yes"/>
119
-    <directorygraph visible="yes"/>
120
+    <directorygraph visible="no"/>
121
     <memberdecl>
122
       <dirs visible="yes"/>
123
       <files visible="yes"/>
124
pipewire-1.0.1.tar.bz2/doc/custom.css -> pipewire-1.2.0.tar.gz/doc/custom.css Changed
39
 
1
@@ -25,16 +25,36 @@
2
 
3
 .textblock h1 {
4
     font-size: 150%;
5
+    border-bottom: 1px solid var(--page-foreground-color);
6
+    margin-top: 1.5em;
7
 }
8
 .textblock h2 {
9
-    font-size: 100%;
10
+    font-size: 120%;
11
+    margin-top: 1.5em;
12
 }
13
 .textblock h3, .textblock h4, .textblock h5, .textblock h6 {
14
     font-size: 100%;
15
     font-style: italic;
16
     font-size: medium;
17
+    margin-top: 1.5em;
18
 }
19
 
20
 .textblock dl.section dd {
21
     margin-left: 2rem;
22
 }
23
+
24
+ul.multicol li {
25
+    word-break: break-word;
26
+    padding-left: 3em;
27
+    text-indent: -3em;
28
+}
29
+
30
+ul.multicol li a.el {
31
+    font-weight: normal;
32
+}
33
+
34
+div.contents div.toc li {
35
+    word-break: break-word;
36
+    padding-left: 2em;
37
+    text-indent: -2em;
38
+}
39
pipewire-1.0.1.tar.bz2/doc/dox/api/spa-design.dox -> pipewire-1.2.0.tar.gz/doc/dox/api/spa-design.dox Changed
10
 
1
@@ -10,7 +10,7 @@
2
 - Integer types: These are enumerations used in the parts where high
3
                  performance/ease of use/low space overhead is needed.
4
 
5
-The SPA type is system is statis and very simple but still allows you
6
+The SPA type is system is static and very simple but still allows you
7
 to make and introspect complex object type hierarchies.
8
 
9
 See the type system docs for more info.
10
pipewire-1.0.1.tar.bz2/doc/dox/api/spa-pod.dox -> pipewire-1.2.0.tar.gz/doc/dox/api/spa-pod.dox Changed
13
 
1
@@ -1018,9 +1018,9 @@
2
     - Step (2)   : child1 is a default value, options are between
3
                    child2 and child3, in steps of child4 in the value array.
4
     - Enum (3)   : child1 is a default value, options are any value from
5
-                   the value array, prefered values come first.
6
+                   the value array, preferred values come first.
7
     - Flags (4)  : child1 is a default value, options are any value from
8
-                   the value array, prefered values come first.
9
+                   the value array, preferred values come first.
10
 - flags: must be 0
11
 
12
 ## Pod (20)
13
pipewire-1.2.0.tar.gz/doc/dox/config Added
2
 
1
+(directory)
2
pipewire-1.2.0.tar.gz/doc/dox/config/index.md Added
91
 
1
@@ -0,0 +1,89 @@
2
+\page page_config Configuration
3
+
4
+One of the design goals of PipeWire is to be able to closely control
5
+and configure all aspects of the processing graph.
6
+
7
+A fully configured PipeWire setup runs various pieces, each with their
8
+configuration options and files:
9
+
10
+- **pipewire**: The PipeWire main daemon that runs and coordinates the processing.
11
+
12
+- **pipewire-pulse**: The PipeWire PulseAudio replacement server. It also configures
13
+  the properties of the PulseAudio clients connecting to it.
14
+
15
+- **wireplumber**: Most configuration of devices is performed by the session manager.
16
+  It typically loads ALSA and other devices and configures the profiles, port volumes and more.
17
+  The session manager also configures new clients and links them to the targets, as configured
18
+  in the session manager policy.
19
+
20
+- **PipeWire clients**: Each native PipeWire client also loads a configuration file.
21
+  Emulated JACK client also have separate configuration.
22
+
23
+# Configuration Settings
24
+
25
+Configuration of daemons:
26
+
27
+- \ref page_man_pipewire_conf_5 "PipeWire daemon configuration reference"
28
+- \ref page_man_pipewire-pulse_conf_5 "PipeWire Pulseaudio daemon configuration reference"
29
+- WirePlumber daemon configuration(https://pipewire.pages.freedesktop.org/wireplumber/)
30
+- Wiki page on PipeWire daemon configuration(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire)
31
+- Wiki page on PipeWire PulseAudio daemon configuration(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PulseAudio)
32
+
33
+Configuration of devices:
34
+
35
+- WirePlumber configuration(https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration.html)
36
+- \ref page_man_pipewire-devices_7 "Device and node property reference"
37
+- Wiki page on device runtime settings(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-Devices)
38
+
39
+Configuration for client applications, either connecting via the
40
+native PipeWire interface, or the emulated ALSA, JACK, or PulseAudio
41
+interfaces:
42
+
43
+- \ref page_man_pipewire-client_conf_5 "PipeWire native and ALSA client configuration reference"
44
+- \ref page_man_pipewire-jack_conf_5 "PipeWire JACK client configuration reference"
45
+- \ref page_man_pipewire-pulse_conf_5 "PipeWire Pulseaudio client configuration reference"
46
+- Wiki page on native clients(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-client)
47
+- Wiki page on ALSA clients(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-ALSA)
48
+- Wiki page on JACK clients(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-JACK)
49
+- Wiki page on PulseAudio clients(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PulseAudio)
50
+
51
+# Manual Pages
52
+
53
+- \subpage page_man_pipewire_conf_5
54
+- \subpage page_man_pipewire-client_conf_5
55
+- \subpage page_man_pipewire-pulse_conf_5
56
+- \subpage page_man_pipewire-jack_conf_5
57
+- \subpage page_man_pipewire-filter-chain_conf_5
58
+- \subpage page_man_pipewire-devices_7
59
+- \subpage page_man_pipewire-pulse-modules_7
60
+- \subpage page_man_libpipewire-modules_7
61
+
62
+# Configuration Index
63
+
64
+\ref page_man_pipewire_conf_5 "pipewire.conf"
65
+
66
+@SECREF@ pipewire.conf
67
+
68
+\ref page_man_pipewire-pulse_conf_5 "pipewire-pulse.conf"
69
+
70
+@SECREF@ pipewire-pulse.conf
71
+
72
+\ref page_man_pipewire-client_conf_5 "client.conf, client-rt.conf"
73
+
74
+@SECREF@ client.conf
75
+
76
+\ref page_man_pipewire-jack_conf_5 "jack.conf"
77
+
78
+@SECREF@ jack.conf
79
+
80
+**Runtime settings**
81
+
82
+@SECREF@ pipewire-settings
83
+
84
+**Environment variables**
85
+
86
+@SECREF@ pipewire-env client-env jack-env pulse-env
87
+
88
+**Device properties**
89
+
90
+@SECREF@ device-param
91
pipewire-1.2.0.tar.gz/doc/dox/config/libpipewire-modules.7.md Changed
2
 
1
(renamed from doc/dox/programs/libpipewire-modules.7.md)
2
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-client.conf.5.md Added
661
 
1
@@ -0,0 +1,659 @@
2
+\page page_man_pipewire-client_conf_5 client.conf
3
+
4
+The PipeWire client configuration file.
5
+
6
+\tableofcontents
7
+
8
+# SYNOPSIS
9
+
10
+*$XDG_CONFIG_HOME/pipewire/client.conf*
11
+
12
+*$(PIPEWIRE_CONFIG_DIR)/client.conf*
13
+
14
+*$(PIPEWIRE_CONFDATADIR)/client.conf*
15
+
16
+*$(PIPEWIRE_CONFDATADIR)/client.conf.d/*
17
+
18
+*$(PIPEWIRE_CONFIG_DIR)/client.conf.d/*
19
+
20
+*$XDG_CONFIG_HOME/pipewire/client.conf.d/*
21
+
22
+*$XDG_CONFIG_HOME/pipewire/client-rt.conf*
23
+
24
+*$(PIPEWIRE_CONFIG_DIR)/client-rt.conf*
25
+
26
+*$(PIPEWIRE_CONFDATADIR)/client-rt.conf*
27
+
28
+*$(PIPEWIRE_CONFDATADIR)/client-rt.conf.d/*
29
+
30
+*$(PIPEWIRE_CONFIG_DIR)/client-rt.conf.d/*
31
+
32
+*$XDG_CONFIG_HOME/pipewire/client-rt.conf.d/*
33
+
34
+# DESCRIPTION
35
+
36
+Configuration for PipeWire native clients, and for PipeWire's ALSA
37
+plugin.
38
+
39
+A PipeWire native client program selects the default config to load,
40
+and if nothing is specified, it usually loads `client.conf`.
41
+
42
+The ALSA plugin uses the `client-rt.conf` file, as do some PipeWire
43
+native clients such as \ref page_man_pw-cat_1 "pw-cat(1)".
44
+
45
+The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
46
+
47
+Drop-in configuration files `client.conf.d/*.conf` can be used, and are recommended.
48
+See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
49
+
50
+# CONFIGURATION FILE SECTIONS  @IDX@ client.conf
51
+
52
+\par stream.properties
53
+Configures options for native client streams.
54
+
55
+\par stream.rules
56
+Configures rules for native client streams.
57
+
58
+\par alsa.properties
59
+ALSA client configuration.
60
+
61
+\par alsa.rules
62
+ALSA client match rules.
63
+
64
+In addition, the PipeWire context configuration sections 
65
+may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
66
+
67
+# STREAM PROPERTIES  @IDX@ client.conf
68
+
69
+The client configuration files contain a stream.properties section that configures the options for client streams:
70
+```
71
+stream.properties = {
72
+    #node.latency = 1024/48000
73
+    #node.autoconnect = true
74
+    #resample.disable = false
75
+    #resample.quality = 4
76
+    #monitor.channel-volumes = false
77
+    #channelmix.disable = false
78
+    #channelmix.min-volume = 0.0
79
+    #channelmix.max-volume = 10.0
80
+    #channelmix.normalize = false
81
+    #channelmix.lock-volume = false
82
+    #channelmix.mix-lfe = true
83
+    #channelmix.upmix = true
84
+    #channelmix.upmix-method = psd  # none, simple
85
+    #channelmix.lfe-cutoff = 150.0
86
+    #channelmix.fc-cutoff  = 12000.0
87
+    #channelmix.rear-delay = 12.0
88
+    #channelmix.stereo-widen = 0.0
89
+    #channelmix.hilbert-taps = 0
90
+    #dither.noise = 0
91
+    #dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
92
+    #debug.wav-path = ""
93
+}
94
+```
95
+
96
+Some of the properties refer to different aspects of the stream:
97
+
98
+* General stream properties to identify the stream.
99
+* General stream properties to classify the stream.
100
+* How it is going to be scheduled by the graph.
101
+* How it is going to be linked by the session manager.
102
+* How the internal processing will be done.
103
+* Properties to configure the media format.
104
+
105
+## Identifying Properties  @IDX@ client.conf
106
+
107
+These contain properties to identify the node or to display the node in a GUI application.
108
+
109
+@PAR@ client.conf  node.name
110
+A (unique) name for the node. This is usually set on sink and sources to identify them
111
+as targets for linking by the session manager.
112
+
113
+@PAR@ client.conf  node.description
114
+A human readable description of the node or stream.
115
+
116
+@PAR@ client.conf  media.name
117
+A user readable media name, usually the artist and title.
118
+These are usually shown in user facing applications
119
+to inform the user about the current playing media.
120
+
121
+@PAR@ client.conf  media.title
122
+A user readable stream title.
123
+
124
+@PAR@ client.conf  media.artist
125
+A user readable stream artist
126
+
127
+@PAR@ client.conf  media.copyright
128
+User readable stream copyright information
129
+
130
+@PAR@ client.conf  media.software
131
+User readable stream generator software information
132
+
133
+@PAR@ client.conf  media.language
134
+Stream language in POSIX format. Ex: `en_GB`
135
+
136
+@PAR@ client.conf  media.filename
137
+File name for the stream
138
+
139
+@PAR@ client.conf  media.icon
140
+Icon for the media, a base64 blob with PNG image data
141
+
142
+@PAR@ client.conf  media.icon-name
143
+An XDG icon name for the media. Ex: `audio-x-mp3`
144
+
145
+@PAR@ client.conf  media.comment
146
+Extra stream comment
147
+
148
+@PAR@ client.conf  media.date
149
+Date of the media
150
+
151
+@PAR@ client.conf  media.format
152
+User readable stream format information
153
+
154
+@PAR@ client.conf  object.linger = false
155
+If the object should outlive its creator.
156
+
157
+## Classifying Properties  @IDX@ client.conf
158
+
159
+The classifying properties of a node are use for routing the signal to its destination and
160
+for configuring the settings.
161
+
162
+@PAR@ client.conf  media.type
163
+The media type contains a broad category of the media that is being processed by the node.
164
+Possible values include "Audio", "Video", "Midi"
165
+
166
+@PAR@ client.conf  media.category
167
+\parblock
168
+What kind of processing is done with the media. Possible values include:
169
+
170
+* Playback: media playback.
171
+* Capture: media capture.
172
+* Duplex: media capture and playback or media processing in general.
173
+* Monitor: a media monitor application. Does not actively change media data but monitors
174
+           activity.
175
+* Manager: Will manage the media graph.
176
+\endparblock
177
+
178
+@PAR@ client.conf  media.role
179
+\parblock
180
+The Use case of the media. Possible values include:
181
+
182
+* Movie: Movie playback with audio and video.
183
+* Music: Music listening.
184
+* Camera: Recording video from a camera.
185
+* Screen: Recording or sharing the desktop screen.
186
+* Communication: VOIP or other video chat application.
187
+* Game: Game.
188
+* Notification: System notification sounds.
189
+* DSP: Audio or Video filters and effect processing.
190
+* Production: Professional audio processing and production.
191
+* Accessibility: Audio and Visual aid for accessibility.
192
+* Test: Test program.
193
+\endparblock
194
+
195
+@PAR@ client.conf  media.class
196
+\parblock
197
+The media class is to classify the stream function. Possible values include:
198
+
199
+* Video/Source: a producer of video, like a webcam.
200
+* Video/Sink: a consumer of video, like a display window.
201
+* Audio/Source: a source of audio samples like a microphone.
202
+* Audio/Sink: a sink for audio samples, like an audio card.
203
+* Audio/Duplex: a node that is both a sink and a source.
204
+* Stream/Output/Audio: a playback stream.
205
+* Stream/Input/Audio: a capture stream.
206
+
207
+The session manager assigns special meaning to the nodes based on the media.class. Sink or Source
208
+classes are used as targets for Stream classes, etc..
209
+\endparblock
210
+
211
+## Scheduling Properties  @IDX@ client.conf
212
+
213
+@PAR@ client.conf  node.latency = 1024/48000
214
+Sets a suggested latency on the node as a fraction. This is just a suggestion, the graph will try to configure this latency or less for the graph. It is however possible that the graph is forced to a higher latency.
215
+
216
+@PAR@ client.conf  node.lock-quantum = false
217
+\parblock
218
+When this node is active, the quantum of the graph is locked and not allowed to change automatically.
219
+It can still be changed forcibly with metadata or when a node forces a quantum.
220
+
221
+JACK clients use this property to avoid unexpected quantum changes.
222
+\endparblock
223
+
224
+@PAR@ client.conf  node.force-quantum = INTEGER
225
+\parblock
226
+While the node is active, force a quantum in the graph. The last node to be activated with this property wins.
227
+
228
+A value of 0 unforces the quantum.
229
+\endparblock
230
+
231
+@PAR@ client.conf  node.rate = RATE
232
+Suggest a rate (samplerate) for the graph. The suggested rate will only be applied when doing so would not cause
233
+interruptions (devices are idle) and when the rate is in the list of allowed rates in the server.
234
+
235
+@PAR@ client.conf  node.lock-rate = false
236
+When the node is active, the rate of the graph will not change automatically. It is still possible to force a rate change with metadata or with a node property.
237
+
238
+@PAR@ client.conf  node.force-rate = RATE
239
+\parblock
240
+When the node is active, force a specific sample rate on the graph. The last node to activate with this property wins.
241
+
242
+A RATE of 0 means to force the rate in `node.rate` denominator.
243
+\endparblock
244
+
245
+@PAR@ client.conf  node.always-process = false
246
+\parblock
247
+When the node is active, it will always be joined with a driver node, even when nothing is linked to the node.
248
+Setting this property to true also implies node.want-driver = true.
249
+
250
+This is the default for JACK nodes, that always need their process callback called.
251
+\endparblock
252
+
253
+@PAR@ client.conf  node.want-driver = true
254
+The node wants to be linked to a driver so that it can start processing. This is the default for streams
255
+and filters since 0.3.51. Nodes that are not linked to anything will still be set to the idle state,
256
+unless node.always-process is set to true.
257
+
258
+@PAR@ client.conf  node.pause-on-idle = false
259
+@PAR@ client.conf  node.suspend-on-idle = false
260
+\parblock
261
+When the node is not linked anymore, it becomes idle. Normally idle nodes keep processing and are suspended by the session manager after some timeout.  It is possible to immediately pause a node when idle with this property.
262
+
263
+When the session manager does not suspend nodes (or when there is no session manager), the node.suspend-on-idle property can be used instead.
264
+\endparblock
265
+
266
+@PAR@ client.conf  node.loop.name = null
267
+@PAR@ client.conf  node.loop.class = data.rt
268
+\parblock
269
+Add the node to a specific loop name or loop class. By default the node is added to the
270
+data.rt loop class. You can make more specific data loops and then assign the nodes to those.
271
+
272
+Other well known names are main-loop.0 and the main node.loop.class which runs the node data processing
273
+in the main loop.
274
+\endparblock
275
+
276
+## Session Manager Properties  @IDX@ client.conf
277
+
278
+@PAR@ client.conf  node.autoconnect = true
279
+Instructs the session manager to automatically connect this node to some other node, usually
280
+a sink or source.
281
+
282
+@PAR@ client.conf  node.exclusive = false
283
+If this node wants to be linked exclusively to the sink/source.
284
+
285
+@PAR@ client.conf  node.target = <node.name|object.id>
286
+Where this node should be linked to. This can be a node.name or an object.id of a node. This property is 
287
+deprecated, the target.object property should be used instead, which uses the more unique object.serial as
288
+a possible target.
289
+
290
+@PAR@ client.conf  target.object = <node.name|object.serial>
291
+Where the node should link to, this can be a node.name or an object.serial.
292
+
293
+@PAR@ client.conf  node.dont-reconnect = false
294
+\parblock
295
+When the node has a target configured and the target is destroyed, destroy the node as well.
296
+This property also inhibits that the node is moved to another sink/source.
297
+
298
+Note that if a stream should appear/disappear in sync with the target, a session manager (WirePlumber) script
299
+should be written instead.
300
+\endparblock
301
+
302
+@PAR@ client.conf  node.passive = false
303
+\parblock
304
+This is a passive node and so it should not keep sinks/sources busy. This property makes the session manager create passive links to the sink/sources. If the node is not otherwise linked (via a non-passive link), the node and the sink it is linked to are idle (and eventually suspended).
305
+
306
+This is used for filter nodes that sit in front of sinks/sources and need to suspend together with the sink/source.
307
+\endparblock
308
+
309
+@PAR@ client.conf  node.link-group = ID
310
+Add the node to a certain link group. Nodes from the same link group are not automatically linked to each other by the session manager. And example is a coupled stream where you don't want the output to link to the input streams, making a useless loop.
311
+
312
+@PAR@ client.conf  stream.dont-remix = false
313
+Instruct the session manager to not remix the channels of a stream. Normally the stream channel configuration is changed to match the sink/source it is connected to. With this property set to true, the stream will keep its original channel layout and the session manager will link matching channels with the sink.
314
+
315
+## Audio Adapter Parameters  @IDX@ client.conf
316
+
317
+An audio stream (and also audio device nodes) contain an audio adapter that can perform,
318
+sample format, sample rate and channel mixing operations.
319
+
320
+### Merger Parameters
321
+
322
+The merger is used as the input for a sink device node or a capture stream. It takes the various channels and merges them into a single stream for further processing. 
323
+
324
+The merger will also provide the monitor ports of the input channels and can
325
+apply a software volume on the monitor signal.
326
+
327
+@PAR@ client.conf  monitor.channel-volumes = false
328
+The volume of the input channels is applied to the volume of the monitor ports. Normally
329
+the monitor ports expose the raw unmodified signal on the input ports.
330
+
331
+### Resampler Parameters
332
+
333
+Source, sinks, capture and playback streams contain a high quality adaptive resampler.
334
+It uses sinc(https://ccrma.stanford.edu/~jos/resample/resample.pdf) based resampling
335
+with linear interpolation of filter banks to perform arbitrary
336
+resample factors. The resampler is activated in the following cases:
337
+
338
+* The hardware of a device node does not support the graph samplerate. Resampling will occur
339
+  from the graph samplerate to the hardware samplerate.
340
+* The hardware clock of a device does not run at the same speed as the graph clock and adaptive
341
+  resampling is required to match the clocks.
342
+* A stream does not have the same samplerate as the graph and needs to be resampled.
343
+* An application wants to activate adaptive resampling in a stream to make it match some other
344
+  clock.
345
+
346
+PipeWire performs most of the sample conversions and resampling in the client (Or in the case of the PulseAudio server, in the pipewire-pulse server that creates the streams). This ensures all the conversions are offloaded to the clients and the server can deal with one single format for performance reasons.
347
+
348
+Below is an explanation of the options that can be tuned in the sample converter.
349
+
350
+@PAR@ client.conf  resample.quality = 4
351
+\parblock
352
+The quality of the resampler. from 0 to 14, the default is 4.
353
+
354
+Increasing the quality will result in better cutoff and less aliasing at the expense of
355
+(much) more CPU consumption. The default quality of 4 has been selected as a good compromise
356
+between quality and performance with no artifacts that are well below the audible range.
357
+
358
+See Infinite Wave(https://src.infinitewave.ca/) for a comparison of the performance.
359
+\endparblock
360
+
361
+@PAR@ client.conf  resample.disable = false
362
+Disable the resampler entirely. The node will only be able to negotiate with the graph
363
+when the samplerates are compatible.
364
+
365
+### Channel Mixer Parameters
366
+
367
+Source, sinks, capture and playback streams can apply channel mixing on the incoming signal.
368
+
369
+Normally the channel mixer is not used for devices, the device channels are usually exposed as they are. This policy is usually enforced by the session manager, so we refer to its documentation there.
370
+
371
+Playback and capture streams are usually configured to the channel layout of the sink/source
372
+they connect to and will thus perform channel mixing.
373
+
374
+The channel mixer also implements a software volume. This volume adjustment is performed on the original
375
+channel layout. ex: A stereo playback stream that is up-mixed to 5.1 has 2 a left an right volume control.
376
+
377
+@PAR@ client.conf  channelmix.disable = false
378
+Disables the channel mixer completely. The stream will only be able to link to compatible
379
+sources/sinks with the exact same channel layout.
380
+
381
+@PAR@ client.conf  channelmix.min-volume = 0.0
382
+@PAR@ client.conf  channelmix.max-volume = 10.0
383
+Gives the min and max volume values allowed. Any volume that is set will be clamped to these
384
+values.
385
+
386
+@PAR@ client.conf  channelmix.normalize = false
387
+\parblock
388
+Makes sure that during such mixing & resampling original 0 dB level is preserved, so nothing sounds wildly quieter/louder.
389
+
390
+While this options prevents clipping, it can in some cases produce too low volume. Increase the
391
+volume in that case or disable normalization.
392
+\endparblock
393
+
394
+@PAR@ client.conf  channelmix.lock-volumes = false
395
+Completely disable volume or mute changes. Defaults to false.
396
+
397
+@PAR@ client.conf  channelmix.mix-lfe = true
398
+Mixes the low frequency effect channel into the front center or stereo pair. This might enhance the dynamic range of the signal if there is no subwoofer and the speakers can reproduce the low frequency signal.
399
+
400
+@PAR@ client.conf  channelmix.upmix = true
401
+\parblock
402
+Enables up-mixing of the front center (FC) when the target has a FC channel.
403
+The sum of the stereo channels is used and an optional lowpass filter can be used
404
+(see `channelmix.fc-cutoff`).
405
+
406
+Also enabled up-mixing of LFE when `channelmix.lfe-cutoff` is set to something else than 0 and
407
+the target has an LFE channel. The LFE channel is produced by adding the stereo channels.
408
+
409
+If `channelmix.upmix` is true, the up-mixing of the rear channels is also enabled and controlled
410
+with the `channelmix-upmix-method` property.
411
+\endparblock
412
+
413
+@PAR@ client.conf  channelmix.upmix-method = psd
414
+\parblock
415
+3 methods are provided to produce the rear channels in a surround sound:
416
+
417
+1. none. No rear channels are produced.
418
+
419
+2. simple. Front channels are copied to the rear. This is fast but can produce phasing effects.
420
+
421
+3. psd. The rear channels as produced from the front left and right ambient sound (the
422
+difference between the channels). A delay and optional phase shift are added to the rear signal
423
+to make the sound bigger. 
424
+\endparblock
425
+
426
+@PAR@ client.conf  channelmix.lfe-cutoff = 150
427
+Apply a lowpass filter to the low frequency effects. The value is expressed in Hz. Typical subwoofers have a cutoff at around 150 and 200. The default value of 0 disables the feature.
428
+
429
+@PAR@ client.conf  channelmix.fc-cutoff = 12000
430
+\parblock
431
+Apply a lowpass filter to the front center frequency. The value is expressed in Hz.
432
+
433
+Since the front center contains the dialogs, a typical cutoff frequency is 12000 Hz.
434
+
435
+This option is only active when the up-mix is enabled.
436
+\endparblock
437
+
438
+@PAR@ client.conf  channelmix.rear-delay = 12.0
439
+\parblock
440
+Apply a delay in milliseconds when up-mixing the rear channels. This improves
441
+specialization of the sound. A typical delay of 12 milliseconds is the default.
442
+
443
+This is only active when the `psd` up-mix method is used.
444
+\endparblock
445
+
446
+@PAR@ client.conf  channelmix.stereo-widen = 0.0
447
+\parblock
448
+Subtracts some of the front center signal from the stereo channels. This moves the dialogs
449
+more to the center speaker and leaves the ambient sound in the stereo channels.
450
+
451
+This is only active when up-mix is enabled and a Front Center channel is mixed.
452
+\endparblock
453
+
454
+@PAR@ client.conf  channelmix.hilbert-taps = 0
455
+\parblock
456
+This option will apply a 90 degree phase shift to the rear channels to improve specialization.
457
+Taps needs to be between 15 and 255 with more accurate results (and more CPU consumption)
458
+for higher values.
459
+
460
+This is only active when the `psd` up-mix method is used.
461
+\endparblock
462
+
463
+@PAR@ client.conf  dither.noise = 0
464
+\parblock
465
+This option will add N bits of random data to the signal. When no dither.method is
466
+specified, the random data will flip between -(1<<(N-1)), 0 every 1024 samples. With
467
+a dither.method, the dither noise is amplified with 1<<(N-1) bits.
468
+
469
+This can be used to keep some amplifiers alive during silent periods. One or two bits of noise is
470
+usually enough, otherwise the noise will become audible. This is usually used together with
471
+`session.suspend-timeout-seconds` to disable suspend in the session manager.
472
+
473
+Note that PipeWire uses floating point operations with 24 bits precission for all of the audio
474
+processing. Conversion to 24 bits integer sample formats is lossless and conversion to 32 bits
475
+integer sample formats are simply padded with 0 bits at the end. This means that the dither noise
476
+is always only in the 24 most significant bits.
477
+\endparblock
478
+
479
+@PAR@ client.conf  dither.method = none
480
+\parblock
481
+Optional dithering(https://en.wikipedia.org/wiki/Dither) can be done on the quantized
482
+output signal.
483
+
484
+There are 6 modes available:
485
+
486
+1. none           No dithering is done.
487
+2. rectangular    Dithering with a rectangular noise distribution. This adds random
488
+                  bits in the -0.5, 0.5 range to the signal with even distribution.
489
+3. triangular     Dithering with a triangular noise distribution. This add random
490
+                  bits in the -1.0, 1.0 range to the signal with triangular distribution
491
+                  around 0.0.
492
+4. triangular-hf  Dithering with a sloped triangular noise distribution.
493
+5. wannamaker3    Additional noise shaping is performed on the sloped triangular
494
+                  dithering to move the noise to the more inaudible range. This is using
495
+                  the "F-Weighted" noise filter described by Wannamaker.
496
+6. shaped5        Additional noise shaping is performed on the triangular dithering
497
+                  to move the noise to the more inaudible range. This is using the
498
+                  Lipshitz filter.
499
+
500
+Dithering is only useful for conversion to a format with less than 24 bits and will be
501
+disabled otherwise.
502
+\endparblock
503
+
504
+## Debug Parameters
505
+
506
+@PAR@ client.conf  debug.wav-path = ""
507
+Make the stream to also write the raw samples to a WAV file for debugging purposes.
508
+
509
+## Format Properties
510
+
511
+Streams and also most device nodes can be configured in a certain format with properties.
512
+
513
+@PAR@ client.conf  audio.rate = RATE
514
+Forces a samplerate on the node.
515
+
516
+@PAR@ client.conf  audio.channels = INTEGER
517
+The number of audio channels to use. Must be a value between 1 and 64.
518
+
519
+@PAR@ client.conf  audio.format = FORMAT
520
+\parblock
521
+Forces an audio format on the node. This is the format used internally in the node because the graph processing format is always float 32.
522
+
523
+Valid formats include: S16, S32, F32, F64, S16LE, S16BE, ...
524
+\endparblock
525
+
526
+@PAR@ client.conf  audio.allowed-rates
527
+An array of allowed samplerates for the node. ex. " 44100 48000 "
528
+
529
+# STREAM RULES  @IDX@ client.conf
530
+
531
+You can add \ref pipewire_conf__match_rules "match rules, see pipewire(1)"
532
+to set properties for certain streams and filters.
533
+
534
+`stream.rules` and `filter.rules` provides an `update-props` action
535
+that takes an object with properties that are updated on the node
536
+object of the stream and filter.
537
+
538
+Add a `stream.rules` or `filter.rules` section in the config file like
539
+this:
540
+
541
+```
542
+stream.rules = 
543
+    {
544
+        matches = 
545
+            {
546
+                # all keys must match the value. ! negates. ~ starts regex.
547
+                application.process.binary = "firefox"
548
+            }
549
+        
550
+        actions = {
551
+            update-props = {
552
+                node.name = "My Name"
553
+            }
554
+        }
555
+    }
556
+
557
+```
558
+
559
+Will set the node.name of Firefox to "My Name".
560
+
561
+# ALSA PROPERTIES  @IDX@ client.conf
562
+
563
+An `alsa.properties` section can be added to configure ALSA specific client config.
564
+
565
+```css
566
+alsa.properties = {
567
+    #alsa.deny = false
568
+    #alsa.format = 0
569
+    #alsa.rate = 0
570
+    #alsa.channels = 0
571
+    #alsa.period-bytes = 0
572
+    #alsa.buffer-bytes = 0
573
+    #alsa.volume-method = cubic         # linear, cubic
574
+}
575
+```
576
+
577
+@PAR@ client.conf  alsa.deny
578
+Denies ALSA access for the client. Useful in rules or PIPEWIRE_ALSA environment variable.
579
+
580
+@PAR@ client.conf  alsa.format
581
+The ALSA format to use for the client. This is an ALSA format name. default 0, which is to
582
+allow all formats.
583
+
584
+@PAR@ client.conf  alsa.rate
585
+The samplerate to use for the client. The default is 0, which is to allow all rates.
586
+
587
+@PAR@ client.conf  alsa.channels
588
+The number of channels for the client. The default is 0, which is to allow any number of channels.
589
+
590
+@PAR@ client.conf  alsa.period-bytes
591
+The number of bytes per period. The default is 0 which is to allow any number of period bytes.
592
+
593
+@PAR@ client.conf  alsa.buffer-bytes
594
+The number of bytes in the alsa buffer. The default is 0, which is to allow any number of bytes.
595
+
596
+@PAR@ client.conf  alsa.volume-method = cubic | linear
597
+This controls the volume curve used on the ALSA mixer. Possible values are `cubic` and
598
+`linear`. The default is to use `cubic`.
599
+
600
+# ALSA RULES  @IDX@ client.conf
601
+
602
+It is possible to set ALSA client specific properties by using
603
+\ref pipewire_conf__match_rules "Match rules, see pipewire(1)". You can
604
+set any of the above ALSA properties or any of the `stream.properties`.
605
+
606
+### Example
607
+```
608
+alsa.rules = 
609
+    {   matches =  { application.process.binary = "resolve" } 
610
+        actions = {
611
+            update-props = {
612
+                alsa.buffer-bytes = 131072
613
+            }
614
+        }
615
+    }
616
+
617
+```
618
+
619
+# ENVIRONMENT VARIABLES  @IDX@ client-env
620
+
621
+See \ref page_man_pipewire_1 "pipewire(1)" for common environment
622
+variables. Many of these also apply to client applications.
623
+
624
+The environment variables also influence ALSA applications that are
625
+using PipeWire's ALSA plugin.
626
+
627
+@PAR@ client-env  PIPEWIRE_ALSA
628
+\parblock
629
+This can be an object with properties from `alsa.properties` or `stream.properties` that will
630
+be used to construct the client and streams.
631
+
632
+For example:
633
+```
634
+PIPEWIRE_ALSA='{ alsa.buffer-bytes=16384 node.name=foo }' aplay ...
635
+```
636
+Starts aplay with custom properties.
637
+\endparblock
638
+
639
+@PAR@ client-env  PIPEWIRE_NODE
640
+\parblock
641
+Instructs the ALSA client to link to a particular sink or source `object.serial` or `node.name`.
642
+
643
+For example:
644
+```
645
+PIPEWIRE_NODE=alsa_output.pci-0000_00_1b.0.analog-stereo aplay ...
646
+```
647
+Makes aplay play on the give audio sink.
648
+\endparblock
649
+
650
+# AUTHORS
651
+
652
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
653
+PipeWire is available from <$(PACKAGE_URL)>
654
+
655
+# SEE ALSO
656
+
657
+\ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)",
658
+\ref page_man_pipewire_conf_5 "pipewire.conf(5)",
659
+\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)",
660
+\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
661
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-devices.7.md Added
520
 
1
@@ -0,0 +1,518 @@
2
+\page page_man_pipewire-devices_7 pipewire-devices
3
+
4
+PipeWire device and node property reference.
5
+
6
+\tableofcontents
7
+
8
+# DESCRIPTION
9
+
10
+Audio sinks and sources, cameras, Bluetooth endpoints, and other
11
+objects have properties that can be set in configuration files or at
12
+runtime.
13
+
14
+Some of the properties are "common object properties" (e.g. such as
15
+`node.description`) and can be set on all types of devices and
16
+nodes. Other properties control settings of a specific type of a
17
+device (ALSA, Bluetooth, ...).
18
+
19
+All the properties are usually configured in the session manager configuration.
20
+For how to configure them, see the session manager documentation.
21
+
22
+In minimal PipeWire setups without a session manager, they can be configured via
23
+\ref pipewire_conf__context_objects "context.objects in pipewire.conf(5)".
24
+
25
+# RUNTIME SETTINGS  @IDX@ device-param
26
+
27
+The settings of most ALSA and virtual device parameters can be
28
+configured also at runtime.
29
+
30
+The settings are available in device `Props` in the `params`
31
+field. They can be seen e.g. using `pw-dump <id>` for an ALSA device:
32
+
33
+```json
34
+{
35
+...
36
+      "Props": 
37
+        {
38
+          ...
39
+          "params": 
40
+              "audio.channels",
41
+              2,
42
+              "audio.rate",
43
+              0,
44
+              "audio.format",
45
+              "UNKNOWN",
46
+              "audio.position",
47
+              " FL, FR ",
48
+              "audio.allowed-rates",
49
+              "  ",
50
+              "api.alsa.period-size",
51
+              0,
52
+              "api.alsa.period-num",
53
+              0,
54
+              "api.alsa.headroom",
55
+              0,
56
+              "api.alsa.start-delay",
57
+              0,
58
+              "api.alsa.disable-mmap",
59
+              false,
60
+              "api.alsa.disable-batch",
61
+              false,
62
+              "api.alsa.use-chmap",
63
+              false,
64
+              "api.alsa.multi-rate",
65
+              true,
66
+              "latency.internal.rate",
67
+              0,
68
+              "latency.internal.ns",
69
+              0,
70
+              "clock.name",
71
+              "api.alsa.c-1"
72
+            
73
+          }
74
+...
75
+```
76
+
77
+One or more `params` can be changed using \ref page_man_pw-cli_1 "pw-cli(1)":
78
+```
79
+pw-cli s <id> Props '{ params =  "api.alsa.headroom" 1024  }'
80
+```
81
+These settings are not saved and need to be reapplied for each session manager restart.
82
+
83
+# COMMON NODE PROPERTIES  @IDX@ device-param
84
+
85
+The properties listed in \ref client_conf__stream_properties "Stream properties"
86
+apply also to sink or source nodes corresponding to real or virtual devices.
87
+
88
+In addition:
89
+
90
+@PAR@ device-param  priority.driver    # integer
91
+\parblock
92
+The priority of choosing this device as the driver in the graph. The driver is selected from all linked devices by selecting the device with the highest priority.
93
+
94
+Normally, the session manager assigns higher priority to sources so that they become the driver in the graph. The reason for this is that adaptive resampling should be done on the sinks rather than the source to avoid signal distortion when capturing audio.
95
+\endparblock
96
+
97
+@PAR@ device-param  priority.session    # integer
98
+The priority for selecting this device as the default device.
99
+
100
+@PAR@ device-param  clock.name    # string
101
+\parblock
102
+The name of the clock. This name is auto generated from the card index and stream direction. Devices with the same clock name will not use a resampler to align the clocks. This can be used to link devices together with a shared word clock.
103
+
104
+In Pro Audio mode, nodes from the same device are assumed to have the same clock and no resampling will happen when linked together. So, linking a capture port to a playback port will not use any adaptive resampling in Pro Audio mode.
105
+
106
+In Non Pro Audio profile, no such assumption is made and adaptive resampling is done in all cases by default. This can also be disabled by setting the same clock.name on the nodes.
107
+\endparblock
108
+
109
+@PAR@ device-param  node.param.PARAM = JSON    # JSON
110
+\parblock
111
+Set value of a node \ref spa_param_type "Param" to a JSON value when the device is loaded.
112
+This works similarly as \ref page_man_pw-cli_1 "pw-cli(1)" `set-param` command.
113
+The `PARAM` should be replaced with the name of the Param to set,
114
+ie. for example `node.param.Props = { ... }` to set `Props`.
115
+\endparblock
116
+
117
+@PAR@ device-param  device.id
118
+ID of the device the node belongs to.
119
+
120
+# COMMON DEVICE PROPERTIES  @IDX@ device-param
121
+
122
+These are common properties for devices.
123
+
124
+@PAR@ device-param  device.name    # string
125
+A (unique) name for the device. It can be used by command-line and other tools to identify the device.
126
+
127
+@PAR@ device-param  device.param.PARAM = JSON    # JSON
128
+\parblock
129
+Set value of a device \ref spa_param_type "Param" to a JSON value when the device is loaded.
130
+This works similarly as \ref page_man_pw-cli_1 "pw-cli(1)" `set-param` command.
131
+The `PARAM` should be replaced with the name of the Param to set,
132
+ie. for example `device.Param.Props = { ... }` to set `Props`.
133
+\endparblock
134
+
135
+Other `device.*` properties: UNDOCUMENTED
136
+
137
+# AUDIO CONVERTER PROPERTIES  @IDX@ device-param
138
+
139
+Most audio nodes (ALSA, Bluetooth, ...) have common properties for the audio
140
+converter. See \ref client_conf__stream_properties "pipewire-client.conf(5) stream.properties"
141
+for explanations.
142
+
143
+## Node properties
144
+
145
+@PAR@ device-param  clock.quantum-limit
146
+\ref pipewire_conf__default_clock_quantum-limit "See pipewire.conf(5)"
147
+
148
+@PAR@ device-param  channelmix.disable
149
+\ref client_conf__channelmix_disable "See pipewire-client.conf(5)"
150
+
151
+@PAR@ device-param  channelmix.min-volume
152
+\ref client_conf__channelmix_min-volume "See pipewire-client.conf(5)"
153
+
154
+@PAR@ device-param  channelmix.max-volume
155
+\ref client_conf__channelmix_max-volume "See pipewire-client.conf(5)"
156
+
157
+@PAR@ device-param  channelmix.normalize
158
+\ref client_conf__channelmix_normalize "See pipewire-client.conf(5)"
159
+
160
+@PAR@ device-param  channelmix.mix-lfe
161
+\ref client_conf__channelmix_mix-lfe "See pipewire-client.conf(5)"
162
+
163
+@PAR@ device-param  channelmix.upmix
164
+\ref client_conf__channelmix_upmix "See pipewire-client.conf(5)"
165
+
166
+@PAR@ device-param  channelmix.lfe-cutoff
167
+\ref client_conf__channelmix_lfe-cutoff "See pipewire-client.conf(5)"
168
+
169
+@PAR@ device-param  channelmix.fc-cutoff
170
+\ref client_conf__channelmix_fc-cutoff "See pipewire-client.conf(5)"
171
+
172
+@PAR@ device-param  channelmix.rear-delay
173
+\ref client_conf__channelmix_rear-delay "See pipewire-client.conf(5)"
174
+
175
+@PAR@ device-param  channelmix.stereo-widen
176
+\ref client_conf__channelmix_stereo-widen "See pipewire-client.conf(5)"
177
+
178
+@PAR@ device-param  channelmix.hilbert-taps
179
+\ref client_conf__channelmix_hilbert-taps "See pipewire-client.conf(5)"
180
+
181
+@PAR@ device-param  channelmix.upmix-method
182
+\ref client_conf__channelmix_upmix-method "See pipewire-client.conf(5)"
183
+
184
+@PAR@ device-param  channelmix.lock-volumes
185
+\ref client_conf__channelmix_lock-volumes "See pipewire-client.conf(5)"
186
+
187
+@PAR@ device-param  resample.quality
188
+\ref client_conf__resample_quality "See pipewire-client.conf(5)"
189
+
190
+@PAR@ device-param  resample.disable
191
+\ref client_conf__resample_disable "See pipewire-client.conf(5)"
192
+
193
+@PAR@ device-param  resample.peaks = false # boolean
194
+Instead of actually resampling, produce peak amplitude values as output.
195
+This is used for volume monitoring, where it is set as a property
196
+of the "recording" stream.
197
+
198
+@PAR@ device-param  resample.prefill = false # boolean
199
+Prefill resampler buffers with silence. This affects the initial
200
+samples produced by the resampler.
201
+
202
+@PAR@ device-param  monitor.channel-volumes
203
+\ref client_conf__monitor_channel-volumes "See pipewire-client.conf(5)"
204
+
205
+@PAR@ device-param  dither.noise
206
+\ref client_conf__dither_noise "See pipewire-client.conf(5)"
207
+
208
+@PAR@ device-param  dither.method
209
+\ref client_conf__dither_method "See pipewire-client.conf(5)"
210
+
211
+@PAR@ device-param  debug.wav-path
212
+\ref client_conf__debug_wav-path "See pipewire-client.conf(5)"
213
+
214
+@PAR@ device-param  adapter.auto-port-config = null # JSON
215
+\parblock
216
+If specified, configure the ports of the node when it is created, instead of
217
+leaving that to the session manager to do. This is useful (only) for minimal
218
+configurations without a session manager.
219
+
220
+Value is SPA JSON of the form:
221
+```json
222
+{
223
+    mode = "none",          # "none", "passthrough", "convert", "dsp"
224
+    monitor = false,        # boolean
225
+    control = false,        # boolean
226
+    position = "preserve"   # "unknown", "aux", "preserve"
227
+}
228
+```
229
+See \ref spa_param_port_config for the meaning.
230
+\endparblock
231
+
232
+# ALSA PROPERTIES  @IDX@ device-param
233
+
234
+## Monitor properties
235
+
236
+@PAR@ device-param  alsa.use-acp    # boolean
237
+Use \ref device-param__alsa_card_profiles "ALSA Card Profiles" (ACP) for device configuration.
238
+
239
+@PAR@ device-param  alsa.udev.expose-busy    # boolean
240
+Expose the ALSA card even if it is busy/in use. Default false. This can be useful when some
241
+of the PCMs are in use by other applications but the other free PCMs should still be exposed.
242
+
243
+## Device properties
244
+
245
+@PAR@ device-param  api.alsa.path    # string
246
+ALSA device path as can be used in snd_pcm_open() and snd_ctl_open().
247
+
248
+@PAR@ device-param  api.acp.auto-port    # boolean
249
+Select reasonable port on device startup. Available for ACP devices.
250
+
251
+@PAR@ device-param  api.acp.auto-profile    # boolean
252
+Select reasonable profile on device startup. Available for ACP devices.
253
+
254
+## Node properties
255
+
256
+@PAR@ device-param  audio.channels    # integer
257
+The number of audio channels to open the device with. Defaults depends on the profile of the device.
258
+
259
+@PAR@ device-param  audio.rate    # integer
260
+The audio rate to open the device with. Default is 0, which means to open the device with a rate as close to the graph rate as possible.
261
+
262
+@PAR@ device-param  audio.format    # string
263
+The audio format to open the device in. By default this is "UNKNOWN", which will open the device in the best possible bits (32/24/16/8..). You can force a format like S16_LE or S32_LE.
264
+
265
+@PAR@ device-param  audio.position    # JSON array of strings
266
+The audio position of the channels in the device. This is auto detected based on the profile. You can configure an array of channel positions, like " FL, FR ".
267
+
268
+@PAR@ device-param  audio.allowed-rates    # JSON array of integers
269
+\parblock
270
+The allowed audio rates to open the device with. Default is " ", which means the device can be opened in any supported rate.
271
+
272
+Only rates from the array will be used to open the device. When the graph is running with a rate not listed in the allowed-rates, the resampler will be used to resample to the nearest allowed rate.
273
+\endparblock
274
+
275
+@PAR@ device-param  api.alsa.period-size    # integer
276
+The period size to open the device in. By default this is 0, which will open the device in the default period size to minimize latency.
277
+
278
+@PAR@ device-param  api.alsa.period-num    # integer
279
+The amount of periods to use in the device. By default this is 0, which means to use as many as possible.
280
+
281
+@PAR@ device-param  api.alsa.headroom    # integer
282
+The amount of extra space to keep in the ringbuffer. The default is 0. Higher values can be configured when the device read and write pointers are not accurately reported.
283
+
284
+@PAR@ device-param  api.alsa.start-delay    # integer
285
+Some devices require a startup period. The default is 0. Higher values can be set to send silence samples to the device first.
286
+
287
+@PAR@ device-param  api.alsa.disable-mmap    # boolean
288
+Disable mmap operation of the device and use the ALSA read/write API instead. Default is false, mmap is preferred.
289
+
290
+@PAR@ device-param  api.alsa.disable-batch    # boolean
291
+Ignore the ALSA batch flag. If the batch flag is set, ALSA will need an extra period to update the read/write pointers. Ignore this flag from ALSA can reduce the latency. Default is false.
292
+
293
+@PAR@ device-param  api.alsa.use-chmap    # boolean
294
+Use the driver provided channel map. Default is true when using UCM, false otherwise because many driver don't report this correctly.
295
+
296
+@PAR@ device-param  api.alsa.multi-rate    # boolean
297
+Allow devices from the same card to be opened in multiple sample rates. Default is true. Some older drivers did not properly advertise the capabilities of the device and only really supported opening the device in one rate.
298
+
299
+@PAR@ device-param  api.alsa.htimestamp = false    # boolean
300
+Use ALSA htimestamps in scheduling, instead of the system clock.
301
+Some ALSA drivers produce bad timestamps, so this is not enabled by default
302
+and will be disabled at runtime if it looks like the ALSA timestamps are bad.
303
+
304
+@PAR@ device-param  api.alsa.htimestamp.max-errors    # integer
305
+Specify the number of consecutive errors before htimestamp is disabled.
306
+Setting this to 0 makes htimestamp never get disabled.
307
+
308
+@PAR@ device-param  api.alsa.disable-tsched = false    # boolean
309
+Disable timer-based scheduling, and use IRQ for scheduling instead.
310
+The "Pro Audio" profile will usually enable this setting, if it is expected it works on the hardware.
311
+
312
+@PAR@ device-param  api.alsa.auto-link = false    # boolean
313
+Link follower PCM devices to the driver PCM device when using IRQ-based scheduling.
314
+The "Pro Audio" profile will usually enable this setting, if it is expected it works on the hardware.
315
+
316
+@PAR@ device-param  latency.internal.rate    # integer
317
+Static set the device systemic latency, in samples at playback rate.
318
+
319
+@PAR@ device-param  latency.internal.ns    # integer
320
+Static set the device systemic latency, in nanoseconds.
321
+
322
+@PAR@ device-param  api.alsa.path    # string
323
+UNDOCUMENTED
324
+
325
+@PAR@ device-param  api.alsa.open.ucm    # boolean
326
+Open device using UCM.
327
+
328
+@PAR@ device-param  api.alsa.bind-ctls    # boolean
329
+UNDOCUMENTED
330
+
331
+@PAR@ device-param  iec958.codecs    # JSON array of string
332
+Enable only specific IEC958 codecs. This can be used to disable some codecs the hardware supports.
333
+Available values: PCM, AC3, DTS, MPEG, MPEG2-AAC, EAC3, TRUEHD, DTSHD
334
+
335
+# BLUETOOTH PROPERTIES  @IDX@ device-param
336
+
337
+## Monitor properties
338
+
339
+The following are settings for the Bluetooth device monitor, not device or
340
+node properties:
341
+
342
+@PAR@ device-param  bluez5.roles   # JSON array of string
343
+\parblock
344
+Enabled roles (default:  a2dp_sink a2dp_source bap_sink bap_source hfp_hf hfp_ag )
345
+
346
+Currently some headsets (Sony WH-1000XM3) are not working with
347
+both hsp_ag and hfp_ag enabled, so by default we enable only HFP.
348
+
349
+Supported roles:
350
+- hsp_hs (HSP Headset),
351
+- hsp_ag (HSP Audio Gateway),
352
+- hfp_hf (HFP Hands-Free),
353
+- hfp_ag (HFP Audio Gateway)
354
+- a2dp_sink (A2DP Audio Sink)
355
+- a2dp_source (A2DP Audio Source)
356
+- bap_sink (LE Audio Basic Audio Profile Sink)
357
+- bap_source (LE Audio Basic Audio Profile Source)
358
+\endparblock
359
+
360
+@PAR@ device-param  bluez5.codecs   # JSON array of string
361
+Enabled A2DP codecs (default: all).
362
+Possible values: sbc sbc_xq aac aac_eld aptx aptx_hd aptx_ll aptx_ll_duplex faststream faststream_duplex lc3plus_h3 ldac opus_05 opus_05_51 opus_05_71 opus_05_duplex opus_05_pro opus_g lc3
363
+
364
+@PAR@ device-param  bluez5.default.rate   # integer
365
+Default audio rate.
366
+
367
+@PAR@ device-param  bluez5.default.channels   # integer
368
+Default audio channels.
369
+
370
+@PAR@ device-param  bluez5.hfphsp-backend   # integer
371
+HFP/HSP backend (default: native). Available values: any, none, hsphfpd, ofono, native
372
+
373
+@PAR@ device-param  bluez5.hfphsp-backend-native-modem   # string
374
+
375
+@PAR@ device-param  bluez5.dummy-avrcp player   # boolean
376
+Register dummy AVRCP player. Some devices have wrongly functioning
377
+volume or playback controls if this is not enabled. Default: false
378
+
379
+@PAR@ device-param  bluez5.enable-sbc-xq   # boolean
380
+Override device quirk list and enable SBC-XQ for devices for which it is disabled.
381
+
382
+@PAR@ device-param  bluez5.enable-msbc   # boolean
383
+Override device quirk list and enable MSBC for devices for which it is disabled.
384
+
385
+@PAR@ device-param  bluez5.enable-hw-volume   # boolean
386
+Override device quirk list and enable hardware volume fo devices for which it is disabled.
387
+
388
+@PAR@ device-param  bluez5.hw-offload-sco   # boolean
389
+\parblock
390
+HFP/HSP hardware offload SCO support (default: false).
391
+
392
+This feature requires a custom configuration that routes SCO audio to ALSA nodes,
393
+in a platform-specific way. See `tests/examples/bt-pinephone.lua` in WirePlumber for an example.
394
+Do not enable this setting if you don't know what all this means, as it won't work.
395
+\endparblock
396
+
397
+@PAR@ device-param  bluez5.a2dp.opus.pro.channels = 3   # integer
398
+PipeWire Opus Pro audio profile channel count.
399
+
400
+@PAR@ device-param  bluez5.a2dp.opus.pro.coupled-streams = 1   # integer
401
+PipeWire Opus Pro audio profile coupled stream count.
402
+
403
+@PAR@ device-param  bluez5.a2dp.opus.pro.locations = "FL,FR,LFE"   # string
404
+PipeWire Opus Pro audio profile audio channel locations.
405
+
406
+@PAR@ device-param  bluez5.a2dp.opus.pro.max-bitrate = 600000   # integer
407
+PipeWire Opus Pro audio profile max bitrate.
408
+
409
+@PAR@ device-param  bluez5.a2dp.opus.pro.frame-dms = 50   # integer
410
+PipeWire Opus Pro audio profile frame duration (1/10 ms).
411
+
412
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.channels = 1   # integer
413
+PipeWire Opus Pro audio profile duplex channels.
414
+
415
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.coupled-streams = 0   # integer
416
+PipeWire Opus Pro audio profile duplex coupled stream count.
417
+
418
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.locations = "FC"   # string
419
+PipeWire Opus Pro audio profile duplex coupled channel locations.
420
+
421
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.max-bitrate = 160000   # integer
422
+PipeWire Opus Pro audio profile duplex max bitrate.
423
+
424
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.frame-dms = 400   # integer
425
+PipeWire Opus Pro audio profile duplex frame duration (1/10 ms).
426
+
427
+@PAR@ device-param  bluez5.bcast_source.config   # JSON
428
+\parblock
429
+Example:
430
+```
431
+bluez5.bcast_source.config = 
432
+  {
433
+    "broadcast_code": "Børne House",
434
+    "encryption: false,
435
+    "bis": 
436
+      { # BIS configuration
437
+        "qos_preset": "16_2_1", # QOS preset name from table Table 6.4 from BAP_v1.0.1.
438
+        "audio_channel_allocation": 1, # audio channel allocation configuration for the BIS
439
+        "metadata":  # metadata configurations for the BIS
440
+           { "type": 1, "value":  1, 1  }
441
+        
442
+      }
443
+    
444
+  }
445
+
446
+```
447
+\endparblock
448
+
449
+## Device properties
450
+
451
+@PAR@ device-param  bluez5.auto-connect   # boolean
452
+Auto-connect devices on start up. Disabled by default if
453
+the property is not specified.
454
+
455
+@PAR@ device-param  bluez5.hw-volume =  PROFILE1 PROFILE2...    # JSON array of string
456
+Profiles for which to enable hardware volume control (default:  hfp_ag hsp_ag a2dp_source ).
457
+
458
+@PAR@ device-param  bluez5.profile   # string
459
+Initial device profile. This usually has no effect as the session manager
460
+overrides it.
461
+
462
+@PAR@ device-param  bluez5.a2dp.ldac.quality   # string
463
+LDAC encoding quality
464
+Available values:
465
+- auto (Adaptive Bitrate, default)
466
+- hq   (High Quality, 990/909kbps)
467
+- sq   (Standard Quality, 660/606kbps)
468
+- mq   (Mobile use Quality, 330/303kbps)
469
+
470
+@PAR@ device-param  bluez5.a2dp.aac.bitratemode   # integer
471
+AAC variable bitrate mode.
472
+Available values: 0 (cbr, default), 1-5 (quality level)
473
+
474
+@PAR@ device-param  bluez5.a2dp.opus.pro.application = "audio"   # string
475
+PipeWire Opus Pro Audio encoding mode: audio, voip, lowdelay
476
+
477
+@PAR@ device-param  bluez5.a2dp.opus.pro.bidi.application = "audio"   # string
478
+PipeWire Opus Pro Audio duplex encoding mode: audio, voip, lowdelay
479
+
480
+@PAR@ device-param  bluez5.bap.cig = auto   # integer, or 'auto'
481
+Set CIG ID for BAP unicast streams of the device.
482
+Default: "auto" (automatic).
483
+
484
+## Node properties
485
+
486
+@PAR@ device-param  bluez5.media-source-role   # string
487
+\parblock
488
+Media source role for Bluetooth clients connecting to
489
+this instance. Available values:
490
+  - playback: playing stream to speakers
491
+  - input: appear as source node.
492
+\endparblock
493
+
494
+# ALSA CARD PROFILES  @IDX@ device-param
495
+
496
+The sound card profiles ("Analog stereo", "Analog stereo duplex", ...) except "Pro Audio" come from two sources:
497
+
498
+- UCM: ALSA Use Case Manager: the profile configuration system from ALSA. See https://github.com/alsa-project/alsa-ucm-conf/
499
+- ACP ("Alsa Card Profiles"): Pulseaudio's profile system ported to PipeWire. See https://www.freedesktop.org/wiki/Software/PulseAudio/Backends/ALSA/Profiles/
500
+
501
+See the above links on how to configure these systems.
502
+
503
+For ACP, PipeWire looks for the profile configuration files under
504
+
505
+- ~/.config/alsa-card-profile
506
+- /etc/alsa-card-profile
507
+- /usr/share/alsa-card-profile/mixer`. 
508
+
509
+The `path` and `profile-set` files are in subdirectories `paths` and `profile-sets` of these directories.
510
+It is possible to override individual files locally by putting a modified copy into the ACP directories under `~/.config` or `/etc`.
511
+
512
+# AUTHORS
513
+
514
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
515
+PipeWire is available from <$(PACKAGE_URL)>
516
+
517
+# SEE ALSO
518
+
519
+\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
520
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-filter-chain.conf.5.md Added
44
 
1
@@ -0,0 +1,42 @@
2
+\page page_man_pipewire-filter-chain_conf_5 filter-chain.conf
3
+
4
+PipeWire example configuration for running audio filters.
5
+
6
+\tableofcontents
7
+
8
+# SYNOPSIS
9
+
10
+*$XDG_CONFIG_HOME/pipewire/filter-chain.conf*
11
+
12
+*$(PIPEWIRE_CONFIG_DIR)/filter-chain.conf*
13
+
14
+*$(PIPEWIRE_CONFDATADIR)/filter-chain.conf*
15
+
16
+*$(PIPEWIRE_CONFDATADIR)/filter-chain.conf.d/*
17
+
18
+*$(PIPEWIRE_CONFIG_DIR)/filter-chain.conf.d/*
19
+
20
+*$XDG_CONFIG_HOME/pipewire/filter-chain.conf.d/*
21
+
22
+# DESCRIPTION
23
+
24
+When \ref page_man_pipewire_1 "pipewire(1)" is run using
25
+this configuration file, `pipewire -c filter-chain.conf`,
26
+it starts a PipeWire client application that publishes
27
+nodes that apply various audio filters to their input.
28
+
29
+It is a normal PipeWire client application in all respects.
30
+
31
+Drop-in configuration files `filter-chain.conf.d/*.conf` can be used
32
+to modify the filter configuration, see \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
33
+Some examples are in *$(PIPEWIRE_CONFDATADIR)/filter-chain/*
34
+
35
+# AUTHORS
36
+
37
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
38
+PipeWire is available from <$(PACKAGE_URL)>
39
+
40
+# SEE ALSO
41
+
42
+\ref page_man_pipewire_1 "pipewire(1)",
43
+\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
44
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-jack.conf.5.md Added
333
 
1
@@ -0,0 +1,331 @@
2
+\page page_man_pipewire-jack_conf_5 jack.conf
3
+
4
+The PipeWire JACK client configuration file.
5
+
6
+\tableofcontents
7
+
8
+# SYNOPSIS
9
+
10
+*$XDG_CONFIG_HOME/pipewire/jack.conf*
11
+
12
+*$(PIPEWIRE_CONFIG_DIR)/jack.conf*
13
+
14
+*$(PIPEWIRE_CONFDATADIR)/jack.conf*
15
+
16
+*$(PIPEWIRE_CONFDATADIR)/jack.conf.d/*
17
+
18
+*$(PIPEWIRE_CONFIG_DIR)/jack.conf.d/*
19
+
20
+*$XDG_CONFIG_HOME/pipewire/jack.conf.d/*
21
+
22
+# DESCRIPTION
23
+
24
+Configuration for PipeWire JACK clients.
25
+
26
+The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
27
+
28
+Drop-in configuration files `jack.conf.d/*.conf` can be used, and are recommended.
29
+See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
30
+
31
+# CONFIGURATION FILE SECTIONS  @IDX@ jack.conf
32
+
33
+\par jack.properties
34
+JACK client configuration.
35
+
36
+\par jack.rules
37
+JACK client match rules.
38
+
39
+In addition, the PipeWire context configuration sections 
40
+may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
41
+
42
+# JACK PROPERTIES  @IDX@ jack.conf
43
+
44
+The configuration file can contain an extra JACK specific section called `jack.properties` like this:
45
+```
46
+...
47
+jack.properties = {
48
+    #rt.prio             = 88
49
+    #node.latency        = 1024/48000
50
+    #node.lock-quantum   = true
51
+    #node.force-quantum  = 0
52
+    #jack.show-monitor   = true
53
+    #jack.merge-monitor  = true
54
+    #jack.show-midi      = true
55
+    #jack.short-name     = false
56
+    #jack.filter-name    = false
57
+    #jack.filter-char    = " "
58
+    #
59
+    # allow:           Don't restrict self connect requests
60
+    # fail-external:   Fail self connect requests to external ports only
61
+    # ignore-external: Ignore self connect requests to external ports only
62
+    # fail-all:        Fail all self connect requests
63
+    # ignore-all:      Ignore all self connect requests
64
+    #jack.self-connect-mode  = allow
65
+    #jack.locked-process     = true
66
+    #jack.default-as-system  = false
67
+    #jack.fix-midi-events    = true
68
+    #jack.global-buffer-size = false
69
+    #jack.passive-links      = false
70
+    #jack.max-client-ports   = 768
71
+    #jack.fill-aliases       = false
72
+    #jack.writable-input     = false
73
+
74
+}
75
+```
76
+
77
+See `stream.properties` in 
78
+\ref client_conf__stream_properties "pipewire-client.conf(5)" for
79
+an explanation of the generic node properties.
80
+
81
+It is also possible to have per-client settings, see Match Rules below.
82
+
83
+@PAR@ jack.conf  rt.prio
84
+To limit the realtime priority that jack clients can acquire.
85
+
86
+@PAR@ jack.conf  node.latency
87
+To force a specific minimum buffer size for the JACK applications, configure:
88
+```
89
+node.latency = 1024/48000
90
+```
91
+This configures a buffer-size of 1024 samples at 48KHz. If the graph is running at a different sample rate, the buffer-size will be adjusted accordingly.
92
+
93
+@PAR@ jack.conf  node.lock-quantum
94
+To make sure that no automatic quantum is changes while JACK applications are running, configure:
95
+```
96
+node.lock-quantum = true
97
+```
98
+The quantum can then only be changed by metadata or when an application is started with node.force-quantum. JACK Applications will also be able to use jack_set_buffersize() to override the quantum.
99
+
100
+@PAR@ jack.conf  node.force-quantum
101
+To force the quantum to a certain value and avoid changes to it:
102
+```
103
+    node.force-quantum = 1024
104
+```
105
+The quantum can then only be changed by metadata or when an application is started with node.force-quantum (or JACK applications that use jack_set_buffersize() to override the quantum).
106
+
107
+@PAR@ jack.conf  jack.show-monitor
108
+Show the Monitor client and its ports. 
109
+
110
+@PAR@ jack.conf  jack.merge-monitor
111
+\parblock
112
+Exposes the capture ports and monitor ports on the same JACK device client. This is how JACK presents monitor ports to the clients. The default is however *not* to merge them together because this results in more user friendly user interfaces, usually. An extra client with a `Monitor` suffix is created that contains the monitor ports.
113
+
114
+For example, this is (part of) the output of `jack_lsp` with the default setting (`jack.merge-monitor = false`):
115
+
116
+Compare:
117
+
118
+| `jack.merge-monitor = true` | `jack.merge-monitor = false` |
119
+|:--|:--|
120
+| Built-in Audio Analog Stereo:playback_FL | Built-in Audio Analog Stereo:playback_FL
121
+| Built-in Audio Analog Stereo:monitor_FL | Built-in Audio Analog Stereo Monitor:monitor_FL
122
+| Built-in Audio Analog Stereo:playback_FR | Built-in Audio Analog Stereo:playback_FR
123
+| Built-in Audio Analog Stereo:monitor_FR |Built-in Audio Analog Stereo Monitor:monitor_FR
124
+\endparblock
125
+
126
+@PAR@ jack.conf  jack.show-midi
127
+Show the MIDI clients and their ports.
128
+
129
+@PAR@ jack.conf  jack.short-name
130
+\parblock
131
+To use shorter names for the device client names use `jack.short-name = true`. Compare:
132
+
133
+| `jack.short-name = true` | `jack.short-name = false` |
134
+|:--|:--|
135
+| HDA Intel PCH:playback_FL | Built-in Audio Analog Stereo:playback_FL
136
+| HDA Intel PCH Monitor:monitor_FL | Built-in Audio Analog Stereo Monitor:monitor_FL
137
+| HDA Intel PCH:playback_FR | Built-in Audio Analog Stereo:playback_FR
138
+| HDA Intel PCH Monitor:monitor_FR |Built-in Audio Analog Stereo Monitor:monitor_FR
139
+\endparblock
140
+
141
+@PAR@ jack.conf  jack.filter-name
142
+@PAR@ jack.conf  jack.filter-char
143
+Will replace all special characters with `jack.filter-char`. For clients the special characters are ` ().:*$` and for ports they are ` ().*$`. Use this option when a client is not able to deal with the special characters. (and older version of PortAudio was known to use the client and port names as a regex, and thus failing when there are regex special characters in the name).
144
+
145
+@PAR@ jack.conf  jack.self-connect-mode
146
+\parblock
147
+Restrict a client from making connections to and from itself. Possible values and their meaning are summarized as:
148
+
149
+| Value | Behavior
150
+|:--|:--|
151
+| `allow` | Don't restrict self connect requests.
152
+| `fail-external` | Fail self connect requests to external ports only.
153
+| `ignore-external` | Ignore self connect requests to external ports only.
154
+| `fail-all` | Fail all self connect requests.
155
+| `ignore-all` | Ignore all self connect requests.
156
+\endparblock
157
+
158
+@PAR@ jack.conf  jack.locked-process
159
+Make sure the process and callbacks can not be called at the same time. This is the
160
+normal operation but it can be disabled in case a specific client can handle this.
161
+
162
+@PAR@ jack.conf  jack.default-as-system
163
+\parblock
164
+Name the default source and sink as `system` and number the ports to maximize
165
+compatibility with JACK programs.
166
+
167
+| `jack.default-as-system = false` | `jack.default-as-system = true` |
168
+|:--|:--|
169
+| HDA Intel PCH:playback_FL | system:playback_1
170
+| HDA Intel PCH Monitor:monitor_FL | system:monitor_1
171
+| HDA Intel PCH:playback_FR | system:playback_2
172
+| HDA Intel PCH Monitor:monitor_FR | system:monitor_2
173
+\endparblock
174
+
175
+@PAR@ jack.conf  jack.fix-midi-events
176
+Fix NoteOn events with a 0 velocity to NoteOff. This is standard behaviour in JACK and is thus
177
+enabled by default to maximize compatibility. Especially LV2 plugins do not allow NoteOn
178
+with 0 velocity.
179
+
180
+@PAR@ jack.conf  jack.global-buffer-size
181
+When a client has this option, buffersize changes will be applied globally and permanently for all PipeWire clients using the metadata.
182
+
183
+@PAR@ jack.conf  jack.passive-links
184
+Makes JACK clients make passive links. This option only works when the server link-factory was configured with the `allow.link.passive` option.
185
+
186
+@PAR@ jack.conf  jack.max-client-ports
187
+Limit the number of allowed ports per client to this value.
188
+
189
+@PAR@ jack.conf  jack.fill-aliases
190
+Automatically set the port alias1 and alias2 on the ports.
191
+
192
+@PAR@ jack.conf  jack.writable-input
193
+\parblock
194
+Makes the input buffers writable. This is the default because some JACK clients write to the
195
+input buffer. This however can cause corruption in other clients when they are also reading
196
+from the buffer.
197
+
198
+Set this to true to avoid buffer corruption if you are only dealing with non-buggy clients.
199
+\endparblock
200
+
201
+# MATCH RULES  @IDX@ jack.conf
202
+
203
+`jack.rules` provides an `update-props` action that takes an object with properties that are updated
204
+on the client and node object of the jack client.
205
+
206
+Add a `jack.rules` section in the config file like this:
207
+
208
+```
209
+jack.rules = 
210
+    {
211
+        matches = 
212
+            {
213
+                # all keys must match the value. ! negates. ~ starts regex.
214
+                application.process.binary = "jack_simple_client"
215
+            }
216
+        
217
+        actions = {
218
+            update-props = {
219
+                node.latency = 512/48000
220
+            }
221
+        }
222
+    }
223
+    {
224
+        matches = 
225
+            {
226
+                client.name = "catia"
227
+            }
228
+        
229
+        actions = {
230
+            update-props = {
231
+                jack.merge-monitor = true
232
+            }
233
+        }
234
+    }
235
+
236
+```
237
+Will set the latency of jack_simple_client to 512/48000 and makes Catia see the monitor client merged with the playback client.
238
+
239
+# ENVIRONMENT VARIABLES  @IDX@ jack-env
240
+
241
+See \ref page_man_pipewire_1 "pipewire(1)" for common environment
242
+variables. Many of these also apply to JACK client applications.
243
+
244
+Environment variables can be used to control the behavior of the PipeWire JACK client library.
245
+
246
+@PAR@ jack-env  PIPEWIRE_NOJACK
247
+@PAR@ jack-env  PIPEWIRE_INTERNAL
248
+When any of these variables is set, the JACK client library will refuse to open a client. The `PIPEWIRE_INTERNAL` variable is set by the PipeWire main daemon to avoid self connections.
249
+
250
+@PAR@ jack-env  PIPEWIRE_PROPS
251
+Adds/overrides the properties specified in the `jack.conf` file. Check out the output of this:
252
+```
253
+> PIPEWIRE_PROPS='{ jack.short-name=true jack.merge-monitor=true }' jack_lsp
254
+...
255
+HDA Intel PCH:playback_FL
256
+HDA Intel PCH:monitor_FL
257
+HDA Intel PCH:playback_FR
258
+HDA Intel PCH:monitor_FR
259
+...
260
+```
261
+
262
+@PAR@ jack-env  PIPEWIRE_LATENCY
263
+\parblock
264
+```
265
+PIPEWIRE_LATENCY=<samples>/<rate> <application>
266
+```
267
+A quick way to configure the maximum buffer-size for a client. It will run this client with the specified buffer-size (or smaller).
268
+
269
+`PIPEWIRE_LATENCY=256/48000 jack_lsp` is equivalent to `PIPEWIRE_PROPS='{ node.latency=256/48000 }' jack_lsp`
270
+
271
+A better way to start a jack session in a specific buffer-size is to force it with:
272
+```
273
+pw-metadata -n settings 0 clock.force-quantum <quantum>
274
+```
275
+This always works immediately and the buffer size will not change until the quantum is changed back to 0.
276
+\endparblock
277
+
278
+@PAR@ jack-env  PIPEWIRE_RATE
279
+\parblock
280
+```
281
+PIPEWIRE_RATE=1/<rate> <application>
282
+```
283
+
284
+A quick way to configure the rate of the graph. It will try to switch the samplerate of the graph. This can usually only be done with the graph is idle and the rate is part of the allowed sample rates.
285
+
286
+`PIPEWIRE_RATE=1/48000 jack_lsp` is equivalent to `PIPEWIRE_PROPS='{ node.rate=1/48000 }' jack_lsp`
287
+
288
+A better way to start a jack session in a specific rate is to force the rate with:
289
+```
290
+pw-metadata -n settings 0 clock.force-rate <rate>
291
+```
292
+This always works and the samplerate does not need to be in the allowed rates. The rate will also not change until it is set back to 0.
293
+\endparblock
294
+
295
+@PAR@ jack-env  PIPEWIRE_QUANTUM
296
+\parblock
297
+```
298
+PIPEWIRE_QUANTUM=<buffersize>/<rate> <application>
299
+```
300
+
301
+Is similar to using `PIPEWIRE_LATENCY=<buffersize>/<rate>` and `PIPEWIRE_RATE=1/<rate>` (see above), except that it is not just a suggestion but it actively *forces* the graph to change the rate and quantum. It can be used to set both a buffersize and samplerate at the same time.
302
+
303
+When 2 applications force a quantum, the last one wins. When the winning app is stopped, the quantum of the previous app is restored.
304
+\endparblock
305
+
306
+@PAR@ jack-env  PIPEWIRE_LINK_PASSIVE
307
+\parblock
308
+```
309
+PIPEWIRE_LINK_PASSIVE=true qjackctl
310
+```
311
+Make this client create passive links only. All links created by the client will be marked passive and will not keep the sink/source busy.
312
+
313
+You can use this to link filters to devices. When there is no client connected to the filter, only passive links remain between the filter and the device and the device will become idle and suspended.
314
+\endparblock
315
+
316
+@PAR@ jack-env  PIPEWIRE_NODE
317
+\parblock
318
+```
319
+PIPEWIRE_NODE=<id> <application>
320
+```
321
+Will sort the ports so that only the ports of the node with <id> are listed. You can use this to force an application to only deal with the ports of a certain node, for example when auto connecting.
322
+\endparblock
323
+
324
+# AUTHORS
325
+
326
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
327
+PipeWire is available from <$(PACKAGE_URL)>
328
+
329
+# SEE ALSO
330
+
331
+\ref page_man_pw-jack_1 "pw-jack(1)",
332
+\ref page_man_pipewire_conf_5 "pipewire.conf(5)"
333
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-pulse-modules.7.md Added
22
 
1
@@ -0,0 +1,20 @@
2
+\page page_man_pipewire-pulse-modules_7 pipewire-pulse-modules
3
+
4
+PipeWire Pulseaudio modules
5
+
6
+# DESCRIPTION
7
+
8
+\include{doc} pulse-modules.inc
9
+
10
+# BUILT-IN MODULES
11
+
12
+$(PIPEWIRE_PULSE_MODULES)
13
+
14
+# AUTHORS
15
+
16
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
17
+PipeWire is available from <$(PACKAGE_URL)>
18
+
19
+# SEE ALSO
20
+
21
+\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)"
22
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire-pulse.conf.5.md Added
192
 
1
@@ -0,0 +1,190 @@
2
+\page page_man_pipewire-pulse_conf_5 pipewire-pulse.conf
3
+
4
+The PipeWire Pulseaudio server configuration file
5
+
6
+\tableofcontents
7
+
8
+# SYNOPSIS
9
+
10
+*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf*
11
+
12
+*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf*
13
+
14
+*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf*
15
+
16
+*$(PIPEWIRE_CONFDATADIR)/pipewire-pulse.conf.d/*
17
+
18
+*$(PIPEWIRE_CONFIG_DIR)/pipewire-pulse.conf.d/*
19
+
20
+*$XDG_CONFIG_HOME/pipewire/pipewire-pulse.conf.d/*
21
+
22
+# DESCRIPTION
23
+
24
+Configuration for PipeWire's PulseAudio-compatible daemon.
25
+
26
+The configuration file format and lookup logic is the same as for \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
27
+
28
+Drop-in configuration files `pipewire-pulse.conf.d/*.conf` can be used, and are recommended.
29
+See \ref pipewire_conf__drop-in_configuration_files "pipewire.conf(5)".
30
+
31
+# CONFIGURATION FILE SECTIONS  @IDX@ pipewire-pulse.conf
32
+
33
+\par stream.properties
34
+Dictionary. These properties configure the PipeWire Pulseaudio server
35
+properties.
36
+
37
+\par stream.rules
38
+Dictionary. These properties configure the PipeWire Pulseaudio server
39
+properties.
40
+
41
+\par pulse.properties
42
+Dictionary. These properties configure the PipeWire Pulseaudio server
43
+properties.
44
+
45
+\par pulse.cmd
46
+Array of dictionaries. A set of commands to be executed on startup.
47
+
48
+\par pulse.rules
49
+Array of dictionaries. A set of match rules and actions to apply to
50
+clients.
51
+
52
+See \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)"
53
+for the detailed description.
54
+
55
+In addition, the PipeWire context configuration sections 
56
+may also be specified, see \ref page_man_pipewire_conf_5 "pipewire.conf(5)".
57
+
58
+# STREAM PROPERTIES  @IDX@ pipewire-pulse.conf
59
+
60
+The `stream.properties` section contains properties for streams created
61
+by the pipewire-pulse server.
62
+
63
+Available options are described in
64
+\ref client_conf__stream_properties "pipewire-client.conf(5) stream.properties".
65
+
66
+Some of these properties map to the PulseAudio `/etc/pulse/default.pa` config entries as follows:
67
+
68
+| PulseAudio                     | PipeWire              | Notes                |
69
+| ------------------------------ | --------------------- | -------------------- |
70
+| remixing-use-all-sink-channels | channelmix.upmix      |                      |
71
+| remixing-produce-lfe           | channelmix.lfe-cutoff | Set to > 0 to enable |
72
+| remixing-consume-lfe           | channelmix.mix-lfe    |                      |
73
+| lfe-crossover-freq             | channelmix.lfe-cutoff |                      |
74
+
75
+## Example
76
+
77
+```css
78
+stream.properties = {
79
+    #node.latency = 1024/48000
80
+    #node.autoconnect = true
81
+    #resample.disable = false
82
+    #resample.quality = 4
83
+    #monitor.channel-volumes = false
84
+    #channelmix.disable = false
85
+    #channelmix.min-volume = 0.0
86
+    #channelmix.max-volume = 10.0
87
+    #channelmix.normalize = false
88
+    #channelmix.mix-lfe = true
89
+    #channelmix.upmix = true
90
+    #channelmix.upmix-method = psd  # none, simple
91
+    #channelmix.lfe-cutoff = 150.0
92
+    #channelmix.fc-cutoff = 12000.0
93
+    #channelmix.rear-delay = 12.0
94
+    #channelmix.stereo-widen = 0.0
95
+    #channelmix.hilbert-taps = 0
96
+    #dither.noise = 0
97
+    #dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
98
+    #debug.wav-path = ""
99
+}
100
+```
101
+
102
+# STREAM RULES  @IDX@ pipewire-pulse.conf
103
+
104
+The `stream.rules` section works the same as
105
+\ref client_conf__stream_rules "pipewire-client.conf(5) stream.rules".
106
+
107
+# PULSEAUDIO PROPERTIES  @IDX@ pipewire-pulse.conf
108
+
109
+For `pulse.properties` section,
110
+see \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)" 
111
+for available options.
112
+
113
+# PULSEAUDIO RULES  @IDX@ pipewire-pulse.conf
114
+
115
+For each client, a set of rules can be written in `pulse.rules`
116
+section to configure quirks of the client or to force some pulse
117
+specific stream configuration.
118
+
119
+The general look of this section is as follows and follows the layout of
120
+\ref pipewire_conf__match_rules "match rules, see pipewire(1)".
121
+
122
+See \ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)" 
123
+for available options.
124
+
125
+## Example
126
+
127
+```css
128
+pulse.rules = 
129
+    {
130
+        # skype does not want to use devices that don't have an S16 sample format.
131
+        matches = 
132
+             { application.process.binary = "teams" }
133
+             { application.process.binary = "teams-insiders" }
134
+             { application.process.binary = "skypeforlinux" }
135
+        
136
+        actions = { quirks =  force-s16-info  }
137
+    }
138
+    {
139
+        # speech dispatcher asks for too small latency and then underruns.
140
+        matches =  { application.name = "~speech-dispatcher*" } 
141
+        actions = {
142
+            update-props = {
143
+                pulse.min.req          = 1024/48000     # 21ms
144
+                pulse.min.quantum      = 1024/48000     # 21ms
145
+            }
146
+        }
147
+    }
148
+
149
+```
150
+
151
+# PULSEAUDIO COMMANDS  @IDX@ pipewire-pulse.conf
152
+
153
+As part of the server startup procedure you can execute some
154
+additional commands with the `pulse.cmd` section in
155
+`pipewire-pulse.conf`.
156
+
157
+```css
158
+...
159
+pulse.cmd = 
160
+    { cmd = "load-module" args = "module-always-sink" flags =   }
161
+    { cmd = "load-module" args = "module-switch-on-connect" }
162
+    { cmd = "load-module" args = "module-gsettings" flags =  "nofail"  }
163
+
164
+...
165
+```
166
+
167
+Additional commands can also be run via the
168
+\ref pipewire_conf__context_exec "context.exec section, see pipewire.conf(5)".
169
+
170
+Supported commands:
171
+
172
+@PAR@ pipewire-pulse.conf load-module
173
+Load the specified Pulseaudio module on startup, as if using **pactl(1)**
174
+to load the module.
175
+
176
+# PULSEAUDIO MODULES  @IDX@ pipewire-pulse.conf
177
+
178
+For contents of section `pulse.modules`,
179
+see \ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)".
180
+
181
+# AUTHORS
182
+
183
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
184
+PipeWire is available from <$(PACKAGE_URL)>
185
+
186
+# SEE ALSO
187
+
188
+\ref page_module_protocol_pulse "libpipewire-module-protocol-pulse(7)",
189
+\ref page_man_pipewire_conf_5 "pipewire.conf(5)",
190
+\ref page_man_pipewire-pulse_1 "pipewire-pulse(1)",
191
+\ref page_man_pipewire-pulse-modules_7 "pipewire-pulse-modules(7)"
192
pipewire-1.2.0.tar.gz/doc/dox/config/pipewire.conf.5.md Added
676
 
1
@@ -0,0 +1,674 @@
2
+\page page_man_pipewire_conf_5 pipewire.conf
3
+
4
+The PipeWire server configuration file
5
+
6
+\tableofcontents
7
+
8
+# SYNOPSIS
9
+
10
+*$XDG_CONFIG_HOME/pipewire/pipewire.conf*
11
+
12
+*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf*
13
+
14
+*$(PIPEWIRE_CONFDATADIR)/pipewire.conf*
15
+
16
+*$(PIPEWIRE_CONFDATADIR)/pipewire.conf.d/*
17
+
18
+*$(PIPEWIRE_CONFIG_DIR)/pipewire.conf.d/*
19
+
20
+*$XDG_CONFIG_HOME/pipewire/pipewire.conf.d/*
21
+
22
+# DESCRIPTION
23
+
24
+PipeWire is a service that facilitates sharing of multimedia content
25
+between devices and applications.
26
+
27
+On startup, the daemon reads a main configuration file to configure
28
+itself. It executes a series of commands listed in the config file.
29
+
30
+The config file is looked up in the order listed in the
31
+SYNOPSIS(#synopsis). The environment variables `PIPEWIRE_CONFIG_DIR`,
32
+`PIPEWIRE_CONFIG_PREFIX` and `PIPEWIRE_CONFIG_NAME` can be used to
33
+specify an alternative config directory, subdirectory and file
34
+respectively.
35
+
36
+Other PipeWire configuration files generally follow the same lookup
37
+logic, replacing `pipewire.conf` with the name of the particular
38
+config file.
39
+
40
+# DROP-IN CONFIGURATION FILES  @IDX@ pipewire.conf
41
+
42
+All `*.conf` files in the `pipewire.conf.d/` directories are loaded
43
+and merged into the configuration.  Dictionary sections are merged,
44
+overriding properties if they already existed, and array sections are
45
+appended to. The drop-in files have same format as the main
46
+configuration file, but only contain the settings to be modified.
47
+
48
+As the `pipewire.conf` configuration file contains various parts
49
+that must be present for correct functioning, using drop-in files
50
+for configuration is recommended.
51
+
52
+## Example
53
+
54
+A configuration file `~/.config/pipewire/pipewire.conf.d/custom.conf`
55
+to change the value of the `default.clock.min-quantum` setting in `pipewire.conf`:
56
+
57
+```css
58
+context.properties = {
59
+    default.clock.min-quantum = 128
60
+}
61
+```
62
+
63
+# CONFIGURATION FILE FORMAT  @IDX@ pipewire.conf
64
+
65
+The configuration file is in "SPA" JSON format.
66
+
67
+The configuration file contains top-level keys, which are the sections.
68
+The value of a section is either a dictionary, `{ }`, or an
69
+array, ` `. Section and dictionary item declarations 
70
+have `KEY = VALUE` form, and are separated by whitespace.
71
+For example:
72
+
73
+```
74
+context.properties = {  # top-level dictionary section
75
+
76
+    key1 = value  # a simple value
77
+
78
+    key2 = { key1 = value1 key2 = value2 }  # a dictionary with two entries
79
+
80
+    key3 =  value1 value2   # an array with two entries
81
+
82
+    key4 =  { k = v1 } { k = v2 }   # an array of dictionaries
83
+
84
+}
85
+
86
+context.modules =   # top-level array section
87
+
88
+    value1
89
+
90
+    value2
91
+
92
+
93
+```
94
+
95
+The configuration files can also be written in standard JSON syntax,
96
+but for easier manual editing, the relaxed "SPA" variant is allowed.
97
+In "SPA" JSON:
98
+
99
+- `:` to delimit keys and values can be substituted by `=` or a space.
100
+- <tt>\"</tt> around keys and string can be omitted as long as no special
101
+  characters are used in the strings.
102
+- `,` to separate objects can be replaced with a whitespace character.
103
+- `#` can be used to start a comment until the line end
104
+
105
+# CONFIGURATION FILE SECTIONS  @IDX@ pipewire.conf
106
+
107
+\par context.properties
108
+Dictionary. These properties configure the PipeWire instance.
109
+
110
+\par context.spa-libs
111
+Dictionary. Maps plugin features with globs to a spa library.
112
+
113
+\par context.modules
114
+Array of dictionaries. Each entry in the array is a dictionary with the
115
+*name* of the module to load, including optional *args* and *flags*.
116
+Most modules support being loaded multiple times.
117
+
118
+\par context.objects
119
+Array of dictionaries. Each entry in the array is a dictionary
120
+containing the *factory* to create an object from and optional extra
121
+arguments specific to that factory.
122
+
123
+\par context.exec
124
+\parblock
125
+Array of dictionaries. Each entry in the array is dictionary containing
126
+the *path* of a program to execute on startup and optional *args*.
127
+
128
+This array used to contain an entry to start the session manager but
129
+this mode of operation has since been demoted to development aid. Avoid
130
+starting a session manager in this way in production environment.
131
+\endparblock
132
+
133
+\par node.rules
134
+Array of dictionaries. Match rules for modifying node properties
135
+on the server.
136
+
137
+\par device.rules
138
+Array of dictionaries. Match rules for modifying device properties
139
+on the server.
140
+
141
+
142
+# CONTEXT PROPERTIES  @IDX@ pipewire.conf
143
+
144
+Available PipeWire properties in `context.properties` and possible
145
+default values.
146
+
147
+@PAR@ pipewire.conf  clock.power-of-two-quantum = true
148
+The quantum requests from the clients and the final graph quantum are
149
+rounded down to a power of two. A power of two quantum can be more
150
+efficient for many processing tasks.
151
+
152
+@PAR@ pipewire.conf  context.data-loop.library.name.system
153
+The name of the shared library to use for the system functions for the data processing
154
+thread. This can typically be changed if the data thread is running on a realtime
155
+kernel such as EVL.
156
+
157
+@PAR@ pipewire.conf  loop.rt-prio = -1
158
+The priority of the data loops. The data loops are used to schedule the nodes in the graph.
159
+A value of -1 uses the default realtime priority from the module-rt. A value of 0 disables
160
+realtime scheduling for the data loops.
161
+
162
+@PAR@ pipewire.conf  loop.class =  data.rt .. 
163
+An array of classes of the data loops. Normally nodes are assigned to a loop by name or by class.
164
+Nodes are by default assigned to the data.rt class so it is good to have a data loop
165
+of this class as well.
166
+
167
+@PAR@ pipewire.conf  context.num-data-loops = 1
168
+The number of data loops to create. By default 1 data-loop is created and all nodes are
169
+scheduled in this thread. A value of 0 disables the real-time data loops and schedules
170
+all nodes in the main thread. A value of -1 spawns as many data threads as there are
171
+cpu cores.
172
+
173
+@PAR@ pipewire.conf  context.data-loops =  ... 
174
+This controls the data loops that will be created for the context. Is is an array of
175
+data loop specifications, one entry for each data loop to start:
176
+```json
177
+context.data-loops = 
178
+    {
179
+         #library.name.system = support/libspa-support
180
+         loop.rt-prio = -1
181
+         loop.class =  data.rt .. 
182
+         thread.name = data-loop.0
183
+         thread.affinity =  0 1 
184
+    }
185
+    ...
186
+
187
+```
188
+A specific priority, classes and name can be given with loop.rt-prio, loop.class and
189
+thread.name respectively. It is also possible to pin the data loop to specific CPU
190
+cores with the thread.affinity property.
191
+
192
+@PAR@ pipewire.conf  core.daemon = false
193
+Makes the PipeWire process, started with this config, a daemon
194
+process. This means that it will manage and schedule a graph for
195
+clients. You would also want to configure a core.name to give it a
196
+well known name.
197
+
198
+@PAR@ pipewire.conf  core.name = pipewire-0
199
+The name of the PipeWire context. This will also be the name of the
200
+PipeWire socket clients can connect to.
201
+
202
+@PAR@ pipewire.conf  cpu.zero.denormals = false
203
+Configures the CPU to zero denormals automatically. This will be
204
+enabled for the data processing thread only, when enabled.
205
+
206
+@PAR@ pipewire.conf  cpu.vm.name = null
207
+This will be set automatically when the context is created and will
208
+contain the name of the VM. It is typically used to write match rules
209
+to set extra properties.
210
+
211
+@PAR@ pipewire.conf  default.clock.rate  = 48000
212
+The default clock rate determines the real time duration of the
213
+min/max/default quantums. You might want to change the quantums when
214
+you change the default clock rate to maintain the same duration for
215
+the quantums.
216
+
217
+@PAR@ pipewire.conf  default.clock.allowed-rates =  
218
+It is possible to specify up to 32 alternative sample rates. The graph
219
+sample rate will be switched when devices are idle. Note that this is
220
+not enabled by default for now because of various kernel and Bluetooth
221
+issues. Note that the min/max/default quantum values are scaled when
222
+the samplerate changes.
223
+
224
+@PAR@ pipewire.conf  default.clock.min-quantum = 32
225
+Default minimum quantum.
226
+
227
+@PAR@ pipewire.conf  default.clock.max-quantum = 8192
228
+Default maximum quantum.
229
+
230
+@PAR@ pipewire.conf  default.clock.quantum  = 1024
231
+Default quantum used when no client specifies one.
232
+
233
+@PAR@ pipewire.conf  default.clock.quantum-limit = 8192
234
+Maximum quantum to reserve space for. This is the maximum buffer size used
235
+in the graph, regardless of the samplerate.
236
+
237
+@PAR@ pipewire.conf  default.clock.quantum-floor = 4
238
+Minimum quantum to reserve space for. This is the minimum buffer size used
239
+in the graph, regardless of the samplerate.
240
+
241
+@PAR@ pipewire.conf  default.video.width
242
+Default video width
243
+
244
+@PAR@ pipewire.conf  default.video.height
245
+Default video height
246
+
247
+@PAR@ pipewire.conf  default.video.rate.num
248
+Default video rate numerator
249
+
250
+@PAR@ pipewire.conf  default.video.rate.denom
251
+Default video rate denominator
252
+
253
+@PAR@ pipewire.conf  library.name.system = support/libspa-support
254
+The name of the shared library to use for the system functions for the main thread.
255
+
256
+@PAR@ pipewire.conf  link.max-buffers = 64
257
+The maximum number of buffers to negotiate between nodes. Note that version < 3 clients
258
+can only support 16 buffers. More buffers is almost always worse than less, latency
259
+and memory wise.
260
+
261
+@PAR@ pipewire.conf  log.level = 2
262
+The default log level used by the process.
263
+
264
+@PAR@ pipewire.conf  mem.allow-mlock = true
265
+Try to mlock the memory for the realtime processes. Locked memory will
266
+not be swapped out by the kernel and avoid hickups in the processing
267
+threads.
268
+
269
+@PAR@ pipewire.conf  mem.warn-mlock = false
270
+Warn about failures to lock memory. 
271
+
272
+@PAR@ pipewire.conf  mem.mlock-all = false
273
+Try to mlock all current and future memory by the process.
274
+
275
+@PAR@ pipewire.conf  settings.check-quantum = false
276
+Check if the quantum in the settings metadata update is compatible
277
+with the configured limits.
278
+
279
+@PAR@ pipewire.conf  settings.check-rate = false
280
+Check if the rate in the settings metadata update is compatible
281
+with the configured limits.
282
+
283
+@PAR@ pipewire.conf  support.dbus = true
284
+Enable DBus support. This will enable DBus support in the various modules that require
285
+it. Disable this if you want to globally disable DBus support in the process.
286
+
287
+@PAR@ pipewire.conf  vm.overrides = { default.clock.min-quantum = 1024 }
288
+Any property in the vm.overrides property object will override the property
289
+in the context.properties when PipeWire detects it is running in a VM. This
290
+is deprecated, use the context.properties.rules instead.
291
+
292
+@PAR@ pipewire.conf  context.modules.allow-empty = false
293
+By default, a warning is logged when there are no context.modules loaded because this
294
+likely indicates there is a problem. Some applications might load the modules themselves
295
+and when they set this property to true, no warning will be logged.
296
+
297
+The context properties may also contain custom values. For example,
298
+the `context.modules` and `context.objects` sections can declare
299
+additional conditions that control whether a module or object is loaded
300
+depending on what properties are present.
301
+
302
+# SPA LIBRARIES  @IDX@ pipewire.conf
303
+
304
+SPA plugins are loaded based on their factory-name. This is a well
305
+known name that uniquely describes the features that the plugin should
306
+have. The `context.spa-libs` section provides a mapping between the
307
+factory-name and the plugin where the factory can be found.
308
+
309
+Factory names can contain a wildcard to group several related factories into one
310
+plugin. The plugin is loaded from the first matching factory-name.
311
+
312
+## Example
313
+
314
+```json
315
+context.spa-libs = {
316
+    audio.convert.* = audioconvert/libspa-audioconvert
317
+    avb.*           = avb/libspa-avb
318
+    api.alsa.*      = alsa/libspa-alsa
319
+    api.v4l2.*      = v4l2/libspa-v4l2
320
+    api.libcamera.* = libcamera/libspa-libcamera
321
+    api.bluez5.*    = bluez5/libspa-bluez5
322
+    api.vulkan.*    = vulkan/libspa-vulkan
323
+    api.jack.*      = jack/libspa-jack
324
+    support.*       = support/libspa-support
325
+    video.convert.* = videoconvert/libspa-videoconvert
326
+}
327
+```
328
+
329
+# MODULES  @IDX@ pipewire.conf
330
+
331
+PipeWire modules to be loaded. See
332
+\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)".
333
+
334
+```json
335
+context.modules = 
336
+    #{ name = MODULENAME
337
+    #    ( args  = { KEY = VALUE ... } )
338
+    #    ( flags =  ( ifexists ) ( nofail )  )
339
+    #    ( condition =  { KEY = VALUE ... } ...  )
340
+    #}
341
+    #
342
+
343
+```
344
+
345
+\par name
346
+Name of module to be loaded
347
+
348
+\par args = { }
349
+Arguments passed to the module
350
+
351
+\par flags =  
352
+Loading flags. `ifexists` to only load module if it exists,
353
+and `nofail` to not fail PipeWire startup if the module fails to load.
354
+
355
+\par condition =  
356
+A \ref pipewire_conf__match_rules "match rule" `matches` condition.
357
+The module is loaded only if one of the expressions in the array matches
358
+to a context property.
359
+
360
+# CONTEXT OBJECTS  @IDX@ pipewire.conf
361
+
362
+The `context.objects` section allows you to make some objects from factories (usually created
363
+by loading modules in `context.modules`).
364
+
365
+```json
366
+context.objects = 
367
+    #{ factory = <factory-name>
368
+    #    ( args  = { <key> = <value> ... } )
369
+    #    ( flags =  ( nofail )  )
370
+    #    ( condition =  { <key> = <value> ... } ...  )
371
+    #}
372
+
373
+```
374
+This section can be used to make nodes or links between nodes.
375
+
376
+\par factory
377
+Name of the factory to create the object.
378
+
379
+\par args = { }
380
+Arguments passed to the factory.
381
+
382
+\par flags =  
383
+Flag `nofail` to not fail PipeWire startup if the object fails to load.
384
+
385
+\par condition =  
386
+A \ref pipewire_conf__match_rules "match rule" `matches` condition.
387
+The object is created only if one of the expressions in the array matches
388
+to a context property.
389
+
390
+## Example
391
+
392
+This fragment creates a new dummy driver node, but only if
393
+`core.daemon` property is true:
394
+
395
+```json
396
+context.objects = 
397
+    { factory = spa-node-factory
398
+      args = {
399
+          factory.name    = support.node.driver
400
+          node.name       = Dummy-Driver
401
+          node.group      = pipewire.dummy
402
+          priority.driver = 20000
403
+      },
404
+      condition =  { core.daemon = true } 
405
+    }
406
+
407
+```
408
+
409
+# COMMAND EXECUTION  @IDX@ pipewire.conf
410
+
411
+The `context.exec` section can be used to start arbitrary commands as
412
+part of the initialization of the PipeWire program.
413
+
414
+```json
415
+context.exec = 
416
+    #{   path = <program-name>
417
+    #    ( args = "<arguments>" |  <arg1> <arg2> ...  )
418
+    #    ( condition =  { <key> = <value> ... } ...  )
419
+    #}
420
+
421
+```
422
+
423
+\par path
424
+Program to execute.
425
+
426
+\par args
427
+Arguments to the program.
428
+
429
+\par condition
430
+A \ref pipewire_conf__match_rules "match rule" `matches` condition.
431
+The object is created only if one of the expressions in the array matches
432
+to a context property.
433
+
434
+## Example
435
+
436
+The following fragment executes a pactl command with the given arguments:
437
+
438
+```json
439
+context.exec = 
440
+    { path = "pactl" args = "load-module module-always-sink" }
441
+
442
+```
443
+
444
+# MATCH RULES  @IDX@ pipewire.conf
445
+
446
+Some configuration file sections contain match rules. This makes it
447
+possible to perform some action when an object (usually a node or
448
+stream) is created/updated that matches certain properties.
449
+
450
+The general rules object follows the following pattern:
451
+```json
452
+<rules> = 
453
+    {
454
+        matches = 
455
+            # any of the following sets of properties are matched, if
456
+            # any matches, the actions are executed
457
+            {
458
+                # <key> = <value>
459
+                # all keys must match the value. ! negates. ~ starts regex.
460
+                #application.process.binary = "teams"
461
+                #application.name = "~speech-dispatcher.*"
462
+
463
+                # Absence of property can be tested by comparing to null
464
+                #pipewire.sec.flatpak = null
465
+            }
466
+            {
467
+                # more matches here...
468
+       }
469
+       ...
470
+        
471
+        actions = {
472
+            <action-name> = <action value>
473
+       ...
474
+        }
475
+    }
476
+
477
+```
478
+Match rules are an array of rules.
479
+
480
+A rule is always a JSON object with two keys: matches and actions. The matches key is used to
481
+define the conditions that need to be met for the rule to be evaluated as true, and the actions
482
+key is used to define the actions that are performed when the rule is evaluated as true.
483
+
484
+The matches key is always a JSON array of objects, where each object defines a condition that needs
485
+to be met. Each condition is a list of key-value pairs, where the key is the name of the property
486
+that is being matched, and the value is the value that the property needs to have. Within a condition,
487
+all the key-value pairs are combined with a logical AND, and all the conditions in the matches
488
+array are combined with a logical OR.
489
+
490
+The actions key is always a JSON object, where each key-value pair defines an action that is
491
+performed when the rule is evaluated as true. The action name is specific to the rule and is
492
+defined by the rule’s documentation, but most frequently you will see the update-props action,
493
+which is used to update the properties of the matched object.
494
+
495
+In the matches array, it is also possible to use regular expressions to match property values.
496
+For example, to match all nodes with a name that starts with my_, you can use the following condition:
497
+
498
+```
499
+matches = 
500
+  {
501
+    node.name = "~my_.*"
502
+  }
503
+
504
+```
505
+
506
+The ~ character signifies that the value is a regular expression. The exact syntax of the regular
507
+expressions is the POSIX extended regex syntax, as described in the regex (7) man page. 
508
+
509
+In addition to regular expressions, you may also use the ! character to negate a condition. For
510
+example, to match all nodes with a name that does not start with my_, you can use the following condition:
511
+
512
+```
513
+matches = 
514
+  {
515
+    node.name = "!~my_.*"
516
+  }
517
+
518
+```
519
+
520
+The ! character can be used with or without a regular expression. For example, to match all
521
+nodes with a name that is not equal to my_node, you can use the following condition:
522
+
523
+```
524
+matches = 
525
+  {
526
+    node.name = "!my_node"
527
+  }
528
+
529
+```
530
+
531
+The null value has a special meaning; it checks if the property is not available
532
+(or unset). To check if a property is not set:
533
+
534
+```
535
+matches = 
536
+  {
537
+    node.name = null
538
+  }
539
+
540
+```
541
+
542
+To check the existence of a property, one can use the !null condition, for example:
543
+
544
+```
545
+matches = 
546
+  {
547
+    node.name = "!null"
548
+  }
549
+  {
550
+    node.name = !null            # simplified syntax
551
+  }
552
+
553
+```
554
+To handle the "null" string, one needs to escape the string. For example, to check
555
+if a property has the string value "null", use:
556
+
557
+```
558
+matches = 
559
+  {
560
+    node.name = "null"
561
+  }
562
+
563
+```
564
+To handle anything but the "null" string, use:
565
+
566
+```
567
+matches = 
568
+  {
569
+    node.name = "!\"null\""
570
+  }
571
+  {
572
+    node.name = !"null"      # simplified syntax
573
+  }
574
+
575
+```
576
+
577
+
578
+# CONTEXT PROPERTIES RULES  @IDX@ pipewire.conf
579
+
580
+`context.properties.rules` can be used to dynamically update the properties
581
+based on other properties.
582
+
583
+A typical case is to update custom settings when running inside a VM.
584
+The `cpu.vm.name` is automatically set when running in a VM with the name of
585
+the VM. A match rule can be written to set custom properties like this:
586
+
587
+```
588
+context.properties.rules = 
589
+    {   matches =  { cpu.vm.name = !null } 
590
+        actions = {
591
+            update-props = {
592
+                # These overrides are only applied when running in a vm.
593
+                default.clock.min-quantum = 1024
594
+       }
595
+        }
596
+    }
597
+}
598
+```
599
+
600
+# NODE RULES  @IDX@ pipewire.conf
601
+
602
+The node.rules are evaluated every time the properties on a node are set
603
+or updated. This can be used on the server side to override client set
604
+properties on arbitrary nodes.
605
+
606
+`node.rules` provides an `update-props` action that takes an object with
607
+properties that are updated on the node object.
608
+
609
+Add a `node.rules` section in the config file like this:
610
+
611
+```
612
+node.rules = 
613
+    {
614
+        matches = 
615
+            {
616
+                # all keys must match the value. ! negates. ~ starts regex.
617
+                client.name = "jack_simple_client"
618
+            }
619
+        
620
+        actions = {
621
+            update-props = {
622
+                node.force-quantum = 512
623
+            }
624
+        }
625
+    }
626
+
627
+```
628
+
629
+Will set the `node.force-quantum` property of `jack_simple_client` to 512.
630
+
631
+# DEVICE RULES  @IDX@ pipewire.conf
632
+
633
+The device.rules are evaluated every time the properties on a device are set
634
+or updated. This can be used on the server side to override client set
635
+properties on arbitrary devices.
636
+
637
+`device.rules` provides an `update-props` action that takes an object with
638
+properties that are updated on the device object.
639
+
640
+Add a `device.rules` section in the config file like this:
641
+
642
+```
643
+device.rules = 
644
+    {
645
+        matches = 
646
+            {
647
+                # all keys must match the value. ! negates. ~ starts regex.
648
+                device.name = ""v4l2_device.pci-0000_00_14.0-usb-0_1.2_1.0
649
+            }
650
+        
651
+        actions = {
652
+            update-props = {
653
+                device.description = "My Webcam"
654
+            }
655
+        }
656
+    }
657
+
658
+```
659
+
660
+Will set the `device.description` property of the device with the given `device.name`
661
+to "My Webcam".
662
+
663
+
664
+# AUTHORS
665
+
666
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
667
+PipeWire is available from <$(PACKAGE_URL)>
668
+
669
+# SEE ALSO
670
+
671
+\ref page_man_pipewire_1 "pipewire(1)",
672
+\ref page_man_pw-mon_1 "pw-mon(1)",
673
+\ref page_man_libpipewire-modules_7 "libpipewire-modules(7)"
674
+\ref page_man_pipewire-pulse_conf_5 "pipewire-pulse.conf(5)"
675
+\ref page_man_pipewire-client_conf_5 "pipewire-client.conf(5)"
676
pipewire-1.0.1.tar.bz2/doc/dox/index.dox -> pipewire-1.2.0.tar.gz/doc/dox/index.dox Changed
30
 
1
@@ -11,6 +11,16 @@
2
 See \ref page_overview for an overview of PipeWire and \ref page_design
3
 for the design principles guiding PipeWire.
4
 
5
+# Documentation
6
+
7
+- \ref page_config
8
+- \ref page_programs
9
+- \ref page_modules
10
+- \ref page_pulse_modules
11
+
12
+See our Wiki(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) for
13
+more information on how to configure and use PipeWire.
14
+
15
 # Components
16
 
17
 PipeWire ships with the following components:
18
@@ -27,11 +37,6 @@
19
 
20
 See \ref page_api.
21
 
22
-# More Documentation
23
-
24
-See our Wiki(https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) for
25
-More information on how to configure and use PipeWire.
26
-
27
 # Resources
28
 
29
 - PipeWire and AGL(https://wiki.automotivelinux.org/_media/pipewire_agl_20181206.pdf)
30
pipewire-1.0.1.tar.bz2/doc/dox/internals/access.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/access.dox Changed
33
 
1
@@ -82,15 +82,14 @@
2
 
3
 If the property is not set it will go through a set of checks to determine
4
 the permissions for a client. See the \ref page_module_access documentation
5
-for details, particularly on the values documented below. Depending on the
6
-value of the \ref PW_KEY_ACCESS property one the following happens:
7
+for details.
8
 
9
-- `"allowed"`, `"unrestricted"`:  ALL permissions are set on the core
10
+Depending on the resolution, it grants permissions to the client as follows:
11
+
12
+- `"unrestricted"`:  ALL permissions are set on the core
13
     object and the client will be able to resume.
14
-- `"restricted"`, `"flatpak"`, `"$access.force"`:  No permissions are set on
15
-    the core object and the client will be suspended.
16
-- `"rejected"`: An error is sent to the client and the client is
17
-    suspended.
18
+- any other value: No permissions are set on the core object
19
+  and the client will be suspended.
20
 
21
 As detailed above, the client may be suspended. In that case the session
22
 manager or another client is required to configure permissions on the object
23
@@ -101,8 +100,7 @@
24
 The session manager listens for new clients to appear. It will use the
25
 \ref PW_KEY_ACCESS property to determine what to do.
26
 
27
-For clients that are suspended with `"restricted"`, `"flatpak"` or
28
-`"$access.force"` access, the session manager needs to set permissions on the
29
+For clients that are not unrestricted, the session manager needs to set permissions on the
30
 client for the various PipeWire objects in the graph that it is allowed to
31
 interact with.  To resume a client, the session manager needs to set
32
 permission `R` on the core object for the client.
33
pipewire-1.0.1.tar.bz2/doc/dox/internals/daemon.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/daemon.dox Changed
10
 
1
@@ -139,7 +139,7 @@
2
   shell globs to match on log topics and the levels are the respective
3
   log level to set for that topic. Globs are applied in order and a matching
4
   glob overrides an earlier glob for that category. A level without a glob
5
-  prefix will set the global log level and is a more preformant version of
6
+  prefix will set the global log level and is a more performant version of
7
   `*:<level>`.  For example, `PIPEWIRE_DEBUG=E,mod.*:D,mod.foo:X` enables global error messages,
8
   debugging on all modules but no messages on the foo module.
9
 - `<level>` specifies the log level:
10
pipewire-1.0.1.tar.bz2/doc/dox/internals/design.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/design.dox Changed
10
 
1
@@ -51,7 +51,7 @@
2
 The functionality of the server is implemented and extended with modules and
3
 extensions. Modules are server side bits of logic that hook into various
4
 places to provide extra features. This mostly means controlling the processing
5
-graph in some way. See \ref page_pipewire_modules for a list of current
6
+graph in some way. See \ref page_modules for a list of current
7
 modules.
8
 
9
 Extensions are the client side version of the modules. Most extensions provide
10
pipewire-1.0.1.tar.bz2/doc/dox/internals/library.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/library.dox Changed
10
 
1
@@ -56,7 +56,7 @@
2
 # Versioning
3
 
4
 All interfaces have a version number. The maximum supported version
5
-number of an interface is advertized in the registry global event.
6
+number of an interface is advertised in the registry global event.
7
 
8
 A client asks for a specific version of an interface when it binds
9
 to them. It is the task of the server to adapt to the version of the
10
pipewire-1.0.1.tar.bz2/doc/dox/internals/protocol.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/protocol.dox Changed
51
 
1
@@ -157,7 +157,7 @@
2
 
3
 ### Core::Error (Opcode 4)
4
 
5
-An error occured in an object on the client.
6
+An error occurred in an object on the client.
7
 
8
 ```
9
    Struct(
10
@@ -274,11 +274,11 @@
11
 
12
 ## Core Events
13
 
14
-Core events are emited by the server.
15
+Core events are emitted by the server.
16
 
17
 ### Core::Info (Opcode 0)
18
 
19
-Emited by the server upon connection with the more information about the
20
+Emitted by the server upon connection with the more information about the
21
 server.
22
 
23
 ```
24
@@ -309,7 +309,7 @@
25
 
26
 ### Core::Done (Opcode 1)
27
 
28
-Emited as a result of a client Sync method.
29
+Emitted as a result of a client Sync method.
30
 
31
 ```
32
    Struct(
33
@@ -322,7 +322,7 @@
34
 
35
 ### Core::Ping (Opcode 2)
36
 
37
-Emited by the server when it wants to check if a client is alive or ensure
38
+Emitted by the server when it wants to check if a client is alive or ensure
39
 that it has processed the previous events.
40
 
41
 ```
42
@@ -1236,7 +1236,7 @@
43
 - type: the plane memory type
44
 - memfd: the plane memfd
45
 - mapoffset: the start offset of where the buffer memory starts
46
-- maxsize: the maxiumum size of the memory.
47
+- maxsize: the maximum size of the memory.
48
 
49
 ## ClientNode events
50
 
51
pipewire-1.0.1.tar.bz2/doc/dox/internals/scheduling.dox -> pipewire-1.2.0.tar.gz/doc/dox/internals/scheduling.dox Changed
10
 
1
@@ -188,7 +188,7 @@
2
 
3
 # Remote nodes.
4
 
5
-For remote nodes, the eventfd and the activation is transfered from the server
6
+For remote nodes, the eventfd and the activation is transferred from the server
7
 to the client.
8
 
9
 This means that writing to the remote client eventfd will wake the client directly
10
pipewire-1.0.1.tar.bz2/doc/dox/modules.dox -> pipewire-1.2.0.tar.gz/doc/dox/modules.dox Changed
17
 
1
@@ -69,6 +69,7 @@
2
 - \subpage page_module_metadata
3
 - \subpage page_module_netjack2_driver
4
 - \subpage page_module_netjack2_manager
5
+- \subpage page_module_parametric_equalizer
6
 - \subpage page_module_pipe_tunnel
7
 - \subpage page_module_portal
8
 - \subpage page_module_profiler
9
@@ -86,6 +87,7 @@
10
 - \subpage page_module_rtp_session
11
 - \subpage page_module_rt
12
 - \subpage page_module_session_manager
13
+- \subpage page_module_snapcast_discover
14
 - \subpage page_module_vban_recv
15
 - \subpage page_module_vban_send
16
 - \subpage page_module_x11_bell
17
pipewire-1.0.1.tar.bz2/doc/dox/overview.dox -> pipewire-1.2.0.tar.gz/doc/dox/overview.dox Changed
189
 
1
@@ -1,42 +1,149 @@
2
 /** \page page_overview Overview
3
 
4
-PipeWire is a new low-level multimedia framework designed from scratch that
5
-aims to provide:
6
-
7
-- Graph based processing.
8
-- Support for out-of-process processing graphs with minimal overhead.
9
-- Flexible and extensible media format negotiation and buffer allocation.
10
-- Hard real-time capable plugins.
11
-- Achieve very low-latency for both audio and video processing.
12
-
13
-The framework is used to build a modular daemon that can be configured to:
14
-
15
-- Be a low-latency audio server with features like PulseAudio and/or JACK.
16
-- A video capture server that can manage hardware video capture devices and
17
-  provide access to them.
18
-- A central hub where video can be made available for other applications
19
-  such as the gnome-shell screencast API.
20
-
21
-
22
-# Motivation
23
-
24
-Linux has no unified framework for exchanging multimedia content between
25
-applications or even devices. In most cases, developers realized that
26
-a user-space daemon is needed to make this possible:
27
-
28
-- For video content, we typically rely on the compositor to render our
29
-  data.
30
-- For video capture, we usually go directly to the hardware devices, with
31
-  all security implications and inflexible routing that this brings.
32
-- For consumer audio, we use PulseAudio to manage and mix multiple streams
33
-  from clients.
34
-- For Pro audio, we use JACK to manage the graph of nodes.
35
-
36
-None of these solutions (except perhaps to some extent Wayland) however 
37
-were designed to support the security features that are required when
38
-dealing with flatpaks or other containerized applications. PipeWire
39
-aims to solve this problem and provides a unified framework to run both
40
-consumer and pro audio as well as video capture and processing in a
41
-secure way.
42
+# Concepts
43
+
44
+## The PipeWire Server
45
+
46
+PipeWire is a graph-based processing framework, that focuses on handling multimedia data (audio, video and MIDI mainly).
47
+
48
+A PipeWire graph is composed of nodes.
49
+Each node takes an arbitrary number of inputs called ports, does some processing over this multimedia data, and sends data out of its output ports.
50
+The edges in the graph are here called links.
51
+They are capable of connecting an output port to an input port.
52
+
53
+Nodes can have an arbitrary number of ports.
54
+A node with only output ports is often called a source, and a sink is a node that only possesses input ports.
55
+
56
+The PipeWire server provides the implementation of some of these nodes itself.
57
+Most importantly, it uses alsa-lib like any other ALSA client to expose statically configured ALSA devices as nodes.
58
+For example
59
+
60
+- a stereo ALSA PCM playback device can appear as a sink with two input ports: front-left and front-right or
61
+- a virtual ALSA device, to which clients which attempt to use ALSA directly connect, can appear as a source with two output ports: front-left and front right.
62
+
63
+Similar mechanisms exist to interface with and accommodate applications which use JACK or Pulseaudio.
64
+
65
+NOTE: `pw-jack` modifies the `LD_LIBRARY_PATH` environment variable so that applications will load PipeWire’s reimplementation of the JACK client libraries instead of JACK’s own libraries. This results in JACK clients being redirected to PipeWire.
66
+
67
+Other nodes are implemented by PipeWire clients.
68
+
69
+## The PipeWire clients
70
+
71
+PipeWire clients can be any process.
72
+They can speak to the PipeWire server through a UNIX domain socket using the PipeWire native protocol.
73
+Besides implementing nodes, they may control the graph.
74
+
75
+### Graph control
76
+
77
+The PipeWire server itself does not perform any management of the graph;
78
+context-dependent behaviour such as monitoring for new ALSA devices, and configuring them so that they appear as nodes, or linking nodes is not done automatically.
79
+It rather provides an API that allows spawning, linking and controlling these nodes.
80
+This API is then relied upon by clients to control the graph structure, without having to worry about the graph execution process.
81
+
82
+A recommended pattern that is often used is a single client be a daemon that deals with the session and policy management. Two implementations are known as of today:
83
+
84
+- pipewire-media-session, which was the first implementation of a session manager.c
85
+  Today, it is used mainly in debugging scenarios.
86
+- WirePlumber, which takes a modular approach:
87
+  It provides another, higher-level API compared to the PipeWire one, and runs Lua scripts that implement the management logic using the said API.
88
+  It ships with default scripts and configuration that handle linking policies as well as monitoring and automatic spawning of ALSA, bluez, libcamera and v4l2 devices.
89
+  The API is available for any process, not only from WirePlumber’s Lua scripts.
90
+ 
91
+### Node implementation 
92
+ 
93
+With the nodes which they implement, clients can send multimedia data into the graph or obtain multimedia data from the graph.
94
+A client can create multiple PipeWire nodes.
95
+That allows one to create more complex applications;
96
+a browser would for example be able to create a node per tab that requests the ability to play audio, letting the session manager handle the routing:
97
+This allows the user to route different tab sources to different sinks.
98
+Another example would be an application that requires many inputs.
99
+
100
+## API Semantics
101
+
102
+The current state of the PipeWire server and its capabilities, and the PipeWire graph are exposed towards clients -- including introspection tools like `pw-dump` -- as a collection of objects, each of which has a specific type.
103
+These objects have associated parameters, and properties, methods, events, and permissions.
104
+
105
+Parameters of an object are data with a specific, well defined meaning, which can be modified and read-out in a controlled fashion through the PipeWire API.
106
+They are used to configure the object at run-time.
107
+Parameters are the key that allow WirePlumber to negotiate data formats and port configuration with nodes by providing information such as:
108
+
109
+- Multiple, supported sample rates
110
+- Channel count
111
+- Positions sample format
112
+- Available monitor ports
113
+
114
+Properties of an object are additional data which have been attached on the behalf of modules and of which the PipeWire server has no native understanding.
115
+Certain properties are, by convention, expected for specific object types.
116
+
117
+Each object type has a list of methods that it needs to implement.
118
+
119
+The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and four flags. The four flags are:
120
+
121
+- Read: the object can be seen and events can be received;
122
+- Write: the object can be modified, usually through methods (which requires the execute flag)
123
+- eXecute: methods can be called;
124
+- Metadata: metadata can be set on the object.
125
+- Link: any link can be made even to a port that is not visible by the owner of the port.
126
+
127
+### Object types
128
+
129
+The following are the known types and their most important, specialized parameters and methods:
130
+
131
+#### Core
132
+
133
+The core is the heart of the PipeWire server.
134
+There can only be one core per server and it has the identifier zero.
135
+It represents global properties of the server.
136
+
137
+#### Clients
138
+
139
+A client object is the representation of an open connection with a client process with the server.
140
+
141
+#### Modules
142
+
143
+Modules are dynamic libraries that are loaded at run time in the clients and in the server and do arbitrary things, such as creating devices or provide methods to create links, nodes, etc.
144
+
145
+Modules in PipeWire can only be loaded in their own process. A client, for example, can not load a module in the server.
146
+
147
+#### Nodes
148
+
149
+Nodes are the core data processing entities in PipeWire.
150
+They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters).
151
+Notes have a method `process`, which eats up data from input ports and provides data for each output port.
152
+
153
+#### Ports
154
+
155
+Ports are the entry and exit point of data for a Node.
156
+A port can either be used for input or output (but not both).
157
+For nodes that work with audio, one type of configuration is whether they have `dsp` ports or a `passthrough` port.
158
+In `dsp` mode, there is one port for channel of multichannel audio (so two ports for stereo audio, for example), and data is always in 32-bit floating point format.
159
+In `passthrough` mode, there is one port for multichannel data in a format that is negotiated between ports.
160
+
161
+#### Links
162
+
163
+Data flows between nodes when there is a Link between their ports.
164
+Links may be `"passive"` in which case the existence of the link does not automatically cause data to flow between those nodes (some link in the graph must be `"active"` for the graph to have data flow).
165
+
166
+#### Devices
167
+
168
+A device is a handle representing an underlying API, which is then used to create nodes or other devices.
169
+Examples of devices are ALSA PCM cards or V4L2 devices.
170
+A device has a profile, which allows one to configure them.
171
+
172
+#### Factories
173
+
174
+A factory is an object whose sole capability is to create other objects.
175
+Once a factory is created, it can only emit the type of object it declared.
176
+Those are most often delivered as a module: the module creates the factory and stays alive to keep it accessible for clients.
177
+
178
+### Common parameters and methods
179
+
180
+Every object implement at least the add_listener method, that allows any client to register event listeners.
181
+Events are used through the PipeWire API to expose information about an object that might change over time (the state of a node for example).
182
+
183
+## Context
184
+
185
+The PipeWire server and PipeWire clients use the PipeWire API through their respective `pw_context`, the so called PipeWire context.
186
+When a PipeWire context is created, it finds and parses a configuration file from the filesystem according to the rules of loading configuration files.
187
 
188
 */
189
pipewire-1.0.1.tar.bz2/doc/dox/programs/index.md -> pipewire-1.2.0.tar.gz/doc/dox/programs/index.md Changed
29
 
1
@@ -3,13 +3,11 @@
2
 Manual pages:
3
 
4
 - \subpage page_man_pipewire_1
5
-- \subpage page_man_pipewire_conf_5
6
 - \subpage page_man_pipewire-pulse_1
7
-- \subpage page_man_pipewire-pulse_conf_5
8
-- \subpage page_man_pipewire-pulse-modules_7
9
 - \subpage page_man_pw-cat_1
10
 - \subpage page_man_pw-cli_1
11
 - \subpage page_man_pw-config_1
12
+- \subpage page_man_pw-container_1
13
 - \subpage page_man_pw-dot_1
14
 - \subpage page_man_pw-dump_1
15
 - \subpage page_man_pw-jack_1
16
@@ -19,5 +17,11 @@
17
 - \subpage page_man_pw-mididump_1
18
 - \subpage page_man_pw-mon_1
19
 - \subpage page_man_pw-profiler_1
20
+- \subpage page_man_pw-reserve_1
21
 - \subpage page_man_pw-top_1
22
-- \subpage page_man_libpipewire-modules_7
23
+- \subpage page_man_pw-v4l2_1
24
+- \subpage page_man_spa-acp-tool_1
25
+- \subpage page_man_spa-inspect_1
26
+- \subpage page_man_spa-json-dump_1
27
+- \subpage page_man_spa-monitor_1
28
+- \subpage page_man_spa-resample_1
29
pipewire-1.0.1.tar.bz2/doc/dox/programs/pipewire-pulse.1.md -> pipewire-1.2.0.tar.gz/doc/dox/programs/pipewire-pulse.1.md Changed
26
 
1
@@ -28,6 +28,24 @@
2
 \par -c | \--config=FILE
3
 Load the given config file (Default: pipewire-pulse.conf).
4
 
5
+# ENVIRONMENT VARIABLES
6
+
7
+The generic \ref pipewire-env "pipewire(1) environment variables"
8
+are supported.
9
+
10
+In addition:
11
+
12
+@PAR@ pulse-env  PULSE_RUNTIME_PATH
13
+
14
+@PAR@ pulse-env  XDG_RUNTIME_DIR
15
+Directory where to create the native protocol pulseaudio socket.
16
+
17
+@PAR@ pulse-env  PULSE_LATENCY_MSEC
18
+Extra buffering latency in milliseconds. This controls buffering
19
+logic in `libpulse` and may be set for PulseAudio client applications
20
+to adjust their buffering. (Setting it on the `pipewire-pulse` server
21
+has no effect.)
22
+
23
 # AUTHORS
24
 
25
 The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
26
pipewire-1.0.1.tar.bz2/doc/dox/programs/pipewire.1.md -> pipewire-1.2.0.tar.gz/doc/dox/programs/pipewire.1.md Changed
218
 
1
@@ -2,6 +2,8 @@
2
 
3
 The PipeWire media server
4
 
5
+\tableofcontents
6
+
7
 # SYNOPSIS
8
 
9
 **pipewire** \*options*\
10
@@ -29,6 +31,207 @@
11
 \par -c | \--config=FILE
12
 Load the given config file (Default: pipewire.conf).
13
 
14
+\par -P | \--properties=PROPS
15
+Add the given properties as a SPA JSON object to the context.
16
+
17
+# RUNTIME SETTINGS  @IDX@ pipewire
18
+
19
+A PipeWire daemon will also expose a settings metadata object that can
20
+be used to change some settings at runtime.
21
+
22
+Normally these settings can bypass any of the restrictions listed in
23
+the config options above, such as quantum and samplerate values.
24
+
25
+The settings can be modified using \ref page_man_pw-metadata_1 "pw-metadata(1)":
26
+```
27
+pw-metadata -n settings                  # list settings
28
+pw-metadata -n settings 0                # list server settings
29
+pw-metadata -n settings 0 log.level 2    # modify a server setting
30
+```
31
+
32
+@PAR@ pipewire-settings  log.level = INTEGER
33
+Change the log level of the PipeWire daemon.
34
+
35
+@PAR@ pipewire-settings  clock.rate = INTEGER
36
+The default samplerate.
37
+
38
+@PAR@ pipewire-settings  clock.allowed-rates =  RATE1 RATE2... 
39
+The allowed samplerates.
40
+
41
+@PAR@ pipewire-settings  clock.force-rate = INTEGER
42
+\parblock
43
+Temporarily forces the graph to operate in a fixed sample rate.
44
+Both DSP processing and devices will switch to the new rate immediately.
45
+Running streams (PulseAudio, native and ALSA applications) will automatically
46
+resample to match the new rate.
47
+
48
+Set the value to 0 to allow the sample rate to vary again.
49
+\endparblock
50
+
51
+@PAR@ pipewire-settings  clock.quantum = INTEGER
52
+The default quantum (buffer size).
53
+
54
+@PAR@ pipewire-settings  clock.min-quantum = INTEGER
55
+Smallest quantum to be used.
56
+
57
+@PAR@ pipewire-settings  clock.max-quantum = INTEGER
58
+Largest quantum to be used.
59
+
60
+@PAR@ pipewire-settings  clock.force-quantum = INTEGER
61
+\parblock
62
+Temporarily force the graph to operate in a fixed quantum.
63
+
64
+Set the value to 0 to allow the quantum to vary again.
65
+\endparblock
66
+
67
+# ENVIRONMENT VARIABLES  @IDX@ pipewire-env
68
+
69
+## Socket directories
70
+
71
+@PAR@ pipewire-env PIPEWIRE_RUNTIME_DIR
72
+
73
+@PAR@ pipewire-env XDG_RUNTIME_DIR
74
+
75
+@PAR@ pipewire-env USERPROFILE
76
+Used to find the PipeWire socket on the server (and native clients).
77
+
78
+@PAR@ pipewire-env PIPEWIRE_CORE
79
+Name of the socket to make.
80
+
81
+@PAR@ pipewire-env PIPEWIRE_REMOTE
82
+Name of the socket to connect to.
83
+
84
+@PAR@ pipewire-env PIPEWIRE_DAEMON
85
+If set to true then the process becomes a new PipeWire server.
86
+
87
+## Config directories, config file name and prefix
88
+
89
+@PAR@ pipewire-env PIPEWIRE_CONFIG_DIR
90
+
91
+@PAR@ pipewire-env XDG_CONFIG_HOME
92
+
93
+@PAR@ pipewire-env HOME
94
+Used to find the config file directories.
95
+
96
+@PAR@ pipewire-env PIPEWIRE_CONFIG_PREFIX
97
+
98
+@PAR@ pipewire-env PIPEWIRE_CONFIG_NAME
99
+Used to override the application provided
100
+config prefix and config name.
101
+
102
+@PAR@ pipewire-env PIPEWIRE_NO_CONFIG
103
+Enables (false) or disables (true) overriding on the default configuration.
104
+
105
+## Context information
106
+
107
+As part of a client context, the following information is collected
108
+from environment variables and placed in the context properties:
109
+
110
+@PAR@ pipewire-env LANG
111
+The current language in `application.language`.
112
+
113
+@PAR@ pipewire-env XDG_SESSION_ID
114
+Set as the `application.process.session-id` property.
115
+
116
+@PAR@ pipewire-env DISPLAY
117
+Is set as the `window.x11.display` property.
118
+
119
+## Modules
120
+
121
+@PAR@ pipewire-env PIPEWIRE_MODULE_DIR
122
+Sets the directory where to find PipeWire modules.
123
+
124
+@PAR@ pipewire-env SPA_SUPPORT_LIB
125
+The name of the SPA support lib to load. This can be used to switch to
126
+an alternative support library, for example, to run on the EVL realtime kernel.
127
+
128
+## Logging options
129
+
130
+@PAR@ pipewire-env JOURNAL_STREAM
131
+Is used to parse the stream used for the journal. This is usually configured by
132
+systemd.
133
+
134
+@PAR@ pipewire-env PIPEWIRE_LOG_LINE
135
+Enables the logging of line numbers. Default true.
136
+
137
+@PAR@ pipewire-env PIPEWIRE_LOG
138
+Specifies a log file to use instead of the default logger.
139
+
140
+@PAR@ pipewire-env PIPEWIRE_LOG_SYSTEMD
141
+Enables the use of systemd for the logger, default true.
142
+
143
+## Other settings
144
+
145
+@PAR@ pipewire-env PIPEWIRE_CPU
146
+Selects the CPU and flags. This is a bitmask of any of the \ref CPU flags
147
+
148
+@PAR@ pipewire-env PIPEWIRE_VM
149
+Selects the Virtual Machine PipeWire is running on.  This can be any of the \ref CPU "VM"
150
+types.
151
+
152
+@PAR@ pipewire-env DISABLE_RTKIT
153
+Disables the use of RTKit or the Realtime Portal for realtime scheduling.
154
+
155
+@PAR@ pipewire-env NO_COLOR
156
+Disables the use of colors in the console output.
157
+
158
+## Debugging options
159
+
160
+@PAR@ pipewire-env PIPEWIRE_DLCLOSE
161
+Enables (true) or disables (false) the use of dlclose when a shared library
162
+is no longer in use. When debugging, it might make sense to disable dlclose to be able to get
163
+debugging symbols from the object.
164
+
165
+## Stream options
166
+
167
+@PAR@ pipewire-env PIPEWIRE_NODE
168
+Makes a stream connect to a specific `object.serial` or `node.name`.
169
+
170
+@PAR@ pipewire-env PIPEWIRE_PROPS
171
+Adds extra properties to a stream or filter.
172
+
173
+@PAR@ pipewire-env PIPEWIRE_QUANTUM
174
+Forces a specific rate and buffer-size for the stream or filter.
175
+
176
+@PAR@ pipewire-env PIPEWIRE_LATENCY
177
+Sets a specific latency for a stream or filter. This is only a suggestion but
178
+the configured latency will not be larger.
179
+
180
+@PAR@ pipewire-env PIPEWIRE_RATE
181
+Sets a rate for a stream or filter. This is only a suggestion. The rate will be
182
+switched when the graph is idle.
183
+
184
+@PAR@ pipewire-env PIPEWIRE_AUTOCONNECT
185
+Overrides the default stream autoconnect settings.
186
+
187
+## Plugin options
188
+
189
+@PAR@ pipewire-env SPA_PLUGIN_DIR
190
+Is used to locate SPA plugins.
191
+
192
+@PAR@ pipewire-env SPA_DATA_DIR
193
+Is used to locate plugin specific config files. This is used by the
194
+bluetooth plugin currently to locate the quirks database.
195
+
196
+@PAR@ pipewire-env SPA_DEBUG
197
+Set the log level for SPA plugins. This is usually controlled by the `PIPEWIRE_DEBUG` variable
198
+when the plugins are managed by PipeWire but some standalone tools (like spa-inspect) uses this
199
+variable.
200
+
201
+@PAR@ pipewire-env ACP_BUILDDIR
202
+If set, the ACP profiles are loaded from the builddir.
203
+
204
+@PAR@ pipewire-env ACP_PATHS_DIR
205
+
206
+@PAR@ pipewire-env ACP_PROFILES_DIR
207
+Used to locate the ACP paths and profile directories respectively.
208
+
209
+@PAR@ pipewire-env LADSPA_PATH
210
+Comma separated list of directories where the ladspa plugins can be found.
211
+
212
+@PAR@ pipewire-env LIBJACK_PATH
213
+Directory where the jack1 or jack2 libjack.so can be found.
214
+
215
 # AUTHORS
216
 
217
 The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
218
pipewire-1.2.0.tar.gz/doc/dox/programs/pw-container.1.md Added
73
 
1
@@ -0,0 +1,71 @@
2
+\page page_man_pw-container_1 pw-container
3
+
4
+The PipeWire container utility
5
+
6
+# SYNOPSIS
7
+
8
+**pw-container** \*options*\ \*PROGRAM*\
9
+
10
+# DESCRIPTION
11
+
12
+Run a program in a new security context 1.
13
+
14
+**pw-container** will create a new temporary unix socket and uses the
15
+SecurityContext extension API to create a server on this socket with
16
+the given properties. Clients created from this server socket will have
17
+the security properties attached to them.
18
+
19
+This can be used to simulate the behaviour of Flatpak or other containers.
20
+
21
+Without any arguments, **pw-container** simply creates the new socket
22
+and prints the address on stdout. Other PipeWire programs can then be run
23
+with `PIPEWIRE_REMOTE=<socket-address>` to connect through this security
24
+context.
25
+
26
+When *PROGRAM* is given, the `PIPEWIRE_REMOTE` env variable will be set
27
+and *PROGRAM* will be passed to system(). Argument to *PROGRAM* need to be
28
+properly quoted.
29
+
30
+# OPTIONS
31
+
32
+\par -P | \--properties=VALUE
33
+Set extra context properties as a JSON object.
34
+
35
+\par -r | \--remote=NAME
36
+The name the *remote* instance to connect to. If left unspecified, a
37
+connection is made to the default PipeWire instance.
38
+
39
+\par -h | \--help
40
+Show help.
41
+
42
+\par \--version
43
+Show version information.
44
+
45
+# EXIT STATUS
46
+
47
+If the security context was successfully created, **pw-container** does
48
+not exit until terminated with a signal. It exits with status 0 if terminated by
49
+SIGINT or SIGTERM in this case.
50
+
51
+Otherwise, it exits with nonzero exit status.
52
+
53
+# EXAMPLES
54
+
55
+**pw-container** 'pw-dump i 0'
56
+
57
+Run pw-dump of the Core object. Note the difference in the object permissions
58
+when running pw-dump with and without **pw-container**.
59
+
60
+**pw-container** 'pw-dump pw-dump'
61
+
62
+Run pw-dump of itself. Note the difference in the Client security tokens when
63
+running pw-dump with and without **pw-container**.
64
+
65
+# AUTHORS
66
+
67
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
68
+PipeWire is available from <$(PACKAGE_URL)>
69
+
70
+# SEE ALSO
71
+
72
+1 https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/security-context/security-context-v1.xml - Creating a security context
73
pipewire-1.0.1.tar.bz2/doc/dox/programs/pw-jack.1.md -> pipewire-1.2.0.tar.gz/doc/dox/programs/pw-jack.1.md Changed
10
 
1
@@ -4,7 +4,7 @@
2
 
3
 # SYNOPSIS
4
 
5
-**pw-jack** \*options*\ *COMMAND* \*FILE*\
6
+**pw-jack** \*options*\ *COMMAND* \*ARGUMENTS...*\
7
 
8
 # DESCRIPTION
9
 
10
pipewire-1.2.0.tar.gz/doc/dox/programs/pw-reserve.1.md Added
81
 
1
@@ -0,0 +1,79 @@
2
+\page page_man_pw-reserve_1 pw-reserve
3
+
4
+The PipeWire device reservation utility
5
+
6
+# SYNOPSIS
7
+
8
+**pw-reserve** \*options*\
9
+
10
+# DESCRIPTION
11
+
12
+Reserves a device using the DBus `org.freedesktop.ReserveDevice1`
13
+device reservation scheme 1, waiting until terminated by `SIGINT` or
14
+another signal.
15
+
16
+It can also request other applications to release a device. This can
17
+be used to make audio servers such as PipeWire, Pulseaudio, JACK, or
18
+other applications that respect the device reservation protocol, to
19
+ignore a device, or to release a sound device they are already using
20
+so that it can be used by other applications.
21
+
22
+# OPTIONS
23
+
24
+\par -r | \--release
25
+Request any client currently holding the device to release it, and try
26
+to reserve it after that. If this option is not given and the device
27
+is already in use, **pw-reserve** will exit with error status.
28
+
29
+\par -n NAME | \--name=NAME
30
+\parblock
31
+Name of the device to reserve. By convention, this is
32
+
33
+- Audio<em>N</em>: for ALSA card number <em>N</em>
34
+
35
+**pw-reserve** can reserve any device name, however PipeWire does
36
+not currently support other values than listed above.
37
+\endparblock
38
+
39
+\par -a NAME | \--appname=NAME
40
+Application name to use when reserving the device.
41
+
42
+\par -p PRIO | \--priority=PRIO
43
+Priority to use when reserving the device.
44
+
45
+\par -m | \--monitor
46
+Monitor reservations of a given device, instead of reserving it.
47
+
48
+\par -h | \--help
49
+Show help.
50
+
51
+\par \--version
52
+Show version information.
53
+
54
+# EXIT STATUS
55
+
56
+If the device reservation succeeds, **pw-reserve** does not exit until
57
+terminated with a signal. It exits with status 0 if terminated by
58
+SIGINT or SIGTERM in this case.
59
+
60
+Otherwise, it exits with nonzero exit status.
61
+
62
+# EXAMPLES
63
+
64
+**pw-reserve** -n Audio0
65
+
66
+Reserve ALSA card 0, and exit with error if it is already reserved.
67
+
68
+**pw-reserve** -n Audio0 -r
69
+
70
+Reserve ALSA card 0, requesting any applications that have reserved
71
+the device to release it for us.
72
+
73
+# AUTHORS
74
+
75
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
76
+PipeWire is available from <$(PACKAGE_URL)>
77
+
78
+# SEE ALSO
79
+
80
+1 https://git.0pointer.net/reserve.git/tree/reserve.txt - A simple device reservation scheme with DBus
81
pipewire-1.0.1.tar.bz2/doc/dox/programs/pw-top.1.md -> pipewire-1.2.0.tar.gz/doc/dox/programs/pw-top.1.md Changed
10
 
1
@@ -27,6 +27,8 @@
2
 - S = SUSPENDED
3
 - I = IDLE
4
 - R = RUNNING
5
+- t = RUNNING + transport starting
6
+- T = RUNNING + transport running
7
 \endparblock
8
 
9
 \par ID
10
pipewire-1.2.0.tar.gz/doc/dox/programs/pw-v4l2.1.md Added
42
 
1
@@ -0,0 +1,40 @@
2
+\page page_man_pw-v4l2_1 pw-v4l2
3
+
4
+Use PipeWire instead of V4L2
5
+
6
+# SYNOPSIS
7
+
8
+**pw-v4l2** \*options*\ *COMMAND* \*ARGUMENTS...*\
9
+
10
+# DESCRIPTION
11
+
12
+**pw-v4l2** runs a command using a compatibility layer that maps PipeWire
13
+video devices to be visible to applications using V4L2.
14
+
15
+This is implemented by preloading a shared library via LD_PRELOAD,
16
+which translates library calls that try to access V4L2 devices.
17
+
18
+# OPTIONS
19
+
20
+\par -h
21
+Show help.
22
+
23
+\par -r NAME
24
+The name of the remote instance to connect to. If left unspecified, a
25
+connection is made to the default PipeWire instance.
26
+
27
+\par -v
28
+Verbose operation.
29
+
30
+# EXAMPLES
31
+
32
+**pw-v4l2** v4l2-ctl --list-devices
33
+
34
+# AUTHORS
35
+
36
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
37
+PipeWire is available from <$(PACKAGE_URL)>
38
+
39
+# SEE ALSO
40
+
41
+\ref page_man_pipewire_1 "pipewire(1)",
42
pipewire-1.2.0.tar.gz/doc/dox/programs/spa-acp-tool.1.md Added
82
 
1
@@ -0,0 +1,80 @@
2
+\page page_man_spa-acp-tool_1 spa-acp-tool
3
+
4
+The PipeWire ALSA profile debugging utility
5
+
6
+# SYNOPSIS
7
+
8
+**spa-acp-tool** \*COMMAND*\
9
+
10
+# DESCRIPTION
11
+
12
+Debug tool for exercising the ALSA card profile probing code, without
13
+running PipeWire.
14
+
15
+May be used to debug problems where PipeWire has incorrectly
16
+functioning ALSA card profiles.
17
+
18
+# COMMANDS
19
+
20
+\par help | h
21
+Show available commands
22
+
23
+\par quit | q
24
+Quit
25
+
26
+\par card ID | c ID
27
+Probe card
28
+
29
+\par info | i
30
+List card info
31
+
32
+\par list | l
33
+List all objects
34
+
35
+\par list-verbose | lv
36
+List all data
37
+
38
+\par list-profiles ID | lpr ID
39
+List profiles
40
+
41
+\par set-profile ID | spr ID
42
+Activate a profile
43
+
44
+\par list-ports ID | lp ID
45
+List ports
46
+
47
+\par set-port ID | sp ID
48
+Activate a port
49
+
50
+\par list-devices ID | ld ID
51
+List available devices
52
+
53
+\par get-volume ID | gv ID
54
+Get volume from device
55
+
56
+\par set-volume ID VOL | v ID VOL
57
+Set volume on device
58
+
59
+\par inc-volume ID | v+ ID
60
+Increase volume on device
61
+
62
+\par dec-volume ID | v- ID
63
+Decrease volume on device
64
+
65
+\par get-mute ID | gm ID
66
+Get mute state from device
67
+
68
+\par set-mute ID VAL | sm ID VAL
69
+Set mute on device
70
+
71
+\par toggle-mute ID  | m ID
72
+Toggle mute on device
73
+
74
+# AUTHORS
75
+
76
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
77
+PipeWire is available from <$(PACKAGE_URL)>
78
+
79
+# SEE ALSO
80
+
81
+\ref page_man_pipewire_1 "pipewire(1)"
82
pipewire-1.2.0.tar.gz/doc/dox/programs/spa-inspect.1.md Added
30
 
1
@@ -0,0 +1,28 @@
2
+\page page_man_spa-inspect_1 spa-inspect
3
+
4
+The PipeWire SPA plugin information utility
5
+
6
+# SYNOPSIS
7
+
8
+**spa-inspect** *FILE*
9
+
10
+# DESCRIPTION
11
+
12
+Displays information about a SPA plugin.
13
+
14
+Lists the SPA factories contained, and tries to instantiate them.
15
+
16
+# EXAMPLES
17
+
18
+**spa-inspect** $(SPA_PLUGINDIR)/bluez5/libspa-codec-bluez5-sbc.so
19
+
20
+Display information about a plugin.
21
+
22
+# AUTHORS
23
+
24
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
25
+PipeWire is available from <$(PACKAGE_URL)>
26
+
27
+# SEE ALSO
28
+
29
+\ref page_man_pipewire_1 "pipewire(1)"
30
pipewire-1.2.0.tar.gz/doc/dox/programs/spa-json-dump.1.md Added
26
 
1
@@ -0,0 +1,24 @@
2
+\page page_man_spa-json-dump_1 spa-json-dump
3
+
4
+SPA JSON to JSON converter
5
+
6
+# SYNOPSIS
7
+
8
+**spa-json** *FILE*
9
+
10
+# DESCRIPTION
11
+
12
+Reads a SPA JSON file or stdin, and outputs it as standard JSON.
13
+
14
+# EXAMPLES
15
+
16
+**spa-json-dump** $(PIPEWIRE_CONFDATADIR)/pipewire.conf
17
+
18
+# AUTHORS
19
+
20
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
21
+PipeWire is available from <$(PACKAGE_URL)>
22
+
23
+# SEE ALSO
24
+
25
+\ref page_man_pipewire_1 "pipewire(1)"
26
pipewire-1.2.0.tar.gz/doc/dox/programs/spa-monitor.1.md Added
28
 
1
@@ -0,0 +1,26 @@
2
+\page page_man_spa-monitor_1 spa-monitor
3
+
4
+The PipeWire SPA device debugging utility
5
+
6
+# SYNOPSIS
7
+
8
+**spa-monitor** *FILE*
9
+
10
+# DESCRIPTION
11
+
12
+Load a SPA plugin and instantiate a device from it.
13
+
14
+This is only useful for debugging device plugins.
15
+
16
+# EXAMPLES
17
+
18
+**spa-monitor** $(SPA_PLUGINDIR)/jack/libspa-jack.so
19
+
20
+# AUTHORS
21
+
22
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
23
+PipeWire is available from <$(PACKAGE_URL)>
24
+
25
+# SEE ALSO
26
+
27
+\ref page_man_pipewire_1 "pipewire(1)"
28
pipewire-1.2.0.tar.gz/doc/dox/programs/spa-resample.1.md Added
49
 
1
@@ -0,0 +1,47 @@
2
+\page page_man_spa-resample_1 spa-resample
3
+
4
+The PipeWire resampler debugging utility
5
+
6
+# SYNOPSIS
7
+
8
+**spa-resample** \*OPTIONS*\ *INFILE* *OUTFILE*
9
+
10
+# DESCRIPTION
11
+
12
+Use the PipeWire resampler to resample input file to output file,
13
+following the given options.
14
+
15
+This is useful only for testing the resampler.
16
+
17
+# OPTIONS
18
+
19
+\par -r RATE | \--rate=RATE
20
+Output sample rate.
21
+
22
+\par -f FORMAT | \--format=FORMAT
23
+Output sample format (s8 | s16 | s32 | f32 | f64).
24
+
25
+\par -q QUALITY | \--quality=QUALITY
26
+Resampler output quality (0-14).
27
+
28
+\par -c FLAGS | \--cpuflags=FLAGS
29
+See \ref spa_cpu "spa/support/cpu.h".
30
+
31
+\par -h
32
+Show help.
33
+
34
+\par -v
35
+Verbose operation.
36
+
37
+# EXAMPLES
38
+
39
+**spa-resample** -r 48000 -f s32 in.wav out.wav
40
+
41
+# AUTHORS
42
+
43
+The PipeWire Developers <$(PACKAGE_BUGREPORT)>;
44
+PipeWire is available from <$(PACKAGE_URL)>
45
+
46
+# SEE ALSO
47
+
48
+\ref page_man_pipewire_1 "pipewire(1)"
49
pipewire-1.0.1.tar.bz2/doc/dox/pulse-modules.dox -> pipewire-1.2.0.tar.gz/doc/dox/pulse-modules.dox Changed
137
 
1
@@ -1,123 +1,15 @@
2
 /** \page page_pulse_modules Pulseaudio Modules
3
 
4
-PipeWire's Pulseaudio emulation implements several Pulseaudio modules.
5
-It only supports its own built-in modules, and cannot load external
6
-modules written for Pulseaudio.
7
+\include{doc} pulse-modules.inc
8
 
9
-# Loading modules
10
-
11
-The built-in modules can be loaded using Pulseaudio client programs,
12
-for example `pactl load-module <module-name> <module-options>`. They
13
-can also added to `pipewire-pulse.conf`, typically by a drop-in file
14
-in `~/.config/pipewire/pipewire-pulse.conf.d/` containing the module
15
-name and its arguments
16
-```
17
-pulse.cmd = 
18
-    { cmd = "load-module" args = "module-null-sink sink_name=foo" flags =   }
19
-
20
-```
21
-
22
-To list all modules currently loaded, with their arguments:
23
-```
24
-pactl list modules
25
-```
26
-
27
-For a short list of loaded modules:
28
-```
29
-pactl list modules short
30
-```
31
-
32
-Modules may be unloaded using either the module-name or index number:
33
-
34
-```
35
-pactl load-module <module-name> <parameters>
36
-pactl unload-module <module-name|index#>
37
-```
38
-
39
-# Common module options
40
-
41
-Most modules that create streams/devices support the following properties:
42
-
43
-## sink_name, source_name
44
-
45
-Name for the sink (resp. source). Allowed characters in the name are a-z, A-Z, numbers, period (.) and underscore (_). The length must be 1-128 characters.
46
-
47
-## format
48
-
49
-The sample format. The supported audio formats are:
50
-
51
-### PCM
52
- - u8: unsigned 8-bit integer
53
- - aLaw: A-law encoded 8-bit integer
54
- - uLaw: μ-law encoded 8-bit integer
55
- - s16le: signed 16-bit little-endian integer
56
- - s16be: signed 16-bit big-endian integer
57
- - s16, s16ne: native-endian aliases for s16le or s16be
58
- - s16re: reverse-endian alias for s16le or s16be
59
- - float32le: 32-bit little-endian float
60
- - float32be: 32-bit big-endian float
61
- - float32, float32ne: native-endian aliases for float32le or float32be
62
- - float32re: reverse-endian alias for float32le or float32be
63
- - s32le: signed 32-bit little-endian integer
64
- - s32be: signed 32-bit big-endian integer
65
- - s32, s32ne: native-endian aliases for s32le or s32be
66
- - s32re: reverse-endian alias for s32le or s32be
67
- - s24le: signed 24-bit little-endian integer (note: ALSA calls this "S24_3LE")
68
- - s24be: signed 24-bit big-endian integer (note: ALSA calls this "S24_3BE")
69
- - s24, s24ne: native-endian aliases for s24le or s24be
70
- - s24re: reverse-endian alias for s24le or s24be
71
- - s24-32le: signed 24-bit little-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_LE")
72
- - s24-32be: signed 24-bit big-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_BE")
73
- - s24-32, s24-32ne: native-endian aliases for s24-32le or s24-32be
74
- - s24-32re: reverse-endian alias for s24-32le or s24-32be
75
-
76
-### Compressed audio formats
77
-
78
-Below is a list of all supported compressed formats. The code at the beginning of each line is used whenever a textual identifier for a format is needed (for example in configuration files or on the command line). The formats whose identifier ends with -iec61937 have to be wrapped in IEC 61937 frames, which makes the compressed audio behave more like normal PCM audio.
79
-
80
- - ac3-iec61937: Dolby Digital (DD / AC-3 / A/52)
81
- - eac3-iec61937: Dolby Digital Plus (DD+ / E-AC-3)
82
- - mpeg-iec61937: MPEG-1 or MPEG-2 Part 3 (not MPEG-2 AAC)
83
- - dts-iec61937: DTS
84
- - mpeg2-aac-iec61937: MPEG-2 AAC (supported since PulseAudio 4.0)
85
- - truehd-iec61937: Dolby TrueHD (added in PulseAudio 13.0, but doesn't work yet in practice)
86
- - dtshd-iec61937: DTS-HD Master Audio (added in PulseAudio 13.0, but doesn't work yet in practice)
87
- - pcm: PCM (not a compressed format, but listed here, because pcm is one of the recognized encoding identifiers)
88
- - any: (special identifier for indicating that any encoding can be used)
89
-
90
-
91
-## rate
92
-
93
-The sample rate.
94
-
95
-##channels
96
-
97
-Number of audio channels.
98
-
99
-## channel_map
100
-
101
-A channel map. A list of comma-separated channel names. The currently defined channel names are:
102
-`left`, `right`, `mono`, `center`, `front-left`, `front-right`, `front-center`,
103
-`rear-center`, `rear-left`, `rear-right`, `lfe`, `subwoofer`, `front-left-of-center`,
104
-`front-right-of-center`, `side-left`, `side-right`, `aux0`, `aux1` to `aux15`, `top-center`,
105
-`top-front-left`, `top-front-right`, `top-front-center`, `top-rear-left`, `top-rear-right`,
106
-`top-rear-center`
107
-
108
-## sink_properties, source_properties
109
-
110
-Set additional properties of the sink/source. For example, you can set the description directly
111
-when the module is loaded by setting this parameter.
112
-
113
-```
114
-load-module module-alsa-sink sink_name=headphones sink_properties=device.description=Headphones
115
-```
116
-
117
-# List of known built-in modules:
118
+# List of built-in modules:
119
 
120
 - \subpage page_pulse_module_alsa_sink
121
 - \subpage page_pulse_module_alsa_source
122
 - \subpage page_pulse_module_always_sink
123
 - \subpage page_pulse_module_combine_sink
124
+- \subpage page_pulse_module_device_manager
125
+- \subpage page_pulse_module_device_restore
126
 - \subpage page_pulse_module_echo_cancel
127
 - \subpage page_pulse_module_gsettings
128
 - \subpage page_pulse_module_jackdbus_detect
129
@@ -137,6 +29,7 @@
130
 - \subpage page_pulse_module_rtp_recv
131
 - \subpage page_pulse_module_rtp_send
132
 - \subpage page_pulse_module_simple_protocol_tcp
133
+- \subpage page_pulse_module_stream_restore
134
 - \subpage page_pulse_module_switch_on_connect
135
 - \subpage page_pulse_module_tunnel_sink
136
 - \subpage page_pulse_module_tunnel_source
137
pipewire-1.2.0.tar.gz/doc/dox/pulse-modules.inc Added
113
 
1
@@ -0,0 +1,111 @@
2
+PipeWire's Pulseaudio emulation implements several Pulseaudio modules.
3
+It only supports its own built-in modules, and cannot load external
4
+modules written for Pulseaudio.
5
+
6
+# Loading modules
7
+
8
+The built-in modules can be loaded using Pulseaudio client programs,
9
+for example `pactl load-module <module-name> <module-options>`. They
10
+can also added to `pipewire-pulse.conf`, typically by a drop-in file
11
+in `~/.config/pipewire/pipewire-pulse.conf.d/` containing the module
12
+name and its arguments
13
+```
14
+pulse.cmd = 
15
+    { cmd = "load-module" args = "module-null-sink sink_name=foo" flags =   }
16
+
17
+```
18
+
19
+To list all modules currently loaded, with their arguments:
20
+```
21
+pactl list modules
22
+```
23
+
24
+For a short list of loaded modules:
25
+```
26
+pactl list modules short
27
+```
28
+
29
+Modules may be unloaded using either the module-name or index number:
30
+
31
+```
32
+pactl load-module <module-name> <parameters>
33
+pactl unload-module <module-name|index#>
34
+```
35
+
36
+# Common module options
37
+
38
+Most modules that create streams/devices support the following properties:
39
+
40
+## sink_name, source_name
41
+
42
+Name for the sink (resp. source). Allowed characters in the name are a-z, A-Z, numbers, period (.) and underscore (_). The length must be 1-128 characters.
43
+
44
+## format
45
+
46
+The sample format. The supported audio formats are:
47
+
48
+### PCM
49
+ - u8: unsigned 8-bit integer
50
+ - aLaw: A-law encoded 8-bit integer
51
+ - uLaw: μ-law encoded 8-bit integer
52
+ - s16le: signed 16-bit little-endian integer
53
+ - s16be: signed 16-bit big-endian integer
54
+ - s16, s16ne: native-endian aliases for s16le or s16be
55
+ - s16re: reverse-endian alias for s16le or s16be
56
+ - float32le: 32-bit little-endian float
57
+ - float32be: 32-bit big-endian float
58
+ - float32, float32ne: native-endian aliases for float32le or float32be
59
+ - float32re: reverse-endian alias for float32le or float32be
60
+ - s32le: signed 32-bit little-endian integer
61
+ - s32be: signed 32-bit big-endian integer
62
+ - s32, s32ne: native-endian aliases for s32le or s32be
63
+ - s32re: reverse-endian alias for s32le or s32be
64
+ - s24le: signed 24-bit little-endian integer (note: ALSA calls this "S24_3LE")
65
+ - s24be: signed 24-bit big-endian integer (note: ALSA calls this "S24_3BE")
66
+ - s24, s24ne: native-endian aliases for s24le or s24be
67
+ - s24re: reverse-endian alias for s24le or s24be
68
+ - s24-32le: signed 24-bit little-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_LE")
69
+ - s24-32be: signed 24-bit big-endian integer, packed into a 32-bit integer so that the 8 most significant bits are ignored (note: ALSA calls this "S24_BE")
70
+ - s24-32, s24-32ne: native-endian aliases for s24-32le or s24-32be
71
+ - s24-32re: reverse-endian alias for s24-32le or s24-32be
72
+
73
+### Compressed audio formats
74
+
75
+Below is a list of all supported compressed formats. The code at the beginning of each line is used whenever a textual identifier for a format is needed (for example in configuration files or on the command line). The formats whose identifier ends with -iec61937 have to be wrapped in IEC 61937 frames, which makes the compressed audio behave more like normal PCM audio.
76
+
77
+ - ac3-iec61937: Dolby Digital (DD / AC-3 / A/52)
78
+ - eac3-iec61937: Dolby Digital Plus (DD+ / E-AC-3)
79
+ - mpeg-iec61937: MPEG-1 or MPEG-2 Part 3 (not MPEG-2 AAC)
80
+ - dts-iec61937: DTS
81
+ - mpeg2-aac-iec61937: MPEG-2 AAC (supported since PulseAudio 4.0)
82
+ - truehd-iec61937: Dolby TrueHD (added in PulseAudio 13.0, but doesn't work yet in practice)
83
+ - dtshd-iec61937: DTS-HD Master Audio (added in PulseAudio 13.0, but doesn't work yet in practice)
84
+ - pcm: PCM (not a compressed format, but listed here, because pcm is one of the recognized encoding identifiers)
85
+ - any: (special identifier for indicating that any encoding can be used)
86
+
87
+
88
+## rate
89
+
90
+The sample rate.
91
+
92
+##channels
93
+
94
+Number of audio channels.
95
+
96
+## channel_map
97
+
98
+A channel map. A list of comma-separated channel names. The currently defined channel names are:
99
+`left`, `right`, `mono`, `center`, `front-left`, `front-right`, `front-center`,
100
+`rear-center`, `rear-left`, `rear-right`, `lfe`, `subwoofer`, `front-left-of-center`,
101
+`front-right-of-center`, `side-left`, `side-right`, `aux0`, `aux1` to `aux15`, `top-center`,
102
+`top-front-left`, `top-front-right`, `top-front-center`, `top-rear-left`, `top-rear-right`,
103
+`top-rear-center`
104
+
105
+## sink_properties, source_properties
106
+
107
+Set additional properties of the sink/source. For example, you can set the description directly
108
+when the module is loaded by setting this parameter.
109
+
110
+```
111
+load-module module-alsa-sink sink_name=headphones sink_properties=device.description=Headphones
112
+```
113
pipewire-1.0.1.tar.bz2/doc/dox/tutorial/index.dox -> pipewire-1.2.0.tar.gz/doc/dox/tutorial/index.dox Changed
7
 
1
@@ -1,4 +1,4 @@
2
-/** \page page_tutorial Tutorial
3
+/** \page page_tutorial API Tutorial
4
 
5
 Welcome to the PipeWire API tutorial. The goal is to learn
6
 PipeWire API step-by-step with simple short examples.
7
pipewire-1.0.1.tar.bz2/doc/examples/tutorial3.c -> pipewire-1.2.0.tar.gz/doc/examples/tutorial3.c Changed
17
 
1
@@ -29,12 +29,14 @@
2
 
3
    struct roundtrip_data d = { .loop = loop };
4
    struct spa_hook core_listener;
5
+   int err;
6
 
7
    pw_core_add_listener(core, &core_listener, &core_events, &d);
8
 
9
    d.pending = pw_core_sync(core, PW_ID_CORE, 0);
10
 
11
-   pw_main_loop_run(loop);
12
+   if ((err = pw_main_loop_run(loop)) < 0)
13
+       printf("main_loop_run error:%d!\n", err);
14
 
15
    spa_hook_remove(&core_listener);
16
 }
17
pipewire-1.0.1.tar.bz2/doc/examples/tutorial4.c -> pipewire-1.2.0.tar.gz/doc/examples/tutorial4.c Changed
16
 
1
@@ -50,7 +50,13 @@
2
        if (data->accumulator >= M_PI_M2)
3
            data->accumulator -= M_PI_M2;
4
 
5
-       val = sin(data->accumulator) * DEFAULT_VOLUME * 16767.f;
6
+       /* sin() gives a value between -1.0 and 1.0, we first apply
7
+        * the volume and then scale with 32767.0 to get a 16 bits value
8
+        * between -32767 32767.
9
+        * Another common method to convert a double to
10
+        * 16 bits is to multiple by 32768.0 and then clamp to
11
+        * -32768 32767 to get the full 16 bits range. */
12
+       val = sin(data->accumulator) * DEFAULT_VOLUME * 32767.0;
13
        for (c = 0; c < DEFAULT_CHANNELS; c++)
14
            *dst++ = val;
15
    }
16
pipewire-1.0.1.tar.bz2/doc/input-filter-h.sh -> pipewire-1.2.0.tar.gz/doc/input-filter-h.sh Changed
34
 
1
@@ -1,4 +1,4 @@
2
-#!/bin/bash
3
+#!/usr/bin/env bash
4
 #
5
 # Doxygen input filter, which tries to fix documentation of callback
6
 # method macros.
7
@@ -11,9 +11,7 @@
8
 # Add \ingroup commands for the file, for each \addgroup in it
9
 BASEFILE=$(echo "$FILENAME" | sed -e 's@.*src/pipewire/@pipewire/@; s@.*spa/include/spa/@spa/@; s@.*src/test/@test/@;')
10
 
11
-# shellcheck disable=SC2028 # \file is not an escape sequence
12
-echo "/** \file"
13
-echo "\`$BASEFILE\`"
14
+printf "/** \\\\file\n\`%s\`\n" "$BASEFILE"
15
 sed -n -e '/.*\\addtogroup a-zA-Z0-9_.*/ { s/.*addtogroup /\\ingroup /; p; }' < "$FILENAME" | sort | uniq
16
 echo " */"
17
 
18
@@ -25,9 +23,15 @@
19
 # Ensure all macros are included (also those defined inside a struct),
20
 # by adding /** \ingroup XXX */ before each definition.
21
 # Also ensure all opaque structs get included.
22
+# Strip SPA_FORMAT_ARG_FUNC(1) etc. things that confuse doxygen
23
 sed -e 's@^\(#define .*:space:\)\(.*_method\)\((.,:space:*\)\(a-z_\+\)\(.*):space:*\)$@\1\2\3\4\5 /**< \\copydoc \2s.\4\n\n\\sa \2s.\4 */@;' \
24
     -e 's@^\(#define .*:space:\)\(.*_method\)\(_rvs(.,:space:*\)\(a-z_\+\)\(.*):space:*\)$@\1\2\3\4\5 /**< \\copydoc \2s.\4\n\n\\sa \2s.\4 */@;' \
25
     -e '/\\addtogroup/ { h; s@.*\\addtogroup \(.*\).*@/** \\ingroup \1 */@; x; }' \
26
     -e '/#define \(PW\|SPA\)_A-Z.*^\\ *$/ { x; p; x; }' \
27
     -e 's@^\( *struct \)\(a-zA-Z0-9_*\)\(;.*\)$@/** \\struct \2 */\n\1\2\3@;' \
28
+    -e 's@^ *SPA_FORMAT_ARG_FUNC(0-9, *)@@;' \
29
+    -e 's@ *SPA_PRINTF_FUNC(0-9, *);@;@;' \
30
+    -e 's@^ *SPA_WARN_UNUSED_RESULT@ @;' \
31
+    -e 's@ SPA_SENTINEL;@;@;' \
32
+    -e 's@ SPA_UNUSED,@,@;' \
33
 < "$FILENAME"
34
pipewire-1.2.0.tar.gz/doc/input-filter-md.py Added
169
 
1
@@ -0,0 +1,167 @@
2
+#!/usr/bin/env python3
3
+# -*- mode: python; coding: utf-8; eval: (blacken-mode); -*-
4
+r"""input-filter-md.py FILENAME
5
+input-filter-md.py --index FILENAMES...
6
+
7
+Doxygen .md input filter that adds extended syntax.
8
+With --index, generates an index file.
9
+Assumes BUILD_DIR environment variable is set.
10
+
11
+@PAR@ <section> <name> (...)
12
+
13
+    Adds an index item and expands to
14
+
15
+    \anchor <key>
16
+    \par <name> (...)
17
+
18
+@SECREF@ <section>
19
+
20
+    Expands to
21
+
22
+    \secreflist
23
+    \refitem ...
24
+    ...
25
+    \endsecreflist
26
+
27
+    containing all index items from the specified section.
28
+
29
+# Section title    @IDX@ <section>
30
+
31
+   Adds the section title to the index, and expands to an anchor
32
+
33
+   # Section title {#key}
34
+
35
+The index keys can be used in \ref and have format
36
+
37
+    {section}__{name}
38
+
39
+where the parts are converted to lowercase and _ replaces
40
+non-alphanumerics.
41
+
42
+"""
43
+import sys
44
+import re
45
+import os
46
+
47
+
48
+def index_key(section, name):
49
+    key = f"{section}__{name}".lower()
50
+    return re.sub(r"^A-Za-z0-9_-", "_", key)
51
+
52
+
53
+BUILD_DIR = os.environ"BUILD_DIR"
54
+PAR_RE = r"^@PAR@\s+(^\s*) \t+(\S+)(.*)$"
55
+IDX_RE = r"^(#+)(.*)@IDX@ \t+(\S+) \t*$"
56
+SECREF_RE = r"^@SECREF@ \t+(^\n*) \t*$"
57
+
58
+
59
+def main(args):
60
+    fn = args0
61
+    with open(fn, "r") as f:
62
+        text = f.read()
63
+
64
+    def par(m):
65
+        section = m.group(1)
66
+        name = m.group(2)
67
+        rest = m.group(3).strip()
68
+        key = index_key(section, name)
69
+        return f"\\anchor {key}\n\\par {name} {rest}"
70
+
71
+    def idx(m):
72
+        level = m.group(1)
73
+        title = name = m.group(2).strip()
74
+        section = m.group(3)
75
+        if title == title.upper():
76
+            name = name.capitalize()
77
+        key = index_key(section, name)
78
+        return f"{level} {title} {{#{key}}}"
79
+
80
+    def secref(m):
81
+        import os
82
+        import json
83
+
84
+        secs = m.group(1).split()
85
+
86
+        with open(os.path.join(BUILD_DIR, "index.json"), "r") as f:
87
+            index = json.load(f)
88
+
89
+        items = {}
90
+
91
+        for sec in secs:
92
+            if sec not in index:
93
+                print(f"{fn}: no index '{sec}'", file=sys.stderr)
94
+            else:
95
+                for name, key in indexsec.items():
96
+                    if name in items:
97
+                        pkey, psec = items.pop(name)
98
+                        nname = f"{name} ({sec})"
99
+                        itemsnname = (key, sec)
100
+                        if pkey is not None:
101
+                            pname = f"{name} ({psec})"
102
+                            itemspname = (pkey, psec)
103
+                            itemsname = (None, None)
104
+                    else:
105
+                        itemsname = (key, sec)
106
+
107
+        text = r"\secreflist"
108
+        for name, (key, sec) in sorted(items.items()):
109
+            if key is not None:
110
+                text.append(rf'\refitem {key} "{name}"')
111
+        text.append(r"\endsecreflist")
112
+        text = "\n".join(text)
113
+        return f"{text}\n"
114
+
115
+    text = re.sub(PAR_RE, par, text, flags=re.M)
116
+    text = re.sub(IDX_RE, idx, text, flags=re.M)
117
+    text = re.sub(SECREF_RE, secref, text, flags=re.M)
118
+
119
+    print(text)
120
+
121
+
122
+def main_index(args):
123
+    import json
124
+
125
+    sections = {}
126
+
127
+    for fn in set(args):
128
+        with open(fn, "r") as f:
129
+            load_index(sections, f.read())
130
+
131
+    result = {}
132
+
133
+    for section, items in sections.items():
134
+        for name in items:
135
+            key = index_key(section, name)
136
+            result.setdefault(section, {})name = key
137
+
138
+    with open(os.path.join(BUILD_DIR, "index.json"), "w") as f:
139
+        json.dump(result, f)
140
+
141
+
142
+def load_index(sections, text):
143
+    def par(m):
144
+        section = m.group(1)
145
+        name = m.group(2)
146
+        sections.setdefault(section, ).append(name)
147
+        return ""
148
+
149
+    def idx(m):
150
+        name = m.group(2).strip()
151
+        section = m.group(3)
152
+        if name == name.upper():
153
+            name = name.capitalize()
154
+        sections.setdefault(section, ).append(name)
155
+        return ""
156
+
157
+    text = re.sub(PAR_RE, par, text, flags=re.M)
158
+    text = re.sub(IDX_RE, idx, text, flags=re.M)
159
+
160
+
161
+if __name__ == "__main__":
162
+    if len(sys.argv) >= 2 and sys.argv1 == "--index":
163
+        main_index(sys.argv2:)
164
+    elif len(sys.argv) == 2:
165
+        main(sys.argv1:)
166
+    else:
167
+        print(__doc__.strip())
168
+        sys.exit(1)
169
pipewire-1.0.1.tar.bz2/doc/man-fixup.py -> pipewire-1.2.0.tar.gz/doc/man-fixup.py Changed
11
 
1
@@ -28,6 +28,9 @@
2
     if not fn.exists():
3
         page2 = page.replace("page_man_", "md_doc_dox_programs_").replace("-", "_")
4
         fn = mandir / f"{page2}.3"
5
+        if not fn.exists():
6
+            page2 = page.replace("page_man_", "md_doc_dox_config_").replace("-", "_")
7
+            fn = mandir / f"{page2}.3"
8
     else:
9
         page2 = None
10
 
11
pipewire-1.0.1.tar.bz2/doc/meson.build -> pipewire-1.2.0.tar.gz/doc/meson.build Changed
138
 
1
@@ -7,13 +7,34 @@
2
 doxyfile_conf.set('top_builddir', meson.project_build_root())
3
 doxyfile_conf.set('output_directory', meson.current_build_dir())
4
 
5
+doc_prefix_value = get_option('doc-prefix-value')
6
+doc_sysconfdir_value = get_option('doc-sysconfdir-value')
7
+
8
+if doc_prefix_value == '' and doc_sysconfdir_value == ''
9
+   doc_spa_plugindir = spa_plugindir
10
+   doc_pipewire_configdir = pipewire_configdir
11
+   doc_pipewire_confdatadir = pipewire_confdatadir
12
+else
13
+   if doc_prefix_value == ''
14
+     doc_prefix_value = get_option('prefix')
15
+   endif
16
+   if doc_sysconfdir_value == ''
17
+     doc_sysconfdir_value = get_option('sysconfdir')
18
+   endif
19
+   doc_spa_plugindir = doc_prefix_value / get_option('libdir') / spa_name
20
+   doc_pipewire_configdir = doc_prefix_value / doc_sysconfdir_value / 'pipewire'
21
+   doc_pipewire_confdatadir = doc_prefix_value / get_option('datadir') / 'pipewire'
22
+endif
23
+
24
 doxygen_env = environment()
25
 doxygen_env.set('PACKAGE_NAME', meson.project_name())
26
 doxygen_env.set('PACKAGE_VERSION', meson.project_version())
27
 doxygen_env.set('PACKAGE_URL', 'https://pipewire.org')
28
 doxygen_env.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/pipewire/pipewire/issues')
29
-doxygen_env.set('PIPEWIRE_CONFIG_DIR', pipewire_configdir)
30
-doxygen_env.set('PIPEWIRE_CONFDATADIR', pipewire_confdatadir)
31
+doxygen_env.set('PIPEWIRE_CONFIG_DIR', doc_pipewire_configdir)
32
+doxygen_env.set('PIPEWIRE_CONFDATADIR', doc_pipewire_confdatadir)
33
+doxygen_env.set('SPA_PLUGINDIR', doc_spa_plugindir)
34
+doxygen_env.set('BUILD_DIR', meson.current_build_dir())
35
 
36
 dot_found = find_program('dot', required: false).found()
37
 summary({'dot (used with doxygen)': dot_found}, bool_yn: true, section: 'Optional programs')
38
@@ -32,6 +53,7 @@
39
   'dox/modules.dox',
40
   'dox/pulse-modules.dox',
41
   'dox/programs/index.md',
42
+  'dox/config/index.md',
43
   'dox/internals/index.dox',
44
   'dox/internals/design.dox',
45
   'dox/internals/access.dox',
46
@@ -62,15 +84,20 @@
47
 
48
 
49
 manpage_docs = 
50
-  'dox/programs/libpipewire-modules.7.md',
51
-  'dox/programs/pipewire-pulse-modules.7.md',
52
+  'dox/config/pipewire-pulse.conf.5.md',
53
+  'dox/config/pipewire.conf.5.md',
54
+  'dox/config/pipewire-client.conf.5.md',
55
+  'dox/config/pipewire-jack.conf.5.md',
56
+  'dox/config/pipewire-devices.7.md',
57
+  'dox/config/pipewire-filter-chain.conf.5.md',
58
+  'dox/config/pipewire-pulse-modules.7.md',
59
+  'dox/config/libpipewire-modules.7.md',
60
   'dox/programs/pipewire-pulse.1.md',
61
-  'dox/programs/pipewire-pulse.conf.5.md',
62
   'dox/programs/pipewire.1.md',
63
-  'dox/programs/pipewire.conf.5.md',
64
   'dox/programs/pw-cat.1.md',
65
   'dox/programs/pw-cli.1.md',
66
   'dox/programs/pw-config.1.md',
67
+  'dox/programs/pw-container.1.md',
68
   'dox/programs/pw-dot.1.md',
69
   'dox/programs/pw-dump.1.md',
70
   'dox/programs/pw-jack.1.md',
71
@@ -80,7 +107,14 @@
72
   'dox/programs/pw-mididump.1.md',
73
   'dox/programs/pw-mon.1.md',
74
   'dox/programs/pw-profiler.1.md',
75
+  'dox/programs/pw-reserve.1.md',
76
   'dox/programs/pw-top.1.md',
77
+  'dox/programs/pw-v4l2.1.md',
78
+  'dox/programs/spa-acp-tool.1.md',
79
+  'dox/programs/spa-inspect.1.md',
80
+  'dox/programs/spa-json-dump.1.md',
81
+  'dox/programs/spa-monitor.1.md',
82
+  'dox/programs/spa-resample.1.md',
83
 
84
 
85
 manpages = 
86
@@ -136,11 +170,17 @@
87
   'tutorial5.c',
88
   'tutorial6.c',
89
 
90
+example_dep_files = 
91
+foreach h : example_files
92
+  example_dep_files += 'examples/' + h
93
+endforeach
94
 foreach h : examples
95
   example_files += h + '.c'
96
+  example_dep_files += '../src/examples/' + h + '.c'
97
 endforeach
98
 foreach h : spa_examples
99
   example_files += 'spa/examples/' + h + '.c'
100
+  example_dep_files += '../spa/examples/' + h + '.c'
101
 endforeach
102
 
103
 example_doxygen = 
104
@@ -187,6 +227,7 @@
105
 doxygen_layout = meson.project_source_root() / 'doc' / 'DoxygenLayout.xml'
106
 doxygen_filter_c = meson.project_source_root() / 'doc' / 'input-filter.py'
107
 doxygen_filter_h = meson.project_source_root() / 'doc' / 'input-filter-h.sh'
108
+doxygen_filter_md = meson.project_source_root() / 'doc' / 'input-filter-md.py'
109
 
110
 doxyfile_conf.set('inputs', ' '.join(inputs + input_dirs))
111
 doxyfile_conf.set('cssfiles', ' '.join(cssfiles))
112
@@ -194,6 +235,7 @@
113
 doxyfile_conf.set('path_prefixes', ' '.join(path_prefixes))
114
 doxyfile_conf.set('c_input_filter', doxygen_filter_c)
115
 doxyfile_conf.set('h_input_filter', doxygen_filter_h)
116
+doxyfile_conf.set('md_input_filter', doxygen_filter_md)
117
 
118
 doxyfile = configure_file(input: 'Doxyfile.in',
119
                           output: 'Doxyfile',
120
@@ -204,8 +246,16 @@
121
   docdir = pipewire_datadir / 'doc' / meson.project_name()
122
 endif
123
 
124
+index_json = custom_target('index.json',
125
+  command:  doxygen_filter_md, '--index', '@INPUT@' ,
126
+  input: extra_docs + manpage_docs,
127
+  output: 'index.json',
128
+  env: doxygen_env
129
+)
130
+
131
 html_target = custom_target('pipewire-docs',
132
-                            input:  doxyfile, doxygen_layout, examples_dox, doxygen_filter_c, doxygen_filter_h  + inputs + cssfiles,
133
+                            input:  doxyfile, doxygen_layout, example_dep_files, examples_dox,
134
+                                     doxygen_filter_c, doxygen_filter_h, index_json  + inputs + cssfiles,
135
                             output:  'html' ,
136
                             command:  doxygen, doxyfile ,
137
                             env: doxygen_env,
138
pipewire-1.0.1.tar.bz2/doc/tree.dox -> pipewire-1.2.0.tar.gz/doc/tree.dox Changed
9
 
1
@@ -123,6 +123,7 @@
2
 \}
3
 
4
 \page page_overview
5
+\page page_config
6
 \page page_programs
7
 \page page_modules
8
 \page page_pulse_modules
9
pipewire-1.0.1.tar.bz2/include/valgrind/memcheck.h -> pipewire-1.2.0.tar.gz/include/valgrind/memcheck.h Changed
30
 
1
@@ -157,7 +157,7 @@
2
 /* Client-code macros to check the state of memory. */
3
 
4
 /* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
5
-   If suitable addressibility is not established, Valgrind prints an
6
+   If suitable addressability is not established, Valgrind prints an
7
    error message and returns the address of the first offending byte.
8
    Otherwise it returns zero. */
9
 #define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len)      \
10
@@ -166,7 +166,7 @@
11
                             (_qzz_addr), (_qzz_len), 0, 0, 0)
12
 
13
 /* Check that memory at _qzz_addr is addressable and defined for
14
-   _qzz_len bytes.  If suitable addressibility and definedness are not
15
+   _qzz_len bytes.  If suitable addressability and definedness are not
16
    established, Valgrind prints an error message and returns the
17
    address of the first offending byte.  Otherwise it returns zero. */
18
 #define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len)        \
19
@@ -174,8 +174,8 @@
20
                             VG_USERREQ__CHECK_MEM_IS_DEFINED,    \
21
                             (_qzz_addr), (_qzz_len), 0, 0, 0)
22
 
23
-/* Use this macro to force the definedness and addressibility of an
24
-   lvalue to be checked.  If suitable addressibility and definedness
25
+/* Use this macro to force the definedness and addressability of an
26
+   lvalue to be checked.  If suitable addressability and definedness
27
    are not established, Valgrind prints an error message and returns
28
    the address of the first offending byte.  Otherwise it returns
29
    zero. */
30
pipewire-1.0.1.tar.bz2/meson.build -> pipewire-1.2.0.tar.gz/meson.build Changed
131
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '1.0.1',
4
+  version : '1.2.0',
5
   license :  'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
   meson_version : '>= 0.61.1',
7
   default_options :  'warning_level=3',
8
@@ -94,6 +94,7 @@
9
   '-Wdeprecated-declarations',
10
   '-Wunused-result',
11
   '-Werror=return-type',
12
+  '-Werror=float-conversion',
13
 
14
 
15
 cc_flags = common_flags + 
16
@@ -101,6 +102,7 @@
17
   '-DFASTPATH',
18
 # '-DSPA_DEBUG_MEMCPY',
19
   '-Werror=implicit-function-declaration',
20
+  '-Werror=incompatible-pointer-types',
21
   '-Werror=int-conversion',
22
   '-Werror=old-style-declaration',
23
   '-Werror=old-style-definition',
24
@@ -220,6 +222,8 @@
25
 cdata.set_quoted('PLUGINDIR', spa_plugindir)
26
 cdata.set_quoted('SPADATADIR', spa_datadir)
27
 cdata.set_quoted('PA_ALSA_DATA_DIR', alsadatadir)
28
+cdata.set('RTPRIO_SERVER', get_option('rtprio-server'))
29
+cdata.set('RTPRIO_CLIENT', get_option('rtprio-client'))
30
 
31
 if host_machine.endian() == 'big'
32
   cdata.set('WORDS_BIGENDIAN', 1)
33
@@ -343,6 +347,9 @@
34
 
35
 gio_dep = dependency('gio-2.0', version : '>= 2.26.0', required : get_option('gsettings'))
36
 summary({'GIO (GSettings)': gio_dep.found()}, bool_yn: true, section: 'Misc dependencies')
37
+if not gio_dep.found() and get_option('gsettings-pulse-schema').enabled()
38
+  error('`gsettings-pulse-schema` is enabled but `gio` was not found.')
39
+endif
40
 
41
 gst_option = get_option('gstreamer')
42
 gst_deps_def = {
43
@@ -359,6 +366,7 @@
44
 }
45
 
46
 gst_dep = 
47
+gst_dma_drm_found = false
48
 foreach depname, kwargs: gst_deps_def
49
   dep = dependency(depname, required: gst_option, kwargs: kwargs)
50
   summary({depname: dep.found()}, bool_yn: true, section: 'GStreamer modules')
51
@@ -371,6 +379,10 @@
52
     break
53
   endif
54
   gst_dep += dep
55
+
56
+  if depname == 'gstreamer-allocators-1.0' and dep.version().version_compare('>= 1.23.1')
57
+    gst_dma_drm_found = true
58
+  endif
59
 endforeach
60
 
61
 # This code relies on the array being empty if any dependency was not found
62
@@ -379,18 +391,26 @@
63
 
64
 cdata.set('HAVE_GSTREAMER_DEVICE_PROVIDER', get_option('gstreamer-device-provider').allowed())
65
 
66
-webrtc_dep = dependency('webrtc-audio-processing-1',
67
-  version : '>= 1.2' ,
68
-  required : false)
69
-cdata.set('HAVE_WEBRTC1', webrtc_dep.found())
70
-if webrtc_dep.found()
71
+summary({'gstreamer DMA_DRM support': gst_dma_drm_found}, bool_yn: true, section: 'Backend')
72
+cdata.set('HAVE_GSTREAMER_DMA_DRM', gst_dma_drm_found)
73
+
74
+if get_option('echo-cancel-webrtc').disabled()
75
+  webrtc_dep = dependency('', required: false)
76
   summary({'WebRTC Echo Canceling >= 1.2': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
77
 else
78
-  webrtc_dep = dependency('webrtc-audio-processing',
79
-    version : '>= 0.2', '< 1.0',
80
-    required : get_option('echo-cancel-webrtc'))
81
-  cdata.set('HAVE_WEBRTC', webrtc_dep.found())
82
-  summary({'WebRTC Echo Canceling < 1.0': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
83
+  webrtc_dep = dependency('webrtc-audio-processing-1',
84
+    version : '>= 1.2' ,
85
+    required : false)
86
+  cdata.set('HAVE_WEBRTC1', webrtc_dep.found())
87
+  if webrtc_dep.found()
88
+    summary({'WebRTC Echo Canceling >= 1.2': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
89
+  else
90
+    webrtc_dep = dependency('webrtc-audio-processing',
91
+      version : '>= 0.2', '< 1.0',
92
+      required : get_option('echo-cancel-webrtc'))
93
+    cdata.set('HAVE_WEBRTC', webrtc_dep.found())
94
+    summary({'WebRTC Echo Canceling < 1.0': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies')
95
+  endif
96
 endif
97
 
98
 # On FreeBSD and MidnightBSD, epoll-shim library is required for eventfd() and timerfd()
99
@@ -430,6 +450,22 @@
100
 
101
 libffado_dep = dependency('libffado', required: get_option('libffado'))
102
 summary({'ffado': libffado_dep.found()}, bool_yn: true)
103
+glib2_snap_dep = dependency('glib-2.0', required : get_option('snap'))
104
+gio2_snap_dep = dependency('gio-2.0', required : get_option('snap'))
105
+apparmor_snap_dep = dependency('libapparmor', required : get_option('snap'))
106
+if dependency('snapd-glib-2', required: false).found()
107
+  snap_dep = dependency('snapd-glib-2', required : get_option('snap'))
108
+else
109
+  snap_dep = dependency('snapd-glib', required : get_option('snap'))
110
+endif
111
+if snap_dep.found() and glib2_snap_dep.found() and gio2_snap_dep.found() and apparmor_snap_dep.found()
112
+  cdata.set('HAVE_SNAP', true)
113
+  snap_deps = glib2_snap_dep, gio2_snap_dep, snap_dep, apparmor_snap_dep
114
+endif
115
+summary({'GLib-2.0 (Snap support)': glib2_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies')
116
+summary({'Gio-2.0 (Snap support)': gio2_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies')
117
+summary({'Apparmor (Snap support)': apparmor_snap_dep.found()}, bool_yn: true, section: 'Misc dependencies')
118
+summary({'Snapd-glib (Snap support)': snap_dep.found()}, bool_yn: true, section: 'Misc dependencies')
119
 
120
 check_functions = 
121
   'gettid', '#include <unistd.h>', '-D_GNU_SOURCE', ,
122
@@ -487,7 +523,7 @@
123
 
124
 generate_docs = get_option('man').enabled() or get_option('docs').enabled()
125
 if get_option('man').allowed() or get_option('docs').allowed()
126
-  doxygen = find_program('doxygen', required : generate_docs)
127
+  doxygen = find_program('doxygen', required : generate_docs, version : '>=1.9')
128
   pymod = import('python')
129
   python = pymod.find_installation('python3', required: generate_docs)
130
   generate_docs = doxygen.found() and python.found()
131
pipewire-1.0.1.tar.bz2/meson_options.txt -> pipewire-1.2.0.tar.gz/meson_options.txt Changed
40
 
1
@@ -306,6 +306,18 @@
2
        description : 'PAM match rule for the generated limits.d file. @<name> denotes matching a group.',
3
        type : 'string',
4
        value: '@pipewire')
5
+option('rtprio-server',
6
+       description : 'PipeWire server realtime priority',
7
+       type : 'integer',
8
+       min: 11,
9
+       max: 99,
10
+       value: 88)
11
+option('rtprio-client',
12
+       description : 'PipeWire clients realtime priority',
13
+       type : 'integer',
14
+       min: 11,
15
+       max: 99,
16
+       value: 83)
17
 option('rlimits-rtprio',
18
        description : 'RR and FIFO scheduler priority permitted for realtime threads of the matching user(s)',
19
        type : 'integer',
20
@@ -330,3 +342,19 @@
21
        description: 'Enable code that depends on libffado',
22
        type: 'feature',
23
        value: 'auto')
24
+option('gsettings-pulse-schema',
25
+       description: 'Install gsettings schema for pulseaudio',
26
+       type: 'feature',
27
+       value: 'auto')
28
+option('snap',
29
+       description : 'Enable Snap permissions support.',
30
+       type : 'feature',
31
+       value : 'auto')
32
+option('doc-prefix-value',
33
+       description : 'Installation prefix to show in documentation instead of the actual value.',
34
+       type : 'string',
35
+       value : '')
36
+option('doc-sysconfdir-value',
37
+       description : 'Sysconf data directory to show in documentation instead of the actual value.',
38
+       type : 'string',
39
+       value : '')
40
pipewire-1.0.1.tar.bz2/pipewire-alsa/alsa-plugins/ctl_pipewire.c -> pipewire-1.2.0.tar.gz/pipewire-alsa/alsa-plugins/ctl_pipewire.c Changed
12
 
1
@@ -1359,8 +1359,8 @@
2
 
3
    ctl->system = loop->system;
4
    ctl->fd = spa_system_eventfd_create(ctl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
5
-   if (ctl->fd == -1) {
6
-       err = -errno;
7
+   if (ctl->fd < 0) {
8
+       err = ctl->fd;
9
        goto error;
10
    }
11
 
12
pipewire-1.0.1.tar.bz2/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-1.2.0.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c Changed
152
 
1
@@ -56,7 +56,6 @@
2
    unsigned int draining:1;
3
    unsigned int xrun_detected:1;
4
    unsigned int hw_params_changed:1;
5
-   unsigned int active:1;
6
    unsigned int negotiated:1;
7
 
8
    snd_pcm_uframes_t hw_ptr;
9
@@ -79,7 +78,7 @@
10
    struct spa_hook stream_listener;
11
 
12
    int64_t delay;
13
-   uint64_t transfered;
14
+   uint64_t transferred;
15
    uint64_t buffered;
16
    int64_t now;
17
    uintptr_t seq;
18
@@ -94,6 +93,7 @@
19
    snd_pcm_pipewire_t *pw = io->private_data;
20
    snd_pcm_sframes_t avail;
21
    bool active;
22
+   uint64_t val;
23
 
24
    avail = snd_pcm_ioplug_avail(io, pw->hw_ptr, io->appl_ptr);
25
 
26
@@ -112,20 +112,17 @@
27
    else {
28
        active = false;
29
    }
30
-   if (pw->active != active) {
31
-       uint64_t val;
32
 
33
-       pw_log_trace("%p: avail:%lu min-avail:%lu state:%s hw:%lu appl:%lu active:%d->%d state:%s",
34
-           pw, avail, pw->min_avail, snd_pcm_state_name(io->state),
35
-           pw->hw_ptr, io->appl_ptr, pw->active, active,
36
-           snd_pcm_state_name(io->state));
37
+   pw_log_trace("%p: avail:%lu min-avail:%lu state:%s hw:%lu appl:%lu active:%d state:%s",
38
+       pw, avail, pw->min_avail, snd_pcm_state_name(io->state),
39
+       pw->hw_ptr, io->appl_ptr, active,
40
+       snd_pcm_state_name(io->state));
41
+
42
+   if (active)
43
+       spa_system_eventfd_write(pw->system, io->poll_fd, 1);
44
+   else
45
+       spa_system_eventfd_read(pw->system, io->poll_fd, &val);
46
 
47
-       pw->active = active;
48
-       if (active)
49
-           spa_system_eventfd_write(pw->system, io->poll_fd, 1);
50
-       else
51
-           spa_system_eventfd_read(pw->system, io->poll_fd, &val);
52
-   }
53
    return active;
54
 }
55
 
56
@@ -200,13 +197,12 @@
57
    snd_pcm_pipewire_t *pw = io->private_data;
58
    uintptr_t seq1, seq2;
59
    int64_t elapsed = 0, delay, now, avail;
60
-   struct timespec ts;
61
    int64_t diff;
62
 
63
    do {
64
        seq1 = SPA_SEQ_READ(pw->seq);
65
 
66
-       delay = pw->delay + pw->transfered;
67
+       delay = pw->delay + pw->transferred;
68
        now = pw->now;
69
        if (io->stream == SND_PCM_STREAM_PLAYBACK)
70
            avail = snd_pcm_ioplug_hw_avail(io, pw->hw_ptr, io->appl_ptr);
71
@@ -218,8 +214,7 @@
72
 
73
    if (now != 0 && (io->state == SND_PCM_STATE_RUNNING ||
74
        io->state == SND_PCM_STATE_DRAINING)) {
75
-       clock_gettime(CLOCK_MONOTONIC, &ts);
76
-       diff = SPA_TIMESPEC_TO_NSEC(&ts) - now;
77
+       diff = pw_stream_get_nsec(pw->stream) - now;
78
        elapsed = (io->rate * diff) / SPA_NSEC_PER_SEC;
79
 
80
        if (io->stream == SND_PCM_STREAM_PLAYBACK)
81
@@ -421,7 +416,7 @@
82
    SPA_SEQ_WRITE(pw->seq);
83
 
84
    if (pw->now != pwt.now) {
85
-       pw->transfered = pw->buffered;
86
+       pw->transferred = pw->buffered;
87
        pw->buffered = 0;
88
    }
89
 
90
@@ -430,10 +425,10 @@
91
    pw->delay = delay;
92
    /* the buffer is now queued in the stream and consumed */
93
    if (io->stream == SND_PCM_STREAM_PLAYBACK)
94
-       pw->transfered += xfer;
95
+       pw->transferred += xfer;
96
 
97
-   /* more then requested data transfered, use them in next iteration */
98
-   pw->buffered = (want == 0 || pw->transfered < want) ?  0 : (pw->transfered % want);
99
+   /* more then requested data transferred, use them in next iteration */
100
+   pw->buffered = (want == 0 || pw->transferred < want) ?  0 : (pw->transferred % want);
101
 
102
    pw->now = pwt.now;
103
    SPA_SEQ_WRITE(pw->seq);
104
@@ -504,7 +499,12 @@
105
 
106
    snd_pcm_sw_params_alloca(&swparams);
107
    if (snd_pcm_sw_params_current(io->pcm, swparams) == 0) {
108
-       snd_pcm_sw_params_get_avail_min(swparams, &pw->min_avail);
109
+       int event;
110
+       snd_pcm_sw_params_get_period_event(swparams, &event);
111
+       if (event)
112
+           pw->min_avail = io->period_size;
113
+       else
114
+           snd_pcm_sw_params_get_avail_min(swparams, &pw->min_avail);
115
        snd_pcm_sw_params_get_boundary(swparams, &pw->boundary);
116
        snd_pcm_sw_params_dump(swparams, pw->output);
117
        fflush(pw->log_file);
118
@@ -530,8 +530,10 @@
119
    params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &pw->format);
120
 
121
    if (pw->stream != NULL) {
122
+       pw_stream_set_active(pw->stream, false);
123
        pw_stream_update_properties(pw->stream, &pw->props->dict);
124
        pw_stream_update_params(pw->stream, params, 1);
125
+       pw_stream_set_active(pw->stream, true);
126
        goto done;
127
    }
128
 
129
@@ -1252,7 +1254,12 @@
130
    pw_core_add_listener(pw->core, &pw->core_listener, &core_events, pw);
131
    pw_thread_loop_unlock(pw->main_loop);
132
 
133
-   pw->fd = spa_system_eventfd_create(pw->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
134
+   pw->fd = spa_system_eventfd_create(pw->system,
135
+           SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
136
+   if (pw->fd < 0) {
137
+       err = pw->fd;
138
+       goto error;
139
+   }
140
 
141
    pw->io.version = SND_PCM_IOPLUG_VERSION;
142
    pw->io.name = "ALSA <-> PipeWire PCM I/O Plugin";
143
@@ -1298,7 +1305,7 @@
144
    int err;
145
 
146
    pw_init(NULL, NULL);
147
-   if (strstr(pw_get_library_version(), "0.2") != NULL)
148
+   if (spa_strstartswith(pw_get_library_version(), "0.2"))
149
        return -ENOTSUP;
150
 
151
    props = pw_properties_new(NULL, NULL);
152
pipewire-1.0.1.tar.bz2/pipewire-jack/src/metadata.c -> pipewire-1.2.0.tar.gz/pipewire-jack/src/metadata.c Changed
72
 
1
@@ -324,7 +324,8 @@
2
 int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key)
3
 {
4
    struct client *c = (struct client *) client;
5
-   uint32_t id;
6
+   struct object *o;
7
+   uint32_t serial;
8
    int res = -1;
9
 
10
    spa_return_val_if_fail(c != NULL, -EINVAL);
11
@@ -335,11 +336,16 @@
12
    if (c->metadata == NULL)
13
        goto done;
14
 
15
-   id = jack_uuid_to_index(subject);
16
+   if (subject & (1<<30))
17
+       goto done;
18
+
19
+   serial = jack_uuid_to_index(subject);
20
+   if ((o = find_by_serial(c, serial)) == NULL)
21
+       goto done;
22
 
23
-   pw_log_info("remove id:%u (%"PRIu64") '%s'", id, subject, key);
24
+   pw_log_info("remove id:%u (%"PRIu64") '%s'", o->id, subject, key);
25
    pw_metadata_set_property(c->metadata->proxy,
26
-           id, key, NULL, NULL);
27
+           o->id, key, NULL, NULL);
28
    res = do_sync(c);
29
 done:
30
    pw_thread_loop_unlock(c->context.loop);
31
@@ -351,7 +357,8 @@
32
 int jack_remove_properties (jack_client_t* client, jack_uuid_t subject)
33
 {
34
    struct client *c = (struct client *) client;
35
-   uint32_t id;
36
+   struct object *o;
37
+   uint32_t serial;
38
    int res = -1;
39
 
40
    spa_return_val_if_fail(c != NULL, -EINVAL);
41
@@ -360,11 +367,16 @@
42
    if (c->metadata == NULL)
43
        goto done;
44
 
45
-   id = jack_uuid_to_index(subject);
46
+   if (subject & (1<<30))
47
+       goto done;
48
 
49
-   pw_log_info("remove id:%u (%"PRIu64")", id, subject);
50
+   serial = jack_uuid_to_index(subject);
51
+   if ((o = find_by_serial(c, serial)) == NULL)
52
+       goto done;
53
+
54
+   pw_log_info("remove id:%u (%"PRIu64")", o->id, subject);
55
    pw_metadata_set_property(c->metadata->proxy,
56
-           id, NULL, NULL, NULL);
57
+           o->id, NULL, NULL, NULL);
58
    res = do_sync(c);
59
 done:
60
    pw_thread_loop_unlock(c->context.loop);
61
@@ -396,8 +408,9 @@
62
    struct client *c = (struct client *) client;
63
 
64
    spa_return_val_if_fail(c != NULL, -EINVAL);
65
-
66
+   pw_thread_loop_lock(c->context.loop);
67
    c->property_callback = callback;
68
    c->property_arg = arg;
69
+   pw_thread_loop_unlock(c->context.loop);
70
    return 0;
71
 }
72
pipewire-1.0.1.tar.bz2/pipewire-jack/src/pipewire-jack.c -> pipewire-1.2.0.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
1412
 
1
@@ -45,7 +45,7 @@
2
  * with mlockall() on many systems */
3
 #define THREAD_STACK 524288
4
 
5
-#define DEFAULT_RT_MAX 88
6
+#define DEFAULT_RT_MAX RTPRIO_CLIENT
7
 
8
 #define JACK_CLIENT_NAME_SIZE      256
9
 #define JACK_PORT_NAME_SIZE        256
10
@@ -75,6 +75,10 @@
11
 #define SELF_CONNECT_FAIL_ALL  -2
12
 #define SELF_CONNECT_IGNORE_ALL    2
13
 
14
+#define OTHER_CONNECT_ALLOW    1
15
+#define OTHER_CONNECT_FAIL -1
16
+#define OTHER_CONNECT_IGNORE   0
17
+
18
 #define NOTIFY_BUFFER_SIZE (1u<<13)
19
 #define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
20
 
21
@@ -106,6 +110,7 @@
22
    struct pw_array descriptions;
23
    struct spa_list free_objects;
24
    struct spa_thread_utils *thread_utils;
25
+   uint32_t max_frames;
26
 };
27
 
28
 static struct globals globals;
29
@@ -223,7 +228,7 @@
30
    struct port *port;
31
    struct port *peer_port;
32
 
33
-   struct spa_io_buffers *io;
34
+   struct spa_io_buffers *io2;
35
 
36
    struct buffer buffersMAX_BUFFERS;
37
    uint32_t n_buffers;
38
@@ -249,7 +254,7 @@
39
 #define N_PORT_PARAMS  5
40
    struct spa_param_info paramsN_PORT_PARAMS;
41
 
42
-   struct spa_io_buffers io;
43
+   struct spa_io_buffers io2;
44
    struct spa_list mix;
45
    uint32_t n_mix;
46
    struct mix *global_mix;
47
@@ -273,6 +278,7 @@
48
    struct pw_memmap *mem;
49
    struct pw_node_activation *activation;
50
    int signalfd;
51
+   void (*trigger) (struct link *l, uint64_t nsec);
52
 };
53
 
54
 struct context {
55
@@ -414,6 +420,9 @@
56
        struct spa_io_position *position;
57
        struct pw_node_activation *driver_activation;
58
        struct spa_list target_links;
59
+       unsigned int prepared:1;
60
+       unsigned int first:1;
61
+       unsigned int thread_entered:1;
62
    } rt;
63
 
64
    pthread_mutex_t rt_lock;
65
@@ -423,8 +432,6 @@
66
    unsigned int started:1;
67
    unsigned int active:1;
68
    unsigned int destroyed:1;
69
-   unsigned int first:1;
70
-   unsigned int thread_entered:1;
71
    unsigned int has_transport:1;
72
    unsigned int allow_mlock:1;
73
    unsigned int warn_mlock:1;
74
@@ -438,18 +445,19 @@
75
    unsigned int locked_process:1;
76
    unsigned int default_as_system:1;
77
    int self_connect_mode;
78
+   int other_connect_mode;
79
    int rt_max;
80
    unsigned int fix_midi_events:1;
81
    unsigned int global_buffer_size:1;
82
    unsigned int global_sample_rate:1;
83
    unsigned int passive_links:1;
84
-   unsigned int graph_callback_pending:1;
85
    unsigned int pending_callbacks:1;
86
    int frozen_callbacks;
87
    char filter_char;
88
    uint32_t max_ports;
89
    unsigned int fill_aliases:1;
90
    unsigned int writable_input:1;
91
+   unsigned int async:1;
92
 
93
    uint32_t max_frames;
94
 
95
@@ -559,6 +567,7 @@
96
 struct io_info {
97
    struct mix *mix;
98
    void *data;
99
+   size_t size;
100
 };
101
 
102
 static int
103
@@ -566,13 +575,35 @@
104
        const void *data, size_t size, void *user_data)
105
 {
106
    const struct io_info *info = data;
107
-   info->mix->io = info->data;
108
+   struct port *port = info->mix->port;
109
+   if (info->data) {
110
+       if (info->size >= sizeof(struct spa_io_async_buffers)) {
111
+           struct spa_io_async_buffers *ab = info->data;
112
+           info->mix->io0 = &ab->buffersport->direction;
113
+           info->mix->io1 = &ab->buffersport->direction^1;
114
+       } else if (info->size >= sizeof(struct spa_io_buffers)) {
115
+           info->mix->io0 = info->data;
116
+           info->mix->io1 = info->data;
117
+       } else {
118
+           info->mix->io0 = NULL;
119
+           info->mix->io1 = NULL;
120
+       }
121
+       if (port->n_mix++ == 0 && port->global_mix != NULL) {
122
+           port->global_mix->io0 = &port->io0;
123
+           port->global_mix->io1 = &port->io1;
124
+       }
125
+   } else {
126
+       if (--port->n_mix == 0 && port->global_mix != NULL) {
127
+           port->global_mix->io0 = NULL;
128
+           port->global_mix->io1 = NULL;
129
+       }
130
+   }
131
    return 0;
132
 }
133
 
134
-static inline void mix_set_io(struct mix *mix, void *data)
135
+static inline void mix_set_io(struct mix *mix, void *data, size_t size)
136
 {
137
-   struct io_info info = { .mix = mix, .data = data };
138
+   struct io_info info = { .mix = mix, .data = data, .size = size };
139
    pw_data_loop_invoke(mix->port->client->loop,
140
        do_mix_set_io, SPA_ID_INVALID, &info, sizeof(info), false, NULL);
141
 }
142
@@ -584,13 +615,14 @@
143
    mix->peer_id = peer_id;
144
    mix->port = port;
145
    mix->peer_port = NULL;
146
-   mix->io = NULL;
147
+   mix->io0 = mix->io1 = NULL;
148
    mix->n_buffers = 0;
149
    spa_list_init(&mix->queue);
150
-   if (mix_id == SPA_ID_INVALID)
151
+   if (mix_id == SPA_ID_INVALID) {
152
        port->global_mix = mix;
153
-   else if (port->n_mix++ == 0 && port->global_mix != NULL)
154
-       mix_set_io(port->global_mix, &port->io);
155
+       if (port->n_mix > 0)
156
+           mix_set_io(port->global_mix, &port->io, sizeof(port->io));
157
+   }
158
 }
159
 static struct mix *find_mix_peer(struct client *c, uint32_t peer_id)
160
 {
161
@@ -677,8 +709,6 @@
162
    spa_list_remove(&mix->port_link);
163
    if (mix->id == SPA_ID_INVALID)
164
        port->global_mix = NULL;
165
-   else if (--port->n_mix == 0 && port->global_mix != NULL)
166
-       mix_set_io(port->global_mix, NULL);
167
    spa_list_remove(&mix->link);
168
    spa_list_append(&c->free_mix, &mix->link);
169
 }
170
@@ -1346,6 +1376,10 @@
171
    return b;
172
 }
173
 
174
+static inline bool is_osc(jack_midi_event_t *ev)
175
+{
176
+   return ev->size >= 1 && (ev->buffer0 == '#' || ev->buffer0 == '/');
177
+}
178
 
179
 static size_t convert_from_midi(void *midi, void *buffer, size_t size)
180
 {
181
@@ -1361,7 +1395,8 @@
182
    for (i = 0; i < count; i++) {
183
        jack_midi_event_t ev;
184
        jack_midi_event_get(&ev, midi, i);
185
-       spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi);
186
+       spa_pod_builder_control(&b, ev.time,
187
+               is_osc(&ev) ? SPA_CONTROL_OSC : SPA_CONTROL_Midi);
188
        spa_pod_builder_bytes(&b, ev.buffer, ev.size);
189
    }
190
    spa_pod_builder_pop(&b, &f);
191
@@ -1450,6 +1485,7 @@
192
            break;
193
 
194
        switch(next->type) {
195
+       case SPA_CONTROL_OSC:
196
        case SPA_CONTROL_Midi:
197
        {
198
            uint8_t *data = SPA_POD_BODY(&next->value);
199
@@ -1474,6 +1510,7 @@
200
    struct buffer *b;
201
    struct spa_data *d;
202
    struct spa_io_buffers *io;
203
+   uint32_t cycle = p->client->rt.position->clock.cycle & 1;
204
 
205
    if (frames == 0 || !p->valid)
206
        return NULL;
207
@@ -1485,7 +1522,7 @@
208
            c, p->object->port.name, p->port_id, frames,
209
            mix->n_buffers, mix->io);
210
 
211
-   if (SPA_UNLIKELY((io = mix->io) == NULL || mix->n_buffers == 0))
212
+   if (SPA_UNLIKELY((io = mix->iocycle) == NULL || mix->n_buffers == 0))
213
        return NULL;
214
 
215
    if (io->status == SPA_STATUS_HAVE_DATA &&
216
@@ -1508,7 +1545,7 @@
217
        }
218
        d = &b->datas0;
219
        d->chunk->offset = 0;
220
-       d->chunk->size = frames * sizeof(float);
221
+       d->chunk->size = c->buffer_frames * sizeof(float);
222
        d->chunk->stride = stride;
223
 
224
        io->buffer_id = b->id;
225
@@ -1557,7 +1594,7 @@
226
    }
227
 }
228
 
229
-static void prepare_output(struct port *p, uint32_t frames)
230
+static void prepare_output(struct port *p, uint32_t frames, uint32_t cycle)
231
 {
232
    struct mix *mix;
233
    struct spa_io_buffers *io;
234
@@ -1565,12 +1602,12 @@
235
    if (SPA_UNLIKELY(p->empty_out || p->tied))
236
        process_empty(p, frames);
237
 
238
-   if (p->global_mix == NULL || (io = p->global_mix->io) == NULL)
239
+   if (p->global_mix == NULL || (io = p->global_mix->iocycle) == NULL)
240
        return;
241
 
242
    spa_list_for_each(mix, &p->mix, port_link) {
243
-       if (SPA_LIKELY(mix->io != NULL))
244
-           *mix->io = *io;
245
+       if (SPA_LIKELY(mix->iocycle != NULL))
246
+           *mix->iocycle = *io;
247
    }
248
 }
249
 
250
@@ -1579,6 +1616,7 @@
251
    struct port *p;
252
    struct mix *mix;
253
    union pw_map_item *item;
254
+   uint32_t cycle = c->rt.position->clock.cycle & 1;
255
 
256
    pw_array_for_each(item, &c->portsSPA_DIRECTION_OUTPUT.items) {
257
                 if (pw_map_item_is_free(item))
258
@@ -1586,8 +1624,8 @@
259
        p = item->data;
260
        if (!p->valid)
261
            continue;
262
-       prepare_output(p, frames);
263
-       p->io.status = SPA_STATUS_NEED_DATA;
264
+       prepare_output(p, frames, cycle);
265
+       p->iocycle.status = SPA_STATUS_NEED_DATA;
266
    }
267
    pw_array_for_each(item, &c->portsSPA_DIRECTION_INPUT.items) {
268
                 if (pw_map_item_is_free(item))
269
@@ -1596,8 +1634,8 @@
270
        if (!p->valid)
271
            continue;
272
        spa_list_for_each(mix, &p->mix, port_link) {
273
-           if (SPA_LIKELY(mix->io != NULL))
274
-               mix->io->status = SPA_STATUS_NEED_DATA;
275
+           if (SPA_LIKELY(mix->iocycle != NULL))
276
+               mix->iocycle->status = SPA_STATUS_NEED_DATA;
277
        }
278
         }
279
 }
280
@@ -1698,7 +1736,7 @@
281
        running = s->clock.position - s->offset;
282
        if (running >= seg->start &&
283
            (seg->duration == 0 || running < seg->start + seg->duration))
284
-           d->frame = (running - seg->start) * seg->rate + seg->position;
285
+           d->frame = (unsigned int)((running - seg->start) * seg->rate + seg->position);
286
        else
287
            d->frame = seg->position;
288
    }
289
@@ -1720,12 +1758,12 @@
290
 
291
        abs_beat = seg->bar.beat;
292
 
293
-       d->bar = abs_beat / d->beats_per_bar;
294
-       beats = d->bar * d->beats_per_bar;
295
+       d->bar = (int32_t) (abs_beat / d->beats_per_bar);
296
+       beats = (long int) (d->bar * d->beats_per_bar);
297
        d->bar_start_tick = beats * d->ticks_per_beat;
298
-       d->beat = abs_beat - beats;
299
+       d->beat = (int32_t) (abs_beat - beats);
300
        beats += d->beat;
301
-       d->tick = (abs_beat - beats) * d->ticks_per_beat;
302
+       d->tick = (int32_t) ((abs_beat - beats) * d->ticks_per_beat);
303
        d->bar++;
304
        d->beat++;
305
    }
306
@@ -1761,13 +1799,6 @@
307
    return c->sample_rate == sample_rate;
308
 }
309
 
310
-static inline uint64_t get_time_ns(void)
311
-{
312
-   struct timespec ts;
313
-   clock_gettime(CLOCK_MONOTONIC, &ts);
314
-   return SPA_TIMESPEC_TO_NSEC(&ts);
315
-}
316
-
317
 static inline uint32_t cycle_run(struct client *c)
318
 {
319
    uint64_t cmd;
320
@@ -1786,16 +1817,25 @@
321
        }
322
        break;
323
    }
324
-   if (SPA_UNLIKELY(cmd > 1))
325
+   if (SPA_UNLIKELY(cmd > 1)) {
326
        pw_log_info("%p: missed %"PRIu64" wakeups", c, cmd - 1);
327
+       activation->xrun_count += cmd - 1;
328
+       activation->xrun_time = activation->awake_time;
329
+       activation->xrun_delay = 0;
330
+       activation->max_delay = SPA_MAX(activation->max_delay, 0u);
331
+   }
332
+
333
+   if (!SPA_ATOMIC_CAS(activation->status,
334
+               PW_NODE_ACTIVATION_TRIGGERED,
335
+               PW_NODE_ACTIVATION_AWAKE))
336
+       return 0;
337
 
338
-   activation->status = PW_NODE_ACTIVATION_AWAKE;
339
-   activation->awake_time = get_time_ns();
340
+   activation->awake_time = get_time_ns(c->l->system);
341
 
342
-   if (SPA_UNLIKELY(c->first)) {
343
+   if (SPA_UNLIKELY(c->rt.first)) {
344
        if (c->thread_init_callback)
345
            c->thread_init_callback(c->thread_init_arg);
346
-       c->first = false;
347
+       c->rt.first = false;
348
    }
349
 
350
    if (SPA_UNLIKELY(pos == NULL)) {
351
@@ -1845,40 +1885,75 @@
352
    return nframes;
353
 }
354
 
355
-static inline void signal_sync(struct client *c)
356
+static void trigger_link_v1(struct link *l, uint64_t nsec)
357
 {
358
-   uint64_t cmd, nsec;
359
-   struct link *l;
360
-   struct pw_node_activation *activation = c->activation;
361
+   struct client *c = l->client;
362
+   struct pw_node_activation *a = l->activation;
363
+   struct pw_node_activation_state *state = &a->state0;
364
+   uint64_t cmd = 1;
365
+
366
+   pw_log_trace_fp("%p: link %p-%d %p %d/%d", c, l, l->node_id, state,
367
+           state->pending, state->required);
368
+
369
+   if (pw_node_activation_state_dec(state)) {
370
+       if (SPA_ATOMIC_CAS(a->status,
371
+                   PW_NODE_ACTIVATION_NOT_TRIGGERED,
372
+                   PW_NODE_ACTIVATION_TRIGGERED)) {
373
+           a->signal_time = nsec;
374
 
375
-   complete_process(c, c->buffer_frames);
376
+           pw_log_trace_fp("%p: signal %p %p", c, l, state);
377
 
378
-   nsec = get_time_ns();
379
-   activation->status = PW_NODE_ACTIVATION_FINISHED;
380
-   activation->finish_time = nsec;
381
+           if (SPA_UNLIKELY(write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)))
382
+               pw_log_warn("%p: write failed %m", c);
383
+       }
384
+   }
385
+}
386
 
387
-   cmd = 1;
388
-   spa_list_for_each(l, &c->rt.target_links, target_link) {
389
-       struct pw_node_activation_state *state;
390
+static void trigger_link_v0(struct link *l, uint64_t nsec)
391
+{
392
+   struct client *c = l->client;
393
+   struct pw_node_activation *a = l->activation;
394
+   struct pw_node_activation_state *state = &a->state0;
395
+   uint64_t cmd = 1;
396
 
397
-       if (SPA_UNLIKELY(l->activation == NULL))
398
-           continue;
399
+   pw_log_trace_fp("%p: link %p-%d %p %d/%d", c, l, l->node_id, state,
400
+           state->pending, state->required);
401
 
402
-       state = &l->activation->state0;
403
+   if (pw_node_activation_state_dec(state)) {
404
+       SPA_ATOMIC_STORE(a->status, PW_NODE_ACTIVATION_TRIGGERED);
405
+       a->signal_time = nsec;
406
 
407
-       pw_log_trace_fp("%p: link %p %p %d/%d", c, l, state,
408
-               state->pending, state->required);
409
+       pw_log_trace_fp("%p: signal %p %p", c, l, state);
410
 
411
-       if (pw_node_activation_state_dec(state)) {
412
-           l->activation->status = PW_NODE_ACTIVATION_TRIGGERED;
413
-           l->activation->signal_time = nsec;
414
+       if (SPA_UNLIKELY(write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)))
415
+           pw_log_warn("%p: write failed %m", c);
416
+   }
417
+}
418
 
419
-           pw_log_trace_fp("%p: signal %p %p", c, l, state);
420
+static inline void deactivate_link(struct client *c, struct link *l, uint64_t trigger)
421
+{
422
+   if (!c->async && trigger != 0)
423
+       l->trigger(l, trigger);
424
+}
425
 
426
-           if (SPA_UNLIKELY(write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)))
427
-               pw_log_warn("%p: write failed %m", c);
428
-       }
429
-   }
430
+static inline void signal_sync(struct client *c)
431
+{
432
+   uint64_t nsec;
433
+   struct link *l;
434
+   struct pw_node_activation *activation = c->activation;
435
+   int old_status;
436
+
437
+   complete_process(c, c->buffer_frames);
438
+
439
+   nsec = get_time_ns(c->l->system);
440
+   old_status = SPA_ATOMIC_XCHG(activation->status, PW_NODE_ACTIVATION_FINISHED);
441
+   activation->finish_time = nsec;
442
+
443
+   if (c->async || old_status != PW_NODE_ACTIVATION_AWAKE)
444
+       return;
445
+
446
+   spa_list_for_each(l, &c->rt.target_links, target_link)
447
+       l->trigger(l, nsec);
448
 }
449
 
450
 static inline void cycle_signal(struct client *c, int status)
451
@@ -1918,8 +1993,8 @@
452
        return;
453
    }
454
    if (SPA_UNLIKELY(c->thread_callback)) {
455
-       if (!c->thread_entered) {
456
-           c->thread_entered = true;
457
+       if (!c->rt.thread_entered) {
458
+           c->rt.thread_entered = true;
459
            c->thread_callback(c->thread_arg);
460
        }
461
    } else if (SPA_LIKELY(mask & SPA_IO_IN)) {
462
@@ -1994,6 +2069,8 @@
463
    pw_log_debug("%p: create client transport with fds %d %d for node %u",
464
            c, readfd, writefd, c->node_id);
465
 
466
+   c->activation->client_version = PW_VERSION_NODE_ACTIVATION;
467
+
468
    close(writefd);
469
    c->socket_source = pw_loop_add_io(c->l,
470
                      readfd,
471
@@ -2057,7 +2134,7 @@
472
    c->rt.position = c->position;
473
    c->rt.driver_activation = c->driver_activation;
474
    if (c->position) {
475
-       pw_log_info("%p: driver:%d clock:%s", c,
476
+       pw_log_debug("%p: driver:%d clock:%s", c,
477
                c->driver_id, c->position->clock.name);
478
        check_sample_rate(c, c->position);
479
        check_buffer_frames(c, c->position);
480
@@ -2099,6 +2176,37 @@
481
    return 0;
482
 }
483
 
484
+static int
485
+do_memmap_free(struct spa_loop *loop,
486
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
487
+{
488
+   struct client *c = user_data;
489
+   struct pw_memmap *mm = *((struct pw_memmap **)data);
490
+   pw_log_trace("memmap %p free", mm);
491
+   pw_memmap_free(mm);
492
+   pw_core_set_paused(c->core, false);
493
+   return 0;
494
+}
495
+
496
+static int
497
+do_queue_memmap_free(struct spa_loop *loop,
498
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
499
+{
500
+   struct client *c = user_data;
501
+   pw_loop_invoke(c->context.l, do_memmap_free, 0, data, size, false, c);
502
+   return 0;
503
+}
504
+
505
+static void queue_memmap_free(struct client *c, struct pw_memmap *mem)
506
+{
507
+   if (mem != NULL) {
508
+       mem->tag0 = SPA_ID_INVALID;
509
+       pw_core_set_paused(c->core, true);
510
+       pw_data_loop_invoke(c->loop,
511
+           do_queue_memmap_free, SPA_ID_INVALID, &mem, sizeof(&mem), false, c);
512
+   }
513
+}
514
+
515
 static int client_node_set_io(void *data,
516
            uint32_t id,
517
            uint32_t mem_id,
518
@@ -2131,6 +2239,9 @@
519
        c->position = ptr;
520
        c->driver_id = ptr ? c->position->clock.id : SPA_ID_INVALID;
521
        update_driver_activation(c);
522
+       c->activation->active_driver_id = c->driver_id;
523
+       queue_memmap_free(c, old);
524
+       old = NULL;
525
        break;
526
    default:
527
        break;
528
@@ -2145,6 +2256,54 @@
529
    return -ENOTSUP;
530
 }
531
 
532
+static int do_prepare_client(struct spa_loop *loop, bool async, uint32_t seq,
533
+       const void *data, size_t size, void *user_data)
534
+{
535
+   struct client *c = user_data;
536
+
537
+   pw_log_debug("%p prepared:%d ", c, c->rt.prepared);
538
+   if (c->rt.prepared)
539
+       return 0;
540
+
541
+   SPA_ATOMIC_STORE(c->activation->status, PW_NODE_ACTIVATION_FINISHED);
542
+   pw_loop_update_io(c->l,
543
+             c->socket_source,
544
+             SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
545
+
546
+   c->rt.first = true;
547
+   c->rt.thread_entered = false;
548
+   c->rt.prepared = true;
549
+   return 0;
550
+}
551
+
552
+static int do_unprepare_client(struct spa_loop *loop, bool async, uint32_t seq,
553
+       const void *data, size_t size, void *user_data)
554
+{
555
+   struct client *c = user_data;
556
+   int old_state;
557
+   uint64_t trigger = 0;
558
+   struct link *l;
559
+
560
+   pw_log_debug("%p prepared:%d ", c, c->rt.prepared);
561
+   if (!c->rt.prepared)
562
+       return 0;
563
+
564
+   old_state = SPA_ATOMIC_XCHG(c->activation->status, PW_NODE_ACTIVATION_INACTIVE);
565
+   if (old_state != PW_NODE_ACTIVATION_FINISHED)
566
+       trigger = get_time_ns(c->l->system);
567
+
568
+   spa_list_for_each(l, &c->rt.target_links, target_link) {
569
+       if (!c->async && trigger != 0)
570
+           l->trigger(l, trigger);
571
+   }
572
+
573
+   pw_loop_update_io(c->l,
574
+             c->socket_source, SPA_IO_ERR | SPA_IO_HUP);
575
+
576
+   c->rt.prepared = false;
577
+   return 0;
578
+}
579
+
580
 static int client_node_command(void *data, const struct spa_command *command)
581
 {
582
    struct client *c = (struct client *) data;
583
@@ -2155,21 +2314,17 @@
584
    case SPA_NODE_COMMAND_Suspend:
585
    case SPA_NODE_COMMAND_Pause:
586
        if (c->started) {
587
-           pw_loop_update_io(c->l,
588
-                     c->socket_source, SPA_IO_ERR | SPA_IO_HUP);
589
-
590
+           pw_data_loop_invoke(c->loop,
591
+               do_unprepare_client, SPA_ID_INVALID, NULL, 0, false, c);
592
            c->started = false;
593
        }
594
        break;
595
 
596
    case SPA_NODE_COMMAND_Start:
597
        if (!c->started) {
598
-           pw_loop_update_io(c->l,
599
-                     c->socket_source,
600
-                     SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
601
+           pw_data_loop_invoke(c->loop,
602
+               do_prepare_client, SPA_ID_INVALID, NULL, 0, false, c);
603
            c->started = true;
604
-           c->first = true;
605
-           c->thread_entered = false;
606
        }
607
        break;
608
    default:
609
@@ -2303,6 +2458,15 @@
610
    return 1;
611
 }
612
 
613
+static int param_io_async(struct client *c, struct port *p,
614
+       struct spa_pod **param, struct spa_pod_builder *b)
615
+{
616
+   *param = spa_pod_builder_add_object(b,
617
+       SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO,
618
+       SPA_PARAM_IO_id,    SPA_POD_Id(SPA_IO_AsyncBuffers),
619
+       SPA_PARAM_IO_size,  SPA_POD_Int(sizeof(struct spa_io_async_buffers)));
620
+   return 1;
621
+}
622
 static int param_latency(struct client *c, struct port *p,
623
        struct spa_pod **param, struct spa_pod_builder *b)
624
 {
625
@@ -2323,7 +2487,7 @@
626
 static int port_set_format(struct client *c, struct port *p,
627
        uint32_t flags, const struct spa_pod *param)
628
 {
629
-   struct spa_pod *params6;
630
+   struct spa_pod *params7;
631
    uint8_t buffer4096;
632
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
633
 
634
@@ -2384,8 +2548,9 @@
635
    param_format(c, p, &params1, &b);
636
    param_buffers(c, p, &params2, &b);
637
    param_io(c, p, &params3, &b);
638
-   param_latency(c, p, &params4, &b);
639
-   param_latency_other(c, p, &params5, &b);
640
+   param_io_async(c, p, &params4, &b);
641
+   param_latency(c, p, &params5, &b);
642
+   param_latency_other(c, p, &params6, &b);
643
 
644
    pw_client_node_port_update(c->node,
645
                     p->direction,
646
@@ -2403,7 +2568,7 @@
647
 static void port_update_latency(struct port *p)
648
 {
649
    struct client *c = p->client;
650
-   struct spa_pod *params6;
651
+   struct spa_pod *params7;
652
    uint8_t buffer4096;
653
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
654
 
655
@@ -2411,8 +2576,9 @@
656
    param_format(c, p, &params1, &b);
657
    param_buffers(c, p, &params2, &b);
658
    param_io(c, p, &params3, &b);
659
-   param_latency(c, p, &params4, &b);
660
-   param_latency_other(c, p, &params5, &b);
661
+   param_io_async(c, p, &params4, &b);
662
+   param_latency(c, p, &params5, &b);
663
+   param_latency_other(c, p, &params6, &b);
664
 
665
    pw_log_info("port %s: update", p->object->port.name);
666
 
667
@@ -2568,18 +2734,18 @@
668
    return 0;
669
 }
670
 
671
-static void midi_init_buffer(void *data, uint32_t max_frames)
672
+static void midi_init_buffer(void *data, uint32_t max_frames, uint32_t nframes)
673
 {
674
    struct midi_buffer *mb = data;
675
    mb->magic = MIDI_BUFFER_MAGIC;
676
    mb->buffer_size = max_frames * sizeof(float);
677
-   mb->nframes = max_frames;
678
+   mb->nframes = nframes;
679
    mb->write_pos = 0;
680
    mb->event_count = 0;
681
    mb->lost_events = 0;
682
 }
683
 
684
-static inline void *init_buffer(struct port *p)
685
+static inline void *init_buffer(struct port *p, uint32_t nframes)
686
 {
687
    struct client *c = p->client;
688
    void *data = p->emptyptr;
689
@@ -2588,8 +2754,9 @@
690
 
691
    if (p->object->port.type_id == TYPE_ID_MIDI) {
692
        struct midi_buffer *mb = data;
693
-       midi_init_buffer(data, c->max_frames);
694
-       pw_log_debug("port %p: init midi buffer size:%d", p, mb->buffer_size);
695
+       midi_init_buffer(data, c->max_frames, nframes);
696
+       pw_log_debug("port %p: init midi buffer size:%d frames:%d", p,
697
+               mb->buffer_size, nframes);
698
    } else
699
        memset(data, 0, c->max_frames * sizeof(float));
700
 
701
@@ -2746,26 +2913,6 @@
702
    return res;
703
 }
704
 
705
-static int
706
-do_memmap_free(struct spa_loop *loop,
707
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
708
-{
709
-   struct pw_memmap *mm = user_data;
710
-   pw_log_trace("memmap %p free", mm);
711
-   pw_memmap_free(mm);
712
-   return 0;
713
-}
714
-
715
-static int
716
-do_queue_memmap_free(struct spa_loop *loop,
717
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
718
-{
719
-   struct client *c = user_data;
720
-   struct pw_memmap *mm = *((struct pw_memmap **)data);
721
-   pw_loop_invoke(c->context.l, do_memmap_free, 0, NULL, 0, false, mm);
722
-   return 0;
723
-}
724
-
725
 static int client_node_port_set_io(void *data,
726
                              enum spa_direction direction,
727
                              uint32_t port_id,
728
@@ -2797,8 +2944,8 @@
729
 
730
         if (mem_id == SPA_ID_INVALID) {
731
                 mm = ptr = NULL;
732
-        }
733
-        else {
734
+       size = 0;
735
+        } else {
736
        mm = pw_mempool_map_id(c->pool, mem_id,
737
                PW_MEMMAP_FLAG_READWRITE, offset, size, tag);
738
                 if (mm == NULL) {
739
@@ -2814,17 +2961,15 @@
740
 
741
    switch (id) {
742
    case SPA_IO_Buffers:
743
-       mix_set_io(mix, ptr);
744
-       if (old != NULL) {
745
-           old->tag0 = SPA_ID_INVALID;
746
-           pw_data_loop_invoke(c->loop,
747
-               do_queue_memmap_free, SPA_ID_INVALID, &old, sizeof(&old), false, c);
748
-           old = NULL;
749
-       }
750
+   case SPA_IO_AsyncBuffers:
751
+       mix_set_io(mix, ptr, size);
752
+       queue_memmap_free(c, old);
753
+       old = NULL;
754
        break;
755
    default:
756
        break;
757
    }
758
+
759
 exit_free:
760
    pw_memmap_free(old);
761
 exit:
762
@@ -2834,27 +2979,63 @@
763
 }
764
 
765
 static int
766
-do_activate_link(struct spa_loop *loop,
767
+do_add_link(struct spa_loop *loop,
768
                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
769
 {
770
    struct link *link = user_data;
771
    struct client *c = link->client;
772
-   pw_log_trace("link %p activate", link);
773
+   pw_log_trace("link %p", link);
774
    spa_list_append(&c->rt.target_links, &link->target_link);
775
    return 0;
776
 }
777
 
778
 static int
779
-do_deactivate_link(struct spa_loop *loop,
780
+do_remove_link(struct spa_loop *loop,
781
                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
782
 {
783
    struct link *link = user_data;
784
-   pw_log_trace("link %p activate", link);
785
+   struct client *c = link->client;
786
+
787
+   pw_log_trace("link %p", link);
788
    spa_list_remove(&link->target_link);
789
-   free_link(link);
790
+
791
+   if (c->rt.prepared) {
792
+       int old_state = SPA_ATOMIC_LOAD(c->activation->status);
793
+       uint64_t trigger = 0;
794
+       if (old_state != PW_NODE_ACTIVATION_FINISHED)
795
+           trigger = get_time_ns(c->l->system);
796
+       deactivate_link(c, link, trigger);
797
+   }
798
+   return 0;
799
+}
800
+
801
+static int
802
+do_free_link(struct spa_loop *loop,
803
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
804
+{
805
+   struct client *c = user_data;
806
+   struct link *l = *((struct link **)data);
807
+   free_link(l);
808
+   pw_core_set_paused(c->core, false);
809
+   return 0;
810
+}
811
+
812
+static int
813
+do_queue_free_link(struct spa_loop *loop,
814
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
815
+{
816
+   struct client *c = user_data;
817
+   pw_loop_invoke(c->context.l, do_free_link, 0, data, size, false, c);
818
    return 0;
819
 }
820
 
821
+static void queue_free_link(struct client *c, struct link *l)
822
+{
823
+   pw_core_set_paused(c->core, true);
824
+   pw_data_loop_invoke(c->loop,
825
+       do_queue_free_link, SPA_ID_INVALID, &l, sizeof(&l), false, c);
826
+}
827
+
828
 static int client_node_set_activation(void *data,
829
                              uint32_t node_id,
830
                              int signalfd,
831
@@ -2902,10 +3083,11 @@
832
        link->mem = mm;
833
        link->activation = ptr;
834
        link->signalfd = signalfd;
835
+       link->trigger = link->activation->server_version < 1 ? trigger_link_v0 : trigger_link_v1;
836
        spa_list_append(&c->links, &link->link);
837
 
838
        pw_data_loop_invoke(c->loop,
839
-                       do_activate_link, SPA_ID_INVALID, NULL, 0, false, link);
840
+                       do_add_link, SPA_ID_INVALID, NULL, 0, false, link);
841
    }
842
    else {
843
        link = find_activation(&c->links, node_id);
844
@@ -2916,7 +3098,8 @@
845
        spa_list_remove(&link->link);
846
 
847
        pw_data_loop_invoke(c->loop,
848
-                       do_deactivate_link, SPA_ID_INVALID, NULL, 0, false, link);
849
+                       do_remove_link, SPA_ID_INVALID, NULL, 0, false, link);
850
+       queue_free_link(c, link);
851
    }
852
 
853
    if (c->driver_id == node_id)
854
@@ -3241,18 +3424,28 @@
855
    .destroy = proxy_destroy,
856
 };
857
 
858
+static bool node_is_active(struct client *c, struct object *n)
859
+{
860
+   return !n->node.is_jack ||
861
+       (c->node_id == n->id ? c->active : n->node.is_running);
862
+}
863
+
864
 static void node_info(void *data, const struct pw_node_info *info)
865
 {
866
    struct object *n = data;
867
    struct client *c = n->client;
868
-   const char *str;
869
+   bool active;
870
 
871
    if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) {
872
-       str = spa_dict_lookup(info->props, PW_KEY_NODE_ALWAYS_PROCESS);
873
+       /* JACK clients always need ALWAYS_PROCESS=true or else they don't
874
+        * conform to the JACK API. We would try to hide the ports of
875
+        * PAUSED JACK clients, for example, even if they are active. */
876
+       const char *str = spa_dict_lookup(info->props, PW_KEY_NODE_ALWAYS_PROCESS);
877
        n->node.is_jack = str ? spa_atob(str) : false;
878
    }
879
 
880
-   n->node.is_running = !n->node.is_jack || (info->state == PW_NODE_STATE_RUNNING);
881
+   n->node.is_running = info->state == PW_NODE_STATE_RUNNING;
882
+   active = node_is_active(c, n);
883
 
884
    pw_log_debug("DSP node %d %08"PRIx64" jack:%u state change %s running:%d", info->id,
885
            info->change_mask, n->node.is_jack,
886
@@ -3264,7 +3457,7 @@
887
            if (p->type != INTERFACE_Port || p->removed ||
888
                p->port.node_id != info->id)
889
                continue;
890
-           if (n->node.is_running)
891
+           if (active)
892
                queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 1, NULL);
893
            else {
894
                spa_list_for_each(l, &c->context.objects, link) {
895
@@ -3492,7 +3685,7 @@
896
            o->port.latencySPA_DIRECTION_INPUT = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
897
            o->port.latencySPA_DIRECTION_OUTPUT = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
898
 
899
-           do_emit = !ot->node.is_jack || ot->node.is_running;
900
+           do_emit = node_is_active(c, ot);
901
 
902
            o->proxy = pw_registry_bind(c->registry,
903
                id, type, PW_VERSION_PORT, 0);
904
@@ -3785,7 +3978,7 @@
905
 
906
         if (getenv("PIPEWIRE_NOJACK") != NULL ||
907
             getenv("PIPEWIRE_INTERNAL") != NULL ||
908
-       strstr(pw_get_library_version(), "0.2") != NULL)
909
+       spa_strstartswith(pw_get_library_version(), "0.2"))
910
        goto disabled;
911
 
912
    return_val_if_fail(client_name != NULL, NULL);
913
@@ -3829,7 +4022,7 @@
914
        client->server_name = NULL;
915
 
916
    client->props = pw_properties_new(
917
-           "loop.cancel", "true",
918
+           PW_KEY_LOOP_CANCEL, "true",
919
            PW_KEY_REMOTE_NAME, client->server_name,
920
            PW_KEY_CLIENT_NAME, client_name,
921
            PW_KEY_CLIENT_API, "jack",
922
@@ -3854,7 +4047,7 @@
923
        goto no_props;
924
    client->context.nl = pw_thread_loop_get_loop(client->context.notify);
925
 
926
-   client->max_frames = client->context.context->settings.clock_quantum_limit;
927
+   globals.max_frames = client->max_frames = client->context.context->settings.clock_quantum_limit;
928
 
929
    client->notify_source = pw_loop_add_event(client->context.nl,
930
            on_notify_event, client);
931
@@ -3930,7 +4123,7 @@
932
        struct spa_fraction q;
933
        if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
934
            pw_properties_setf(client->props, PW_KEY_NODE_FORCE_RATE,
935
-                   "1/%u", q.denom);
936
+                   "%u", q.denom);
937
            pw_properties_setf(client->props, PW_KEY_NODE_FORCE_QUANTUM,
938
                    "%u", q.num);
939
        } else {
940
@@ -4012,6 +4205,7 @@
941
    client->max_ports = pw_properties_get_uint32(client->props, "jack.max-client-ports", MAX_CLIENT_PORTS);
942
    client->fill_aliases = pw_properties_get_bool(client->props, "jack.fill-aliases", false);
943
    client->writable_input = pw_properties_get_bool(client->props, "jack.writable-input", true);
944
+   client->async = pw_properties_get_bool(client->props, PW_KEY_NODE_ASYNC, false);
945
 
946
    client->self_connect_mode = SELF_CONNECT_ALLOW;
947
    if ((str = pw_properties_get(client->props, "jack.self-connect-mode")) != NULL) {
948
@@ -4024,6 +4218,14 @@
949
        else if (spa_streq(str, "ignore-all"))
950
            client->self_connect_mode = SELF_CONNECT_IGNORE_ALL;
951
    }
952
+   client->other_connect_mode = OTHER_CONNECT_ALLOW;
953
+   if ((str = pw_properties_get(client->props, "jack.other-connect-mode")) != NULL) {
954
+       if (spa_streq(str, "fail"))
955
+           client->other_connect_mode = OTHER_CONNECT_FAIL;
956
+       else if (spa_streq(str, "ignore"))
957
+           client->other_connect_mode = OTHER_CONNECT_IGNORE;
958
+   }
959
+
960
    client->rt_max = pw_properties_get_int32(client->props, "rt.prio", DEFAULT_RT_MAX);
961
 
962
    if (status)
963
@@ -4749,12 +4951,29 @@
964
 int jack_set_freewheel(jack_client_t* client, int onoff)
965
 {
966
    struct client *c = (struct client *) client;
967
+   const char *str;
968
 
969
    pw_log_info("%p: freewheel %d", client, onoff);
970
 
971
    pw_thread_loop_lock(c->context.loop);
972
-   pw_properties_set(c->props, "node.group",
973
-           onoff ? "pipewire.freewheel" : "");
974
+   str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
975
+   if (str != NULL) {
976
+       char *p = strstr(str, ",pipewire.freewheel");
977
+       if (p == NULL)
978
+           p = strstr(str, "pipewire.freewheel");
979
+       if (p == NULL && onoff)
980
+           pw_properties_setf(c->props, PW_KEY_NODE_GROUP,
981
+                   "%s,pipewire.freewheel", str);
982
+       else if (p != NULL && !onoff) {
983
+           pw_log_info("%s %d %s %.*s", p, (int)(p - str),
984
+                   str, (int)(p - str), str);
985
+           pw_properties_setf(c->props, PW_KEY_NODE_GROUP,
986
+                   "%.*s", (int)(p - str), str);
987
+       }
988
+   } else {
989
+       pw_properties_set(c->props, PW_KEY_NODE_GROUP,
990
+               onoff ? "pipewire.freewheel" : "");
991
+   }
992
 
993
    c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
994
    c->info.props = &c->props->dict;
995
@@ -4922,7 +5141,7 @@
996
    jack_port_type_id_t type_id;
997
    uint8_t buffer1024;
998
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
999
-   struct spa_pod *params6;
1000
+   struct spa_pod *params7;
1001
    uint32_t n_params = 0;
1002
    struct port *p;
1003
    int res, len;
1004
@@ -4972,7 +5191,7 @@
1005
    strcpy(o->port.name, name);
1006
    o->port.type_id = type_id;
1007
 
1008
-   init_buffer(p);
1009
+   init_buffer(p, c->max_frames);
1010
 
1011
    if (direction == SPA_DIRECTION_INPUT) {
1012
        switch (type_id) {
1013
@@ -5034,6 +5253,7 @@
1014
    param_enum_format(c, p, &paramsn_params++, &b);
1015
    param_buffers(c, p, &paramsn_params++, &b);
1016
    param_io(c, p, &paramsn_params++, &b);
1017
+   param_io_async(c, p, &paramsn_params++, &b);
1018
    param_latency(c, p, &paramsn_params++, &b);
1019
    param_latency_other(c, p, &paramsn_params++, &b);
1020
 
1021
@@ -5140,14 +5360,15 @@
1022
    return res;
1023
 }
1024
 
1025
-static struct buffer *get_mix_buffer(struct mix *mix, jack_nframes_t frames)
1026
+static struct buffer *get_mix_buffer(struct client *c, struct mix *mix, jack_nframes_t frames)
1027
 {
1028
    struct spa_io_buffers *io;
1029
+   uint32_t cycle = c->rt.position->clock.cycle & 1;
1030
 
1031
    if (mix->peer_port != NULL)
1032
-       prepare_output(mix->peer_port, frames);
1033
+       prepare_output(mix->peer_port, frames, cycle);
1034
 
1035
-   io = mix->io;
1036
+   io = mix->iocycle;
1037
    if (io == NULL ||
1038
        io->status != SPA_STATUS_HAVE_DATA ||
1039
        io->buffer_id >= mix->n_buffers)
1040
@@ -5185,7 +5406,7 @@
1041
        pw_log_trace_fp("%p: port %s mix %d.%d get buffer %d",
1042
                p->client, p->object->port.name, p->port_id, mix->id, frames);
1043
 
1044
-       if ((b = get_mix_buffer(mix, frames)) == NULL)
1045
+       if ((b = get_mix_buffer(p->client, mix, frames)) == NULL)
1046
            continue;
1047
 
1048
        if ((np = get_buffer_data(b, frames)) == NULL)
1049
@@ -5206,14 +5427,15 @@
1050
        p->zeroed = false;
1051
    }
1052
    if (ptr == NULL)
1053
-       ptr = init_buffer(p);
1054
+       ptr = init_buffer(p, frames);
1055
    return ptr;
1056
 }
1057
 
1058
 static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames)
1059
 {
1060
    struct mix *mix;
1061
-   void *ptr = midi_scratch;
1062
+   void *ptr = p->emptyptr;
1063
+   struct midi_buffer *mb = (struct midi_buffer*)midi_scratch;
1064
    struct spa_pod_sequence *seqMAX_MIX;
1065
    uint32_t n_seq = 0;
1066
 
1067
@@ -5228,7 +5450,7 @@
1068
        pw_log_trace_fp("%p: port %p mix %d.%d get buffer %d",
1069
                p->client, p, p->port_id, mix->id, frames);
1070
 
1071
-       if ((b = get_mix_buffer(mix, frames)) == NULL)
1072
+       if ((b = get_mix_buffer(p->client, mix, frames)) == NULL)
1073
            continue;
1074
 
1075
        d = &b->datas0;
1076
@@ -5242,8 +5464,17 @@
1077
        if (n_seq == MAX_MIX)
1078
            break;
1079
    }
1080
-   midi_init_buffer(ptr, MIDI_SCRATCH_FRAMES);
1081
-   convert_to_midi(seq, n_seq, ptr, p->client->fix_midi_events);
1082
+   midi_init_buffer(mb, MIDI_SCRATCH_FRAMES, frames);
1083
+   /* first convert to a thread local scratch buffer, then memcpy into
1084
+    * the per port buffer. This makes it possible to call this function concurrently
1085
+    * but also have different pointers per port */
1086
+   convert_to_midi(seq, n_seq, mb, p->client->fix_midi_events);
1087
+   memcpy(ptr, mb, sizeof(struct midi_buffer) + (mb->event_count
1088
+                              * sizeof(struct midi_event)));
1089
+   if (mb->write_pos) {
1090
+       size_t offs = mb->buffer_size - 1 - mb->write_pos;
1091
+       memcpy(SPA_PTROFF(ptr, offs, void), SPA_PTROFF(mb, offs, void), mb->write_pos);
1092
+   }
1093
    return ptr;
1094
 }
1095
 
1096
@@ -5271,7 +5502,7 @@
1097
 
1098
 static void *get_buffer_input_empty(struct port *p, jack_nframes_t frames)
1099
 {
1100
-   return init_buffer(p);
1101
+   return init_buffer(p, frames);
1102
 }
1103
 
1104
 SPA_EXPORT
1105
@@ -5280,22 +5511,27 @@
1106
    struct object *o = port_to_object(port);
1107
    struct port *p = NULL;
1108
    void *ptr = NULL;
1109
+   struct client *c;
1110
 
1111
    return_val_if_fail(o != NULL, NULL);
1112
 
1113
-   if (o->type != INTERFACE_Port || o->client == NULL)
1114
+   c = o->client;
1115
+   if (o->type != INTERFACE_Port || c == NULL)
1116
+       goto done;
1117
+
1118
+   if (frames > c->max_frames)
1119
        goto done;
1120
 
1121
    if ((p = o->port.port) == NULL) {
1122
        struct mix *mix;
1123
        struct buffer *b;
1124
 
1125
-       if ((mix = find_mix_peer(o->client, o->id)) == NULL)
1126
+       if ((mix = find_mix_peer(c, o->id)) == NULL)
1127
            goto done;
1128
 
1129
        pw_log_trace("peer mix: %p %d", mix, mix->peer_id);
1130
 
1131
-       if ((b = get_mix_buffer(mix, frames)) == NULL)
1132
+       if ((b = get_mix_buffer(c, mix, frames)) == NULL)
1133
            goto done;
1134
 
1135
        if (o->port.type_id == TYPE_ID_MIDI) {
1136
@@ -5304,7 +5540,7 @@
1137
            void *pod;
1138
 
1139
            ptr = midi_scratch;
1140
-           midi_init_buffer(ptr, MIDI_SCRATCH_FRAMES);
1141
+           midi_init_buffer(ptr, MIDI_SCRATCH_FRAMES, frames);
1142
 
1143
            d = &b->datas0;
1144
            if ((pod = spa_pod_from_data(d->data, d->maxsize,
1145
@@ -5313,7 +5549,7 @@
1146
            if (!spa_pod_is_sequence(pod))
1147
                goto done;
1148
            seq0 = pod;
1149
-           convert_to_midi(seq, 1, ptr, o->client->fix_midi_events);
1150
+           convert_to_midi(seq, 1, ptr, c->fix_midi_events);
1151
        } else {
1152
            ptr = get_buffer_data(b, frames);
1153
        }
1154
@@ -5321,7 +5557,7 @@
1155
        ptr = p->get_buffer(p, frames);
1156
    }
1157
 done:
1158
-   pw_log_trace_fp("%p: port %p buffer %p", o->client, p, ptr);
1159
+   pw_log_trace_fp("%p: port:%p buffer:%p frames:%d", c, p, ptr, frames);
1160
    return ptr;
1161
 }
1162
 
1163
@@ -5821,14 +6057,17 @@
1164
 {
1165
    int src_self, dst_self, sum;
1166
 
1167
-   if (c->self_connect_mode == SELF_CONNECT_ALLOW)
1168
-       return 1;
1169
-
1170
    src_self = src->port.node_id == c->node_id ? 1 : 0;
1171
    dst_self = dst->port.node_id == c->node_id ? 1 : 0;
1172
    sum = src_self + dst_self;
1173
-   /* check for no self connection first */
1174
+
1175
+   pw_log_debug("sum %d %d", sum, c->self_connect_mode);
1176
+
1177
+   /* check for other connection first */
1178
    if (sum == 0)
1179
+       return c->other_connect_mode;
1180
+
1181
+   if (c->self_connect_mode == SELF_CONNECT_ALLOW)
1182
        return 1;
1183
 
1184
    /* internal connection */
1185
@@ -6083,10 +6322,10 @@
1186
    rate = jack_get_sample_rate((jack_client_t*)c);
1187
    info = &o->port.latencydirection;
1188
 
1189
-   range->min = (info->min_quantum * nframes) +
1190
-       info->min_rate + (info->min_ns * rate) / SPA_NSEC_PER_SEC;
1191
-   range->max = (info->max_quantum * nframes) +
1192
-       info->max_rate + (info->max_ns * rate) / SPA_NSEC_PER_SEC;
1193
+   range->min = (jack_nframes_t)((info->min_quantum * nframes) +
1194
+       info->min_rate + (info->min_ns * rate) / SPA_NSEC_PER_SEC);
1195
+   range->max = (jack_nframes_t)((info->max_quantum * nframes) +
1196
+       info->max_rate + (info->max_ns * rate) / SPA_NSEC_PER_SEC);
1197
 
1198
    pw_log_debug("%p: %s get %d latency range %d %d", c, o->port.name,
1199
            mode, range->min, range->max);
1200
@@ -6270,13 +6509,13 @@
1201
 
1202
    if (port_name_pattern && port_name_pattern0) {
1203
        if ((r = regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB)) != 0) {
1204
-           pw_log_error("cant compile regex %s: %d", port_name_pattern, r);
1205
+           pw_log_error("can't compile regex %s: %d", port_name_pattern, r);
1206
            return NULL;
1207
        }
1208
    }
1209
    if (type_name_pattern && type_name_pattern0) {
1210
        if ((r = regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB)) != 0) {
1211
-           pw_log_error("cant compile regex %s: %d", type_name_pattern, r);
1212
+           pw_log_error("can't compile regex %s: %d", type_name_pattern, r);
1213
            return NULL;
1214
        }
1215
    }
1216
@@ -6391,7 +6630,7 @@
1217
        u1 = c->jack_position.unique_1;
1218
        *times = c->jack_times;
1219
        if (++count == 10) {
1220
-           pw_log_warn("could not get snapshot %lu %lu", u1, c->jack_position.unique_2);
1221
+           pw_log_warn("could not get snapshot %" PRIu64 " %" PRIu64, u1, c->jack_position.unique_2);
1222
            break;
1223
        }
1224
    } while (u1 != c->jack_position.unique_2);
1225
@@ -6407,7 +6646,7 @@
1226
    return_val_if_fail(c != NULL, 0);
1227
 
1228
    get_frame_times(c, &times);
1229
-   diff = get_time_ns() - times.nsec;
1230
+   diff = get_time_ns(c->l->system) - times.nsec;
1231
    return (jack_nframes_t) floor(((double)times.sample_rate * diff) / SPA_NSEC_PER_SEC);
1232
 }
1233
 
1234
@@ -6449,8 +6688,8 @@
1235
 
1236
    *current_frames = times.frames;
1237
    *next_usecs = times.next_nsec / SPA_NSEC_PER_USEC;
1238
-   *period_usecs = times.buffer_frames *
1239
-           (float)SPA_USEC_PER_SEC / (times.sample_rate * times.rate_diff);
1240
+   *period_usecs = (float)(times.buffer_frames *
1241
+           SPA_USEC_PER_SEC / (times.sample_rate * times.rate_diff));
1242
    *current_usecs = *next_usecs - (jack_time_t)*period_usecs;
1243
 
1244
    pw_log_trace("%p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames,
1245
@@ -6505,7 +6744,9 @@
1246
 SPA_EXPORT
1247
 jack_time_t jack_get_time(void)
1248
 {
1249
-   return get_time_ns()/SPA_NSEC_PER_USEC;
1250
+   struct timespec ts;
1251
+   clock_gettime(CLOCK_MONOTONIC, &ts);
1252
+   return SPA_TIMESPEC_TO_NSEC(&ts);
1253
 }
1254
 
1255
 SPA_EXPORT
1256
@@ -6684,7 +6925,7 @@
1257
        if (pos != NULL)
1258
            *pos = c->jack_position;
1259
        if (++count == 10) {
1260
-           pw_log_warn("could not get snapshot %lu %lu", u1, c->jack_position.unique_2);
1261
+           pw_log_warn("could not get snapshot %" PRIu64 " %" PRIu64, u1, c->jack_position.unique_2);
1262
            break;
1263
        }
1264
    } while (u1 != c->jack_position.unique_2);
1265
@@ -6706,7 +6947,7 @@
1266
    res = pos.frame;
1267
 
1268
    if (state == JackTransportRolling) {
1269
-       float usecs = get_time_ns()/1000 - pos.usecs;
1270
+       float usecs = get_time_ns(c->l->system)/1000 - pos.usecs;
1271
        res += (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
1272
    }
1273
    return res;
1274
@@ -6749,12 +6990,36 @@
1275
    SPA_ATOMIC_STORE(a->command, command);
1276
 }
1277
 
1278
+static int transport_update(struct client* c, int active)
1279
+{
1280
+   pw_log_info("%p: transport %d", c, active);
1281
+
1282
+   pw_thread_loop_lock(c->context.loop);
1283
+   pw_properties_set(c->props, PW_KEY_NODE_SYNC, "true");
1284
+   pw_properties_set(c->props, PW_KEY_NODE_TRANSPORT,
1285
+           active ? "true" : "false");
1286
+
1287
+   c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
1288
+   c->info.props = &c->props->dict;
1289
+
1290
+   pw_client_node_update(c->node,
1291
+                                    PW_CLIENT_NODE_UPDATE_INFO,
1292
+                   0, NULL, &c->info);
1293
+   c->info.change_mask = 0;
1294
+   pw_thread_loop_unlock(c->context.loop);
1295
+
1296
+   return 0;
1297
+}
1298
+
1299
 SPA_EXPORT
1300
 void jack_transport_start (jack_client_t *client)
1301
 {
1302
    struct client *c = (struct client *) client;
1303
    return_if_fail(c != NULL);
1304
-   update_command(c, PW_NODE_ACTIVATION_COMMAND_START);
1305
+   if (c->activation->server_version < 1)
1306
+       update_command(c, PW_NODE_ACTIVATION_COMMAND_START);
1307
+   else
1308
+       transport_update(c, true);
1309
 }
1310
 
1311
 SPA_EXPORT
1312
@@ -6762,7 +7027,10 @@
1313
 {
1314
    struct client *c = (struct client *) client;
1315
    return_if_fail(c != NULL);
1316
-   update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP);
1317
+   if (c->activation->server_version < 1)
1318
+       update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP);
1319
+   else
1320
+       transport_update(c, false);
1321
 }
1322
 
1323
 SPA_EXPORT
1324
@@ -7035,8 +7303,8 @@
1325
 {
1326
    struct midi_buffer *mb = port_buffer;
1327
    struct midi_event *ev = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
1328
-   return_val_if_fail(mb != NULL, -EINVAL);
1329
-   return_val_if_fail(ev != NULL, -EINVAL);
1330
+   if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
1331
+       return -EINVAL;
1332
    if (event_index >= mb->event_count)
1333
        return -ENOBUFS;
1334
    ev += event_index;
1335
@@ -7050,7 +7318,8 @@
1336
 void jack_midi_clear_buffer(void *port_buffer)
1337
 {
1338
    struct midi_buffer *mb = port_buffer;
1339
-   return_if_fail(mb != NULL);
1340
+   if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
1341
+       return;
1342
    mb->event_count = 0;
1343
    mb->write_pos = 0;
1344
    mb->lost_events = 0;
1345
@@ -7059,7 +7328,7 @@
1346
 SPA_EXPORT
1347
 void jack_midi_reset_buffer(void *port_buffer)
1348
 {
1349
-   jack_midi_clear_buffer(port_buffer);
1350
+   midi_init_buffer(port_buffer, globals.max_frames, globals.max_frames);
1351
 }
1352
 
1353
 SPA_EXPORT
1354
@@ -7068,7 +7337,8 @@
1355
    struct midi_buffer *mb = port_buffer;
1356
    size_t buffer_size;
1357
 
1358
-   return_val_if_fail(mb != NULL, 0);
1359
+   if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
1360
+       return 0;
1361
 
1362
    buffer_size = mb->buffer_size;
1363
 
1364
@@ -7094,18 +7364,21 @@
1365
                         size_t data_size)
1366
 {
1367
    struct midi_buffer *mb = port_buffer;
1368
-   struct midi_event *events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
1369
-   size_t buffer_size;
1370
-
1371
-   return_val_if_fail(mb != NULL, NULL);
1372
-
1373
-   buffer_size = mb->buffer_size;
1374
+   struct midi_event *events;
1375
 
1376
+   if (SPA_UNLIKELY(mb == NULL)) {
1377
+       pw_log_warn("port buffer is NULL");
1378
+       return NULL;
1379
+   }
1380
+   if (SPA_UNLIKELY(mb->magic != MIDI_BUFFER_MAGIC)) {
1381
+       pw_log_warn("port buffer is invalid");
1382
+       return NULL;
1383
+   }
1384
    if (SPA_UNLIKELY(time >= mb->nframes)) {
1385
        pw_log_warn("midi %p: time:%d frames:%d", port_buffer, time, mb->nframes);
1386
        goto failed;
1387
    }
1388
-
1389
+   events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
1390
    if (SPA_UNLIKELY(mb->event_count > 0 && time < eventsmb->event_count - 1.time)) {
1391
        pw_log_warn("midi %p: time:%d ev:%d", port_buffer, time, mb->event_count);
1392
        goto failed;
1393
@@ -7128,7 +7401,7 @@
1394
            res = ev->inline_data;
1395
        } else {
1396
            mb->write_pos += data_size;
1397
-           ev->byte_offset = buffer_size - 1 - mb->write_pos;
1398
+           ev->byte_offset = mb->buffer_size - 1 - mb->write_pos;
1399
            res = SPA_PTROFF(mb, ev->byte_offset, uint8_t);
1400
        }
1401
        mb->event_count += 1;
1402
@@ -7152,7 +7425,8 @@
1403
 uint32_t jack_midi_get_lost_event_count(void *port_buffer)
1404
 {
1405
    struct midi_buffer *mb = port_buffer;
1406
-   return_val_if_fail(mb != NULL, 0);
1407
+   if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
1408
+       return 0;
1409
    return mb->lost_events;
1410
 }
1411
 
1412
pipewire-1.0.1.tar.bz2/pipewire-v4l2/src/meson.build -> pipewire-1.2.0.tar.gz/pipewire-v4l2/src/meson.build Changed
9
 
1
@@ -12,6 +12,7 @@
2
   '-U_FILE_OFFSET_BITS',
3
   '-D_FILE_OFFSET_BITS=32',
4
   '-D_LARGEFILE64_SOURCE',
5
+  '-U_TIME_BITS',
6
   '-fvisibility=hidden',
7
 
8
 
9
pipewire-1.0.1.tar.bz2/pipewire-v4l2/src/pipewire-v4l2.c -> pipewire-1.2.0.tar.gz/pipewire-v4l2/src/pipewire-v4l2.c Changed
44
 
1
@@ -21,6 +21,7 @@
2
 
3
 #include <spa/utils/atomic.h>
4
 #include <spa/utils/result.h>
5
+#include <spa/utils/keys.h>
6
 #include <spa/pod/iter.h>
7
 #include <spa/pod/parser.h>
8
 #include <spa/pod/filter.h>
9
@@ -912,7 +913,7 @@
10
 static int vidioc_querycap(struct file *file, struct v4l2_capability *arg)
11
 {
12
    int res = 0;
13
-   const char *str = NULL;
14
+   const char *card = NULL, *bus_info = NULL;
15
    struct pw_node_info *info;
16
 
17
    if (file->node == NULL)
18
@@ -921,15 +922,19 @@
19
    info = file->node->info;
20
 
21
    if (info != NULL && info->props != NULL) {
22
-       str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION);
23
+       card = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION);
24
+       bus_info = spa_dict_lookup(info->props, SPA_KEY_API_V4L2_CAP_BUS_INFO);
25
    }
26
-   if (str == NULL)
27
-       str = DEFAULT_CARD;
28
+   if (card == NULL)
29
+       card = DEFAULT_CARD;
30
 
31
    spa_scnprintf((char*)arg->driver, sizeof(arg->driver), "%s", DEFAULT_DRIVER);
32
-   spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", str);
33
-   spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "platform:%s-%d",
34
-           DEFAULT_BUS_INFO, file->node->id);
35
+   spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", card);
36
+   if (bus_info == NULL)
37
+       spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "platform:%s-%d",
38
+               DEFAULT_BUS_INFO, file->node->id);
39
+   else
40
+       spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "%s", bus_info);
41
 
42
    arg->version = KERNEL_VERSION(5, 2, 0);
43
    arg->device_caps = V4L2_CAP_VIDEO_CAPTURE
44
pipewire-1.0.1.tar.bz2/pipewire-v4l2/src/v4l2-func.c -> pipewire-1.2.0.tar.gz/pipewire-v4l2/src/v4l2-func.c Changed
91
 
1
@@ -11,8 +11,9 @@
2
 #undef _FORTIFY_SOURCE
3
 #endif
4
 
5
+#include <assert.h>
6
+#include <stdbool.h>
7
 #include <stdio.h>
8
-#include <errno.h>
9
 #include <fcntl.h>
10
 #include <stdarg.h>
11
 #include <sys/mman.h>
12
@@ -31,10 +32,15 @@
13
    va_end(ap);         \
14
 }
15
 
16
+static inline bool needs_mode(int flags)
17
+{
18
+   return (flags & O_CREAT) || ((flags & O_TMPFILE) == O_TMPFILE);
19
+}
20
+
21
 SPA_EXPORT int open(const char *path, int oflag, ...)
22
 {
23
    mode_t mode = 0;
24
-   if (oflag & O_CREAT || oflag & O_TMPFILE)
25
+   if (needs_mode(oflag))
26
        extract_va_arg(mode_t, mode, oflag);
27
 
28
    return get_fops()->openat(AT_FDCWD, path, oflag, mode);
29
@@ -43,6 +49,7 @@
30
 /* _FORTIFY_SOURCE redirects open to __open_2 */
31
 SPA_EXPORT int __open_2(const char *path, int oflag)
32
 {
33
+   assert(!needs_mode(oflag));
34
    return open(path, oflag);
35
 }
36
 
37
@@ -50,7 +57,7 @@
38
 SPA_EXPORT int open64(const char *path, int oflag, ...)
39
 {
40
    mode_t mode = 0;
41
-   if (oflag & O_CREAT || oflag & O_TMPFILE)
42
+   if (needs_mode(oflag))
43
        extract_va_arg(mode_t, mode, oflag);
44
 
45
    return get_fops()->openat(AT_FDCWD, path, oflag | O_LARGEFILE, mode);
46
@@ -58,14 +65,15 @@
47
 
48
 SPA_EXPORT int __open64_2(const char *path, int oflag)
49
 {
50
-        return open(path, oflag);
51
+   assert(!needs_mode(oflag));
52
+   return open64(path, oflag);
53
 }
54
 #endif
55
 
56
 SPA_EXPORT int openat(int dirfd, const char *path, int oflag, ...)
57
 {
58
    mode_t mode = 0;
59
-   if (oflag & O_CREAT || oflag & O_TMPFILE)
60
+   if (needs_mode(oflag))
61
        extract_va_arg(mode_t, mode, oflag);
62
 
63
    return get_fops()->openat(dirfd, path, oflag, mode);
64
@@ -73,6 +81,7 @@
65
 
66
 SPA_EXPORT int __openat_2(int dirfd, const char *path, int oflag)
67
 {
68
+   assert(!needs_mode(oflag));
69
    return openat(dirfd, path, oflag);
70
 }
71
 
72
@@ -80,7 +89,7 @@
73
 SPA_EXPORT int openat64(int dirfd, const char *path, int oflag, ...)
74
 {
75
    mode_t mode = 0;
76
-   if (oflag & O_CREAT || oflag & O_TMPFILE)
77
+   if (needs_mode(oflag))
78
        extract_va_arg(mode_t, mode, oflag);
79
 
80
    return get_fops()->openat(dirfd, path, oflag | O_LARGEFILE, mode);
81
@@ -88,7 +97,8 @@
82
 
83
 SPA_EXPORT int __openat64_2(int dirfd, const char *path, int oflag)
84
 {
85
-   return openat(dirfd, path, oflag);
86
+   assert(!needs_mode(oflag));
87
+   return openat64(dirfd, path, oflag);
88
 }
89
 #endif
90
 
91
pipewire-1.0.1.tar.bz2/po/bg.po -> pipewire-1.2.0.tar.gz/po/bg.po Changed
908
 
1
@@ -1,22 +1,24 @@
2
-# Valentin Laskov <laskov@festa.bg>, 2016. #zanata
3
+# Bulgarian translation of pipewire po-file.
4
+# Copyright (C) 2023 Alexander Shopov <ash@kambanaria.org>
5
+# Alexander Shopov <ash@kambanaria.org>, 2023.
6
+# This file is licensed under the same terms as pipewire.
7
+#
8
 msgid ""
9
 msgstr ""
10
-"Project-Id-Version: PACKAGE VERSION\n"
11
+"Project-Id-Version: pipewire 1.0.0.\n"
12
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
13
 "issues/new\n"
14
-"POT-Creation-Date: 2021-04-18 16:54+0800\n"
15
-"PO-Revision-Date: 2020-10-15 21:30+0000\n"
16
-"Last-Translator: Emanuil Novachev <em.novachev@gmail.com>\n"
17
-"Language-Team: Bulgarian <https://translate.fedoraproject.org/projects/"
18
-"pipewire/pipewire/bg/>\n"
19
+"POT-Creation-Date: 2022-06-30 12:50+0200\n"
20
+"PO-Revision-Date: 2023-12-09 15:17+0100\n"
21
+"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
22
+"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
23
 "Language: bg\n"
24
 "MIME-Version: 1.0\n"
25
 "Content-Type: text/plain; charset=UTF-8\n"
26
 "Content-Transfer-Encoding: 8bit\n"
27
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
28
-"X-Generator: Weblate 4.2.2\n"
29
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
30
 
31
-#: src/daemon/pipewire.c:43
32
+#: src/daemon/pipewire.c:46
33
 #, c-format
34
 msgid ""
35
 "%s options\n"
36
@@ -24,40 +26,57 @@
37
 "      --version                         Show version\n"
38
 "  -c, --config                          Load config (Default %s)\n"
39
 msgstr ""
40
+"%s ОПЦИЯ…\n"
41
+"\n"
42
+"    -h --help            Извеждане на тази помощ\n"
43
+"    -V --version         Извеждане на версията\n"
44
+"    -c, --config         Зареждане на настройки (стандартно „%s“)\n"
45
 
46
-#: src/daemon/pipewire.desktop.in:4
47
-msgid "PipeWire Media System"
48
-msgstr ""
49
-
50
-#: src/daemon/pipewire.desktop.in:5
51
-msgid "Start the PipeWire Media System"
52
-msgstr ""
53
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180
54
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180
55
+#, c-format
56
+msgid "Tunnel to %s/%s"
57
+msgstr "Тунелиране към „%s/%s“"
58
 
59
-#: src/examples/media-session/alsa-monitor.c:526
60
-#: spa/plugins/alsa/acp/compat.c:187
61
-msgid "Built-in Audio"
62
-msgstr ""
63
+#: src/modules/module-fallback-sink.c:51
64
+msgid "Dummy Output"
65
+msgstr "Фиктивен изход"
66
 
67
-#: src/examples/media-session/alsa-monitor.c:530
68
-#: spa/plugins/alsa/acp/compat.c:192
69
-msgid "Modem"
70
-msgstr ""
71
+#: src/modules/module-pulse-tunnel.c:662
72
+#, c-format
73
+msgid "Tunnel for %s@%s"
74
+msgstr "Тунелиране за %s@%s"
75
 
76
-#: src/examples/media-session/alsa-monitor.c:539
77
+#: src/modules/module-zeroconf-discover.c:332
78
 msgid "Unknown device"
79
-msgstr ""
80
+msgstr "Непознато устройство"
81
+
82
+#: src/modules/module-zeroconf-discover.c:344
83
+#, c-format
84
+msgid "%s on %s@%s"
85
+msgstr "„%s“ на „%s@%s“"
86
+
87
+#: src/modules/module-zeroconf-discover.c:348
88
+#, c-format
89
+msgid "%s on %s"
90
+msgstr "„%s“ на „%s“"
91
 
92
-#: src/tools/pw-cat.c:991
93
+#: src/tools/pw-cat.c:784
94
 #, c-format
95
 msgid ""
96
-"%s options <file>\n"
97
+"%s options <file>|-\n"
98
 "  -h, --help                            Show this help\n"
99
 "      --version                         Show version\n"
100
 "  -v, --verbose                         Enable verbose operations\n"
101
 "\n"
102
 msgstr ""
103
+"%s ОПЦИЯ… ФАЙЛ|-\n"
104
+"\n"
105
+"  -h, --help             Извеждане на тази помощ\n"
106
+"      --version          Извеждане на версията\n"
107
+"  -v, --verbose          Включване на подробен режим\n"
108
 
109
-#: src/tools/pw-cat.c:998
110
+#: src/tools/pw-cat.c:791
111
 #, c-format
112
 msgid ""
113
 "  -R, --remote                          Remote daemon name\n"
114
@@ -71,11 +90,21 @@
115
 "                                          or direct samples (256)\n"
116
 "                                          the rate is the one of the source "
117
 "file\n"
118
-"      --list-targets                    List available targets for --target\n"
119
+"  -P  --properties                      Set node properties\n"
120
 "\n"
121
 msgstr ""
122
-
123
-#: src/tools/pw-cat.c:1016
124
+"  -R, --remote           Име на отдалечения демон\n"
125
+"      --media-type       Вид на медията (стандартно: „%s“)\n"
126
+"      --media-category   Категория на медията (стандартно: „%s“)\n"
127
+"      --media-role       Роля на медията (стандартно: „%s“)\n"
128
+"      --target           Цел на възела (стандартно: „%s“)\n"
129
+"                           „0“ означава без свързване\n"
130
+"      --latency          Латентност на възела (стандартно: „%s“)\n"
131
+"                           Xединица (единица е една от „s“/„ms“/„us“/„ns“)\n"
132
+"                           или пряко отчитане (256)\n"
133
+"                           честотата е тази на изходния файл\n"
134
+
135
+#: src/tools/pw-cat.c:809
136
 #, c-format
137
 msgid ""
138
 "      --rate                            Sample rate (req. for rec) (default "
139
@@ -94,16 +123,33 @@
140
 "%d)\n"
141
 "\n"
142
 msgstr ""
143
-
144
-#: src/tools/pw-cat.c:1033
145
+"      --rate             Честота на отчитане (задължително при запис)\n"
146
+"                           (стандартно: „%u“)\n"
147
+"      --channels         Брой канали (задължително при запис)\n"
148
+"                           (стандартно: „%u“)\n"
149
+"      --channel-map      Карта на каналите:\n"
150
+"                           едно от: „stereo“, „surround-51“, … или\n"
151
+"                           списък с разделител „,“, напр. „FL,FR“\n"
152
+"      --format           Формат на отчѐта %s (задължително при запис)\n"
153
+"                           (стандартно: „%s“)\n"
154
+"      --volume           Сила на звука на потока 0-1.0 (стандартно: %.3f)\n"
155
+"  -q  --quality          Качество при ново отчитане (0 - 15) (стандартно: %d)\n"
156
+
157
+#: src/tools/pw-cat.c:826
158
 msgid ""
159
 "  -p, --playback                        Playback mode\n"
160
 "  -r, --record                          Recording mode\n"
161
 "  -m, --midi                            Midi mode\n"
162
+"  -d, --dsd                             DSD mode\n"
163
 "\n"
164
 msgstr ""
165
+"  -p, --playback         Режим за изпълнение\n"
166
+"  -r, --record           Режим за запис\n"
167
+"  -m, --midi             Режим за Midi\n"
168
+"  -d, --dsd              Режим за DSD\n"
169
+"\n"
170
 
171
-#: src/tools/pw-cli.c:2932
172
+#: src/tools/pw-cli.c:2250
173
 #, c-format
174
 msgid ""
175
 "%s options command\n"
176
@@ -113,349 +159,350 @@
177
 "  -r, --remote                          Remote daemon name\n"
178
 "\n"
179
 msgstr ""
180
+"%s ОПЦИЯ… КОМАНДА\n"
181
+"  -h, --help             Извеждане на тази помощ\n"
182
+"      --version          Извеждане на версията\n"
183
+"  -d, --daemon           Стартиране като демон (стандартно: не)\n"
184
+"  -r, --remote           Име на отдалечения демон\n"
185
 
186
-#: spa/plugins/alsa/acp/acp.c:290
187
+#: spa/plugins/alsa/acp/acp.c:321
188
 msgid "Pro Audio"
189
-msgstr ""
190
+msgstr "Професионално аудио"
191
 
192
-#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
193
-#: spa/plugins/bluez5/bluez5-device.c:1000
194
+#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
195
+#: spa/plugins/bluez5/bluez5-device.c:1236
196
 msgid "Off"
197
-msgstr ""
198
-
199
-#: spa/plugins/alsa/acp/channelmap.h:466
200
-msgid "(invalid)"
201
-msgstr ""
202
+msgstr "Изключено"
203
 
204
-#: spa/plugins/alsa/acp/alsa-mixer.c:2709
205
+#: spa/plugins/alsa/acp/alsa-mixer.c:2652
206
 msgid "Input"
207
-msgstr ""
208
+msgstr "Вход"
209
 
210
-#: spa/plugins/alsa/acp/alsa-mixer.c:2710
211
+#: spa/plugins/alsa/acp/alsa-mixer.c:2653
212
 msgid "Docking Station Input"
213
-msgstr ""
214
+msgstr "Вход на станцията за скачане"
215
 
216
-#: spa/plugins/alsa/acp/alsa-mixer.c:2711
217
+#: spa/plugins/alsa/acp/alsa-mixer.c:2654
218
 msgid "Docking Station Microphone"
219
-msgstr ""
220
+msgstr "Микрофон на станцията за скачане"
221
 
222
-#: spa/plugins/alsa/acp/alsa-mixer.c:2712
223
+#: spa/plugins/alsa/acp/alsa-mixer.c:2655
224
 msgid "Docking Station Line In"
225
-msgstr ""
226
+msgstr "Вход на станцията за скачане"
227
 
228
-#: spa/plugins/alsa/acp/alsa-mixer.c:2713
229
-#: spa/plugins/alsa/acp/alsa-mixer.c:2804
230
+#: spa/plugins/alsa/acp/alsa-mixer.c:2656
231
+#: spa/plugins/alsa/acp/alsa-mixer.c:2747
232
 msgid "Line In"
233
-msgstr ""
234
+msgstr "Вход"
235
 
236
-#: spa/plugins/alsa/acp/alsa-mixer.c:2714
237
-#: spa/plugins/alsa/acp/alsa-mixer.c:2798
238
-#: spa/plugins/bluez5/bluez5-device.c:1145
239
+#: spa/plugins/alsa/acp/alsa-mixer.c:2657
240
+#: spa/plugins/alsa/acp/alsa-mixer.c:2741
241
+#: spa/plugins/bluez5/bluez5-device.c:1454
242
 msgid "Microphone"
243
-msgstr ""
244
+msgstr "Микрофон"
245
 
246
-#: spa/plugins/alsa/acp/alsa-mixer.c:2715
247
-#: spa/plugins/alsa/acp/alsa-mixer.c:2799
248
+#: spa/plugins/alsa/acp/alsa-mixer.c:2658
249
+#: spa/plugins/alsa/acp/alsa-mixer.c:2742
250
 msgid "Front Microphone"
251
-msgstr ""
252
+msgstr "Преден микрофон"
253
 
254
-#: spa/plugins/alsa/acp/alsa-mixer.c:2716
255
-#: spa/plugins/alsa/acp/alsa-mixer.c:2800
256
+#: spa/plugins/alsa/acp/alsa-mixer.c:2659
257
+#: spa/plugins/alsa/acp/alsa-mixer.c:2743
258
 msgid "Rear Microphone"
259
-msgstr ""
260
+msgstr "Заден микрофон"
261
 
262
-#: spa/plugins/alsa/acp/alsa-mixer.c:2717
263
+#: spa/plugins/alsa/acp/alsa-mixer.c:2660
264
 msgid "External Microphone"
265
-msgstr ""
266
+msgstr "Външен микрофон"
267
 
268
-#: spa/plugins/alsa/acp/alsa-mixer.c:2718
269
-#: spa/plugins/alsa/acp/alsa-mixer.c:2802
270
+#: spa/plugins/alsa/acp/alsa-mixer.c:2661
271
+#: spa/plugins/alsa/acp/alsa-mixer.c:2745
272
 msgid "Internal Microphone"
273
-msgstr ""
274
+msgstr "Вътрешен микрофон"
275
 
276
-#: spa/plugins/alsa/acp/alsa-mixer.c:2719
277
-#: spa/plugins/alsa/acp/alsa-mixer.c:2805
278
+#: spa/plugins/alsa/acp/alsa-mixer.c:2662
279
+#: spa/plugins/alsa/acp/alsa-mixer.c:2748
280
 msgid "Radio"
281
-msgstr ""
282
+msgstr "Радио"
283
 
284
-#: spa/plugins/alsa/acp/alsa-mixer.c:2720
285
-#: spa/plugins/alsa/acp/alsa-mixer.c:2806
286
+#: spa/plugins/alsa/acp/alsa-mixer.c:2663
287
+#: spa/plugins/alsa/acp/alsa-mixer.c:2749
288
 msgid "Video"
289
-msgstr ""
290
+msgstr "Видео"
291
 
292
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
293
+#: spa/plugins/alsa/acp/alsa-mixer.c:2664
294
 msgid "Automatic Gain Control"
295
-msgstr ""
296
+msgstr "Автоматично усилване"
297
 
298
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
299
+#: spa/plugins/alsa/acp/alsa-mixer.c:2665
300
 msgid "No Automatic Gain Control"
301
-msgstr ""
302
+msgstr "Без автоматично усилване"
303
 
304
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
305
+#: spa/plugins/alsa/acp/alsa-mixer.c:2666
306
 msgid "Boost"
307
-msgstr ""
308
+msgstr "Подсилване"
309
 
310
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
311
+#: spa/plugins/alsa/acp/alsa-mixer.c:2667
312
 msgid "No Boost"
313
-msgstr ""
314
+msgstr "Без подсилване"
315
 
316
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
317
+#: spa/plugins/alsa/acp/alsa-mixer.c:2668
318
 msgid "Amplifier"
319
-msgstr ""
320
+msgstr "Усилвател"
321
 
322
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
323
+#: spa/plugins/alsa/acp/alsa-mixer.c:2669
324
 msgid "No Amplifier"
325
-msgstr ""
326
+msgstr "Без усилвател"
327
 
328
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
329
+#: spa/plugins/alsa/acp/alsa-mixer.c:2670
330
 msgid "Bass Boost"
331
-msgstr ""
332
+msgstr "Усилване на басите"
333
 
334
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
335
+#: spa/plugins/alsa/acp/alsa-mixer.c:2671
336
 msgid "No Bass Boost"
337
-msgstr ""
338
+msgstr "Без усилване на басите"
339
 
340
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
341
-#: spa/plugins/bluez5/bluez5-device.c:1150
342
+#: spa/plugins/alsa/acp/alsa-mixer.c:2672
343
+#: spa/plugins/bluez5/bluez5-device.c:1460
344
 msgid "Speaker"
345
-msgstr ""
346
+msgstr "Високоговорител"
347
 
348
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
349
-#: spa/plugins/alsa/acp/alsa-mixer.c:2808
350
+#: spa/plugins/alsa/acp/alsa-mixer.c:2673
351
+#: spa/plugins/alsa/acp/alsa-mixer.c:2751
352
 msgid "Headphones"
353
-msgstr ""
354
+msgstr "Слушалки"
355
 
356
-#: spa/plugins/alsa/acp/alsa-mixer.c:2797
357
+#: spa/plugins/alsa/acp/alsa-mixer.c:2740
358
 msgid "Analog Input"
359
-msgstr ""
360
+msgstr "Аналогов вход"
361
 
362
-#: spa/plugins/alsa/acp/alsa-mixer.c:2801
363
+#: spa/plugins/alsa/acp/alsa-mixer.c:2744
364
 msgid "Dock Microphone"
365
-msgstr ""
366
+msgstr "Микрофон за скачане"
367
 
368
-#: spa/plugins/alsa/acp/alsa-mixer.c:2803
369
+#: spa/plugins/alsa/acp/alsa-mixer.c:2746
370
 msgid "Headset Microphone"
371
-msgstr ""
372
+msgstr "Микрофон на слушалките"
373
 
374
-#: spa/plugins/alsa/acp/alsa-mixer.c:2807
375
+#: spa/plugins/alsa/acp/alsa-mixer.c:2750
376
 msgid "Analog Output"
377
-msgstr ""
378
+msgstr "Аналогов изход"
379
 
380
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
381
+#: spa/plugins/alsa/acp/alsa-mixer.c:2752
382
 msgid "Headphones 2"
383
-msgstr ""
384
+msgstr "Слушалки 2"
385
 
386
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
387
+#: spa/plugins/alsa/acp/alsa-mixer.c:2753
388
 msgid "Headphones Mono Output"
389
-msgstr ""
390
+msgstr "Моно изход на слушалките"
391
 
392
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
393
+#: spa/plugins/alsa/acp/alsa-mixer.c:2754
394
 msgid "Line Out"
395
-msgstr ""
396
+msgstr "Изход"
397
 
398
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
399
+#: spa/plugins/alsa/acp/alsa-mixer.c:2755
400
 msgid "Analog Mono Output"
401
-msgstr ""
402
+msgstr "Аналогов моно изход"
403
 
404
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
405
+#: spa/plugins/alsa/acp/alsa-mixer.c:2756
406
 msgid "Speakers"
407
-msgstr ""
408
+msgstr "Тонколони"
409
 
410
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
411
+#: spa/plugins/alsa/acp/alsa-mixer.c:2757
412
 msgid "HDMI / DisplayPort"
413
-msgstr ""
414
+msgstr "HDMI/DisplayPort"
415
 
416
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
417
+#: spa/plugins/alsa/acp/alsa-mixer.c:2758
418
 msgid "Digital Output (S/PDIF)"
419
-msgstr ""
420
+msgstr "Цифров изход (S/PDIF)"
421
 
422
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
423
+#: spa/plugins/alsa/acp/alsa-mixer.c:2759
424
 msgid "Digital Input (S/PDIF)"
425
-msgstr ""
426
+msgstr "Цифров вход (S/PDIF)"
427
 
428
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
429
+#: spa/plugins/alsa/acp/alsa-mixer.c:2760
430
 msgid "Multichannel Input"
431
-msgstr ""
432
+msgstr "Многоканален вход"
433
 
434
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
435
+#: spa/plugins/alsa/acp/alsa-mixer.c:2761
436
 msgid "Multichannel Output"
437
-msgstr ""
438
+msgstr "Многоканален изход"
439
 
440
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
441
+#: spa/plugins/alsa/acp/alsa-mixer.c:2762
442
 msgid "Game Output"
443
-msgstr ""
444
+msgstr "Изход за игри"
445
 
446
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
447
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
448
+#: spa/plugins/alsa/acp/alsa-mixer.c:2763
449
+#: spa/plugins/alsa/acp/alsa-mixer.c:2764
450
 msgid "Chat Output"
451
-msgstr ""
452
+msgstr "Изход за разговори"
453
 
454
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
455
+#: spa/plugins/alsa/acp/alsa-mixer.c:2765
456
 msgid "Chat Input"
457
-msgstr ""
458
+msgstr "Вход за разговори"
459
 
460
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
461
+#: spa/plugins/alsa/acp/alsa-mixer.c:2766
462
 msgid "Virtual Surround 7.1"
463
-msgstr ""
464
+msgstr "Виртуален съраунд 7.1"
465
 
466
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
467
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
468
 msgid "Analog Mono"
469
-msgstr ""
470
+msgstr "Аналогово моно"
471
 
472
-#: spa/plugins/alsa/acp/alsa-mixer.c:4528
473
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
474
 msgid "Analog Mono (Left)"
475
-msgstr ""
476
+msgstr "Аналогово моно (отляво)"
477
 
478
-#: spa/plugins/alsa/acp/alsa-mixer.c:4529
479
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
480
 msgid "Analog Mono (Right)"
481
-msgstr ""
482
+msgstr "Аналогово моно (отдясно)"
483
 
484
 #. Note: Not translated to "Analog Stereo Input", because the source
485
 #. * name gets "Input" appended to it automatically, so adding "Input"
486
 #. * here would lead to the source name to become "Analog Stereo Input
487
 #. * Input". The same logic applies to analog-stereo-output,
488
 #. * multichannel-input and multichannel-output.
489
-#: spa/plugins/alsa/acp/alsa-mixer.c:4530
490
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
491
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
492
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
493
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
494
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
495
 msgid "Analog Stereo"
496
-msgstr ""
497
+msgstr "Аналогово стерео"
498
 
499
-#: spa/plugins/alsa/acp/alsa-mixer.c:4531
500
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
501
 msgid "Mono"
502
-msgstr ""
503
+msgstr "Моно"
504
 
505
-#: spa/plugins/alsa/acp/alsa-mixer.c:4532
506
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
507
 msgid "Stereo"
508
-msgstr ""
509
+msgstr "Стерео"
510
 
511
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
512
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
513
-#: spa/plugins/bluez5/bluez5-device.c:1135
514
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
515
+#: spa/plugins/alsa/acp/alsa-mixer.c:4642
516
+#: spa/plugins/bluez5/bluez5-device.c:1442
517
 msgid "Headset"
518
-msgstr ""
519
+msgstr "Слушалки с микрофон"
520
 
521
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
522
-#: spa/plugins/alsa/acp/alsa-mixer.c:4699
523
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
524
+#: spa/plugins/alsa/acp/alsa-mixer.c:4643
525
 msgid "Speakerphone"
526
-msgstr ""
527
+msgstr "Високоговорител на телефон"
528
 
529
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
530
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
531
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
532
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
533
 msgid "Multichannel"
534
-msgstr ""
535
+msgstr "Многоканален"
536
 
537
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
538
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
539
 msgid "Analog Surround 2.1"
540
-msgstr ""
541
+msgstr "Аналогов съраунд 2.1"
542
 
543
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
544
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
545
 msgid "Analog Surround 3.0"
546
-msgstr ""
547
+msgstr "Аналогов съраунд 3.0"
548
 
549
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
550
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
551
 msgid "Analog Surround 3.1"
552
-msgstr ""
553
+msgstr "Аналогов съраунд 3.1"
554
 
555
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
556
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
557
 msgid "Analog Surround 4.0"
558
-msgstr ""
559
+msgstr "Аналогов съраунд 4.0"
560
 
561
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
562
+#: spa/plugins/alsa/acp/alsa-mixer.c:4492
563
 msgid "Analog Surround 4.1"
564
-msgstr ""
565
+msgstr "Аналогов съраунд 4.1"
566
 
567
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
568
+#: spa/plugins/alsa/acp/alsa-mixer.c:4493
569
 msgid "Analog Surround 5.0"
570
-msgstr ""
571
+msgstr "Аналогов съраунд 5.0"
572
 
573
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
574
+#: spa/plugins/alsa/acp/alsa-mixer.c:4494
575
 msgid "Analog Surround 5.1"
576
-msgstr ""
577
+msgstr "Аналогов съраунд 5.1"
578
 
579
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
580
+#: spa/plugins/alsa/acp/alsa-mixer.c:4495
581
 msgid "Analog Surround 6.0"
582
-msgstr ""
583
+msgstr "Аналогов съраунд 6.0"
584
 
585
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
586
+#: spa/plugins/alsa/acp/alsa-mixer.c:4496
587
 msgid "Analog Surround 6.1"
588
-msgstr ""
589
+msgstr "Аналогов съраунд 6.1"
590
 
591
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
592
+#: spa/plugins/alsa/acp/alsa-mixer.c:4497
593
 msgid "Analog Surround 7.0"
594
-msgstr ""
595
+msgstr "Аналогов съраунд 7.0"
596
 
597
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
598
+#: spa/plugins/alsa/acp/alsa-mixer.c:4498
599
 msgid "Analog Surround 7.1"
600
-msgstr ""
601
+msgstr "Аналогов съраунд 7.1"
602
 
603
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
604
+#: spa/plugins/alsa/acp/alsa-mixer.c:4499
605
 msgid "Digital Stereo (IEC958)"
606
-msgstr ""
607
+msgstr "Цифрово стерео (IEC958)"
608
 
609
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
610
+#: spa/plugins/alsa/acp/alsa-mixer.c:4500
611
 msgid "Digital Surround 4.0 (IEC958/AC3)"
612
-msgstr ""
613
+msgstr "Цифров съраунд 4.0 (IEC958/AC3)"
614
 
615
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
616
+#: spa/plugins/alsa/acp/alsa-mixer.c:4501
617
 msgid "Digital Surround 5.1 (IEC958/AC3)"
618
-msgstr ""
619
+msgstr "Цифров съраунд 5.1 (IEC958/AC3)"
620
 
621
-#: spa/plugins/alsa/acp/alsa-mixer.c:4558
622
+#: spa/plugins/alsa/acp/alsa-mixer.c:4502
623
 msgid "Digital Surround 5.1 (IEC958/DTS)"
624
-msgstr ""
625
+msgstr "Цифров съраунд 5.1 (IEC958/DTS)"
626
 
627
-#: spa/plugins/alsa/acp/alsa-mixer.c:4559
628
+#: spa/plugins/alsa/acp/alsa-mixer.c:4503
629
 msgid "Digital Stereo (HDMI)"
630
-msgstr ""
631
+msgstr "Цифрово стерео (HDMI)"
632
 
633
-#: spa/plugins/alsa/acp/alsa-mixer.c:4560
634
+#: spa/plugins/alsa/acp/alsa-mixer.c:4504
635
 msgid "Digital Surround 5.1 (HDMI)"
636
-msgstr ""
637
+msgstr "Цифров съраунд 5.1 (HDMI)"
638
 
639
-#: spa/plugins/alsa/acp/alsa-mixer.c:4561
640
+#: spa/plugins/alsa/acp/alsa-mixer.c:4505
641
 msgid "Chat"
642
-msgstr ""
643
+msgstr "Разговор"
644
 
645
-#: spa/plugins/alsa/acp/alsa-mixer.c:4562
646
+#: spa/plugins/alsa/acp/alsa-mixer.c:4506
647
 msgid "Game"
648
-msgstr ""
649
+msgstr "Игра"
650
 
651
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
652
+#: spa/plugins/alsa/acp/alsa-mixer.c:4640
653
 msgid "Analog Mono Duplex"
654
-msgstr ""
655
+msgstr "Двупосочно аналогово моно"
656
 
657
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
658
+#: spa/plugins/alsa/acp/alsa-mixer.c:4641
659
 msgid "Analog Stereo Duplex"
660
-msgstr ""
661
+msgstr "Двупосочно аналогово стерео"
662
 
663
-#: spa/plugins/alsa/acp/alsa-mixer.c:4700
664
+#: spa/plugins/alsa/acp/alsa-mixer.c:4644
665
 msgid "Digital Stereo Duplex (IEC958)"
666
-msgstr ""
667
+msgstr "Двупосочно цифрово стерео (IEC958)"
668
 
669
-#: spa/plugins/alsa/acp/alsa-mixer.c:4701
670
+#: spa/plugins/alsa/acp/alsa-mixer.c:4645
671
 msgid "Multichannel Duplex"
672
-msgstr ""
673
+msgstr "Двупосочна многоканална връзка"
674
 
675
-#: spa/plugins/alsa/acp/alsa-mixer.c:4702
676
+#: spa/plugins/alsa/acp/alsa-mixer.c:4646
677
 msgid "Stereo Duplex"
678
-msgstr ""
679
+msgstr "Двупосочно стерео"
680
 
681
-#: spa/plugins/alsa/acp/alsa-mixer.c:4703
682
+#: spa/plugins/alsa/acp/alsa-mixer.c:4647
683
 msgid "Mono Chat + 7.1 Surround"
684
-msgstr ""
685
+msgstr "Моно разговор + съраунд 7.1"
686
 
687
-#: spa/plugins/alsa/acp/alsa-mixer.c:4806
688
+#: spa/plugins/alsa/acp/alsa-mixer.c:4754
689
 #, c-format
690
 msgid "%s Output"
691
-msgstr ""
692
+msgstr "Изход %s"
693
 
694
-#: spa/plugins/alsa/acp/alsa-mixer.c:4813
695
+#: spa/plugins/alsa/acp/alsa-mixer.c:4761
696
 #, c-format
697
 msgid "%s Input"
698
-msgstr ""
699
+msgstr "Вход %s"
700
 
701
-#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
702
+#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
703
 #, c-format
704
 msgid ""
705
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
706
@@ -468,24 +515,36 @@
707
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
708
 "to the ALSA developers."
709
 msgstr0 ""
710
+"„snd_pcm_avail()“ върна неочаквано голяма стойност: %lu байт (%lu ms).\n"
711
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
712
+"това на разработчиците на ALSA."
713
 msgstr1 ""
714
+"„snd_pcm_avail()“ върна неочаквано голяма стойност: %lu байта (%lu ms).\n"
715
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
716
+"това на разработчиците на ALSA."
717
 
718
-#: spa/plugins/alsa/acp/alsa-util.c:1241
719
+#: spa/plugins/alsa/acp/alsa-util.c:1253
720
 #, c-format
721
 msgid ""
722
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
723
-"%lu ms).\n"
724
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
725
+"(%s%lu ms).\n"
726
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
727
 "to the ALSA developers."
728
 msgid_plural ""
729
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
730
-"%lu ms).\n"
731
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
732
+"(%s%lu ms).\n"
733
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
734
 "to the ALSA developers."
735
 msgstr0 ""
736
+"„snd_pcm_delay()“ върна неочаквано голяма стойност: %li байт (%s%lu ms).\n"
737
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
738
+"това на разработчиците на ALSA."
739
 msgstr1 ""
740
+"„snd_pcm_delay()“ върна неочаквано голяма стойност: %li байта (%s%lu ms).\n"
741
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
742
+"това на разработчиците на ALSA.<"
743
 
744
-#: spa/plugins/alsa/acp/alsa-util.c:1288
745
+#: spa/plugins/alsa/acp/alsa-util.c:1300
746
 #, c-format
747
 msgid ""
748
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
749
@@ -493,8 +552,12 @@
750
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
751
 "to the ALSA developers."
752
 msgstr ""
753
+"„snd_pcm_avail_delay()“ върна неочаквана стойност: забавянето %lu е по-малко "
754
+"от наличното %lu.\n"
755
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
756
+"това на разработчиците на ALSA."
757
 
758
-#: spa/plugins/alsa/acp/alsa-util.c:1331
759
+#: spa/plugins/alsa/acp/alsa-util.c:1343
760
 #, c-format
761
 msgid ""
762
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
763
@@ -507,63 +570,117 @@
764
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
765
 "to the ALSA developers."
766
 msgstr0 ""
767
+"„snd_pcm_mmap_begin()“ върна неочаквано голяма стойност: %lu байт (%lu ms).\n"
768
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
769
+"това на разработчиците на ALSA."
770
 msgstr1 ""
771
+"„snd_pcm_mmap_begin()“ върна неочаквано голяма стойност: %lu байта (%lu "
772
+"ms).\n"
773
+"Най-вероятно се дължи на грешка в драйвера на ALSA „%s“. Молим да докладвате "
774
+"това на разработчиците на ALSA."
775
+
776
+#: spa/plugins/alsa/acp/channelmap.h:457
777
+msgid "(invalid)"
778
+msgstr "(неправилно)"
779
+
780
+#: spa/plugins/alsa/acp/compat.c:189
781
+msgid "Built-in Audio"
782
+msgstr "Вградено аудио"
783
+
784
+#: spa/plugins/alsa/acp/compat.c:194
785
+msgid "Modem"
786
+msgstr "Модем"
787
 
788
-#: spa/plugins/bluez5/bluez5-device.c:1010
789
+#: spa/plugins/bluez5/bluez5-device.c:1247
790
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
791
-msgstr ""
792
+msgstr "Аудио шлюз (източник A2DP и HSP/HFP AG)"
793
 
794
-#: spa/plugins/bluez5/bluez5-device.c:1033
795
+#: spa/plugins/bluez5/bluez5-device.c:1272
796
 #, c-format
797
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
798
-msgstr ""
799
+msgstr "Изпълнение с висока точност (елемент-приемник A2DP, кодер „%s“)"
800
 
801
-#: spa/plugins/bluez5/bluez5-device.c:1035
802
+#: spa/plugins/bluez5/bluez5-device.c:1275
803
 #, c-format
804
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
805
 msgstr ""
806
+"Двупосочна връзка с висока точност (елемент-източник/приемник A2DP, кодер "
807
+"„%s“)"
808
 
809
-#: spa/plugins/bluez5/bluez5-device.c:1041
810
+#: spa/plugins/bluez5/bluez5-device.c:1283
811
 msgid "High Fidelity Playback (A2DP Sink)"
812
-msgstr ""
813
+msgstr "Изпълнение с висока точност (елемент-приемник A2DP)"
814
 
815
-#: spa/plugins/bluez5/bluez5-device.c:1043
816
+#: spa/plugins/bluez5/bluez5-device.c:1285
817
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
818
+msgstr "Двупосочна връзка с висока точност (елемент-източник/приемник A2DP)"
819
+
820
+#: spa/plugins/bluez5/bluez5-device.c:1322
821
+#, c-format
822
+msgid "High Fidelity Playback (BAP Sink, codec %s)"
823
+msgstr "Изпълнение с висока точност (елемент-приемник BAP, кодер „%s“)"
824
+
825
+#: spa/plugins/bluez5/bluez5-device.c:1326
826
+#, c-format
827
+msgid "High Fidelity Input (BAP Source, codec %s)"
828
+msgstr "Вход с висока точност (елемент-източник BAP, кодер „%s“)"
829
+
830
+#: spa/plugins/bluez5/bluez5-device.c:1330
831
+#, c-format
832
+msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
833
 msgstr ""
834
+"Двупосочна връзка с висока точност (елемент-източник/приемник BAP, кодер "
835
+"„%s“)"
836
 
837
-#: spa/plugins/bluez5/bluez5-device.c:1070
838
+#: spa/plugins/bluez5/bluez5-device.c:1359
839
 #, c-format
840
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
841
-msgstr ""
842
+msgstr "Слушалки с микрофон (HSP/HFP, кодер „%s“)<"
843
 
844
-#: spa/plugins/bluez5/bluez5-device.c:1074
845
+#: spa/plugins/bluez5/bluez5-device.c:1364
846
 msgid "Headset Head Unit (HSP/HFP)"
847
-msgstr ""
848
-
849
-#: spa/plugins/bluez5/bluez5-device.c:1140
850
+msgstr "Слушалки с микрофон (HSP/HFP)"
851
+
852
+#: spa/plugins/bluez5/bluez5-device.c:1443
853
+#: spa/plugins/bluez5/bluez5-device.c:1448
854
+#: spa/plugins/bluez5/bluez5-device.c:1455
855
+#: spa/plugins/bluez5/bluez5-device.c:1461
856
+#: spa/plugins/bluez5/bluez5-device.c:1467
857
+#: spa/plugins/bluez5/bluez5-device.c:1473
858
+#: spa/plugins/bluez5/bluez5-device.c:1479
859
+#: spa/plugins/bluez5/bluez5-device.c:1485
860
+#: spa/plugins/bluez5/bluez5-device.c:1491
861
 msgid "Handsfree"
862
-msgstr ""
863
+msgstr "Слушалка за свободни ръце"
864
 
865
-#: spa/plugins/bluez5/bluez5-device.c:1155
866
+#: spa/plugins/bluez5/bluez5-device.c:1449
867
+msgid "Handsfree (HFP)"
868
+msgstr "Слушалка за свободни ръце (HFP)"
869
+
870
+#: spa/plugins/bluez5/bluez5-device.c:1466
871
 msgid "Headphone"
872
-msgstr ""
873
+msgstr "Слушалки"
874
 
875
-#: spa/plugins/bluez5/bluez5-device.c:1160
876
+#: spa/plugins/bluez5/bluez5-device.c:1472
877
 msgid "Portable"
878
-msgstr ""
879
+msgstr "Преносимо"
880
 
881
-#: spa/plugins/bluez5/bluez5-device.c:1165
882
+#: spa/plugins/bluez5/bluez5-device.c:1478
883
 msgid "Car"
884
-msgstr ""
885
+msgstr "Кола̀"
886
 
887
-#: spa/plugins/bluez5/bluez5-device.c:1170
888
+#: spa/plugins/bluez5/bluez5-device.c:1484
889
 msgid "HiFi"
890
-msgstr ""
891
+msgstr "HiFi"
892
 
893
-#: spa/plugins/bluez5/bluez5-device.c:1175
894
+#: spa/plugins/bluez5/bluez5-device.c:1490
895
 msgid "Phone"
896
-msgstr ""
897
+msgstr "Телефон"
898
 
899
-#: spa/plugins/bluez5/bluez5-device.c:1181
900
+#: spa/plugins/bluez5/bluez5-device.c:1497
901
 msgid "Bluetooth"
902
-msgstr ""
903
+msgstr "Bluetooth"
904
+
905
+#: spa/plugins/bluez5/bluez5-device.c:1498
906
+msgid "Bluetooth (HFP)"
907
+msgstr "Bluetooth (HFP)"
908
pipewire-1.0.1.tar.bz2/po/de.po -> pipewire-1.2.0.tar.gz/po/de.po Changed
899
 
1
@@ -8,15 +8,16 @@
2
 # Micha Pietsch <barney@fedoraproject.org>, 2008, 2009.
3
 # Hedda Peters <hpeters@redhat.com>, 2009, 2012.
4
 # Mario Blättermann <mario.blaettermann@gmail.com>, 2016.
5
+# Jürgen Benvenuti <gastornis@posteo.org>, 2024.
6
 #
7
 msgid ""
8
 msgstr ""
9
 "Project-Id-Version: pipewire.master-tx.de\n"
10
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
11
-"issues/new\n"
12
-"POT-Creation-Date: 2021-04-18 16:54+0800\n"
13
-"PO-Revision-Date: 2021-01-09 11:36+0000\n"
14
-"Last-Translator: Tobias Weise <tobias.weise@web.de>\n"
15
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
16
+"issues\n"
17
+"POT-Creation-Date: 2024-01-28 15:27+0000\n"
18
+"PO-Revision-Date: 2024-01-28 19:19+0100\n"
19
+"Last-Translator: Jürgen Benvenuti <gastornis@posteo.org>\n"
20
 "Language-Team: German <https://translate.fedoraproject.org/projects/pipewire/"
21
 "pipewire/de/>\n"
22
 "Language: de\n"
23
@@ -24,9 +25,9 @@
24
 "Content-Type: text/plain; charset=UTF-8\n"
25
 "Content-Transfer-Encoding: 8bit\n"
26
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
27
-"X-Generator: Weblate 4.4\n"
28
+"X-Generator: Poedit 3.4.2\n"
29
 
30
-#: src/daemon/pipewire.c:43
31
+#: src/daemon/pipewire.c:26
32
 #, c-format
33
 msgid ""
34
 "%s options\n"
35
@@ -34,38 +35,64 @@
36
 "      --version                         Show version\n"
37
 "  -c, --config                          Load config (Default %s)\n"
38
 msgstr ""
39
+"%s Optionen\n"
40
+"  -h, --help                            Diese Hilfe ausgeben\n"
41
+"      --version                         Version anzeigen\n"
42
+"  -c, --config                          Konfiguration laden (Voreinstellung "
43
+"%s)\n"
44
 
45
 #: src/daemon/pipewire.desktop.in:4
46
 msgid "PipeWire Media System"
47
-msgstr ""
48
+msgstr "PipeWire-Mediensystem"
49
 
50
 #: src/daemon/pipewire.desktop.in:5
51
 msgid "Start the PipeWire Media System"
52
-msgstr ""
53
+msgstr "Das PipeWire-Mediensystem starten"
54
 
55
-#: src/examples/media-session/alsa-monitor.c:526
56
-#: spa/plugins/alsa/acp/compat.c:187
57
-msgid "Built-in Audio"
58
-msgstr "Internes Audio"
59
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
60
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
61
+#, c-format
62
+msgid "Tunnel to %s%s%s"
63
+msgstr "Tunnel zu %s%s%s"
64
 
65
-#: src/examples/media-session/alsa-monitor.c:530
66
-#: spa/plugins/alsa/acp/compat.c:192
67
-msgid "Modem"
68
-msgstr "Modem"
69
+#: src/modules/module-fallback-sink.c:40
70
+msgid "Dummy Output"
71
+msgstr "Schein-Ausgabe"
72
+
73
+#: src/modules/module-pulse-tunnel.c:774
74
+#, c-format
75
+msgid "Tunnel for %s@%s"
76
+msgstr "Tunnel für %s@%s"
77
 
78
-#: src/examples/media-session/alsa-monitor.c:539
79
+#: src/modules/module-zeroconf-discover.c:315
80
 msgid "Unknown device"
81
-msgstr ""
82
+msgstr "Unbekanntes Gerät"
83
+
84
+#: src/modules/module-zeroconf-discover.c:327
85
+#, c-format
86
+msgid "%s on %s@%s"
87
+msgstr "%s auf %s@%s"
88
+
89
+#: src/modules/module-zeroconf-discover.c:331
90
+#, c-format
91
+msgid "%s on %s"
92
+msgstr "%s auf %s"
93
 
94
 #: src/tools/pw-cat.c:991
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 Optionen <Datei>|-\n"
105
+"  -h, --help                            Diese Hilfe ausgeben\n"
106
+"      --version                         Version anzeigen\n"
107
+"  -v, --verbose                         Ausführliche Vorgänge einschalten\n"
108
+"\n"
109
+"\n"
110
 
111
 #: src/tools/pw-cat.c:998
112
 #, c-format
113
@@ -74,16 +101,32 @@
114
 "      --media-type                      Set media type (default %s)\n"
115
 "      --media-category                  Set media category (default %s)\n"
116
 "      --media-role                      Set media role (default %s)\n"
117
-"      --target                          Set node target (default %s)\n"
118
+"      --target                          Set node target serial or name "
119
+"(default %s)\n"
120
 "                                          0 means don't link\n"
121
 "      --latency                         Set node latency (default %s)\n"
122
 "                                          Xunit (unit = s, ms, us, ns)\n"
123
 "                                          or direct samples (256)\n"
124
 "                                          the rate is the one of the source "
125
 "file\n"
126
-"      --list-targets                    List available targets for --target\n"
127
+"  -P  --properties                      Set node properties\n"
128
 "\n"
129
 msgstr ""
130
+"  -R, --remote                          Name des entfernten Daemon\n"
131
+"      --media-type                      Medientyp festlegen (Vorgabe %s)\n"
132
+"      --media-category                  Medienkategorie festlegen (Vorgabe "
133
+"%s)\n"
134
+"      --media-role                      Medienrolle festlegen (Vorgabe %s)\n"
135
+"      --target                          Seriennummer oder Name des "
136
+"Knotenziels festlegen (Vorgabe %s)\n"
137
+"                                          0 bedeutet keine Verbindung\n"
138
+"      --latency                         Knotenlatenz festlegen (Vorgabe %s)\n"
139
+"                                          Xunit (unit = s, ms, us, ns)\n"
140
+"                                          oder direkte Abtastung (256)\n"
141
+"                                          die Rate entspricht der "
142
+"Quelldatei\n"
143
+"  -P  --properties                      Knoteneigenschaften festlegen\n"
144
+"\n"
145
 
146
 #: src/tools/pw-cat.c:1016
147
 #, c-format
148
@@ -104,16 +147,40 @@
149
 "%d)\n"
150
 "\n"
151
 msgstr ""
152
+"      --rate                            Abtastrate (notw. für Aufzeichn.) "
153
+"(Vorgabe %u)\n"
154
+"      --channels                        Anzahl der Kanäle (notw. für "
155
+"Aufzeichn.) (Vorgabe %u)\n"
156
+"      --channel-map                     Kanalabbildung\n"
157
+"                                            eines von: »stereo«, "
158
+"»surround-51«, … oder\n"
159
+"                                            eine mit Kommata getrennte Liste "
160
+"mit Kanalnamen: z.B. »FL,FR«\n"
161
+"      --format                          Abtastformat %s (notw. für "
162
+"Aufzeichn.) (Vorgabe %s)\n"
163
+"      --volume                          Strom-Lautstärke 0-1.0 (Vorgabe "
164
+"%.3f)\n"
165
+"  -q  --quality                         Qualität der Neu-Abtastung (0 - 15) "
166
+"(Vorgabe %d)\n"
167
+"\n"
168
 
169
 #: src/tools/pw-cat.c:1033
170
 msgid ""
171
 "  -p, --playback                        Playback mode\n"
172
 "  -r, --record                          Recording mode\n"
173
 "  -m, --midi                            Midi mode\n"
174
+"  -d, --dsd                             DSD mode\n"
175
+"  -o, --encoded                         Encoded mode\n"
176
 "\n"
177
 msgstr ""
178
+"  -p, --playback                        Wiedergabe-Modus\n"
179
+"  -r, --record                          Aufnahme-Modus\n"
180
+"  -m, --midi                            Midi-Modus\n"
181
+"  -d, --dsd                             DSD-Modus\n"
182
+"  -o, --encoded                         Codieren-Modus\n"
183
+"\n"
184
 
185
-#: src/tools/pw-cli.c:2932
186
+#: src/tools/pw-cli.c:2248
187
 #, c-format
188
 msgid ""
189
 "%s options command\n"
190
@@ -121,362 +188,356 @@
191
 "      --version                         Show version\n"
192
 "  -d, --daemon                          Start as daemon (Default false)\n"
193
 "  -r, --remote                          Remote daemon name\n"
194
+"  -m, --monitor                         Monitor activity\n"
195
 "\n"
196
 msgstr ""
197
+"%s Optionen Befehl\n"
198
+"  -h, --help                            Diese Hilfe ausgeben\n"
199
+"      --version                         Version anzeigen\n"
200
+"  -d, --daemon                          Als Daemon starten (Vorgabe: nein)\n"
201
+"  -r, --remote                          Name des entfernten Daemon\n"
202
+"  -m, --monitor                         Aktivitäten überwachen\n"
203
+"\n"
204
 
205
-#: spa/plugins/alsa/acp/acp.c:290
206
+#: spa/plugins/alsa/acp/acp.c:327
207
 msgid "Pro Audio"
208
-msgstr ""
209
+msgstr "Pro Audio"
210
 
211
-#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
212
-#: spa/plugins/bluez5/bluez5-device.c:1000
213
+#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
214
+#: spa/plugins/bluez5/bluez5-device.c:1725
215
 msgid "Off"
216
 msgstr "Aus"
217
 
218
-#: spa/plugins/alsa/acp/channelmap.h:466
219
-msgid "(invalid)"
220
-msgstr "(ungültig)"
221
-
222
-#: spa/plugins/alsa/acp/alsa-mixer.c:2709
223
+#: spa/plugins/alsa/acp/alsa-mixer.c:2652
224
 msgid "Input"
225
 msgstr "Eingabe"
226
 
227
-#: spa/plugins/alsa/acp/alsa-mixer.c:2710
228
+#: spa/plugins/alsa/acp/alsa-mixer.c:2653
229
 msgid "Docking Station Input"
230
 msgstr "Eingabe über Docking-Station"
231
 
232
-#: spa/plugins/alsa/acp/alsa-mixer.c:2711
233
+#: spa/plugins/alsa/acp/alsa-mixer.c:2654
234
 msgid "Docking Station Microphone"
235
 msgstr "Mikrofon der Docking-Station"
236
 
237
-#: spa/plugins/alsa/acp/alsa-mixer.c:2712
238
+#: spa/plugins/alsa/acp/alsa-mixer.c:2655
239
 msgid "Docking Station Line In"
240
 msgstr "Line-Eingang der Docking-Station"
241
 
242
-#: spa/plugins/alsa/acp/alsa-mixer.c:2713
243
-#: spa/plugins/alsa/acp/alsa-mixer.c:2804
244
+#: spa/plugins/alsa/acp/alsa-mixer.c:2656
245
+#: spa/plugins/alsa/acp/alsa-mixer.c:2747
246
 msgid "Line In"
247
 msgstr "Line-Eingang"
248
 
249
-#: spa/plugins/alsa/acp/alsa-mixer.c:2714
250
-#: spa/plugins/alsa/acp/alsa-mixer.c:2798
251
-#: spa/plugins/bluez5/bluez5-device.c:1145
252
+#: spa/plugins/alsa/acp/alsa-mixer.c:2657
253
+#: spa/plugins/alsa/acp/alsa-mixer.c:2741
254
+#: spa/plugins/bluez5/bluez5-device.c:2004
255
 msgid "Microphone"
256
 msgstr "Mikrofon"
257
 
258
-#: spa/plugins/alsa/acp/alsa-mixer.c:2715
259
-#: spa/plugins/alsa/acp/alsa-mixer.c:2799
260
+#: spa/plugins/alsa/acp/alsa-mixer.c:2658
261
+#: spa/plugins/alsa/acp/alsa-mixer.c:2742
262
 msgid "Front Microphone"
263
 msgstr "Vorderes Mikrofon"
264
 
265
-#: spa/plugins/alsa/acp/alsa-mixer.c:2716
266
-#: spa/plugins/alsa/acp/alsa-mixer.c:2800
267
+#: spa/plugins/alsa/acp/alsa-mixer.c:2659
268
+#: spa/plugins/alsa/acp/alsa-mixer.c:2743
269
 msgid "Rear Microphone"
270
 msgstr "Rückwärtiges Mikrofon"
271
 
272
-#: spa/plugins/alsa/acp/alsa-mixer.c:2717
273
+#: spa/plugins/alsa/acp/alsa-mixer.c:2660
274
 msgid "External Microphone"
275
 msgstr "Externes Mikrofon"
276
 
277
-#: spa/plugins/alsa/acp/alsa-mixer.c:2718
278
-#: spa/plugins/alsa/acp/alsa-mixer.c:2802
279
+#: spa/plugins/alsa/acp/alsa-mixer.c:2661
280
+#: spa/plugins/alsa/acp/alsa-mixer.c:2745
281
 msgid "Internal Microphone"
282
 msgstr "Internes Mikrofon"
283
 
284
-#: spa/plugins/alsa/acp/alsa-mixer.c:2719
285
-#: spa/plugins/alsa/acp/alsa-mixer.c:2805
286
+#: spa/plugins/alsa/acp/alsa-mixer.c:2662
287
+#: spa/plugins/alsa/acp/alsa-mixer.c:2748
288
 msgid "Radio"
289
 msgstr "Radio"
290
 
291
-#: spa/plugins/alsa/acp/alsa-mixer.c:2720
292
-#: spa/plugins/alsa/acp/alsa-mixer.c:2806
293
+#: spa/plugins/alsa/acp/alsa-mixer.c:2663
294
+#: spa/plugins/alsa/acp/alsa-mixer.c:2749
295
 msgid "Video"
296
 msgstr "Video"
297
 
298
-#: spa/plugins/alsa/acp/alsa-mixer.c:2721
299
+#: spa/plugins/alsa/acp/alsa-mixer.c:2664
300
 msgid "Automatic Gain Control"
301
 msgstr "Automatische Verstärkungsregelung"
302
 
303
-#: spa/plugins/alsa/acp/alsa-mixer.c:2722
304
+#: spa/plugins/alsa/acp/alsa-mixer.c:2665
305
 msgid "No Automatic Gain Control"
306
 msgstr "Keine automatische Verstärkungsregelung"
307
 
308
-#: spa/plugins/alsa/acp/alsa-mixer.c:2723
309
+#: spa/plugins/alsa/acp/alsa-mixer.c:2666
310
 msgid "Boost"
311
 msgstr "Boost"
312
 
313
-#: spa/plugins/alsa/acp/alsa-mixer.c:2724
314
+#: spa/plugins/alsa/acp/alsa-mixer.c:2667
315
 msgid "No Boost"
316
 msgstr "Kein Boost"
317
 
318
-#: spa/plugins/alsa/acp/alsa-mixer.c:2725
319
+#: spa/plugins/alsa/acp/alsa-mixer.c:2668
320
 msgid "Amplifier"
321
 msgstr "Verstärker"
322
 
323
-#: spa/plugins/alsa/acp/alsa-mixer.c:2726
324
+#: spa/plugins/alsa/acp/alsa-mixer.c:2669
325
 msgid "No Amplifier"
326
 msgstr "Kein Verstärker"
327
 
328
-#: spa/plugins/alsa/acp/alsa-mixer.c:2727
329
+#: spa/plugins/alsa/acp/alsa-mixer.c:2670
330
 msgid "Bass Boost"
331
 msgstr "Bassverstärkung"
332
 
333
-#: spa/plugins/alsa/acp/alsa-mixer.c:2728
334
+#: spa/plugins/alsa/acp/alsa-mixer.c:2671
335
 msgid "No Bass Boost"
336
 msgstr "Keine Bassverstärkung"
337
 
338
-#: spa/plugins/alsa/acp/alsa-mixer.c:2729
339
-#: spa/plugins/bluez5/bluez5-device.c:1150
340
+#: spa/plugins/alsa/acp/alsa-mixer.c:2672
341
+#: spa/plugins/bluez5/bluez5-device.c:2010
342
 msgid "Speaker"
343
 msgstr "Lautsprecher"
344
 
345
-#: spa/plugins/alsa/acp/alsa-mixer.c:2730
346
-#: spa/plugins/alsa/acp/alsa-mixer.c:2808
347
+#: spa/plugins/alsa/acp/alsa-mixer.c:2673
348
+#: spa/plugins/alsa/acp/alsa-mixer.c:2751
349
 msgid "Headphones"
350
 msgstr "Kopfhörer"
351
 
352
-#: spa/plugins/alsa/acp/alsa-mixer.c:2797
353
+#: spa/plugins/alsa/acp/alsa-mixer.c:2740
354
 msgid "Analog Input"
355
 msgstr "Analoger Eingang"
356
 
357
-#: spa/plugins/alsa/acp/alsa-mixer.c:2801
358
+#: spa/plugins/alsa/acp/alsa-mixer.c:2744
359
 msgid "Dock Microphone"
360
 msgstr "Mikrofon der Docking-Station"
361
 
362
-#: spa/plugins/alsa/acp/alsa-mixer.c:2803
363
+#: spa/plugins/alsa/acp/alsa-mixer.c:2746
364
 msgid "Headset Microphone"
365
 msgstr "Mikrofon am Kopfhörer"
366
 
367
-#: spa/plugins/alsa/acp/alsa-mixer.c:2807
368
+#: spa/plugins/alsa/acp/alsa-mixer.c:2750
369
 msgid "Analog Output"
370
 msgstr "Analoge Ausgabe"
371
 
372
-#: spa/plugins/alsa/acp/alsa-mixer.c:2809
373
-#, fuzzy
374
+#: spa/plugins/alsa/acp/alsa-mixer.c:2752
375
 msgid "Headphones 2"
376
-msgstr "Kopfhörer"
377
+msgstr "Kopfhörer 2"
378
 
379
-#: spa/plugins/alsa/acp/alsa-mixer.c:2810
380
-#, fuzzy
381
+#: spa/plugins/alsa/acp/alsa-mixer.c:2753
382
 msgid "Headphones Mono Output"
383
-msgstr "Analoge Mono-Ausgabe"
384
+msgstr "Kopfhörer Mono-Ausgabe"
385
 
386
-#: spa/plugins/alsa/acp/alsa-mixer.c:2811
387
+#: spa/plugins/alsa/acp/alsa-mixer.c:2754
388
 msgid "Line Out"
389
 msgstr "Line-Ausgang"
390
 
391
-#: spa/plugins/alsa/acp/alsa-mixer.c:2812
392
+#: spa/plugins/alsa/acp/alsa-mixer.c:2755
393
 msgid "Analog Mono Output"
394
 msgstr "Analoge Mono-Ausgabe"
395
 
396
-#: spa/plugins/alsa/acp/alsa-mixer.c:2813
397
+#: spa/plugins/alsa/acp/alsa-mixer.c:2756
398
 msgid "Speakers"
399
 msgstr "Lautsprecher"
400
 
401
-#: spa/plugins/alsa/acp/alsa-mixer.c:2814
402
+#: spa/plugins/alsa/acp/alsa-mixer.c:2757
403
 msgid "HDMI / DisplayPort"
404
 msgstr "HDMI / DisplayPort"
405
 
406
-#: spa/plugins/alsa/acp/alsa-mixer.c:2815
407
+#: spa/plugins/alsa/acp/alsa-mixer.c:2758
408
 msgid "Digital Output (S/PDIF)"
409
 msgstr "Digitalausgang (S/PDIF)"
410
 
411
-#: spa/plugins/alsa/acp/alsa-mixer.c:2816
412
+#: spa/plugins/alsa/acp/alsa-mixer.c:2759
413
 msgid "Digital Input (S/PDIF)"
414
 msgstr "Digitaleingang (S/PDIF)"
415
 
416
-#: spa/plugins/alsa/acp/alsa-mixer.c:2817
417
+#: spa/plugins/alsa/acp/alsa-mixer.c:2760
418
 msgid "Multichannel Input"
419
-msgstr "Mehrkanaleingang"
420
+msgstr "Mehrkanal-Eingang"
421
 
422
-#: spa/plugins/alsa/acp/alsa-mixer.c:2818
423
+#: spa/plugins/alsa/acp/alsa-mixer.c:2761
424
 msgid "Multichannel Output"
425
-msgstr "Mehrkanalausgang"
426
+msgstr "Mehrkanal-Ausgang"
427
 
428
-#: spa/plugins/alsa/acp/alsa-mixer.c:2819
429
-#, fuzzy
430
+#: spa/plugins/alsa/acp/alsa-mixer.c:2762
431
 msgid "Game Output"
432
-msgstr "%s-Ausgabe"
433
+msgstr "Spiel-Ausgabe"
434
 
435
-#: spa/plugins/alsa/acp/alsa-mixer.c:2820
436
-#: spa/plugins/alsa/acp/alsa-mixer.c:2821
437
-#, fuzzy
438
+#: spa/plugins/alsa/acp/alsa-mixer.c:2763
439
+#: spa/plugins/alsa/acp/alsa-mixer.c:2764
440
 msgid "Chat Output"
441
-msgstr "%s-Ausgabe"
442
+msgstr "Unterhaltungs-Ausgabe"
443
 
444
-#: spa/plugins/alsa/acp/alsa-mixer.c:2822
445
-#, fuzzy
446
+#: spa/plugins/alsa/acp/alsa-mixer.c:2765
447
 msgid "Chat Input"
448
-msgstr "%s-Eingabe"
449
+msgstr "Unterhaltungs-Eingang"
450
 
451
-#: spa/plugins/alsa/acp/alsa-mixer.c:2823
452
-#, fuzzy
453
+#: spa/plugins/alsa/acp/alsa-mixer.c:2766
454
 msgid "Virtual Surround 7.1"
455
-msgstr "Virtuelles Surround-Ziel"
456
+msgstr "Virtuelles 7.1 Surround"
457
 
458
-#: spa/plugins/alsa/acp/alsa-mixer.c:4527
459
+#: spa/plugins/alsa/acp/alsa-mixer.c:4456
460
 msgid "Analog Mono"
461
 msgstr "Analog Mono"
462
 
463
-#: spa/plugins/alsa/acp/alsa-mixer.c:4528
464
-#, fuzzy
465
+#: spa/plugins/alsa/acp/alsa-mixer.c:4457
466
 msgid "Analog Mono (Left)"
467
-msgstr "Analog Mono"
468
+msgstr "Analoges Mono (links)"
469
 
470
-#: spa/plugins/alsa/acp/alsa-mixer.c:4529
471
-#, fuzzy
472
+#: spa/plugins/alsa/acp/alsa-mixer.c:4458
473
 msgid "Analog Mono (Right)"
474
-msgstr "Analog Mono"
475
+msgstr "Analoges Mono (rechts)"
476
 
477
 #. Note: Not translated to "Analog Stereo Input", because the source
478
 #. * name gets "Input" appended to it automatically, so adding "Input"
479
 #. * here would lead to the source name to become "Analog Stereo Input
480
 #. * Input". The same logic applies to analog-stereo-output,
481
 #. * multichannel-input and multichannel-output.
482
-#: spa/plugins/alsa/acp/alsa-mixer.c:4530
483
-#: spa/plugins/alsa/acp/alsa-mixer.c:4538
484
-#: spa/plugins/alsa/acp/alsa-mixer.c:4539
485
+#: spa/plugins/alsa/acp/alsa-mixer.c:4459
486
+#: spa/plugins/alsa/acp/alsa-mixer.c:4467
487
+#: spa/plugins/alsa/acp/alsa-mixer.c:4468
488
 msgid "Analog Stereo"
489
 msgstr "Analog Stereo"
490
 
491
-#: spa/plugins/alsa/acp/alsa-mixer.c:4531
492
+#: spa/plugins/alsa/acp/alsa-mixer.c:4460
493
 msgid "Mono"
494
 msgstr "Mono"
495
 
496
-#: spa/plugins/alsa/acp/alsa-mixer.c:4532
497
+#: spa/plugins/alsa/acp/alsa-mixer.c:4461
498
 msgid "Stereo"
499
 msgstr "Stereo"
500
 
501
-#: spa/plugins/alsa/acp/alsa-mixer.c:4540
502
-#: spa/plugins/alsa/acp/alsa-mixer.c:4698
503
-#: spa/plugins/bluez5/bluez5-device.c:1135
504
+#: spa/plugins/alsa/acp/alsa-mixer.c:4469
505
+#: spa/plugins/alsa/acp/alsa-mixer.c:4627
506
+#: spa/plugins/bluez5/bluez5-device.c:1992
507
 msgid "Headset"
508
 msgstr "Headset"
509
 
510
-#: spa/plugins/alsa/acp/alsa-mixer.c:4541
511
-#: spa/plugins/alsa/acp/alsa-mixer.c:4699
512
-#, fuzzy
513
+#: spa/plugins/alsa/acp/alsa-mixer.c:4470
514
+#: spa/plugins/alsa/acp/alsa-mixer.c:4628
515
 msgid "Speakerphone"
516
-msgstr "Lautsprecher"
517
+msgstr "Lautsprechertelefon"
518
 
519
-#: spa/plugins/alsa/acp/alsa-mixer.c:4542
520
-#: spa/plugins/alsa/acp/alsa-mixer.c:4543
521
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
522
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
523
 msgid "Multichannel"
524
 msgstr "Mehrkanal"
525
 
526
-#: spa/plugins/alsa/acp/alsa-mixer.c:4544
527
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
528
 msgid "Analog Surround 2.1"
529
 msgstr "Analog Surround 2.1"
530
 
531
-#: spa/plugins/alsa/acp/alsa-mixer.c:4545
532
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
533
 msgid "Analog Surround 3.0"
534
 msgstr "Analog Surround 3.0"
535
 
536
-#: spa/plugins/alsa/acp/alsa-mixer.c:4546
537
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
538
 msgid "Analog Surround 3.1"
539
 msgstr "Analog Surround 3.1"
540
 
541
-#: spa/plugins/alsa/acp/alsa-mixer.c:4547
542
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
543
 msgid "Analog Surround 4.0"
544
 msgstr "Analog Surround 4.0"
545
 
546
-#: spa/plugins/alsa/acp/alsa-mixer.c:4548
547
+#: spa/plugins/alsa/acp/alsa-mixer.c:4477
548
 msgid "Analog Surround 4.1"
549
 msgstr "Analog Surround 4.1"
550
 
551
-#: spa/plugins/alsa/acp/alsa-mixer.c:4549
552
+#: spa/plugins/alsa/acp/alsa-mixer.c:4478
553
 msgid "Analog Surround 5.0"
554
 msgstr "Analog Surround 5.0"
555
 
556
-#: spa/plugins/alsa/acp/alsa-mixer.c:4550
557
+#: spa/plugins/alsa/acp/alsa-mixer.c:4479
558
 msgid "Analog Surround 5.1"
559
 msgstr "Analog Surround 5.1"
560
 
561
-#: spa/plugins/alsa/acp/alsa-mixer.c:4551
562
+#: spa/plugins/alsa/acp/alsa-mixer.c:4480
563
 msgid "Analog Surround 6.0"
564
 msgstr "Analog Surround 6.0"
565
 
566
-#: spa/plugins/alsa/acp/alsa-mixer.c:4552
567
+#: spa/plugins/alsa/acp/alsa-mixer.c:4481
568
 msgid "Analog Surround 6.1"
569
 msgstr "Analog Surround 6.1"
570
 
571
-#: spa/plugins/alsa/acp/alsa-mixer.c:4553
572
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
573
 msgid "Analog Surround 7.0"
574
 msgstr "Analog Surround 7.0"
575
 
576
-#: spa/plugins/alsa/acp/alsa-mixer.c:4554
577
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
578
 msgid "Analog Surround 7.1"
579
 msgstr "Analog Surround 7.1"
580
 
581
-#: spa/plugins/alsa/acp/alsa-mixer.c:4555
582
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
583
 msgid "Digital Stereo (IEC958)"
584
 msgstr "Digital Stereo (IEC958)"
585
 
586
-#: spa/plugins/alsa/acp/alsa-mixer.c:4556
587
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
588
 msgid "Digital Surround 4.0 (IEC958/AC3)"
589
 msgstr "Digital Surround 4.0 (IEC958/AC3)"
590
 
591
-#: spa/plugins/alsa/acp/alsa-mixer.c:4557
592
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
593
 msgid "Digital Surround 5.1 (IEC958/AC3)"
594
 msgstr "Digital Surround 5.1 (IEC958/AC3)"
595
 
596
-#: spa/plugins/alsa/acp/alsa-mixer.c:4558
597
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
598
 msgid "Digital Surround 5.1 (IEC958/DTS)"
599
 msgstr "Digital Surround 5.1 (IEC958/DTS)"
600
 
601
-#: spa/plugins/alsa/acp/alsa-mixer.c:4559
602
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
603
 msgid "Digital Stereo (HDMI)"
604
 msgstr "Digital Stereo (HDMI)"
605
 
606
-#: spa/plugins/alsa/acp/alsa-mixer.c:4560
607
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
608
 msgid "Digital Surround 5.1 (HDMI)"
609
 msgstr "Digital Surround 5.1 (HDMI)"
610
 
611
-#: spa/plugins/alsa/acp/alsa-mixer.c:4561
612
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
613
 msgid "Chat"
614
-msgstr ""
615
+msgstr "Unterhaltung"
616
 
617
-#: spa/plugins/alsa/acp/alsa-mixer.c:4562
618
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
619
 msgid "Game"
620
-msgstr ""
621
+msgstr "Spiel"
622
 
623
-#: spa/plugins/alsa/acp/alsa-mixer.c:4696
624
+#: spa/plugins/alsa/acp/alsa-mixer.c:4625
625
 msgid "Analog Mono Duplex"
626
 msgstr "Analog Mono Duplex"
627
 
628
-#: spa/plugins/alsa/acp/alsa-mixer.c:4697
629
+#: spa/plugins/alsa/acp/alsa-mixer.c:4626
630
 msgid "Analog Stereo Duplex"
631
 msgstr "Analog Stereo Duplex"
632
 
633
-#: spa/plugins/alsa/acp/alsa-mixer.c:4700
634
+#: spa/plugins/alsa/acp/alsa-mixer.c:4629
635
 msgid "Digital Stereo Duplex (IEC958)"
636
 msgstr "Digital Stereo Duplex (IEC958)"
637
 
638
-#: spa/plugins/alsa/acp/alsa-mixer.c:4701
639
+#: spa/plugins/alsa/acp/alsa-mixer.c:4630
640
 msgid "Multichannel Duplex"
641
 msgstr "Mehrkanal-Duplex"
642
 
643
-#: spa/plugins/alsa/acp/alsa-mixer.c:4702
644
-#, fuzzy
645
+#: spa/plugins/alsa/acp/alsa-mixer.c:4631
646
 msgid "Stereo Duplex"
647
-msgstr "Analog Stereo Duplex"
648
+msgstr "Stereo Duplex"
649
 
650
-#: spa/plugins/alsa/acp/alsa-mixer.c:4703
651
+#: spa/plugins/alsa/acp/alsa-mixer.c:4632
652
 msgid "Mono Chat + 7.1 Surround"
653
-msgstr ""
654
+msgstr "Mono-Unterhaltung + 7.1 Surround"
655
 
656
-#: spa/plugins/alsa/acp/alsa-mixer.c:4806
657
+#: spa/plugins/alsa/acp/alsa-mixer.c:4733
658
 #, c-format
659
 msgid "%s Output"
660
 msgstr "%s-Ausgabe"
661
 
662
-#: spa/plugins/alsa/acp/alsa-mixer.c:4813
663
+#: spa/plugins/alsa/acp/alsa-mixer.c:4741
664
 #, c-format
665
 msgid "%s Input"
666
-msgstr "%s-Eingabe"
667
+msgstr "%s-Eingang"
668
 
669
-#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
670
-#, fuzzy, c-format
671
+#: spa/plugins/alsa/acp/alsa-util.c:1211 spa/plugins/alsa/acp/alsa-util.c:1305
672
+#, c-format
673
 msgid ""
674
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
675
 "ms).\n"
676
@@ -488,40 +549,40 @@
677
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
678
 "to the ALSA developers."
679
 msgstr0 ""
680
-"snd_pcm_avail() gibt einen Wert zurück, der außerordentlich groß ist: %lu "
681
-"bytes (%lu ms).\n"
682
+"snd_pcm_avail() gab einen Wert zurück, der außerordentlich groß ist: %lu "
683
+"Byte (%lu ms).\n"
684
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
685
 "dieses Problem den ALSA-Entwicklern."
686
 msgstr1 ""
687
-"snd_pcm_avail() gibt einen Wert zurück, der außerordentlich groß ist: %lu "
688
-"bytes (%lu ms).\n"
689
+"snd_pcm_avail() gab einen Wert zurück, der außerordentlich groß ist: %lu "
690
+"Bytes (%lu ms).\n"
691
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
692
 "dieses Problem den ALSA-Entwicklern."
693
 
694
-#: spa/plugins/alsa/acp/alsa-util.c:1241
695
-#, fuzzy, c-format
696
+#: spa/plugins/alsa/acp/alsa-util.c:1277
697
+#, c-format
698
 msgid ""
699
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
700
-"%lu ms).\n"
701
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
702
+"(%s%lu ms).\n"
703
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
704
 "to the ALSA developers."
705
 msgid_plural ""
706
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
707
-"%lu ms).\n"
708
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
709
+"(%s%lu ms).\n"
710
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
711
 "to the ALSA developers."
712
 msgstr0 ""
713
-"snd_pcm_delay() gibt einen Wert zurück, der außerordentlich groß ist: %li "
714
-"Bytes (%s%lu ms).\n"
715
+"snd_pcm_delay() gab einen Wert zurück, der außerordentlich groß ist: %li "
716
+"Byte (%s%lu ms).\n"
717
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
718
 "dieses Problem den ALSA-Entwicklern."
719
 msgstr1 ""
720
-"snd_pcm_delay() gibt einen Wert zurück, der außerordentlich groß ist: %li "
721
+"snd_pcm_delay() gab einen Wert zurück, der außerordentlich groß ist: %li "
722
 "Bytes (%s%lu ms).\n"
723
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
724
 "dieses Problem den ALSA-Entwicklern."
725
 
726
-#: spa/plugins/alsa/acp/alsa-util.c:1288
727
+#: spa/plugins/alsa/acp/alsa-util.c:1324
728
 #, c-format
729
 msgid ""
730
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
731
@@ -534,8 +595,8 @@
732
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
733
 "dieses Problem den ALSA-Entwicklern."
734
 
735
-#: spa/plugins/alsa/acp/alsa-util.c:1331
736
-#, fuzzy, c-format
737
+#: spa/plugins/alsa/acp/alsa-util.c:1367
738
+#, c-format
739
 msgid ""
740
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
741
 "(%lu ms).\n"
742
@@ -547,72 +608,126 @@
743
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
744
 "to the ALSA developers."
745
 msgstr0 ""
746
-"snd_pcm_mmap_begin() gibt einen Wert zurück, der außerordentlich groß ist: "
747
-"%lu Bytes (%lu ms).\n"
748
+"snd_pcm_mmap_begin() gab einen Wert zurück, der außerordentlich groß ist: "
749
+"%lu Byte (%lu ms).\n"
750
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
751
 "dieses Problem den ALSA-Entwicklern."
752
 msgstr1 ""
753
-"snd_pcm_mmap_begin() gibt einen Wert zurück, der außerordentlich groß ist: "
754
-"%lu Bytes (%lu ms).\n"
755
+"snd_pcm_mmap_begin() gab einen Wert zurück, der außerordentlich groß ist: "
756
+"%lu Byte (%lu ms).\n"
757
 "Dies ist wahrscheinlich ein Fehler im ALSA-Treiber »%s«. Bitte melden Sie "
758
 "dieses Problem den ALSA-Entwicklern."
759
 
760
-#: spa/plugins/bluez5/bluez5-device.c:1010
761
+#: spa/plugins/alsa/acp/channelmap.h:457
762
+msgid "(invalid)"
763
+msgstr "(ungültig)"
764
+
765
+#: spa/plugins/alsa/acp/compat.c:193
766
+msgid "Built-in Audio"
767
+msgstr "Internes Audio"
768
+
769
+#: spa/plugins/alsa/acp/compat.c:198
770
+msgid "Modem"
771
+msgstr "Modem"
772
+
773
+#: spa/plugins/bluez5/bluez5-device.c:1736
774
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
775
-msgstr ""
776
+msgstr "Audio-Gateway (A2DP-Quelle und HSP/HFP AG)"
777
 
778
-#: spa/plugins/bluez5/bluez5-device.c:1033
779
+#: spa/plugins/bluez5/bluez5-device.c:1784
780
 #, c-format
781
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
782
-msgstr ""
783
+msgstr "High Fidelity-Wiedergabe (A2DP-Senke, Codec %s)"
784
 
785
-#: spa/plugins/bluez5/bluez5-device.c:1035
786
+#: spa/plugins/bluez5/bluez5-device.c:1787
787
 #, c-format
788
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
789
-msgstr ""
790
+msgstr "High Fidelity Duplex (A2DP-Quelle/-Senke, Codec %s)"
791
 
792
-#: spa/plugins/bluez5/bluez5-device.c:1041
793
+#: spa/plugins/bluez5/bluez5-device.c:1795
794
 msgid "High Fidelity Playback (A2DP Sink)"
795
-msgstr ""
796
+msgstr "High Fidelity-Wiedergabe (A2DP-Senke)"
797
 
798
-#: spa/plugins/bluez5/bluez5-device.c:1043
799
+#: spa/plugins/bluez5/bluez5-device.c:1797
800
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
801
-msgstr ""
802
+msgstr "High Fidelity Duplex (A2DP-Quelle/-Senke)"
803
+
804
+#: spa/plugins/bluez5/bluez5-device.c:1847
805
+#, c-format
806
+msgid "High Fidelity Playback (BAP Sink, codec %s)"
807
+msgstr "High Fidelity-Wiedergabe (BAP-Senke, Codec %s)"
808
 
809
-#: spa/plugins/bluez5/bluez5-device.c:1070
810
+#: spa/plugins/bluez5/bluez5-device.c:1852
811
+#, c-format
812
+msgid "High Fidelity Input (BAP Source, codec %s)"
813
+msgstr "High Fidelity-Eingang (BAP-Quelle, Codec %s)"
814
+
815
+#: spa/plugins/bluez5/bluez5-device.c:1856
816
+#, c-format
817
+msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
818
+msgstr "High Fidelity Duplex (BAP-Quelle/-Senke, Codec %s)"
819
+
820
+#: spa/plugins/bluez5/bluez5-device.c:1865
821
+msgid "High Fidelity Playback (BAP Sink)"
822
+msgstr "High Fidelity-Wiedergabe (BAP-Senke)"
823
+
824
+#: spa/plugins/bluez5/bluez5-device.c:1869
825
+msgid "High Fidelity Input (BAP Source)"
826
+msgstr "High Fidelity-Eingang (BAP-Quelle)"
827
+
828
+#: spa/plugins/bluez5/bluez5-device.c:1872
829
+msgid "High Fidelity Duplex (BAP Source/Sink)"
830
+msgstr "High Fidelity Duplex (BAP-Quelle/-Senke)"
831
+
832
+#: spa/plugins/bluez5/bluez5-device.c:1908
833
 #, c-format
834
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
835
-msgstr ""
836
+msgstr "Kopfhörer-Garnitur (HSP/HFP, Codec %s)"
837
 
838
-#: spa/plugins/bluez5/bluez5-device.c:1074
839
+#: spa/plugins/bluez5/bluez5-device.c:1913
840
 msgid "Headset Head Unit (HSP/HFP)"
841
-msgstr ""
842
-
843
-#: spa/plugins/bluez5/bluez5-device.c:1140
844
+msgstr "Kopfhörer-Garnitur (HSP/HFP)"
845
+
846
+#: spa/plugins/bluez5/bluez5-device.c:1993
847
+#: spa/plugins/bluez5/bluez5-device.c:1998
848
+#: spa/plugins/bluez5/bluez5-device.c:2005
849
+#: spa/plugins/bluez5/bluez5-device.c:2011
850
+#: spa/plugins/bluez5/bluez5-device.c:2017
851
+#: spa/plugins/bluez5/bluez5-device.c:2023
852
+#: spa/plugins/bluez5/bluez5-device.c:2029
853
+#: spa/plugins/bluez5/bluez5-device.c:2035
854
+#: spa/plugins/bluez5/bluez5-device.c:2041
855
 msgid "Handsfree"
856
-msgstr "Freisprecheinrichtung"
857
+msgstr "Freisprech"
858
 
859
-#: spa/plugins/bluez5/bluez5-device.c:1155
860
+#: spa/plugins/bluez5/bluez5-device.c:1999
861
+msgid "Handsfree (HFP)"
862
+msgstr "Freisprech (HFP)"
863
+
864
+#: spa/plugins/bluez5/bluez5-device.c:2016
865
 msgid "Headphone"
866
 msgstr "Kopfhörer"
867
 
868
-#: spa/plugins/bluez5/bluez5-device.c:1160
869
+#: spa/plugins/bluez5/bluez5-device.c:2022
870
 msgid "Portable"
871
-msgstr "tragbar"
872
+msgstr "Tragbar"
873
 
874
-#: spa/plugins/bluez5/bluez5-device.c:1165
875
+#: spa/plugins/bluez5/bluez5-device.c:2028
876
 msgid "Car"
877
 msgstr "Auto"
878
 
879
-#: spa/plugins/bluez5/bluez5-device.c:1170
880
+#: spa/plugins/bluez5/bluez5-device.c:2034
881
 msgid "HiFi"
882
 msgstr "HiFi"
883
 
884
-#: spa/plugins/bluez5/bluez5-device.c:1175
885
+#: spa/plugins/bluez5/bluez5-device.c:2040
886
 msgid "Phone"
887
 msgstr "Telefon"
888
 
889
-#: spa/plugins/bluez5/bluez5-device.c:1181
890
-#, fuzzy
891
+#: spa/plugins/bluez5/bluez5-device.c:2047
892
 msgid "Bluetooth"
893
-msgstr "Bluetooth-Eingabe"
894
+msgstr "Bluetooth"
895
+
896
+#: spa/plugins/bluez5/bluez5-device.c:2048
897
+msgid "Bluetooth (HFP)"
898
+msgstr "Bluetooth (HFP)"
899
pipewire-1.0.1.tar.bz2/po/oc.po -> pipewire-1.2.0.tar.gz/po/oc.po Changed
604
 
1
@@ -8,14 +8,14 @@
2
 # Thomas Canniot <mrtom@fedoraproject.org>, 2009, 2012.
3
 # Cédric Valmary (Tot en Òc) <cvalmary@yahoo.fr>, 2015.
4
 # Cédric Valmary (totenoc.eu) <cvalmary@yahoo.fr>, 2016.
5
-# Quentin PAGÈS, 2023.
6
+# Quentin PAGÈS, 2023.-2024
7
 msgid ""
8
 msgstr ""
9
 "Project-Id-Version: pipewire trunk\n"
10
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
11
 "issues/new\n"
12
-"POT-Creation-Date: 2022-06-30 12:50+0200\n"
13
-"PO-Revision-Date: 2023-02-11 00:11+0100\n"
14
+"POT-Creation-Date: 2024-02-25 03:43+0300\n"
15
+"PO-Revision-Date: 2024-06-24 11:53+0200\n"
16
 "Last-Translator: Quentin PAGÈS\n"
17
 "Language-Team: Tot En Òc\n"
18
 "Language: oc\n"
19
@@ -23,10 +23,10 @@
20
 "Content-Type: text/plain; charset=UTF-8\n"
21
 "Content-Transfer-Encoding: 8bit\n"
22
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
23
-"X-Generator: Poedit 3.2.2\n"
24
+"X-Generator: Poedit 3.4.3\n"
25
 "X-Launchpad-Export-Date: 2016-10-12 20:12+0000\n"
26
 
27
-#: src/daemon/pipewire.c:46
28
+#: src/daemon/pipewire.c:26
29
 #, c-format
30
 msgid ""
31
 "%s options\n"
32
@@ -39,36 +39,44 @@
33
 "      --version                         Afichar la version\n"
34
 "  -c, --config                          Cargar la conf. (Defaut %s)\n"
35
 
36
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180
37
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180
38
+#: src/daemon/pipewire.desktop.in:4
39
+msgid "PipeWire Media System"
40
+msgstr "Sistèma mèdia PipeWire"
41
+
42
+#: src/daemon/pipewire.desktop.in:5
43
+msgid "Start the PipeWire Media System"
44
+msgstr "Aviar lo sistèma mèdia PipeWire"
45
+
46
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
47
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
48
 #, c-format
49
-msgid "Tunnel to %s/%s"
50
-msgstr "Tunèl cap a %s/%s"
51
+msgid "Tunnel to %s%s%s"
52
+msgstr "Tunèl cap a %s%s%s"
53
 
54
-#: src/modules/module-fallback-sink.c:51
55
+#: src/modules/module-fallback-sink.c:40
56
 msgid "Dummy Output"
57
 msgstr "Sortida factícia"
58
 
59
-#: src/modules/module-pulse-tunnel.c:662
60
+#: src/modules/module-pulse-tunnel.c:774
61
 #, c-format
62
 msgid "Tunnel for %s@%s"
63
 msgstr "Tunèl per %s@%s"
64
 
65
-#: src/modules/module-zeroconf-discover.c:332
66
+#: src/modules/module-zeroconf-discover.c:315
67
 msgid "Unknown device"
68
 msgstr "Periferic desconegut"
69
 
70
-#: src/modules/module-zeroconf-discover.c:344
71
+#: src/modules/module-zeroconf-discover.c:327
72
 #, c-format
73
 msgid "%s on %s@%s"
74
 msgstr "%s sus %s@%s"
75
 
76
-#: src/modules/module-zeroconf-discover.c:348
77
+#: src/modules/module-zeroconf-discover.c:331
78
 #, c-format
79
 msgid "%s on %s"
80
 msgstr "%s sus %s"
81
 
82
-#: src/tools/pw-cat.c:784
83
+#: src/tools/pw-cat.c:991
84
 #, c-format
85
 msgid ""
86
 "%s options <file>|-\n"
87
@@ -83,14 +91,15 @@
88
 "  -v, --verbose                         Activar las operacions verbosas\n"
89
 "\n"
90
 
91
-#: src/tools/pw-cat.c:791
92
+#: src/tools/pw-cat.c:998
93
 #, c-format
94
 msgid ""
95
 "  -R, --remote                          Remote daemon name\n"
96
 "      --media-type                      Set media type (default %s)\n"
97
 "      --media-category                  Set media category (default %s)\n"
98
 "      --media-role                      Set media role (default %s)\n"
99
-"      --target                          Set node target (default %s)\n"
100
+"      --target                          Set node target serial or name "
101
+"(default %s)\n"
102
 "                                          0 means don't link\n"
103
 "      --latency                         Set node latency (default %s)\n"
104
 "                                          Xunit (unit = s, ms, us, ns)\n"
105
@@ -100,8 +109,25 @@
106
 "  -P  --properties                      Set node properties\n"
107
 "\n"
108
 msgstr ""
109
+"  -R, --remote                          Nom del demòni distant\n"
110
+"      --media-type                      Definir lo tipe de mèdia (per defaut "
111
+"%s)\n"
112
+"      --media-category                  Definir la categoria de mèdia (per "
113
+"defaut %s)\n"
114
+"      --media-role                      Definir lo ròtle del mèdia (per "
115
+"defaut %s)\n"
116
+"      --target                          Definir lo numèro de seria o lo nom "
117
+"de la cibla del nos (per defaut %s)\n"
118
+"                                          0 significa ligar pas\n"
119
+"      --latency                         Definir la laténcia del nos (per "
120
+"defaut %s)\n"
121
+"                                          Xunit (unit = s, ms, us, ns)\n"
122
+"                                          o escandalhatge dirècte (256)\n"
123
+"                                          lo taus es çò del fichièr font\n"
124
+"  -P  --properties                      Definir las proprietats del nos\n"
125
+"\n"
126
 
127
-#: src/tools/pw-cat.c:809
128
+#: src/tools/pw-cat.c:1016
129
 #, c-format
130
 msgid ""
131
 "      --rate                            Sample rate (req. for rec) (default "
132
@@ -120,22 +146,41 @@
133
 "%d)\n"
134
 "\n"
135
 msgstr ""
136
+"      --rate                            Taus d'escandalhatge (req. per rec) "
137
+"(per defaut %u)\n"
138
+"      --channels                        Nombre de canals (req. per rec) (per "
139
+"defaut %u)\n"
140
+"      --channel-map                     Mapa de canal\n"
141
+"                                            un de : \"stereo\", "
142
+"\"surround-51\",... o\n"
143
+"                                            lista de nom de canal separats "
144
+"per de virgula : ex. \"FL,FR\"\n"
145
+"      --format                          Format d'escandalhatge %s (req. per "
146
+"rec) (per defaut %s)\n"
147
+"      --volume                          Volum del flux 0-1.0 (per defaut "
148
+"%.3f)\n"
149
+"  -q  --quality                         Qualitat del aus reescandalhatge (0 "
150
+"- 15) (per defaut %d)\n"
151
+"\n"
152
 
153
-#: src/tools/pw-cat.c:826
154
+#: src/tools/pw-cat.c:1033
155
 msgid ""
156
 "  -p, --playback                        Playback mode\n"
157
 "  -r, --record                          Recording mode\n"
158
 "  -m, --midi                            Midi mode\n"
159
 "  -d, --dsd                             DSD mode\n"
160
+"  -o, --encoded                         Encoded mode\n"
161
 "\n"
162
 msgstr ""
163
 "  -p, --playback                        Mòde lectura\n"
164
 "  -r, --record                          Mòde enregistrament\n"
165
 "  -m, --midi                            Mòde Midi\n"
166
 "  -d, --dsd                             Mòde DSD\n"
167
+"  -o, --encoded                         %òde encodat\n"
168
+"\n"
169
 "\n"
170
 
171
-#: src/tools/pw-cli.c:2250
172
+#: src/tools/pw-cli.c:2252
173
 #, c-format
174
 msgid ""
175
 "%s options command\n"
176
@@ -143,6 +188,7 @@
177
 "      --version                         Show version\n"
178
 "  -d, --daemon                          Start as daemon (Default false)\n"
179
 "  -r, --remote                          Remote daemon name\n"
180
+"  -m, --monitor                         Monitor activity\n"
181
 "\n"
182
 msgstr ""
183
 "%s opcions comanda\n"
184
@@ -150,15 +196,17 @@
185
 "      --version                         Afichar la version\n"
186
 "  -d, --daemon                          Aviar coma demòni (Per defaut "
187
 "false)\n"
188
-"  -r, --remote                          Remote daemon name\n"
189
+"  -r, --remote                          Nom del demòni distant\n"
190
+"  -m, --monitor                         Susvelhar l’activitat\n"
191
+"\n"
192
 "\n"
193
 
194
-#: spa/plugins/alsa/acp/acp.c:321
195
+#: spa/plugins/alsa/acp/acp.c:327
196
 msgid "Pro Audio"
197
-msgstr ""
198
+msgstr "Àudio pro"
199
 
200
-#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
201
-#: spa/plugins/bluez5/bluez5-device.c:1236
202
+#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
203
+#: spa/plugins/bluez5/bluez5-device.c:1701
204
 msgid "Off"
205
 msgstr "Atudat"
206
 
207
@@ -185,7 +233,7 @@
208
 
209
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
210
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
211
-#: spa/plugins/bluez5/bluez5-device.c:1454
212
+#: spa/plugins/bluez5/bluez5-device.c:1989
213
 msgid "Microphone"
214
 msgstr "Microfòn"
215
 
216
@@ -251,7 +299,7 @@
217
 msgstr "Pas d'amplificacion de las bassas"
218
 
219
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
220
-#: spa/plugins/bluez5/bluez5-device.c:1460
221
+#: spa/plugins/bluez5/bluez5-device.c:1995
222
 msgid "Speaker"
223
 msgstr "Nautparlaire"
224
 
225
@@ -333,15 +381,15 @@
226
 msgid "Virtual Surround 7.1"
227
 msgstr "Surround 7.1 virtual"
228
 
229
-#: spa/plugins/alsa/acp/alsa-mixer.c:4471
230
+#: spa/plugins/alsa/acp/alsa-mixer.c:4456
231
 msgid "Analog Mono"
232
 msgstr "Mono analogic"
233
 
234
-#: spa/plugins/alsa/acp/alsa-mixer.c:4472
235
+#: spa/plugins/alsa/acp/alsa-mixer.c:4457
236
 msgid "Analog Mono (Left)"
237
 msgstr "Mono analogic (esquèrra)"
238
 
239
-#: spa/plugins/alsa/acp/alsa-mixer.c:4473
240
+#: spa/plugins/alsa/acp/alsa-mixer.c:4458
241
 msgid "Analog Mono (Right)"
242
 msgstr "Mono analogic (drecha)"
243
 
244
@@ -350,147 +398,147 @@
245
 #. * here would lead to the source name to become "Analog Stereo Input
246
 #. * Input". The same logic applies to analog-stereo-output,
247
 #. * multichannel-input and multichannel-output.
248
-#: spa/plugins/alsa/acp/alsa-mixer.c:4474
249
-#: spa/plugins/alsa/acp/alsa-mixer.c:4482
250
-#: spa/plugins/alsa/acp/alsa-mixer.c:4483
251
+#: spa/plugins/alsa/acp/alsa-mixer.c:4459
252
+#: spa/plugins/alsa/acp/alsa-mixer.c:4467
253
+#: spa/plugins/alsa/acp/alsa-mixer.c:4468
254
 msgid "Analog Stereo"
255
 msgstr "Estereo analogic"
256
 
257
-#: spa/plugins/alsa/acp/alsa-mixer.c:4475
258
+#: spa/plugins/alsa/acp/alsa-mixer.c:4460
259
 msgid "Mono"
260
 msgstr "Mono"
261
 
262
-#: spa/plugins/alsa/acp/alsa-mixer.c:4476
263
+#: spa/plugins/alsa/acp/alsa-mixer.c:4461
264
 msgid "Stereo"
265
 msgstr "Estereo"
266
 
267
-#: spa/plugins/alsa/acp/alsa-mixer.c:4484
268
-#: spa/plugins/alsa/acp/alsa-mixer.c:4642
269
-#: spa/plugins/bluez5/bluez5-device.c:1442
270
+#: spa/plugins/alsa/acp/alsa-mixer.c:4469
271
+#: spa/plugins/alsa/acp/alsa-mixer.c:4627
272
+#: spa/plugins/bluez5/bluez5-device.c:1977
273
 msgid "Headset"
274
 msgstr "Casc àudio"
275
 
276
-#: spa/plugins/alsa/acp/alsa-mixer.c:4485
277
-#: spa/plugins/alsa/acp/alsa-mixer.c:4643
278
+#: spa/plugins/alsa/acp/alsa-mixer.c:4470
279
+#: spa/plugins/alsa/acp/alsa-mixer.c:4628
280
 msgid "Speakerphone"
281
 msgstr "Nautparlaire"
282
 
283
-#: spa/plugins/alsa/acp/alsa-mixer.c:4486
284
-#: spa/plugins/alsa/acp/alsa-mixer.c:4487
285
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
286
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
287
 msgid "Multichannel"
288
 msgstr "Multicanal"
289
 
290
-#: spa/plugins/alsa/acp/alsa-mixer.c:4488
291
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
292
 msgid "Analog Surround 2.1"
293
 msgstr "Surround analogic 2.1"
294
 
295
-#: spa/plugins/alsa/acp/alsa-mixer.c:4489
296
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
297
 msgid "Analog Surround 3.0"
298
 msgstr "Surround analogic 3.0"
299
 
300
-#: spa/plugins/alsa/acp/alsa-mixer.c:4490
301
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
302
 msgid "Analog Surround 3.1"
303
 msgstr "Surround analogic 3.1"
304
 
305
-#: spa/plugins/alsa/acp/alsa-mixer.c:4491
306
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
307
 msgid "Analog Surround 4.0"
308
 msgstr "Surround analogic 4.0"
309
 
310
-#: spa/plugins/alsa/acp/alsa-mixer.c:4492
311
+#: spa/plugins/alsa/acp/alsa-mixer.c:4477
312
 msgid "Analog Surround 4.1"
313
 msgstr "Surround analogic 4.1"
314
 
315
-#: spa/plugins/alsa/acp/alsa-mixer.c:4493
316
+#: spa/plugins/alsa/acp/alsa-mixer.c:4478
317
 msgid "Analog Surround 5.0"
318
 msgstr "Surround analogic 5.0"
319
 
320
-#: spa/plugins/alsa/acp/alsa-mixer.c:4494
321
+#: spa/plugins/alsa/acp/alsa-mixer.c:4479
322
 msgid "Analog Surround 5.1"
323
 msgstr "Surround analogic 5.1"
324
 
325
-#: spa/plugins/alsa/acp/alsa-mixer.c:4495
326
+#: spa/plugins/alsa/acp/alsa-mixer.c:4480
327
 msgid "Analog Surround 6.0"
328
 msgstr "Surround analogic 6.0"
329
 
330
-#: spa/plugins/alsa/acp/alsa-mixer.c:4496
331
+#: spa/plugins/alsa/acp/alsa-mixer.c:4481
332
 msgid "Analog Surround 6.1"
333
 msgstr "Surround analogic 6.1"
334
 
335
-#: spa/plugins/alsa/acp/alsa-mixer.c:4497
336
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
337
 msgid "Analog Surround 7.0"
338
 msgstr "Surround analogic 7.0"
339
 
340
-#: spa/plugins/alsa/acp/alsa-mixer.c:4498
341
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
342
 msgid "Analog Surround 7.1"
343
 msgstr "Surround analogic 7.1"
344
 
345
-#: spa/plugins/alsa/acp/alsa-mixer.c:4499
346
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
347
 msgid "Digital Stereo (IEC958)"
348
 msgstr "Estereo numeric (IEC958)"
349
 
350
-#: spa/plugins/alsa/acp/alsa-mixer.c:4500
351
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
352
 msgid "Digital Surround 4.0 (IEC958/AC3)"
353
 msgstr "Surround numeric 4.0 (IEC958/AC3)"
354
 
355
-#: spa/plugins/alsa/acp/alsa-mixer.c:4501
356
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
357
 msgid "Digital Surround 5.1 (IEC958/AC3)"
358
 msgstr "Surround numeric 5.1 (IEC958/AC3)"
359
 
360
-#: spa/plugins/alsa/acp/alsa-mixer.c:4502
361
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
362
 msgid "Digital Surround 5.1 (IEC958/DTS)"
363
 msgstr "Digital Surround 5.1 (IEC958/DTS)"
364
 
365
-#: spa/plugins/alsa/acp/alsa-mixer.c:4503
366
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
367
 msgid "Digital Stereo (HDMI)"
368
 msgstr "Estereo numeric (HDMI)"
369
 
370
-#: spa/plugins/alsa/acp/alsa-mixer.c:4504
371
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
372
 msgid "Digital Surround 5.1 (HDMI)"
373
 msgstr "Digital Surround 5.1 (HDMI)"
374
 
375
-#: spa/plugins/alsa/acp/alsa-mixer.c:4505
376
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
377
 msgid "Chat"
378
 msgstr "Messatjariá instantanèa"
379
 
380
-#: spa/plugins/alsa/acp/alsa-mixer.c:4506
381
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
382
 msgid "Game"
383
 msgstr "Jòc"
384
 
385
-#: spa/plugins/alsa/acp/alsa-mixer.c:4640
386
+#: spa/plugins/alsa/acp/alsa-mixer.c:4625
387
 msgid "Analog Mono Duplex"
388
 msgstr "Duplèx Mono analogic"
389
 
390
-#: spa/plugins/alsa/acp/alsa-mixer.c:4641
391
+#: spa/plugins/alsa/acp/alsa-mixer.c:4626
392
 msgid "Analog Stereo Duplex"
393
 msgstr "Duplèx esterèo analogic"
394
 
395
-#: spa/plugins/alsa/acp/alsa-mixer.c:4644
396
+#: spa/plugins/alsa/acp/alsa-mixer.c:4629
397
 msgid "Digital Stereo Duplex (IEC958)"
398
 msgstr "Duplèx estèreo numeric (IEC958)"
399
 
400
-#: spa/plugins/alsa/acp/alsa-mixer.c:4645
401
+#: spa/plugins/alsa/acp/alsa-mixer.c:4630
402
 msgid "Multichannel Duplex"
403
 msgstr "Duplèx multicanal"
404
 
405
-#: spa/plugins/alsa/acp/alsa-mixer.c:4646
406
+#: spa/plugins/alsa/acp/alsa-mixer.c:4631
407
 msgid "Stereo Duplex"
408
 msgstr "Duplèx estereo"
409
 
410
-#: spa/plugins/alsa/acp/alsa-mixer.c:4647
411
+#: spa/plugins/alsa/acp/alsa-mixer.c:4632
412
 msgid "Mono Chat + 7.1 Surround"
413
 msgstr "Messatjariá mono + Surround 7.1"
414
 
415
-#: spa/plugins/alsa/acp/alsa-mixer.c:4754
416
+#: spa/plugins/alsa/acp/alsa-mixer.c:4733
417
 #, c-format
418
 msgid "%s Output"
419
 msgstr "%s Sortida"
420
 
421
-#: spa/plugins/alsa/acp/alsa-mixer.c:4761
422
+#: spa/plugins/alsa/acp/alsa-mixer.c:4741
423
 #, c-format
424
 msgid "%s Input"
425
 msgstr "%s Entrada"
426
 
427
-#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
428
+#: spa/plugins/alsa/acp/alsa-util.c:1220 spa/plugins/alsa/acp/alsa-util.c:1314
429
 #, c-format
430
 msgid ""
431
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
432
@@ -513,7 +561,7 @@
433
 "Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
434
 "desvolopaires d’ALSA."
435
 
436
-#: spa/plugins/alsa/acp/alsa-util.c:1253
437
+#: spa/plugins/alsa/acp/alsa-util.c:1286
438
 #, c-format
439
 msgid ""
440
 "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
441
@@ -536,7 +584,7 @@
442
 "Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
443
 "desvolopaires d’ALSA."
444
 
445
-#: spa/plugins/alsa/acp/alsa-util.c:1300
446
+#: spa/plugins/alsa/acp/alsa-util.c:1333
447
 #, c-format
448
 msgid ""
449
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
450
@@ -549,7 +597,7 @@
451
 "Es fòrt probablament un bug dins lo pilòt ALSA « %s ». Senhalatz-lo als "
452
 "desvolopaires d’ALSA."
453
 
454
-#: spa/plugins/alsa/acp/alsa-util.c:1343
455
+#: spa/plugins/alsa/acp/alsa-util.c:1376
456
 #, c-format
457
 msgid ""
458
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
459
@@ -576,100 +624,108 @@
460
 msgid "(invalid)"
461
 msgstr "(invalid)"
462
 
463
-#: spa/plugins/alsa/acp/compat.c:189
464
+#: spa/plugins/alsa/acp/compat.c:193
465
 msgid "Built-in Audio"
466
 msgstr "Àudio integrat"
467
 
468
-#: spa/plugins/alsa/acp/compat.c:194
469
+#: spa/plugins/alsa/acp/compat.c:198
470
 msgid "Modem"
471
 msgstr "Modèm"
472
 
473
-#: spa/plugins/bluez5/bluez5-device.c:1247
474
+#: spa/plugins/bluez5/bluez5-device.c:1712
475
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
476
 msgstr "Palanca àudio (Font A2DP & HSP/HFP AG)"
477
 
478
-#: spa/plugins/bluez5/bluez5-device.c:1272
479
+#: spa/plugins/bluez5/bluez5-device.c:1760
480
 #, c-format
481
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
482
 msgstr "Lectura nauta fidelitat (A2DP Sink, codec %s)"
483
 
484
-#: spa/plugins/bluez5/bluez5-device.c:1275
485
+#: spa/plugins/bluez5/bluez5-device.c:1763
486
 #, c-format
487
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
488
 msgstr "Duplèx nauta fidelitat (A2DP Source/Sink, codec %s)"
489
 
490
-#: spa/plugins/bluez5/bluez5-device.c:1283
491
+#: spa/plugins/bluez5/bluez5-device.c:1771
492
 msgid "High Fidelity Playback (A2DP Sink)"
493
 msgstr "Lectura nauta fidelitat (A2DP Sink)"
494
 
495
-#: spa/plugins/bluez5/bluez5-device.c:1285
496
+#: spa/plugins/bluez5/bluez5-device.c:1773
497
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
498
 msgstr "Duplèx nauta fidelitat (A2DP Source/Sink)"
499
 
500
-#: spa/plugins/bluez5/bluez5-device.c:1322
501
+#: spa/plugins/bluez5/bluez5-device.c:1823
502
 #, c-format
503
 msgid "High Fidelity Playback (BAP Sink, codec %s)"
504
 msgstr "Lectura nauta fidelitat (A2DP Sink, codec %s)"
505
 
506
-#: spa/plugins/bluez5/bluez5-device.c:1326
507
+#: spa/plugins/bluez5/bluez5-device.c:1828
508
 #, c-format
509
 msgid "High Fidelity Input (BAP Source, codec %s)"
510
 msgstr "Duplèx nauta fidelitat (Font BAP, codec %s)"
511
 
512
-#: spa/plugins/bluez5/bluez5-device.c:1330
513
+#: spa/plugins/bluez5/bluez5-device.c:1832
514
 #, c-format
515
 msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
516
 msgstr "Duplèx nauta fidelitat (Font BAP/Sink, codec %s)"
517
 
518
-#: spa/plugins/bluez5/bluez5-device.c:1359
519
-#, c-format
520
-msgid "Headset Head Unit (HSP/HFP, codec %s)"
521
-msgstr ""
522
+#: spa/plugins/bluez5/bluez5-device.c:1841
523
+msgid "High Fidelity Playback (BAP Sink)"
524
+msgstr "Lectura nauta fidelitat (receptor BAP)"
525
 
526
-#: spa/plugins/bluez5/bluez5-device.c:1364
527
-msgid "Headset Head Unit (HSP/HFP)"
528
-msgstr ""
529
+#: spa/plugins/bluez5/bluez5-device.c:1845
530
+msgid "High Fidelity Input (BAP Source)"
531
+msgstr "Duplèx nauta fidelitat (Font BAP)"
532
 
533
-#: spa/plugins/bluez5/bluez5-device.c:1443
534
-#: spa/plugins/bluez5/bluez5-device.c:1448
535
-#: spa/plugins/bluez5/bluez5-device.c:1455
536
-#: spa/plugins/bluez5/bluez5-device.c:1461
537
-#: spa/plugins/bluez5/bluez5-device.c:1467
538
-#: spa/plugins/bluez5/bluez5-device.c:1473
539
-#: spa/plugins/bluez5/bluez5-device.c:1479
540
-#: spa/plugins/bluez5/bluez5-device.c:1485
541
-#: spa/plugins/bluez5/bluez5-device.c:1491
542
+#: spa/plugins/bluez5/bluez5-device.c:1848
543
+msgid "High Fidelity Duplex (BAP Source/Sink)"
544
+msgstr "Duplèx nauta fidelitat (Font/Receptor BAP)"
545
+
546
+#: spa/plugins/bluez5/bluez5-device.c:1897
547
+#, c-format
548
+msgid "Headset Head Unit (HSP/HFP, codec %s)"
549
+msgstr "Controlador de casc (HSP/HFP, codec %s)"
550
+
551
+#: spa/plugins/bluez5/bluez5-device.c:1978
552
+#: spa/plugins/bluez5/bluez5-device.c:1983
553
+#: spa/plugins/bluez5/bluez5-device.c:1990
554
+#: spa/plugins/bluez5/bluez5-device.c:1996
555
+#: spa/plugins/bluez5/bluez5-device.c:2002
556
+#: spa/plugins/bluez5/bluez5-device.c:2008
557
+#: spa/plugins/bluez5/bluez5-device.c:2014
558
+#: spa/plugins/bluez5/bluez5-device.c:2020
559
+#: spa/plugins/bluez5/bluez5-device.c:2026
560
 msgid "Handsfree"
561
 msgstr "Mans liuras"
562
 
563
-#: spa/plugins/bluez5/bluez5-device.c:1449
564
+#: spa/plugins/bluez5/bluez5-device.c:1984
565
 msgid "Handsfree (HFP)"
566
 msgstr "Mans liuras (HFP)"
567
 
568
-#: spa/plugins/bluez5/bluez5-device.c:1466
569
+#: spa/plugins/bluez5/bluez5-device.c:2001
570
 msgid "Headphone"
571
 msgstr "Escotador"
572
 
573
-#: spa/plugins/bluez5/bluez5-device.c:1472
574
+#: spa/plugins/bluez5/bluez5-device.c:2007
575
 msgid "Portable"
576
 msgstr "Portable"
577
 
578
-#: spa/plugins/bluez5/bluez5-device.c:1478
579
+#: spa/plugins/bluez5/bluez5-device.c:2013
580
 msgid "Car"
581
 msgstr "Telefòn de veitura"
582
 
583
-#: spa/plugins/bluez5/bluez5-device.c:1484
584
+#: spa/plugins/bluez5/bluez5-device.c:2019
585
 msgid "HiFi"
586
 msgstr "HiFi"
587
 
588
-#: spa/plugins/bluez5/bluez5-device.c:1490
589
+#: spa/plugins/bluez5/bluez5-device.c:2025
590
 msgid "Phone"
591
 msgstr "Telefòn"
592
 
593
-#: spa/plugins/bluez5/bluez5-device.c:1497
594
+#: spa/plugins/bluez5/bluez5-device.c:2032
595
 msgid "Bluetooth"
596
 msgstr "Bluetooth"
597
 
598
-#: spa/plugins/bluez5/bluez5-device.c:1498
599
+#: spa/plugins/bluez5/bluez5-device.c:2033
600
 msgid "Bluetooth (HFP)"
601
-msgstr "Bluetooth (HFP)"
602
+msgstr "Bluetooth (HFP)"
603
\ No newline at end of file
604
pipewire-1.0.1.tar.bz2/po/pipewire.pot -> pipewire-1.2.0.tar.gz/po/pipewire.pot Changed
534
 
1
@@ -8,7 +8,7 @@
2
 msgstr ""
3
 "Project-Id-Version: pipewire\n"
4
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/issues/new\n"
5
-"POT-Creation-Date: 2022-06-30 12:50+0200\n"
6
+"POT-Creation-Date: 2024-02-25 03:43+0300\n"
7
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
8
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
9
 "Language-Team: LANGUAGE <LL@li.org>\n"
10
@@ -18,7 +18,7 @@
11
 "Content-Transfer-Encoding: 8bit\n"
12
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
13
 
14
-#: src/daemon/pipewire.c:46
15
+#: src/daemon/pipewire.c:26
16
 #, c-format
17
 msgid ""
18
 "%s options\n"
19
@@ -27,36 +27,44 @@
20
 "  -c, --config                          Load config (Default %s)\n"
21
 msgstr ""
22
 
23
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180
24
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180
25
+#: src/daemon/pipewire.desktop.in:4
26
+msgid "PipeWire Media System"
27
+msgstr ""
28
+
29
+#: src/daemon/pipewire.desktop.in:5
30
+msgid "Start the PipeWire Media System"
31
+msgstr ""
32
+
33
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
34
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
35
 #, c-format
36
-msgid "Tunnel to %s/%s"
37
+msgid "Tunnel to %s%s%s"
38
 msgstr ""
39
 
40
-#: src/modules/module-fallback-sink.c:51
41
+#: src/modules/module-fallback-sink.c:40
42
 msgid "Dummy Output"
43
 msgstr ""
44
 
45
-#: src/modules/module-pulse-tunnel.c:662
46
+#: src/modules/module-pulse-tunnel.c:774
47
 #, c-format
48
 msgid "Tunnel for %s@%s"
49
 msgstr ""
50
 
51
-#: src/modules/module-zeroconf-discover.c:332
52
+#: src/modules/module-zeroconf-discover.c:315
53
 msgid "Unknown device"
54
 msgstr ""
55
 
56
-#: src/modules/module-zeroconf-discover.c:344
57
+#: src/modules/module-zeroconf-discover.c:327
58
 #, c-format
59
 msgid "%s on %s@%s"
60
 msgstr ""
61
 
62
-#: src/modules/module-zeroconf-discover.c:348
63
+#: src/modules/module-zeroconf-discover.c:331
64
 #, c-format
65
 msgid "%s on %s"
66
 msgstr ""
67
 
68
-#: src/tools/pw-cat.c:784
69
+#: src/tools/pw-cat.c:991
70
 #, c-format
71
 msgid ""
72
 "%s options <file>|-\n"
73
@@ -66,14 +74,15 @@
74
 "\n"
75
 msgstr ""
76
 
77
-#: src/tools/pw-cat.c:791
78
+#: src/tools/pw-cat.c:998
79
 #, c-format
80
 msgid ""
81
 "  -R, --remote                          Remote daemon name\n"
82
 "      --media-type                      Set media type (default %s)\n"
83
 "      --media-category                  Set media category (default %s)\n"
84
 "      --media-role                      Set media role (default %s)\n"
85
-"      --target                          Set node target (default %s)\n"
86
+"      --target                          Set node target serial or name "
87
+"(default %s)\n"
88
 "                                          0 means don't link\n"
89
 "      --latency                         Set node latency (default %s)\n"
90
 "                                          Xunit (unit = s, ms, us, ns)\n"
91
@@ -84,7 +93,7 @@
92
 "\n"
93
 msgstr ""
94
 
95
-#: src/tools/pw-cat.c:809
96
+#: src/tools/pw-cat.c:1016
97
 #, c-format
98
 msgid ""
99
 "      --rate                            Sample rate (req. for rec) (default "
100
@@ -104,16 +113,17 @@
101
 "\n"
102
 msgstr ""
103
 
104
-#: src/tools/pw-cat.c:826
105
+#: src/tools/pw-cat.c:1033
106
 msgid ""
107
 "  -p, --playback                        Playback mode\n"
108
 "  -r, --record                          Recording mode\n"
109
 "  -m, --midi                            Midi mode\n"
110
 "  -d, --dsd                             DSD mode\n"
111
+"  -o, --encoded                         Encoded mode\n"
112
 "\n"
113
 msgstr ""
114
 
115
-#: src/tools/pw-cli.c:2250
116
+#: src/tools/pw-cli.c:2252
117
 #, c-format
118
 msgid ""
119
 "%s options command\n"
120
@@ -121,15 +131,16 @@
121
 "      --version                         Show version\n"
122
 "  -d, --daemon                          Start as daemon (Default false)\n"
123
 "  -r, --remote                          Remote daemon name\n"
124
+"  -m, --monitor                         Monitor activity\n"
125
 "\n"
126
 msgstr ""
127
 
128
-#: spa/plugins/alsa/acp/acp.c:321
129
+#: spa/plugins/alsa/acp/acp.c:327
130
 msgid "Pro Audio"
131
 msgstr ""
132
 
133
-#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
134
-#: spa/plugins/bluez5/bluez5-device.c:1236
135
+#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
136
+#: spa/plugins/bluez5/bluez5-device.c:1701
137
 msgid "Off"
138
 msgstr ""
139
 
140
@@ -156,7 +167,7 @@
141
 
142
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
143
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
144
-#: spa/plugins/bluez5/bluez5-device.c:1454
145
+#: spa/plugins/bluez5/bluez5-device.c:1989
146
 msgid "Microphone"
147
 msgstr ""
148
 
149
@@ -222,7 +233,7 @@
150
 msgstr ""
151
 
152
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
153
-#: spa/plugins/bluez5/bluez5-device.c:1460
154
+#: spa/plugins/bluez5/bluez5-device.c:1995
155
 msgid "Speaker"
156
 msgstr ""
157
 
158
@@ -304,15 +315,15 @@
159
 msgid "Virtual Surround 7.1"
160
 msgstr ""
161
 
162
-#: spa/plugins/alsa/acp/alsa-mixer.c:4471
163
+#: spa/plugins/alsa/acp/alsa-mixer.c:4456
164
 msgid "Analog Mono"
165
 msgstr ""
166
 
167
-#: spa/plugins/alsa/acp/alsa-mixer.c:4472
168
+#: spa/plugins/alsa/acp/alsa-mixer.c:4457
169
 msgid "Analog Mono (Left)"
170
 msgstr ""
171
 
172
-#: spa/plugins/alsa/acp/alsa-mixer.c:4473
173
+#: spa/plugins/alsa/acp/alsa-mixer.c:4458
174
 msgid "Analog Mono (Right)"
175
 msgstr ""
176
 
177
@@ -321,148 +332,147 @@
178
 #. * here would lead to the source name to become "Analog Stereo Input
179
 #. * Input". The same logic applies to analog-stereo-output,
180
 #. * multichannel-input and multichannel-output.
181
-#: spa/plugins/alsa/acp/alsa-mixer.c:4474
182
-#: spa/plugins/alsa/acp/alsa-mixer.c:4482
183
-#: spa/plugins/alsa/acp/alsa-mixer.c:4483
184
+#: spa/plugins/alsa/acp/alsa-mixer.c:4459
185
+#: spa/plugins/alsa/acp/alsa-mixer.c:4467
186
+#: spa/plugins/alsa/acp/alsa-mixer.c:4468
187
 msgid "Analog Stereo"
188
 msgstr ""
189
 
190
-#: spa/plugins/alsa/acp/alsa-mixer.c:4475
191
+#: spa/plugins/alsa/acp/alsa-mixer.c:4460
192
 msgid "Mono"
193
 msgstr ""
194
 
195
-#: spa/plugins/alsa/acp/alsa-mixer.c:4476
196
+#: spa/plugins/alsa/acp/alsa-mixer.c:4461
197
 msgid "Stereo"
198
 msgstr ""
199
 
200
-#: spa/plugins/alsa/acp/alsa-mixer.c:4484
201
-#: spa/plugins/alsa/acp/alsa-mixer.c:4642
202
-#: spa/plugins/bluez5/bluez5-device.c:1442
203
+#: spa/plugins/alsa/acp/alsa-mixer.c:4469
204
+#: spa/plugins/alsa/acp/alsa-mixer.c:4627
205
+#: spa/plugins/bluez5/bluez5-device.c:1977
206
 msgid "Headset"
207
 msgstr ""
208
 
209
-#: spa/plugins/alsa/acp/alsa-mixer.c:4485
210
-#: spa/plugins/alsa/acp/alsa-mixer.c:4643
211
+#: spa/plugins/alsa/acp/alsa-mixer.c:4470
212
+#: spa/plugins/alsa/acp/alsa-mixer.c:4628
213
 msgid "Speakerphone"
214
 msgstr ""
215
 
216
-#: spa/plugins/alsa/acp/alsa-mixer.c:4486
217
-#: spa/plugins/alsa/acp/alsa-mixer.c:4487
218
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
219
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
220
 msgid "Multichannel"
221
 msgstr ""
222
 
223
-#: spa/plugins/alsa/acp/alsa-mixer.c:4488
224
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
225
 msgid "Analog Surround 2.1"
226
 msgstr ""
227
 
228
-#: spa/plugins/alsa/acp/alsa-mixer.c:4489
229
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
230
 msgid "Analog Surround 3.0"
231
 msgstr ""
232
 
233
-#: spa/plugins/alsa/acp/alsa-mixer.c:4490
234
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
235
 msgid "Analog Surround 3.1"
236
 msgstr ""
237
 
238
-#: spa/plugins/alsa/acp/alsa-mixer.c:4491
239
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
240
 msgid "Analog Surround 4.0"
241
 msgstr ""
242
 
243
-#: spa/plugins/alsa/acp/alsa-mixer.c:4492
244
+#: spa/plugins/alsa/acp/alsa-mixer.c:4477
245
 msgid "Analog Surround 4.1"
246
 msgstr ""
247
 
248
-#: spa/plugins/alsa/acp/alsa-mixer.c:4493
249
+#: spa/plugins/alsa/acp/alsa-mixer.c:4478
250
 msgid "Analog Surround 5.0"
251
 msgstr ""
252
 
253
-#: spa/plugins/alsa/acp/alsa-mixer.c:4494
254
+#: spa/plugins/alsa/acp/alsa-mixer.c:4479
255
 msgid "Analog Surround 5.1"
256
 msgstr ""
257
 
258
-#: spa/plugins/alsa/acp/alsa-mixer.c:4495
259
+#: spa/plugins/alsa/acp/alsa-mixer.c:4480
260
 msgid "Analog Surround 6.0"
261
 msgstr ""
262
 
263
-#: spa/plugins/alsa/acp/alsa-mixer.c:4496
264
+#: spa/plugins/alsa/acp/alsa-mixer.c:4481
265
 msgid "Analog Surround 6.1"
266
 msgstr ""
267
 
268
-#: spa/plugins/alsa/acp/alsa-mixer.c:4497
269
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
270
 msgid "Analog Surround 7.0"
271
 msgstr ""
272
 
273
-#: spa/plugins/alsa/acp/alsa-mixer.c:4498
274
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
275
 msgid "Analog Surround 7.1"
276
 msgstr ""
277
 
278
-#: spa/plugins/alsa/acp/alsa-mixer.c:4499
279
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
280
 msgid "Digital Stereo (IEC958)"
281
 msgstr ""
282
 
283
-#: spa/plugins/alsa/acp/alsa-mixer.c:4500
284
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
285
 msgid "Digital Surround 4.0 (IEC958/AC3)"
286
 msgstr ""
287
 
288
-#: spa/plugins/alsa/acp/alsa-mixer.c:4501
289
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
290
 msgid "Digital Surround 5.1 (IEC958/AC3)"
291
 msgstr ""
292
 
293
-#: spa/plugins/alsa/acp/alsa-mixer.c:4502
294
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
295
 msgid "Digital Surround 5.1 (IEC958/DTS)"
296
 msgstr ""
297
 
298
-#: spa/plugins/alsa/acp/alsa-mixer.c:4503
299
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
300
 msgid "Digital Stereo (HDMI)"
301
 msgstr ""
302
 
303
-#: spa/plugins/alsa/acp/alsa-mixer.c:4504
304
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
305
 msgid "Digital Surround 5.1 (HDMI)"
306
 msgstr ""
307
 
308
-#: spa/plugins/alsa/acp/alsa-mixer.c:4505
309
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
310
 msgid "Chat"
311
 msgstr ""
312
 
313
-#: spa/plugins/alsa/acp/alsa-mixer.c:4506
314
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
315
 msgid "Game"
316
 msgstr ""
317
 
318
-#: spa/plugins/alsa/acp/alsa-mixer.c:4640
319
+#: spa/plugins/alsa/acp/alsa-mixer.c:4625
320
 msgid "Analog Mono Duplex"
321
 msgstr ""
322
 
323
-#: spa/plugins/alsa/acp/alsa-mixer.c:4641
324
+#: spa/plugins/alsa/acp/alsa-mixer.c:4626
325
 msgid "Analog Stereo Duplex"
326
 msgstr ""
327
 
328
-#: spa/plugins/alsa/acp/alsa-mixer.c:4644
329
+#: spa/plugins/alsa/acp/alsa-mixer.c:4629
330
 msgid "Digital Stereo Duplex (IEC958)"
331
 msgstr ""
332
 
333
-#: spa/plugins/alsa/acp/alsa-mixer.c:4645
334
+#: spa/plugins/alsa/acp/alsa-mixer.c:4630
335
 msgid "Multichannel Duplex"
336
 msgstr ""
337
 
338
-#: spa/plugins/alsa/acp/alsa-mixer.c:4646
339
+#: spa/plugins/alsa/acp/alsa-mixer.c:4631
340
 msgid "Stereo Duplex"
341
 msgstr ""
342
 
343
-#: spa/plugins/alsa/acp/alsa-mixer.c:4647
344
+#: spa/plugins/alsa/acp/alsa-mixer.c:4632
345
 msgid "Mono Chat + 7.1 Surround"
346
 msgstr ""
347
 
348
-#: spa/plugins/alsa/acp/alsa-mixer.c:4754
349
+#: spa/plugins/alsa/acp/alsa-mixer.c:4733
350
 #, c-format
351
 msgid "%s Output"
352
 msgstr ""
353
 
354
-#: spa/plugins/alsa/acp/alsa-mixer.c:4761
355
+#: spa/plugins/alsa/acp/alsa-mixer.c:4741
356
 #, c-format
357
 msgid "%s Input"
358
 msgstr ""
359
 
360
-#: spa/plugins/alsa/acp/alsa-util.c:1187
361
-#: spa/plugins/alsa/acp/alsa-util.c:1281
362
+#: spa/plugins/alsa/acp/alsa-util.c:1220 spa/plugins/alsa/acp/alsa-util.c:1314
363
 #, c-format
364
 msgid ""
365
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
366
@@ -477,7 +487,7 @@
367
 msgstr0 ""
368
 msgstr1 ""
369
 
370
-#: spa/plugins/alsa/acp/alsa-util.c:1253
371
+#: spa/plugins/alsa/acp/alsa-util.c:1286
372
 #, c-format
373
 msgid ""
374
 "snd_pcm_delay() returned a value that is exceptionally large: %li byte "
375
@@ -492,7 +502,7 @@
376
 msgstr0 ""
377
 msgstr1 ""
378
 
379
-#: spa/plugins/alsa/acp/alsa-util.c:1300
380
+#: spa/plugins/alsa/acp/alsa-util.c:1333
381
 #, c-format
382
 msgid ""
383
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
384
@@ -501,7 +511,7 @@
385
 "to the ALSA developers."
386
 msgstr ""
387
 
388
-#: spa/plugins/alsa/acp/alsa-util.c:1343
389
+#: spa/plugins/alsa/acp/alsa-util.c:1376
390
 #, c-format
391
 msgid ""
392
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
393
@@ -520,100 +530,108 @@
394
 msgid "(invalid)"
395
 msgstr ""
396
 
397
-#: spa/plugins/alsa/acp/compat.c:189
398
+#: spa/plugins/alsa/acp/compat.c:193
399
 msgid "Built-in Audio"
400
 msgstr ""
401
 
402
-#: spa/plugins/alsa/acp/compat.c:194
403
+#: spa/plugins/alsa/acp/compat.c:198
404
 msgid "Modem"
405
 msgstr ""
406
 
407
-#: spa/plugins/bluez5/bluez5-device.c:1247
408
+#: spa/plugins/bluez5/bluez5-device.c:1712
409
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
410
 msgstr ""
411
 
412
-#: spa/plugins/bluez5/bluez5-device.c:1272
413
+#: spa/plugins/bluez5/bluez5-device.c:1760
414
 #, c-format
415
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
416
 msgstr ""
417
 
418
-#: spa/plugins/bluez5/bluez5-device.c:1275
419
+#: spa/plugins/bluez5/bluez5-device.c:1763
420
 #, c-format
421
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
422
 msgstr ""
423
 
424
-#: spa/plugins/bluez5/bluez5-device.c:1283
425
+#: spa/plugins/bluez5/bluez5-device.c:1771
426
 msgid "High Fidelity Playback (A2DP Sink)"
427
 msgstr ""
428
 
429
-#: spa/plugins/bluez5/bluez5-device.c:1285
430
+#: spa/plugins/bluez5/bluez5-device.c:1773
431
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
432
 msgstr ""
433
 
434
-#: spa/plugins/bluez5/bluez5-device.c:1322
435
+#: spa/plugins/bluez5/bluez5-device.c:1823
436
 #, c-format
437
 msgid "High Fidelity Playback (BAP Sink, codec %s)"
438
 msgstr ""
439
 
440
-#: spa/plugins/bluez5/bluez5-device.c:1326
441
+#: spa/plugins/bluez5/bluez5-device.c:1828
442
 #, c-format
443
 msgid "High Fidelity Input (BAP Source, codec %s)"
444
 msgstr ""
445
 
446
-#: spa/plugins/bluez5/bluez5-device.c:1330
447
+#: spa/plugins/bluez5/bluez5-device.c:1832
448
 #, c-format
449
 msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
450
 msgstr ""
451
 
452
-#: spa/plugins/bluez5/bluez5-device.c:1359
453
-#, c-format
454
-msgid "Headset Head Unit (HSP/HFP, codec %s)"
455
+#: spa/plugins/bluez5/bluez5-device.c:1841
456
+msgid "High Fidelity Playback (BAP Sink)"
457
 msgstr ""
458
 
459
-#: spa/plugins/bluez5/bluez5-device.c:1364
460
-msgid "Headset Head Unit (HSP/HFP)"
461
+#: spa/plugins/bluez5/bluez5-device.c:1845
462
+msgid "High Fidelity Input (BAP Source)"
463
+msgstr ""
464
+
465
+#: spa/plugins/bluez5/bluez5-device.c:1848
466
+msgid "High Fidelity Duplex (BAP Source/Sink)"
467
+msgstr ""
468
+
469
+#: spa/plugins/bluez5/bluez5-device.c:1897
470
+#, c-format
471
+msgid "Headset Head Unit (HSP/HFP, codec %s)"
472
 msgstr ""
473
 
474
-#: spa/plugins/bluez5/bluez5-device.c:1443
475
-#: spa/plugins/bluez5/bluez5-device.c:1448
476
-#: spa/plugins/bluez5/bluez5-device.c:1455
477
-#: spa/plugins/bluez5/bluez5-device.c:1461
478
-#: spa/plugins/bluez5/bluez5-device.c:1467
479
-#: spa/plugins/bluez5/bluez5-device.c:1473
480
-#: spa/plugins/bluez5/bluez5-device.c:1479
481
-#: spa/plugins/bluez5/bluez5-device.c:1485
482
-#: spa/plugins/bluez5/bluez5-device.c:1491
483
+#: spa/plugins/bluez5/bluez5-device.c:1978
484
+#: spa/plugins/bluez5/bluez5-device.c:1983
485
+#: spa/plugins/bluez5/bluez5-device.c:1990
486
+#: spa/plugins/bluez5/bluez5-device.c:1996
487
+#: spa/plugins/bluez5/bluez5-device.c:2002
488
+#: spa/plugins/bluez5/bluez5-device.c:2008
489
+#: spa/plugins/bluez5/bluez5-device.c:2014
490
+#: spa/plugins/bluez5/bluez5-device.c:2020
491
+#: spa/plugins/bluez5/bluez5-device.c:2026
492
 msgid "Handsfree"
493
 msgstr ""
494
 
495
-#: spa/plugins/bluez5/bluez5-device.c:1449
496
+#: spa/plugins/bluez5/bluez5-device.c:1984
497
 msgid "Handsfree (HFP)"
498
 msgstr ""
499
 
500
-#: spa/plugins/bluez5/bluez5-device.c:1466
501
+#: spa/plugins/bluez5/bluez5-device.c:2001
502
 msgid "Headphone"
503
 msgstr ""
504
 
505
-#: spa/plugins/bluez5/bluez5-device.c:1472
506
+#: spa/plugins/bluez5/bluez5-device.c:2007
507
 msgid "Portable"
508
 msgstr ""
509
 
510
-#: spa/plugins/bluez5/bluez5-device.c:1478
511
+#: spa/plugins/bluez5/bluez5-device.c:2013
512
 msgid "Car"
513
 msgstr ""
514
 
515
-#: spa/plugins/bluez5/bluez5-device.c:1484
516
+#: spa/plugins/bluez5/bluez5-device.c:2019
517
 msgid "HiFi"
518
 msgstr ""
519
 
520
-#: spa/plugins/bluez5/bluez5-device.c:1490
521
+#: spa/plugins/bluez5/bluez5-device.c:2025
522
 msgid "Phone"
523
 msgstr ""
524
 
525
-#: spa/plugins/bluez5/bluez5-device.c:1497
526
+#: spa/plugins/bluez5/bluez5-device.c:2032
527
 msgid "Bluetooth"
528
 msgstr ""
529
 
530
-#: spa/plugins/bluez5/bluez5-device.c:1498
531
+#: spa/plugins/bluez5/bluez5-device.c:2033
532
 msgid "Bluetooth (HFP)"
533
 msgstr ""
534
pipewire-1.0.1.tar.bz2/po/ru.po -> pipewire-1.2.0.tar.gz/po/ru.po Changed
287
 
1
@@ -10,8 +10,8 @@
2
 "Project-Id-Version: pipewire\n"
3
 "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
4
 "issues\n"
5
-"POT-Creation-Date: 2023-04-06 03:27+0000\n"
6
-"PO-Revision-Date: 2023-04-06 10:51+0300\n"
7
+"POT-Creation-Date: 2023-05-28 10:45+0000\n"
8
+"PO-Revision-Date: 2023-11-28 14:30+0300\n"
9
 "Last-Translator: Aleksandr Melman <Alexmelman88@gmail.com>\n"
10
 "Language-Team: ru\n"
11
 "Language: ru\n"
12
@@ -20,7 +20,7 @@
13
 "Content-Transfer-Encoding: 8bit\n"
14
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
15
 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
16
-"X-Generator: Poedit 3.2.2\n"
17
+"X-Generator: Poedit 3.4.1\n"
18
 
19
 #: src/daemon/pipewire.c:26
20
 #, c-format
21
@@ -30,9 +30,9 @@
22
 "      --version                         Show version\n"
23
 "  -c, --config                          Load config (Default %s)\n"
24
 msgstr ""
25
-"%s опции\n"
26
+"%s параметры\n"
27
 "  -h, --help                            Показать справку\n"
28
-"      --version                         Информация о версии\n"
29
+"      --version                         Показать версию\n"
30
 "  -c, --config                          Загрузить конфигурацию (По умолчанию "
31
 "%s)\n"
32
 
33
@@ -44,17 +44,17 @@
34
 msgid "Start the PipeWire Media System"
35
 msgstr "Запустить PipeWire"
36
 
37
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
38
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
39
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:141
40
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:141
41
 #, c-format
42
-msgid "Tunnel to %s/%s"
43
-msgstr "Туннель на %s/%s"
44
+msgid "Tunnel to %s%s%s"
45
+msgstr "Туннель на %s%s%s"
46
 
47
 #: src/modules/module-fallback-sink.c:31
48
 msgid "Dummy Output"
49
 msgstr "Холостой выход"
50
 
51
-#: src/modules/module-pulse-tunnel.c:688
52
+#: src/modules/module-pulse-tunnel.c:844
53
 #, c-format
54
 msgid "Tunnel for %s@%s"
55
 msgstr "Туннель для %s@%s"
56
@@ -82,9 +82,9 @@
57
 "  -v, --verbose                         Enable verbose operations\n"
58
 "\n"
59
 msgstr ""
60
-"%s опции <file>|-\n"
61
+"%s параметры <file>|-\n"
62
 "  -h, --help                            Показать справку\n"
63
-"      --version                         Информация о версии\n"
64
+"      --version                         Показать версию\n"
65
 "  -v, --verbose                         Включить показ подробной информации\n"
66
 "\n"
67
 
68
@@ -188,9 +188,9 @@
69
 "  -m, --monitor                         Monitor activity\n"
70
 "\n"
71
 msgstr ""
72
-"%s опции команда\n"
73
+"%s параметры команда\n"
74
 "  -h, --help                            Показать справку\n"
75
-"      --version                         Информация о версии\n"
76
+"      --version                         Показать версию\n"
77
 "  -d, --daemon                          Запустить в режиме фоновой службы "
78
 "(По умолчанию false)\n"
79
 "  -r, --remote                          Имя удаленного фоновой службы\n"
80
@@ -202,7 +202,7 @@
81
 msgstr "Pro Audio"
82
 
83
 #: spa/plugins/alsa/acp/acp.c:427 spa/plugins/alsa/acp/alsa-mixer.c:4648
84
-#: spa/plugins/bluez5/bluez5-device.c:1286
85
+#: spa/plugins/bluez5/bluez5-device.c:1586
86
 msgid "Off"
87
 msgstr "Выключено"
88
 
89
@@ -229,7 +229,7 @@
90
 
91
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
92
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
93
-#: spa/plugins/bluez5/bluez5-device.c:1522
94
+#: spa/plugins/bluez5/bluez5-device.c:1831
95
 msgid "Microphone"
96
 msgstr "Микрофон"
97
 
98
@@ -295,7 +295,7 @@
99
 msgstr "Нет усиления басов"
100
 
101
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
102
-#: spa/plugins/bluez5/bluez5-device.c:1528
103
+#: spa/plugins/bluez5/bluez5-device.c:1837
104
 msgid "Speaker"
105
 msgstr "Динамик"
106
 
107
@@ -410,7 +410,7 @@
108
 
109
 #: spa/plugins/alsa/acp/alsa-mixer.c:4484
110
 #: spa/plugins/alsa/acp/alsa-mixer.c:4642
111
-#: spa/plugins/bluez5/bluez5-device.c:1510
112
+#: spa/plugins/bluez5/bluez5-device.c:1819
113
 msgid "Headset"
114
 msgstr "Гарнитура"
115
 
116
@@ -534,7 +534,7 @@
117
 msgid "%s Input"
118
 msgstr "%s вход"
119
 
120
-#: spa/plugins/alsa/acp/alsa-util.c:1211 spa/plugins/alsa/acp/alsa-util.c:1305
121
+#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
122
 #, c-format
123
 msgid ""
124
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
125
@@ -562,7 +562,7 @@
126
 "Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
127
 "проблеме разработчикам ALSA."
128
 
129
-#: spa/plugins/alsa/acp/alsa-util.c:1277
130
+#: spa/plugins/alsa/acp/alsa-util.c:1253
131
 #, c-format
132
 msgid ""
133
 "snd_pcm_delay() returned a value that is exceptionally large: %li byte "
134
@@ -590,7 +590,7 @@
135
 "Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
136
 "проблеме разработчикам ALSA."
137
 
138
-#: spa/plugins/alsa/acp/alsa-util.c:1324
139
+#: spa/plugins/alsa/acp/alsa-util.c:1300
140
 #, c-format
141
 msgid ""
142
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
143
@@ -603,7 +603,7 @@
144
 "Вероятно, это ошибка в драйвере ALSA «%s». Пожалуйста, сообщите об этой "
145
 "проблеме разработчикам ALSA."
146
 
147
-#: spa/plugins/alsa/acp/alsa-util.c:1367
148
+#: spa/plugins/alsa/acp/alsa-util.c:1343
149
 #, c-format
150
 msgid ""
151
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
152
@@ -643,104 +643,104 @@
153
 msgid "Modem"
154
 msgstr "Модем"
155
 
156
-#: spa/plugins/bluez5/bluez5-device.c:1297
157
+#: spa/plugins/bluez5/bluez5-device.c:1597
158
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
159
 msgstr "Адаптер аудиогарнитуры (приёмник A2DP и HSP/HFP AG)"
160
 
161
-#: spa/plugins/bluez5/bluez5-device.c:1322
162
+#: spa/plugins/bluez5/bluez5-device.c:1622
163
 #, c-format
164
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
165
 msgstr "Воспроизведение высокого качества (приёмник A2DP, кодек %s)"
166
 
167
-#: spa/plugins/bluez5/bluez5-device.c:1325
168
+#: spa/plugins/bluez5/bluez5-device.c:1625
169
 #, c-format
170
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
171
 msgstr "Дуплекс высокого качества (источник / приёмник A2DP,  кодек %s)"
172
 
173
-#: spa/plugins/bluez5/bluez5-device.c:1333
174
+#: spa/plugins/bluez5/bluez5-device.c:1633
175
 msgid "High Fidelity Playback (A2DP Sink)"
176
 msgstr "Воспроизведение высокого качества (приёмник A2DP)"
177
 
178
-#: spa/plugins/bluez5/bluez5-device.c:1335
179
+#: spa/plugins/bluez5/bluez5-device.c:1635
180
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
181
 msgstr "Дуплекс высокого качества (источник / приёмник A2DP)"
182
 
183
-#: spa/plugins/bluez5/bluez5-device.c:1377
184
+#: spa/plugins/bluez5/bluez5-device.c:1677
185
 #, c-format
186
 msgid "High Fidelity Playback (BAP Sink, codec %s)"
187
 msgstr "Воспроизведение высокого качества (приёмник BAP, кодек %s)"
188
 
189
-#: spa/plugins/bluez5/bluez5-device.c:1381
190
+#: spa/plugins/bluez5/bluez5-device.c:1681
191
 #, c-format
192
 msgid "High Fidelity Input (BAP Source, codec %s)"
193
 msgstr "Вход высокого качества (источник BAP, кодек %s)"
194
 
195
-#: spa/plugins/bluez5/bluez5-device.c:1385
196
+#: spa/plugins/bluez5/bluez5-device.c:1685
197
 #, c-format
198
 msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
199
 msgstr "Дуплекс высокого качества (источник / приёмник BAP, кодек %s)"
200
 
201
-#: spa/plugins/bluez5/bluez5-device.c:1393
202
+#: spa/plugins/bluez5/bluez5-device.c:1693
203
 msgid "High Fidelity Playback (BAP Sink)"
204
 msgstr "Воспроизведение высокого качества (приёмник BAP)"
205
 
206
-#: spa/plugins/bluez5/bluez5-device.c:1396
207
+#: spa/plugins/bluez5/bluez5-device.c:1696
208
 msgid "High Fidelity Input (BAP Source)"
209
 msgstr "Вход высокого качества (источник BAP)"
210
 
211
-#: spa/plugins/bluez5/bluez5-device.c:1399
212
+#: spa/plugins/bluez5/bluez5-device.c:1699
213
 msgid "High Fidelity Duplex (BAP Source/Sink)"
214
 msgstr "Дуплекс высокого качества (источник / приёмник BAP)"
215
 
216
-#: spa/plugins/bluez5/bluez5-device.c:1427
217
+#: spa/plugins/bluez5/bluez5-device.c:1735
218
 #, c-format
219
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
220
 msgstr "Гарнитура (HSP/HFP, кодек %s)"
221
 
222
-#: spa/plugins/bluez5/bluez5-device.c:1432
223
+#: spa/plugins/bluez5/bluez5-device.c:1740
224
 msgid "Headset Head Unit (HSP/HFP)"
225
 msgstr "Гарнитура (HSP/HFP)"
226
 
227
-#: spa/plugins/bluez5/bluez5-device.c:1511
228
-#: spa/plugins/bluez5/bluez5-device.c:1516
229
-#: spa/plugins/bluez5/bluez5-device.c:1523
230
-#: spa/plugins/bluez5/bluez5-device.c:1529
231
-#: spa/plugins/bluez5/bluez5-device.c:1535
232
-#: spa/plugins/bluez5/bluez5-device.c:1541
233
-#: spa/plugins/bluez5/bluez5-device.c:1547
234
-#: spa/plugins/bluez5/bluez5-device.c:1553
235
-#: spa/plugins/bluez5/bluez5-device.c:1559
236
+#: spa/plugins/bluez5/bluez5-device.c:1820
237
+#: spa/plugins/bluez5/bluez5-device.c:1825
238
+#: spa/plugins/bluez5/bluez5-device.c:1832
239
+#: spa/plugins/bluez5/bluez5-device.c:1838
240
+#: spa/plugins/bluez5/bluez5-device.c:1844
241
+#: spa/plugins/bluez5/bluez5-device.c:1850
242
+#: spa/plugins/bluez5/bluez5-device.c:1856
243
+#: spa/plugins/bluez5/bluez5-device.c:1862
244
+#: spa/plugins/bluez5/bluez5-device.c:1868
245
 msgid "Handsfree"
246
 msgstr "Handsfree"
247
 
248
-#: spa/plugins/bluez5/bluez5-device.c:1517
249
+#: spa/plugins/bluez5/bluez5-device.c:1826
250
 msgid "Handsfree (HFP)"
251
 msgstr "Handsfree (HFP)"
252
 
253
-#: spa/plugins/bluez5/bluez5-device.c:1534
254
+#: spa/plugins/bluez5/bluez5-device.c:1843
255
 msgid "Headphone"
256
 msgstr "Наушники"
257
 
258
-#: spa/plugins/bluez5/bluez5-device.c:1540
259
+#: spa/plugins/bluez5/bluez5-device.c:1849
260
 msgid "Portable"
261
 msgstr "Портативное устройство"
262
 
263
-#: spa/plugins/bluez5/bluez5-device.c:1546
264
+#: spa/plugins/bluez5/bluez5-device.c:1855
265
 msgid "Car"
266
 msgstr "Автомобильное устройство"
267
 
268
-#: spa/plugins/bluez5/bluez5-device.c:1552
269
+#: spa/plugins/bluez5/bluez5-device.c:1861
270
 msgid "HiFi"
271
 msgstr "Hi-Fi"
272
 
273
-#: spa/plugins/bluez5/bluez5-device.c:1558
274
+#: spa/plugins/bluez5/bluez5-device.c:1867
275
 msgid "Phone"
276
 msgstr "Телефон"
277
 
278
-#: spa/plugins/bluez5/bluez5-device.c:1565
279
+#: spa/plugins/bluez5/bluez5-device.c:1874
280
 msgid "Bluetooth"
281
 msgstr "Bluetooth"
282
 
283
-#: spa/plugins/bluez5/bluez5-device.c:1566
284
+#: spa/plugins/bluez5/bluez5-device.c:1875
285
 msgid "Bluetooth (HFP)"
286
 msgstr "Bluetooth (HFP)"
287
pipewire-1.0.1.tar.bz2/po/tr.po -> pipewire-1.2.0.tar.gz/po/tr.po Changed
594
 
1
@@ -1,19 +1,19 @@
2
 # Turkish translation for PipeWire.
3
-# Copyright (C) 2014-2023 PipeWire's COPYRIGHT HOLDER
4
+# Copyright (C) 2014-2024 PipeWire's COPYRIGHT HOLDER
5
 # This file is distributed under the same license as the PipeWire package.
6
 #
7
 # Necdet Yücel <necdetyucel@gmail.com>, 2014.
8
 # Kaan Özdinçer <kaanozdincer@gmail.com>, 2014.
9
 # Muhammet Kara <muhammetk@gmail.com>, 2015, 2016, 2017.
10
 # Oğuz Ersen <oguz@ersen.moe>, 2021-2022.
11
+# Sabri Ünal <libreajans@gmail.com>, 2024.
12
 #
13
 msgid ""
14
 msgstr ""
15
 "Project-Id-Version: PipeWire master\n"
16
-"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
17
-"issues/new\n"
18
-"POT-Creation-Date: 2022-06-30 12:50+0200\n"
19
-"PO-Revision-Date: 2023-05-26 23:39+0300\n"
20
+"Report-Msgid-Bugs-To: \n"
21
+"POT-Creation-Date: 2024-02-25 03:43+0300\n"
22
+"PO-Revision-Date: 2024-02-25 03:49+0300\n"
23
 "Last-Translator: Sabri Ünal <libreajans@gmail.com>\n"
24
 "Language-Team: Türkçe <takim@gnome.org.tr>\n"
25
 "Language: tr\n"
26
@@ -21,9 +21,9 @@
27
 "Content-Type: text/plain; charset=UTF-8\n"
28
 "Content-Transfer-Encoding: 8bit\n"
29
 "Plural-Forms: nplurals=1; plural=0;\n"
30
-"X-Generator: Poedit 3.2.2\n"
31
+"X-Generator: Poedit 3.4.2\n"
32
 
33
-#: src/daemon/pipewire.c:46
34
+#: src/daemon/pipewire.c:26
35
 #, c-format
36
 msgid ""
37
 "%s options\n"
38
@@ -36,36 +36,44 @@
39
 "      --version                         Sürümü göster\n"
40
 "  -c, --config                          Yapılandırmayı yükle (Öntanımlı %s)\n"
41
 
42
-#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180
43
-#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180
44
+#: src/daemon/pipewire.desktop.in:4
45
+msgid "PipeWire Media System"
46
+msgstr "PipeWire Ortam Sistemi"
47
+
48
+#: src/daemon/pipewire.desktop.in:5
49
+msgid "Start the PipeWire Media System"
50
+msgstr "PipeWire Ortam Sistemini Başlat"
51
+
52
+#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
53
+#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
54
 #, c-format
55
-msgid "Tunnel to %s/%s"
56
-msgstr "%s/%s tüneli"
57
+msgid "Tunnel to %s%s%s"
58
+msgstr "%s%s%s tüneli"
59
 
60
-#: src/modules/module-fallback-sink.c:51
61
+#: src/modules/module-fallback-sink.c:40
62
 msgid "Dummy Output"
63
 msgstr "Temsili Çıkış"
64
 
65
-#: src/modules/module-pulse-tunnel.c:662
66
+#: src/modules/module-pulse-tunnel.c:774
67
 #, c-format
68
 msgid "Tunnel for %s@%s"
69
 msgstr "%s@%s için tünel"
70
 
71
-#: src/modules/module-zeroconf-discover.c:332
72
+#: src/modules/module-zeroconf-discover.c:315
73
 msgid "Unknown device"
74
 msgstr "Bilinmeyen aygıt"
75
 
76
-#: src/modules/module-zeroconf-discover.c:344
77
+#: src/modules/module-zeroconf-discover.c:327
78
 #, c-format
79
 msgid "%s on %s@%s"
80
 msgstr "%s, %s@%s"
81
 
82
-#: src/modules/module-zeroconf-discover.c:348
83
+#: src/modules/module-zeroconf-discover.c:331
84
 #, c-format
85
 msgid "%s on %s"
86
 msgstr "%s, %s"
87
 
88
-#: src/tools/pw-cat.c:784
89
+#: src/tools/pw-cat.c:991
90
 #, c-format
91
 msgid ""
92
 "%s options <file>|-\n"
93
@@ -80,14 +88,15 @@
94
 "  -v, --verbose                         Ayrıntılı işlemleri etkinleştir\n"
95
 "\n"
96
 
97
-#: src/tools/pw-cat.c:791
98
+#: src/tools/pw-cat.c:998
99
 #, c-format
100
 msgid ""
101
 "  -R, --remote                          Remote daemon name\n"
102
 "      --media-type                      Set media type (default %s)\n"
103
 "      --media-category                  Set media category (default %s)\n"
104
 "      --media-role                      Set media role (default %s)\n"
105
-"      --target                          Set node target (default %s)\n"
106
+"      --target                          Set node target serial or name "
107
+"(default %s)\n"
108
 "                                          0 means don't link\n"
109
 "      --latency                         Set node latency (default %s)\n"
110
 "                                          Xunit (unit = s, ms, us, ns)\n"
111
@@ -102,8 +111,8 @@
112
 "      --media-category                  Ortam kategorisini ayarla (öntanımlı "
113
 "%s)\n"
114
 "      --media-role                      Ortam rolünü ayarla (öntanımlı %s)\n"
115
-"      --target                          Düğüm hedefini ayarla (öntanımlı "
116
-"%s)\n"
117
+"      --target                          Düğüm hedefi seri ya da adını ayarla "
118
+"(öntanımlı %s)\n"
119
 "                                          0, bağlanmayacağı anlamına gelir\n"
120
 "      --latency                         Düğüm gecikmesini ayarla (öntanımlı "
121
 "%s)\n"
122
@@ -113,7 +122,7 @@
123
 "  -P  --properties                      Düğüm özelliklerini ayarla\n"
124
 "\n"
125
 
126
-#: src/tools/pw-cat.c:809
127
+#: src/tools/pw-cat.c:1016
128
 #, c-format
129
 msgid ""
130
 "      --rate                            Sample rate (req. for rec) (default "
131
@@ -149,21 +158,23 @@
132
 "15) (öntanımlı %d)\n"
133
 "\n"
134
 
135
-#: src/tools/pw-cat.c:826
136
+#: src/tools/pw-cat.c:1033
137
 msgid ""
138
 "  -p, --playback                        Playback mode\n"
139
 "  -r, --record                          Recording mode\n"
140
 "  -m, --midi                            Midi mode\n"
141
 "  -d, --dsd                             DSD mode\n"
142
+"  -o, --encoded                         Encoded mode\n"
143
 "\n"
144
 msgstr ""
145
-"  -p, --playback                        Çalma modu\n"
146
-"  -r, --record                          Kayıt modu\n"
147
-"  -m, --midi                            Midi modu\n"
148
-"  -d, --dsd                             DSD modu\n"
149
+"  -p, --playback                        Çalma kipi\n"
150
+"  -r, --record                          Kayıt kipi\n"
151
+"  -m, --midi                            Midi kipi\n"
152
+"  -d, --dsd                             DSD kipi\n"
153
+"  -o, --encoded                         Kodlanmış kip\n"
154
 "\n"
155
 
156
-#: src/tools/pw-cli.c:2250
157
+#: src/tools/pw-cli.c:2252
158
 #, c-format
159
 msgid ""
160
 "%s options command\n"
161
@@ -171,6 +182,7 @@
162
 "      --version                         Show version\n"
163
 "  -d, --daemon                          Start as daemon (Default false)\n"
164
 "  -r, --remote                          Remote daemon name\n"
165
+"  -m, --monitor                         Monitor activity\n"
166
 "\n"
167
 msgstr ""
168
 "%s seçenekler komut\n"
169
@@ -179,14 +191,14 @@
170
 "  -d, --daemon                          Art alan hizmeti olarak başlat "
171
 "(Öntanımlı olarak yanlış)\n"
172
 "  -r, --remote                          Uzak arka plan programı adı\n"
173
-"\n"
174
+"  -m, --monitor                         Etkinliği izle\n"
175
 
176
-#: spa/plugins/alsa/acp/acp.c:321
177
+#: spa/plugins/alsa/acp/acp.c:327
178
 msgid "Pro Audio"
179
 msgstr "Profesyonel Ses"
180
 
181
-#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648
182
-#: spa/plugins/bluez5/bluez5-device.c:1236
183
+#: spa/plugins/alsa/acp/acp.c:488 spa/plugins/alsa/acp/alsa-mixer.c:4633
184
+#: spa/plugins/bluez5/bluez5-device.c:1701
185
 msgid "Off"
186
 msgstr "Kapalı"
187
 
188
@@ -213,7 +225,7 @@
189
 
190
 #: spa/plugins/alsa/acp/alsa-mixer.c:2657
191
 #: spa/plugins/alsa/acp/alsa-mixer.c:2741
192
-#: spa/plugins/bluez5/bluez5-device.c:1454
193
+#: spa/plugins/bluez5/bluez5-device.c:1989
194
 msgid "Microphone"
195
 msgstr "Mikrofon"
196
 
197
@@ -279,7 +291,7 @@
198
 msgstr "Bas Artırma Yok"
199
 
200
 #: spa/plugins/alsa/acp/alsa-mixer.c:2672
201
-#: spa/plugins/bluez5/bluez5-device.c:1460
202
+#: spa/plugins/bluez5/bluez5-device.c:1995
203
 msgid "Speaker"
204
 msgstr "Hoparlör"
205
 
206
@@ -361,15 +373,15 @@
207
 msgid "Virtual Surround 7.1"
208
 msgstr "Sanal Çevresel Ses 7.1"
209
 
210
-#: spa/plugins/alsa/acp/alsa-mixer.c:4471
211
+#: spa/plugins/alsa/acp/alsa-mixer.c:4456
212
 msgid "Analog Mono"
213
 msgstr "Analog Tek Kanallı"
214
 
215
-#: spa/plugins/alsa/acp/alsa-mixer.c:4472
216
+#: spa/plugins/alsa/acp/alsa-mixer.c:4457
217
 msgid "Analog Mono (Left)"
218
 msgstr "Analog Tek Kanallı (Sol)"
219
 
220
-#: spa/plugins/alsa/acp/alsa-mixer.c:4473
221
+#: spa/plugins/alsa/acp/alsa-mixer.c:4458
222
 msgid "Analog Mono (Right)"
223
 msgstr "Analog Tek Kanallı (Sağ)"
224
 
225
@@ -378,147 +390,147 @@
226
 #. * here would lead to the source name to become "Analog Stereo Input
227
 #. * Input". The same logic applies to analog-stereo-output,
228
 #. * multichannel-input and multichannel-output.
229
-#: spa/plugins/alsa/acp/alsa-mixer.c:4474
230
-#: spa/plugins/alsa/acp/alsa-mixer.c:4482
231
-#: spa/plugins/alsa/acp/alsa-mixer.c:4483
232
+#: spa/plugins/alsa/acp/alsa-mixer.c:4459
233
+#: spa/plugins/alsa/acp/alsa-mixer.c:4467
234
+#: spa/plugins/alsa/acp/alsa-mixer.c:4468
235
 msgid "Analog Stereo"
236
 msgstr "Analog Stereo"
237
 
238
-#: spa/plugins/alsa/acp/alsa-mixer.c:4475
239
+#: spa/plugins/alsa/acp/alsa-mixer.c:4460
240
 msgid "Mono"
241
 msgstr "Tek Kanallı"
242
 
243
-#: spa/plugins/alsa/acp/alsa-mixer.c:4476
244
+#: spa/plugins/alsa/acp/alsa-mixer.c:4461
245
 msgid "Stereo"
246
 msgstr "Stereo"
247
 
248
-#: spa/plugins/alsa/acp/alsa-mixer.c:4484
249
-#: spa/plugins/alsa/acp/alsa-mixer.c:4642
250
-#: spa/plugins/bluez5/bluez5-device.c:1442
251
+#: spa/plugins/alsa/acp/alsa-mixer.c:4469
252
+#: spa/plugins/alsa/acp/alsa-mixer.c:4627
253
+#: spa/plugins/bluez5/bluez5-device.c:1977
254
 msgid "Headset"
255
 msgstr "Kulaklık"
256
 
257
-#: spa/plugins/alsa/acp/alsa-mixer.c:4485
258
-#: spa/plugins/alsa/acp/alsa-mixer.c:4643
259
+#: spa/plugins/alsa/acp/alsa-mixer.c:4470
260
+#: spa/plugins/alsa/acp/alsa-mixer.c:4628
261
 msgid "Speakerphone"
262
 msgstr "Hoparlör"
263
 
264
-#: spa/plugins/alsa/acp/alsa-mixer.c:4486
265
-#: spa/plugins/alsa/acp/alsa-mixer.c:4487
266
+#: spa/plugins/alsa/acp/alsa-mixer.c:4471
267
+#: spa/plugins/alsa/acp/alsa-mixer.c:4472
268
 msgid "Multichannel"
269
 msgstr "Çok kanallı"
270
 
271
-#: spa/plugins/alsa/acp/alsa-mixer.c:4488
272
+#: spa/plugins/alsa/acp/alsa-mixer.c:4473
273
 msgid "Analog Surround 2.1"
274
 msgstr "Analog Çevresel Ses 2.1"
275
 
276
-#: spa/plugins/alsa/acp/alsa-mixer.c:4489
277
+#: spa/plugins/alsa/acp/alsa-mixer.c:4474
278
 msgid "Analog Surround 3.0"
279
 msgstr "Analog Çevresel Ses 3.0"
280
 
281
-#: spa/plugins/alsa/acp/alsa-mixer.c:4490
282
+#: spa/plugins/alsa/acp/alsa-mixer.c:4475
283
 msgid "Analog Surround 3.1"
284
 msgstr "Analog Çevresel Ses 3.1"
285
 
286
-#: spa/plugins/alsa/acp/alsa-mixer.c:4491
287
+#: spa/plugins/alsa/acp/alsa-mixer.c:4476
288
 msgid "Analog Surround 4.0"
289
 msgstr "Analog Çevresel Ses 4.0"
290
 
291
-#: spa/plugins/alsa/acp/alsa-mixer.c:4492
292
+#: spa/plugins/alsa/acp/alsa-mixer.c:4477
293
 msgid "Analog Surround 4.1"
294
 msgstr "Analog Çevresel Ses 4.1"
295
 
296
-#: spa/plugins/alsa/acp/alsa-mixer.c:4493
297
+#: spa/plugins/alsa/acp/alsa-mixer.c:4478
298
 msgid "Analog Surround 5.0"
299
 msgstr "Analog Çevresel Ses 5.0"
300
 
301
-#: spa/plugins/alsa/acp/alsa-mixer.c:4494
302
+#: spa/plugins/alsa/acp/alsa-mixer.c:4479
303
 msgid "Analog Surround 5.1"
304
 msgstr "Analog Çevresel Ses 5.1"
305
 
306
-#: spa/plugins/alsa/acp/alsa-mixer.c:4495
307
+#: spa/plugins/alsa/acp/alsa-mixer.c:4480
308
 msgid "Analog Surround 6.0"
309
 msgstr "Analog Çevresel Ses 6.0"
310
 
311
-#: spa/plugins/alsa/acp/alsa-mixer.c:4496
312
+#: spa/plugins/alsa/acp/alsa-mixer.c:4481
313
 msgid "Analog Surround 6.1"
314
 msgstr "Analog Çevresel Ses 6.1"
315
 
316
-#: spa/plugins/alsa/acp/alsa-mixer.c:4497
317
+#: spa/plugins/alsa/acp/alsa-mixer.c:4482
318
 msgid "Analog Surround 7.0"
319
 msgstr "Analog Çevresel Ses 7.0"
320
 
321
-#: spa/plugins/alsa/acp/alsa-mixer.c:4498
322
+#: spa/plugins/alsa/acp/alsa-mixer.c:4483
323
 msgid "Analog Surround 7.1"
324
 msgstr "Analog Çevresel Ses 7.1"
325
 
326
-#: spa/plugins/alsa/acp/alsa-mixer.c:4499
327
+#: spa/plugins/alsa/acp/alsa-mixer.c:4484
328
 msgid "Digital Stereo (IEC958)"
329
 msgstr "Sayısal Stereo (IEC958)"
330
 
331
-#: spa/plugins/alsa/acp/alsa-mixer.c:4500
332
+#: spa/plugins/alsa/acp/alsa-mixer.c:4485
333
 msgid "Digital Surround 4.0 (IEC958/AC3)"
334
 msgstr "Sayısal Çevresel Ses 4.0 (IEC958/AC3)"
335
 
336
-#: spa/plugins/alsa/acp/alsa-mixer.c:4501
337
+#: spa/plugins/alsa/acp/alsa-mixer.c:4486
338
 msgid "Digital Surround 5.1 (IEC958/AC3)"
339
 msgstr "Sayısal Çevresel Ses 5.1 (IEC958/AC3)"
340
 
341
-#: spa/plugins/alsa/acp/alsa-mixer.c:4502
342
+#: spa/plugins/alsa/acp/alsa-mixer.c:4487
343
 msgid "Digital Surround 5.1 (IEC958/DTS)"
344
 msgstr "Sayısal Çevresel Ses 5.1 (IEC958/DTS)"
345
 
346
-#: spa/plugins/alsa/acp/alsa-mixer.c:4503
347
+#: spa/plugins/alsa/acp/alsa-mixer.c:4488
348
 msgid "Digital Stereo (HDMI)"
349
 msgstr "Sayısal Stereo (HDMI)"
350
 
351
-#: spa/plugins/alsa/acp/alsa-mixer.c:4504
352
+#: spa/plugins/alsa/acp/alsa-mixer.c:4489
353
 msgid "Digital Surround 5.1 (HDMI)"
354
 msgstr "Sayısal Çevresel Ses 5.1 (HDMI)"
355
 
356
-#: spa/plugins/alsa/acp/alsa-mixer.c:4505
357
+#: spa/plugins/alsa/acp/alsa-mixer.c:4490
358
 msgid "Chat"
359
 msgstr "Sohbet"
360
 
361
-#: spa/plugins/alsa/acp/alsa-mixer.c:4506
362
+#: spa/plugins/alsa/acp/alsa-mixer.c:4491
363
 msgid "Game"
364
 msgstr "Oyun"
365
 
366
-#: spa/plugins/alsa/acp/alsa-mixer.c:4640
367
+#: spa/plugins/alsa/acp/alsa-mixer.c:4625
368
 msgid "Analog Mono Duplex"
369
 msgstr "Analog Tek Kanallı İkili"
370
 
371
-#: spa/plugins/alsa/acp/alsa-mixer.c:4641
372
+#: spa/plugins/alsa/acp/alsa-mixer.c:4626
373
 msgid "Analog Stereo Duplex"
374
 msgstr "Analog İkili Stereo"
375
 
376
-#: spa/plugins/alsa/acp/alsa-mixer.c:4644
377
+#: spa/plugins/alsa/acp/alsa-mixer.c:4629
378
 msgid "Digital Stereo Duplex (IEC958)"
379
 msgstr "Sayısal İkili Stereo (IEC958)"
380
 
381
-#: spa/plugins/alsa/acp/alsa-mixer.c:4645
382
+#: spa/plugins/alsa/acp/alsa-mixer.c:4630
383
 msgid "Multichannel Duplex"
384
 msgstr "Çok Kanallı İkili"
385
 
386
-#: spa/plugins/alsa/acp/alsa-mixer.c:4646
387
+#: spa/plugins/alsa/acp/alsa-mixer.c:4631
388
 msgid "Stereo Duplex"
389
 msgstr "İkili Stereo"
390
 
391
-#: spa/plugins/alsa/acp/alsa-mixer.c:4647
392
+#: spa/plugins/alsa/acp/alsa-mixer.c:4632
393
 msgid "Mono Chat + 7.1 Surround"
394
 msgstr "Tek Kanallı Sohbet + 7.1 Çevresel Ses"
395
 
396
-#: spa/plugins/alsa/acp/alsa-mixer.c:4754
397
+#: spa/plugins/alsa/acp/alsa-mixer.c:4733
398
 #, c-format
399
 msgid "%s Output"
400
 msgstr "%s Çıkışı"
401
 
402
-#: spa/plugins/alsa/acp/alsa-mixer.c:4761
403
+#: spa/plugins/alsa/acp/alsa-mixer.c:4741
404
 #, c-format
405
 msgid "%s Input"
406
 msgstr "%s Girişi"
407
 
408
-#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281
409
+#: spa/plugins/alsa/acp/alsa-util.c:1220 spa/plugins/alsa/acp/alsa-util.c:1314
410
 #, c-format
411
 msgid ""
412
 "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
413
@@ -535,16 +547,16 @@
414
 "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
415
 "geliştiricilerine bildirin."
416
 
417
-#: spa/plugins/alsa/acp/alsa-util.c:1253
418
+#: spa/plugins/alsa/acp/alsa-util.c:1286
419
 #, c-format
420
 msgid ""
421
-"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
422
-"(%s%lu ms).\n"
423
+"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
424
+"%lu ms).\n"
425
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
426
 "to the ALSA developers."
427
 msgid_plural ""
428
-"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
429
-"(%s%lu ms).\n"
430
+"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
431
+"%lu ms).\n"
432
 "Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
433
 "to the ALSA developers."
434
 msgstr0 ""
435
@@ -552,7 +564,7 @@
436
 "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
437
 "geliştiricilerine bildirin."
438
 
439
-#: spa/plugins/alsa/acp/alsa-util.c:1300
440
+#: spa/plugins/alsa/acp/alsa-util.c:1333
441
 #, c-format
442
 msgid ""
443
 "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
444
@@ -565,7 +577,7 @@
445
 "Büyük ihtimalle bu bir ALSA sürücüsü '%s' hatasıdır. Lütfen bu sorunu ALSA "
446
 "geliştiricilerine bildirin."
447
 
448
-#: spa/plugins/alsa/acp/alsa-util.c:1343
449
+#: spa/plugins/alsa/acp/alsa-util.c:1376
450
 #, c-format
451
 msgid ""
452
 "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
453
@@ -587,100 +599,108 @@
454
 msgid "(invalid)"
455
 msgstr "(geçersiz)"
456
 
457
-#: spa/plugins/alsa/acp/compat.c:189
458
+#: spa/plugins/alsa/acp/compat.c:193
459
 msgid "Built-in Audio"
460
 msgstr "Dahili Ses"
461
 
462
-#: spa/plugins/alsa/acp/compat.c:194
463
+#: spa/plugins/alsa/acp/compat.c:198
464
 msgid "Modem"
465
 msgstr "Modem"
466
 
467
-#: spa/plugins/bluez5/bluez5-device.c:1247
468
+#: spa/plugins/bluez5/bluez5-device.c:1712
469
 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
470
 msgstr "Ses Geçidi (A2DP Kaynak & HSP/HFP AG)"
471
 
472
-#: spa/plugins/bluez5/bluez5-device.c:1272
473
+#: spa/plugins/bluez5/bluez5-device.c:1760
474
 #, c-format
475
 msgid "High Fidelity Playback (A2DP Sink, codec %s)"
476
 msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı, çözücü %s)"
477
 
478
-#: spa/plugins/bluez5/bluez5-device.c:1275
479
+#: spa/plugins/bluez5/bluez5-device.c:1763
480
 #, c-format
481
 msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
482
 msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı, çözücü %s)"
483
 
484
-#: spa/plugins/bluez5/bluez5-device.c:1283
485
+#: spa/plugins/bluez5/bluez5-device.c:1771
486
 msgid "High Fidelity Playback (A2DP Sink)"
487
 msgstr "Yüksek Kaliteli Çalma (A2DP Alıcı)"
488
 
489
-#: spa/plugins/bluez5/bluez5-device.c:1285
490
+#: spa/plugins/bluez5/bluez5-device.c:1773
491
 msgid "High Fidelity Duplex (A2DP Source/Sink)"
492
 msgstr "Yüksek Kaliteli İkili (A2DP Kaynak/Alıcı)"
493
 
494
-#: spa/plugins/bluez5/bluez5-device.c:1322
495
+#: spa/plugins/bluez5/bluez5-device.c:1823
496
 #, c-format
497
 msgid "High Fidelity Playback (BAP Sink, codec %s)"
498
 msgstr "Yüksek Kaliteli Çalma (BAP Alıcı, çözücü %s)"
499
 
500
-#: spa/plugins/bluez5/bluez5-device.c:1326
501
+#: spa/plugins/bluez5/bluez5-device.c:1828
502
 #, c-format
503
 msgid "High Fidelity Input (BAP Source, codec %s)"
504
 msgstr "Yüksek Kaliteli Giriş (BAP Kaynak, çözücü %s)"
505
 
506
-#: spa/plugins/bluez5/bluez5-device.c:1330
507
+#: spa/plugins/bluez5/bluez5-device.c:1832
508
 #, c-format
509
 msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
510
 msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı, çözücü %s)"
511
 
512
-#: spa/plugins/bluez5/bluez5-device.c:1359
513
+#: spa/plugins/bluez5/bluez5-device.c:1841
514
+msgid "High Fidelity Playback (BAP Sink)"
515
+msgstr "Yüksek Kaliteli Çalma (BAP Alıcı)"
516
+
517
+#: spa/plugins/bluez5/bluez5-device.c:1845
518
+msgid "High Fidelity Input (BAP Source)"
519
+msgstr "Yüksek Kaliteli Giriş (BAP Kaynak)"
520
+
521
+#: spa/plugins/bluez5/bluez5-device.c:1848
522
+msgid "High Fidelity Duplex (BAP Source/Sink)"
523
+msgstr "Yüksek Kaliteli İkili (BAP Kaynak/Alıcı)"
524
+
525
+#: spa/plugins/bluez5/bluez5-device.c:1897
526
 #, c-format
527
 msgid "Headset Head Unit (HSP/HFP, codec %s)"
528
 msgstr "Kulaklık Ana Birimi (HSP/HFP, çözücü %s)"
529
 
530
-#: spa/plugins/bluez5/bluez5-device.c:1364
531
-msgid "Headset Head Unit (HSP/HFP)"
532
-msgstr "Kulaklık Ana Birimi (HSP/HFP)"
533
-
534
-#: spa/plugins/bluez5/bluez5-device.c:1443
535
-#: spa/plugins/bluez5/bluez5-device.c:1448
536
-#: spa/plugins/bluez5/bluez5-device.c:1455
537
-#: spa/plugins/bluez5/bluez5-device.c:1461
538
-#: spa/plugins/bluez5/bluez5-device.c:1467
539
-#: spa/plugins/bluez5/bluez5-device.c:1473
540
-#: spa/plugins/bluez5/bluez5-device.c:1479
541
-#: spa/plugins/bluez5/bluez5-device.c:1485
542
-#: spa/plugins/bluez5/bluez5-device.c:1491
543
+#: spa/plugins/bluez5/bluez5-device.c:1978
544
+#: spa/plugins/bluez5/bluez5-device.c:1983
545
+#: spa/plugins/bluez5/bluez5-device.c:1990
546
+#: spa/plugins/bluez5/bluez5-device.c:1996
547
+#: spa/plugins/bluez5/bluez5-device.c:2002
548
+#: spa/plugins/bluez5/bluez5-device.c:2008
549
+#: spa/plugins/bluez5/bluez5-device.c:2014
550
+#: spa/plugins/bluez5/bluez5-device.c:2020
551
+#: spa/plugins/bluez5/bluez5-device.c:2026
552
 msgid "Handsfree"
553
 msgstr "Ahizesiz"
554
 
555
-#: spa/plugins/bluez5/bluez5-device.c:1449
556
+#: spa/plugins/bluez5/bluez5-device.c:1984
557
 msgid "Handsfree (HFP)"
558
 msgstr "Ahizesiz (HFP)"
559
 
560
-#: spa/plugins/bluez5/bluez5-device.c:1466
561
+#: spa/plugins/bluez5/bluez5-device.c:2001
562
 msgid "Headphone"
563
 msgstr "Kulaklık"
564
 
565
-#: spa/plugins/bluez5/bluez5-device.c:1472
566
+#: spa/plugins/bluez5/bluez5-device.c:2007
567
 msgid "Portable"
568
 msgstr "Taşınabilir"
569
 
570
-#: spa/plugins/bluez5/bluez5-device.c:1478
571
+#: spa/plugins/bluez5/bluez5-device.c:2013
572
 msgid "Car"
573
 msgstr "Araba"
574
 
575
-#: spa/plugins/bluez5/bluez5-device.c:1484
576
+#: spa/plugins/bluez5/bluez5-device.c:2019
577
 msgid "HiFi"
578
 msgstr "Yüksek Kalite"
579
 
580
-#: spa/plugins/bluez5/bluez5-device.c:1490
581
+#: spa/plugins/bluez5/bluez5-device.c:2025
582
 msgid "Phone"
583
 msgstr "Telefon"
584
 
585
-#: spa/plugins/bluez5/bluez5-device.c:1497
586
+#: spa/plugins/bluez5/bluez5-device.c:2032
587
 msgid "Bluetooth"
588
 msgstr "Bluetooth"
589
 
590
-#: spa/plugins/bluez5/bluez5-device.c:1498
591
+#: spa/plugins/bluez5/bluez5-device.c:2033
592
 msgid "Bluetooth (HFP)"
593
 msgstr "Bluetooth (HFP)"
594
pipewire-1.0.1.tar.bz2/spa/examples/adapter-control.c -> pipewire-1.2.0.tar.gz/spa/examples/adapter-control.c Changed
10
 
1
@@ -832,7 +832,7 @@
2
    if ((res = spa_node_port_use_buffers(data->source_node,
3
        SPA_DIRECTION_OUTPUT, 0, 0, data->source_buffers, 1)) < 0)
4
        return res;
5
-   printf("allocated and assigned buffer(%ld) to source node %p\n", buffer_size, data->source_node);
6
+   printf("allocated and assigned buffer (%zu) to source node %p\n", buffer_size, data->source_node);
7
    if ((res = spa_node_port_use_buffers(data->sink_node,
8
        SPA_DIRECTION_INPUT, 0, 0, data->source_buffers, 1)) < 0)
9
        return res;
10
pipewire-1.0.1.tar.bz2/spa/examples/local-libcamera.c -> pipewire-1.2.0.tar.gz/spa/examples/local-libcamera.c Changed
25
 
1
@@ -190,11 +190,11 @@
2
        sdata = datas0.data;
3
        if (datas0.type == SPA_DATA_MemFd ||
4
            datas0.type == SPA_DATA_DmaBuf) {
5
-           map = mmap(NULL, datas0.maxsize + datas0.mapoffset, PROT_READ,
6
-                  MAP_PRIVATE, datas0.fd, 0);
7
+           map = mmap(NULL, datas0.maxsize, PROT_READ,
8
+                  MAP_PRIVATE, datas0.fd, datas0.mapoffset);
9
            if (map == MAP_FAILED)
10
                return -errno;
11
-           sdata = SPA_PTROFF(map, datas0.mapoffset, uint8_t);
12
+           sdata = map;
13
        } else if (datas0.type == SPA_DATA_MemPtr) {
14
            map = NULL;
15
            sdata = datas0.data;
16
@@ -215,7 +215,7 @@
17
        SDL_RenderPresent(data->renderer);
18
 
19
        if (map)
20
-           munmap(map, datas0.maxsize + datas0.mapoffset);
21
+           munmap(map, datas0.maxsize);
22
    }
23
 
24
    if ((res = spa_node_process(data->source)) < 0)
25
pipewire-1.0.1.tar.bz2/spa/examples/local-v4l2.c -> pipewire-1.2.0.tar.gz/spa/examples/local-v4l2.c Changed
25
 
1
@@ -185,11 +185,11 @@
2
        sdata = datas0.data;
3
        if (datas0.type == SPA_DATA_MemFd ||
4
            datas0.type == SPA_DATA_DmaBuf) {
5
-           map = mmap(NULL, datas0.maxsize + datas0.mapoffset, PROT_READ,
6
-                  MAP_PRIVATE, datas0.fd, 0);
7
+           map = mmap(NULL, datas0.maxsize, PROT_READ,
8
+                  MAP_PRIVATE, datas0.fd, datas0.mapoffset);
9
            if (map == MAP_FAILED)
10
                return -errno;
11
-           sdata = SPA_PTROFF(map, datas0.mapoffset, uint8_t);
12
+           sdata = map;
13
        } else if (datas0.type == SPA_DATA_MemPtr) {
14
            map = NULL;
15
            sdata = datas0.data;
16
@@ -210,7 +210,7 @@
17
        SDL_RenderPresent(data->renderer);
18
 
19
        if (map)
20
-           munmap(map, datas0.maxsize + datas0.mapoffset);
21
+           munmap(map, datas0.maxsize);
22
    }
23
 
24
    if ((res = spa_node_process(data->source)) < 0)
25
pipewire-1.0.1.tar.bz2/spa/examples/meson.build -> pipewire-1.2.0.tar.gz/spa/examples/meson.build Changed
10
 
1
@@ -6,7 +6,7 @@
2
   'local-v4l2',
3
 
4
 
5
-if not get_option('examples').allowed()
6
+if not get_option('examples').allowed() or not get_option('spa-plugins').allowed()
7
   subdir_done()
8
 endif
9
 
10
pipewire-1.2.0.tar.gz/spa/include-private Added
2
 
1
+(directory)
2
pipewire-1.2.0.tar.gz/spa/include-private/spa-private Added
2
 
1
+(directory)
2
pipewire-1.2.0.tar.gz/spa/include-private/spa-private/dbus-helpers.h Added
74
 
1
@@ -0,0 +1,72 @@
2
+/* Spa DBus helpers */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 PipeWire authors */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_PRIVATE_DBUS_HELPERS_H
7
+#define SPA_PRIVATE_DBUS_HELPERS_H
8
+
9
+#include <stdbool.h>
10
+
11
+#include <dbus/dbus.h>
12
+
13
+#include <spa/utils/cleanup.h>
14
+
15
+static inline void cancel_and_unref(DBusPendingCall **pp)
16
+{
17
+   DBusPendingCall *pending_call = spa_steal_ptr(*pp);
18
+
19
+   if (pending_call) {
20
+       dbus_pending_call_cancel(pending_call);
21
+       dbus_pending_call_unref(pending_call);
22
+   }
23
+}
24
+
25
+static inline DBusMessage *steal_reply_and_unref(DBusPendingCall **pp)
26
+{
27
+   DBusPendingCall *pending_call = spa_steal_ptr(*pp);
28
+
29
+   DBusMessage *reply = dbus_pending_call_steal_reply(pending_call);
30
+   dbus_pending_call_unref(pending_call);
31
+
32
+   return reply;
33
+}
34
+
35
+SPA_DEFINE_AUTOPTR_CLEANUP(DBusMessage, DBusMessage, {
36
+   spa_clear_ptr(*thing, dbus_message_unref);
37
+})
38
+
39
+static inline bool reply_with_error(DBusConnection *conn,
40
+                   DBusMessage *reply_to,
41
+                   const char *error_name, const char *error_message)
42
+{
43
+   spa_autoptr(DBusMessage) reply = dbus_message_new_error(reply_to, error_name, error_message);
44
+
45
+   return reply && dbus_connection_send(conn, reply, NULL);
46
+}
47
+
48
+static inline DBusPendingCall *send_with_reply(DBusConnection *conn,
49
+                          DBusMessage *m,
50
+                          DBusPendingCallNotifyFunction callback, void *user_data)
51
+{
52
+   DBusPendingCall *pending_call;
53
+
54
+   if (!dbus_connection_send_with_reply(conn, m, &pending_call, DBUS_TIMEOUT_USE_DEFAULT))
55
+       return NULL;
56
+
57
+   if (!pending_call)
58
+       return NULL;
59
+
60
+   if (!dbus_pending_call_set_notify(pending_call, callback, user_data, NULL)) {
61
+       dbus_pending_call_cancel(pending_call);
62
+       dbus_pending_call_unref(pending_call);
63
+       return NULL;
64
+   }
65
+
66
+   return pending_call;
67
+}
68
+
69
+SPA_DEFINE_AUTO_CLEANUP(DBusError, DBusError, {
70
+   dbus_error_free(thing);
71
+})
72
+
73
+#endif /* SPA_PRIVATE_DBUS_HELPERS_H */
74
pipewire-1.0.1.tar.bz2/spa/include/spa/buffer/buffer.h -> pipewire-1.2.0.tar.gz/spa/include/spa/buffer/buffer.h Changed
34
 
1
@@ -27,9 +27,15 @@
2
    SPA_DATA_Invalid,
3
    SPA_DATA_MemPtr,        /**< pointer to memory, the data field in
4
                      *  struct spa_data is set. */
5
-   SPA_DATA_MemFd,         /**< generic fd, mmap to get to memory */
6
-   SPA_DATA_DmaBuf,        /**< fd to dmabuf memory */
7
-   SPA_DATA_MemId,         /**< memory is identified with an id */
8
+   SPA_DATA_MemFd,         /**< memfd, mmap to get to memory. */
9
+   SPA_DATA_DmaBuf,        /**< fd to dmabuf memory. This might not be readily
10
+                     *  mappable (unless the MAPPABLE flag is set) and should
11
+                     *  normally be handled with DMABUF apis. */
12
+   SPA_DATA_MemId,         /**< memory is identified with an id. The actual memory
13
+                     *  can be obtained in some other way and can be identified
14
+                     *  with this id. */
15
+   SPA_DATA_SyncObj,       /**< a syncobj, usually requires a spa_meta_sync_timeline metadata
16
+                     *  with timeline points. */
17
 
18
    _SPA_DATA_LAST,         /**< not part of ABI */
19
 };
20
@@ -65,9 +71,12 @@
21
 #define SPA_DATA_FLAG_WRITABLE (1u<<1) /**< data is writable */
22
 #define SPA_DATA_FLAG_DYNAMIC  (1u<<2) /**< data pointer can be changed */
23
 #define SPA_DATA_FLAG_READWRITE    (SPA_DATA_FLAG_READABLE|SPA_DATA_FLAG_WRITABLE)
24
+#define SPA_DATA_FLAG_MAPPABLE (1u<<3) /**< data is mappable with simple mmap/munmap. Some memory
25
+                     *  types are not simply mappable (DmaBuf) unless explicitly
26
+                     *  specified with this flag. */
27
    uint32_t flags;         /**< data flags */
28
    int64_t fd;         /**< optional fd for data */
29
-   uint32_t mapoffset;     /**< offset to map fd at */
30
+   uint32_t mapoffset;     /**< offset to map fd at, this is page aligned */
31
    uint32_t maxsize;       /**< max size of data */
32
    void *data;         /**< optional data pointer */
33
    struct spa_chunk *chunk;    /**< valid chunk of memory */
34
pipewire-1.0.1.tar.bz2/spa/include/spa/buffer/meta.h -> pipewire-1.2.0.tar.gz/spa/include/spa/buffer/meta.h Changed
40
 
1
@@ -28,6 +28,7 @@
2
                      *  associated with the data */
3
    SPA_META_Busy,          /**< don't write to buffer when count > 0 */
4
    SPA_META_VideoTransform,    /**< struct spa_meta_transform */
5
+   SPA_META_SyncTimeline,      /**< struct spa_meta_sync_timeline */
6
 
7
    _SPA_META_LAST,         /**< not part of ABI/API */
8
 };
9
@@ -149,7 +150,7 @@
10
    SPA_META_TRANSFORMATION_270,        /**< 270 degree counter-clockwise */
11
    SPA_META_TRANSFORMATION_Flipped,    /**< 180 degree flipped around the vertical axis. Equivalent
12
                          * to a reflexion through the vertical line splitting the
13
-                         * bufffer in two equal sized parts */
14
+                         * buffer in two equal sized parts */
15
    SPA_META_TRANSFORMATION_Flipped90,  /**< flip then rotate around 90 degree counter-clockwise */
16
    SPA_META_TRANSFORMATION_Flipped180, /**< flip then rotate around 180 degree counter-clockwise */
17
    SPA_META_TRANSFORMATION_Flipped270, /**< flip then rotate around 270 degree counter-clockwise */
18
@@ -162,6 +163,21 @@
19
 };
20
 
21
 /**
22
+ * A timeline point for explicit sync
23
+ *
24
+ * Metadata to describe the time on the timeline when the buffer
25
+ * can be acquired and when it can be reused.
26
+ */
27
+struct spa_meta_sync_timeline {
28
+   uint32_t flags;
29
+   uint32_t padding;
30
+   uint64_t acquire_point;         /**< the timeline acquire point, this is when the data
31
+                         *  can be accessed. */
32
+   uint64_t release_point;         /**< the timeline release point, this timeline point should
33
+                         *  be signaled when the data is no longer accessed. */
34
+};
35
+
36
+/**
37
  * \}
38
  */
39
 
40
pipewire-1.0.1.tar.bz2/spa/include/spa/buffer/type-info.h -> pipewire-1.2.0.tar.gz/spa/include/spa/buffer/type-info.h Changed
40
 
1
@@ -35,6 +35,7 @@
2
    { SPA_DATA_MemFd, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "MemFd", NULL },
3
    { SPA_DATA_DmaBuf, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", NULL },
4
    { SPA_DATA_MemId, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemId", NULL },
5
+   { SPA_DATA_SyncObj, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "SyncObj", NULL },
6
    { 0, 0, NULL, NULL },
7
 };
8
 
9
@@ -50,6 +51,22 @@
10
 #define SPA_TYPE_INFO_META_ARRAY_Region        SPA_TYPE_INFO_META_ARRAY_BASE "Region"
11
 #define SPA_TYPE_INFO_META_ARRAY_REGION_BASE   SPA_TYPE_INFO_META_ARRAY_Region ":"
12
 
13
+/* VideoTransform meta */
14
+#define SPA_TYPE_INFO_META_Transformation  SPA_TYPE_INFO_ENUM_BASE "Meta:Transformation"
15
+#define SPA_TYPE_INFO_META_TRANSFORMATION_BASE SPA_TYPE_INFO_META_Transformation ":"
16
+
17
+static const struct spa_type_info spa_type_meta_videotransform_type = {
18
+   { SPA_META_TRANSFORMATION_None, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "None", NULL },
19
+   { SPA_META_TRANSFORMATION_90, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "90", NULL },
20
+   { SPA_META_TRANSFORMATION_180, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "180", NULL },
21
+   { SPA_META_TRANSFORMATION_270, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "270", NULL },
22
+   { SPA_META_TRANSFORMATION_Flipped, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped", NULL },
23
+   { SPA_META_TRANSFORMATION_Flipped90, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped90", NULL },
24
+   { SPA_META_TRANSFORMATION_Flipped180, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped180", NULL },
25
+   { SPA_META_TRANSFORMATION_Flipped270, SPA_TYPE_Int, SPA_TYPE_INFO_META_TRANSFORMATION_BASE "Flipped270", NULL },
26
+   { 0, 0, NULL, NULL },
27
+};
28
+
29
 static const struct spa_type_info spa_type_meta_type = {
30
    { SPA_META_Invalid, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Invalid", NULL },
31
    { SPA_META_Header, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Header", NULL },
32
@@ -60,6 +77,7 @@
33
    { SPA_META_Control, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Control", NULL },
34
    { SPA_META_Busy, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Busy", NULL },
35
    { SPA_META_VideoTransform, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "VideoTransform", NULL },
36
+   { SPA_META_SyncTimeline, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "SyncTimeline", NULL },
37
    { 0, 0, NULL, NULL },
38
 };
39
 
40
pipewire-1.0.1.tar.bz2/spa/include/spa/debug/context.h -> pipewire-1.2.0.tar.gz/spa/include/spa/debug/context.h Changed
35
 
1
@@ -11,6 +11,7 @@
2
 
3
 #include <stdio.h>
4
 #include <stdarg.h>
5
+#include <ctype.h>
6
 
7
 #include <spa/utils/defs.h>
8
 /**
9
@@ -31,6 +32,25 @@
10
 
11
 #define spa_debugc(_c,_fmt,...)    (_c)?((_c)->log((_c),_fmt, ## __VA_ARGS__)):(void)spa_debug(_fmt, ## __VA_ARGS__)
12
 
13
+static inline void spa_debugc_error_location(struct spa_debug_context *c,
14
+       struct spa_error_location *loc)
15
+{
16
+   int i, skip = loc->col > 80 ? loc->col - 40 : 0, lc = loc->col-skip-1;
17
+   char buf80;
18
+
19
+   for (i = 0; (size_t)i < sizeof(buf)-1 && (size_t)(i + skip) < loc->len; i++) {
20
+       char ch = loc->locationi + skip;
21
+       if (ch == '\n' || ch == '\0')
22
+           break;
23
+       bufi = isspace(ch) ? ' ' : ch;
24
+   }
25
+   bufi = '\0';
26
+   spa_debugc(c, "line:%6d | %s%s", loc->line, skip ? "..." : "", buf);
27
+   for (i = 0; bufi; i++)
28
+       bufi = i < lc ? '-' : i == lc ? '^' : ' ';
29
+   spa_debugc(c, "column:%4d |-%s%s", loc->col, skip ? "---" : "", buf);
30
+}
31
+
32
 /**
33
  * \}
34
  */
35
pipewire-1.2.0.tar.gz/spa/include/spa/debug/file.h Added
64
 
1
@@ -0,0 +1,62 @@
2
+/* Simple Plugin API */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_DEBUG_FILE_H
7
+#define SPA_DEBUG_FILE_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#include <stdio.h>
14
+#include <stdarg.h>
15
+#include <ctype.h>
16
+
17
+#include <spa/utils/defs.h>
18
+#include <spa/support/log.h>
19
+#include <spa/debug/context.h>
20
+#include <spa/debug/dict.h>
21
+#include <spa/debug/format.h>
22
+#include <spa/debug/mem.h>
23
+#include <spa/debug/pod.h>
24
+
25
+/**
26
+ * \addtogroup spa_debug
27
+ * \{
28
+ */
29
+
30
+struct spa_debug_file_ctx {
31
+   struct spa_debug_context ctx;
32
+   FILE *f;
33
+};
34
+
35
+SPA_PRINTF_FUNC(2,3)
36
+static inline void spa_debug_file_log(struct spa_debug_context *ctx, const char *fmt, ...)
37
+{
38
+   struct spa_debug_file_ctx *c = SPA_CONTAINER_OF(ctx, struct spa_debug_file_ctx, ctx);
39
+   va_list args;
40
+   va_start(args, fmt);
41
+   vfprintf(c->f, fmt, args); fputc('\n', c->f);
42
+   va_end(args);
43
+}
44
+
45
+#define SPA_DEBUG_FILE_INIT(_f)                            \
46
+   (struct spa_debug_file_ctx){ { spa_debug_file_log }, _f, }
47
+
48
+#define spa_debug_file_error_location(f,loc,fmt,...)               \
49
+({                                     \
50
+   struct spa_debug_file_ctx c = SPA_DEBUG_FILE_INIT(f);           \
51
+   if (fmt) spa_debugc(&c.ctx, fmt, __VA_ARGS__);              \
52
+   spa_debugc_error_location(&c.ctx, loc);                 \
53
+})
54
+
55
+/**
56
+ * \}
57
+ */
58
+
59
+#ifdef __cplusplus
60
+}  /* extern "C" */
61
+#endif
62
+
63
+#endif /* SPA_DEBUG_FILE_H */
64
pipewire-1.0.1.tar.bz2/spa/include/spa/debug/log.h -> pipewire-1.2.0.tar.gz/spa/include/spa/debug/log.h Changed
17
 
1
@@ -83,6 +83,15 @@
2
        spa_debugc_dict(&c.ctx, indent, dict);              \
3
 })
4
 
5
+#define spa_debug_log_error_location(l,lev,loc,fmt,...)                    \
6
+({                                         \
7
+   struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev);             \
8
+   if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) {   \
9
+       if (fmt) spa_debugc(&c.ctx, fmt, __VA_ARGS__);              \
10
+       spa_debugc_error_location(&c.ctx, loc);                 \
11
+   }                                       \
12
+})
13
+
14
 /**
15
  * \}
16
  */
17
pipewire-1.0.1.tar.bz2/spa/include/spa/graph/graph.h -> pipewire-1.2.0.tar.gz/spa/include/spa/graph/graph.h Changed
19
 
1
@@ -223,7 +223,7 @@
2
 }
3
 
4
 static const struct spa_graph_node_callbacks spa_graph_node_sub_impl_default = {
5
-   SPA_VERSION_GRAPH_NODE_CALLBACKS,
6
+   .version = SPA_VERSION_GRAPH_NODE_CALLBACKS,
7
    .process = spa_graph_node_impl_sub_process,
8
 };
9
 
10
@@ -330,7 +330,7 @@
11
 }
12
 
13
 static const struct spa_graph_node_callbacks spa_graph_node_impl_default = {
14
-   SPA_VERSION_GRAPH_NODE_CALLBACKS,
15
+   .version = SPA_VERSION_GRAPH_NODE_CALLBACKS,
16
    .process = spa_graph_node_impl_process,
17
    .reuse_buffer = spa_graph_node_impl_reuse_buffer,
18
 };
19
pipewire-1.0.1.tar.bz2/spa/include/spa/node/io.h -> pipewire-1.2.0.tar.gz/spa/include/spa/node/io.h Changed
129
 
1
@@ -31,14 +31,16 @@
2
 enum spa_io_type {
3
    SPA_IO_Invalid,
4
    SPA_IO_Buffers,     /**< area to exchange buffers, struct spa_io_buffers */
5
-   SPA_IO_Range,       /**< expected byte range, struct spa_io_range */
6
+   SPA_IO_Range,       /**< expected byte range, struct spa_io_range (currently not used in PipeWire) */
7
    SPA_IO_Clock,       /**< area to update clock information, struct spa_io_clock */
8
-   SPA_IO_Latency,     /**< latency reporting, struct spa_io_latency */
9
+   SPA_IO_Latency,     /**< latency reporting, struct spa_io_latency (currently not used in
10
+                 * PipeWire). \see spa_param_latency */
11
    SPA_IO_Control,     /**< area for control messages, struct spa_io_sequence */
12
    SPA_IO_Notify,      /**< area for notify messages, struct spa_io_sequence */
13
    SPA_IO_Position,    /**< position information in the graph, struct spa_io_position */
14
    SPA_IO_RateMatch,   /**< rate matching between nodes, struct spa_io_rate_match */
15
-   SPA_IO_Memory,      /**< memory pointer, struct spa_io_memory */
16
+   SPA_IO_Memory,      /**< memory pointer, struct spa_io_memory (currently not used in PipeWire) */
17
+   SPA_IO_AsyncBuffers,    /**< async area to exchange buffers, struct spa_io_async_buffers */
18
 };
19
 
20
 /**
21
@@ -108,29 +110,47 @@
22
  *
23
  * The clock counts the elapsed time according to the clock provider
24
  * since the provider was last started.
25
+ *
26
+ * Driver nodes are supposed to update the contents of \ref SPA_IO_Clock before
27
+ * signaling the start of a graph cycle.  These updated clock values become
28
+ * visible to other nodes in \ref SPA_IO_Position. Non-driver nodes do
29
+ * not need to update the contents of their \ref SPA_IO_Clock.
30
+ *
31
+ * The host generally gives each node a separate \ref spa_io_clock in \ref
32
+ * SPA_IO_Clock, so that updates made by the driver are not visible in the
33
+ * contents of \ref SPA_IO_Clock of other nodes. Instead, \ref SPA_IO_Position
34
+ * is used to look up the current graph time.
35
+ *
36
+ * A node is a driver when \ref spa_io_clock.id in \ref SPA_IO_Clock and
37
+ * \ref spa_io_position.clock.id in \ref SPA_IO_Position are the same.
38
  */
39
 struct spa_io_clock {
40
 #define SPA_IO_CLOCK_FLAG_FREEWHEEL (1u<<0)
41
-   uint32_t flags;         /**< clock flags */
42
-   uint32_t id;            /**< unique clock id, set by application */
43
-   char name64;            /**< clock name prefixed with API, set by node. The clock name
44
-                     *  is unique per clock and can be used to check if nodes
45
-                     *  share the same clock. */
46
-   uint64_t nsec;          /**< time in nanoseconds against monotonic clock */
47
-   struct spa_fraction rate;   /**< rate for position/duration/delay/xrun */
48
-   uint64_t position;      /**< current position */
49
-   uint64_t duration;      /**< duration of current cycle */
50
-   int64_t delay;          /**< delay between position and hardware,
51
-                     *  positive for capture, negative for playback */
52
-   double rate_diff;       /**< rate difference between clock and monotonic time */
53
-   uint64_t next_nsec;     /**< estimated next wakeup time in nanoseconds */
54
+   uint32_t flags;         /**< Clock flags */
55
+   uint32_t id;            /**< Unique clock id, set by host application */
56
+   char name64;            /**< Clock name prefixed with API, set by node when it receives
57
+                     *  \ref SPA_IO_Clock. The clock name is unique per clock and
58
+                     *  can be used to check if nodes share the same clock. */
59
+   uint64_t nsec;          /**< Time in nanoseconds against monotonic clock
60
+                     * (CLOCK_MONOTONIC). This fields reflects a real time instant
61
+                     * in the past. The value may have jitter. */
62
+   struct spa_fraction rate;   /**< Rate for position/duration/delay/xrun */
63
+   uint64_t position;      /**< Current position, in samples @ \ref rate */
64
+   uint64_t duration;      /**< Duration of current cycle, in samples @ \ref rate */
65
+   int64_t delay;          /**< Delay between position and hardware, in samples @ \ref rate */
66
+   double rate_diff;       /**< Rate difference between clock and monotonic time, as a ratio of
67
+                     *  clock speeds. */
68
+   uint64_t next_nsec;     /**< Estimated next wakeup time in nanoseconds.
69
+                     *  This time is a logical start time of the next cycle, and
70
+                     *  is not necessarily in the future.
71
+                     */
72
 
73
-   struct spa_fraction target_rate;    /**< target rate of next cycle */
74
-   uint64_t target_duration;       /**< target duration of next cycle */
75
-   uint32_t target_seq;            /**< seq counter. must be equal at start and
76
+   struct spa_fraction target_rate;    /**< Target rate of next cycle */
77
+   uint64_t target_duration;       /**< Target duration of next cycle */
78
+   uint32_t target_seq;            /**< Seq counter. must be equal at start and
79
                          *  end of read and lower bit must be 0 */
80
-   uint32_t padding;
81
-   uint64_t xrun;          /**< estimated accumulated xrun duration */
82
+   uint32_t cycle;         /**< incremented each time the graph is started */
83
+   uint64_t xrun;          /**< Estimated accumulated xrun duration */
84
 };
85
 
86
 /* the size of the video in this cycle */
87
@@ -145,7 +165,11 @@
88
    uint32_t padding4;
89
 };
90
 
91
-/** latency reporting */
92
+/**
93
+ * Latency reporting
94
+ *
95
+ * Currently not used in PipeWire. Instead, \see spa_param_latency
96
+ */
97
 struct spa_io_latency {
98
    struct spa_fraction rate;   /**< rate for min/max */
99
    uint64_t min;           /**< min latency */
100
@@ -245,8 +269,13 @@
101
 /**
102
  * The position information adds extra meaning to the raw clock times.
103
  *
104
- * It is set on all nodes and the clock id will contain the clock of the
105
- * driving node in the graph.
106
+ * It is set on all nodes in \ref SPA_IO_Position, and the contents of \ref
107
+ * spa_io_position.clock contain the clock updates made by the driving node in
108
+ * the graph in its \ref SPA_IO_Clock.  Also, \ref spa_io_position.clock.id
109
+ * will contain the clock id of the driving node in the graph.
110
+ *
111
+ * The position clock indicates the logical start time of the current graph
112
+ * cycle.
113
  *
114
  * The position information contains 1 or more segments that convert the
115
  * raw clock times to a stream time. They are sorted based on their
116
@@ -279,6 +308,12 @@
117
    uint32_t padding7;
118
 };
119
 
120
+/** async buffers */
121
+struct spa_io_async_buffers {
122
+   struct spa_io_buffers buffers2; /**< async buffers, writers write to current (cycle+1)&1,
123
+                         *  readers read from (cycle)&1 */
124
+};
125
+
126
 /**
127
  * \}
128
  */
129
pipewire-1.0.1.tar.bz2/spa/include/spa/node/keys.h -> pipewire-1.2.0.tar.gz/spa/include/spa/node/keys.h Changed
9
 
1
@@ -34,6 +34,7 @@
2
 #define SPA_KEY_PORT_ALIAS     "port.alias"        /**< a port alias */
3
 #define SPA_KEY_PORT_MONITOR       "port.monitor"      /**< this port is a monitor port */
4
 #define SPA_KEY_PORT_IGNORE_LATENCY    "port.ignore-latency"   /**< latency ignored by peers */
5
+#define SPA_KEY_PORT_GROUP     "port.group"        /**< the port group this port belongs to */
6
 
7
 
8
 /**
9
pipewire-1.0.1.tar.bz2/spa/include/spa/node/node.h -> pipewire-1.2.0.tar.gz/spa/include/spa/node/node.h Changed
20
 
1
@@ -450,6 +450,9 @@
2
     * Enumerate all possible parameters of \a id on \a port_id of \a node
3
     * that are compatible with \a filter.
4
     *
5
+    * When SPA_ID_INVALID is given as the port_id, the node will reply with
6
+    * the params that would be returned for a new port in the given direction.
7
+    *
8
     * The result parameters can be queried and modified and ultimately be used
9
     * to call port_set_param.
10
     *
11
@@ -464,7 +467,7 @@
12
     * \param seq a sequence number to pass to the result event when
13
     *  this method is executed synchronously.
14
     * \param direction an spa_direction
15
-    * \param port_id the port to query
16
+    * \param port_id the port to query or SPA_ID_INVALID
17
     * \param id the parameter id to query
18
     * \param start the first index to query, 0 to get the first item
19
     * \param max the maximum number of params to query
20
pipewire-1.0.1.tar.bz2/spa/include/spa/node/type-info.h -> pipewire-1.2.0.tar.gz/spa/include/spa/node/type-info.h Changed
9
 
1
@@ -34,6 +34,7 @@
2
    { SPA_IO_Position, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Position", NULL },
3
    { SPA_IO_RateMatch, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "RateMatch", NULL },
4
    { SPA_IO_Memory, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Memory", NULL },
5
+   { SPA_IO_AsyncBuffers, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "AsyncBuffers", NULL },
6
    { 0, 0, NULL, NULL },
7
 };
8
 
9
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/aac-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/aac-types.h Changed
13
 
1
@@ -12,6 +12,11 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/param/audio/aac.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 #define SPA_TYPE_INFO_AudioAACStreamFormat     SPA_TYPE_INFO_ENUM_BASE "AudioAACStreamFormat"
11
 #define SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE SPA_TYPE_INFO_AudioAACStreamFormat ":"
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/aac-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/aac-utils.h Changed
11
 
1
@@ -34,7 +34,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_aac_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_aac *info)
6
+spa_format_audio_aac_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_aac *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/aac.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/aac.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 enum spa_audio_aac_stream_format {
11
    SPA_AUDIO_AAC_STREAM_FORMAT_UNKNOWN,
12
    /* Raw AAC frames */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/alac-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/alac-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_alac_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_alac *info)
6
+spa_format_audio_alac_build(struct spa_pod_builder *builder, uint32_t id,
7
+               const struct spa_audio_info_alac *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/alac.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/alac.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_alac {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/amr-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/amr-types.h Changed
13
 
1
@@ -12,6 +12,11 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/param/audio/amr.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 #define SPA_TYPE_INFO_AudioAMRBandMode     SPA_TYPE_INFO_ENUM_BASE "AudioAMRBandMode"
11
 #define SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE SPA_TYPE_INFO_AudioAMRBandMode ":"
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/amr-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/amr-utils.h Changed
11
 
1
@@ -32,7 +32,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_amr_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_amr *info)
6
+spa_format_audio_amr_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_amr *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/amr.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/amr.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 enum spa_audio_amr_band_mode {
11
    SPA_AUDIO_AMR_BAND_MODE_UNKNOWN,
12
    SPA_AUDIO_AMR_BAND_MODE_NB,
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/ape-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/ape-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_ape_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_ape *info)
6
+spa_format_audio_ape_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_ape *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/ape.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/ape.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_ape {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/dsd-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/dsd-utils.h Changed
11
 
1
@@ -40,7 +40,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_dsd_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_dsd *info)
6
+spa_format_audio_dsd_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_dsd *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/dsp-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/dsp-utils.h Changed
11
 
1
@@ -30,7 +30,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_dsp_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_dsp *info)
6
+spa_format_audio_dsp_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_dsp *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/dsp.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/dsp.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_dsp {
11
    enum spa_audio_format format;       /*< format, one of the DSP formats in enum spa_audio_format */
12
 };
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/flac-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/flac-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_flac_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_flac *info)
6
+spa_format_audio_flac_build(struct spa_pod_builder *builder, uint32_t id,
7
+               const struct spa_audio_info_flac *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/flac.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/flac.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_flac {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/format-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/format-utils.h Changed
11
 
1
@@ -77,7 +77,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info *info)
6
+spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info *info)
8
 {
9
    switch (info->media_subtype) {
10
    case SPA_MEDIA_SUBTYPE_raw:
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/iec958-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/iec958-types.h Changed
13
 
1
@@ -12,6 +12,11 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/param/audio/iec958.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 #define SPA_TYPE_INFO_AudioIEC958Codec     SPA_TYPE_INFO_ENUM_BASE "AudioIEC958Codec"
11
 #define SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE  SPA_TYPE_INFO_AudioIEC958Codec ":"
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/iec958-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/iec958-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_iec958_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_iec958 *info)
6
+spa_format_audio_iec958_build(struct spa_pod_builder *builder, uint32_t id,
7
+                 const struct spa_audio_info_iec958 *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/mp3-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/mp3-types.h Changed
13
 
1
@@ -12,6 +12,11 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/param/audio/mp3.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 #define SPA_TYPE_INFO_AudioMP3ChannelMode      SPA_TYPE_INFO_ENUM_BASE "AudioMP3ChannelMode"
11
 #define SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE  SPA_TYPE_INFO_AudioMP3ChannelMode ":"
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/mp3-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/mp3-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_mp3_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_mp3 *info)
6
+spa_format_audio_mp3_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_mp3 *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/mp3.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/mp3.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 enum spa_audio_mp3_channel_mode {
11
    SPA_AUDIO_MP3_CHANNEL_MODE_UNKNOWN,
12
    SPA_AUDIO_MP3_CHANNEL_MODE_MONO,
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/opus.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/opus.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_opus {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/ra-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/ra-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_ra_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_ra *info)
6
+spa_format_audio_ra_build(struct spa_pod_builder *builder, uint32_t id,
7
+             const struct spa_audio_info_ra *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/ra.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/ra.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_ra {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/raw-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/raw-utils.h Changed
11
 
1
@@ -39,7 +39,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_raw *info)
6
+spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_raw *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/vorbis-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/vorbis-utils.h Changed
11
 
1
@@ -31,7 +31,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_vorbis_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_vorbis *info)
6
+spa_format_audio_vorbis_build(struct spa_pod_builder *builder, uint32_t id,
7
+                 const struct spa_audio_info_vorbis *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/vorbis.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/vorbis.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 struct spa_audio_info_vorbis {
11
    uint32_t rate;              /*< sample rate */
12
    uint32_t channels;          /*< number of channels */
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/wma-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/wma-types.h Changed
13
 
1
@@ -12,6 +12,11 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/param/audio/wma.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 #define SPA_TYPE_INFO_AudioWMAProfile      SPA_TYPE_INFO_ENUM_BASE "AudioWMAProfile"
11
 #define SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE   SPA_TYPE_INFO_AudioWMAProfile ":"
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/wma-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/wma-utils.h Changed
11
 
1
@@ -34,7 +34,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_audio_wma_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_wma *info)
6
+spa_format_audio_wma_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_audio_info_wma *info)
8
 {
9
    struct spa_pod_frame f;
10
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/audio/wma.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/audio/wma.h Changed
13
 
1
@@ -11,6 +11,11 @@
2
 
3
 #include <spa/param/audio/raw.h>
4
 
5
+/**
6
+ * \addtogroup spa_param
7
+ * \{
8
+ */
9
+
10
 enum spa_audio_wma_profile {
11
    SPA_AUDIO_WMA_PROFILE_UNKNOWN,
12
 
13
pipewire-1.0.1.tar.bz2/spa/include/spa/param/bluetooth/audio.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/bluetooth/audio.h Changed
22
 
1
@@ -21,6 +21,7 @@
2
    SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ,
3
    SPA_BLUETOOTH_AUDIO_CODEC_MPEG,
4
    SPA_BLUETOOTH_AUDIO_CODEC_AAC,
5
+   SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD,
6
    SPA_BLUETOOTH_AUDIO_CODEC_APTX,
7
    SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD,
8
    SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
9
@@ -34,10 +35,12 @@
10
    SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71,
11
    SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX,
12
    SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO,
13
+   SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
14
 
15
    /* HFP */
16
    SPA_BLUETOOTH_AUDIO_CODEC_CVSD = 0x100,
17
    SPA_BLUETOOTH_AUDIO_CODEC_MSBC,
18
+   SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB,
19
 
20
    /* BAP */
21
    SPA_BLUETOOTH_AUDIO_CODEC_LC3 = 0x200,
22
pipewire-1.0.1.tar.bz2/spa/include/spa/param/bluetooth/type-info.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/bluetooth/type-info.h Changed
21
 
1
@@ -25,6 +25,7 @@
2
    { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc_xq", NULL },
3
    { SPA_BLUETOOTH_AUDIO_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "mpeg", NULL },
4
    { SPA_BLUETOOTH_AUDIO_CODEC_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac", NULL },
5
+   { SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac_eld", NULL },
6
    { SPA_BLUETOOTH_AUDIO_CODEC_APTX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx", NULL },
7
    { SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_hd", NULL },
8
    { SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "ldac", NULL },
9
@@ -38,9 +39,11 @@
10
    { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_71", NULL },
11
    { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_duplex", NULL },
12
    { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_pro", NULL },
13
+   { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_g", NULL },
14
 
15
    { SPA_BLUETOOTH_AUDIO_CODEC_CVSD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "cvsd", NULL },
16
    { SPA_BLUETOOTH_AUDIO_CODEC_MSBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "msbc", NULL },
17
+   { SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3_swb", NULL },
18
 
19
    { SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3", NULL },
20
 
21
pipewire-1.0.1.tar.bz2/spa/include/spa/param/buffers-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/buffers-types.h Changed
9
 
1
@@ -56,6 +56,7 @@
2
    { SPA_PARAM_BUFFERS_stride,   SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "stride", NULL },
3
    { SPA_PARAM_BUFFERS_align,    SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "align", NULL },
4
    { SPA_PARAM_BUFFERS_dataType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "dataType", NULL },
5
+   { SPA_PARAM_BUFFERS_metaType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "metaType", NULL },
6
    { 0, 0, NULL, NULL },
7
 };
8
 
9
pipewire-1.0.1.tar.bz2/spa/include/spa/param/buffers.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/buffers.h Changed
20
 
1
@@ -24,14 +24,15 @@
2
    SPA_PARAM_BUFFERS_size,     /**< size of a data block memory (Int)*/
3
    SPA_PARAM_BUFFERS_stride,   /**< stride of data block memory (Int) */
4
    SPA_PARAM_BUFFERS_align,    /**< alignment of data block memory (Int) */
5
-   SPA_PARAM_BUFFERS_dataType, /**< possible memory types (Int, mask of enum spa_data_type) */
6
+   SPA_PARAM_BUFFERS_dataType, /**< possible memory types (flags choice Int, mask of enum spa_data_type) */
7
+   SPA_PARAM_BUFFERS_metaType, /**< required meta data types (Int, mask of enum spa_meta_type) */
8
 };
9
 
10
 /** properties for SPA_TYPE_OBJECT_ParamMeta */
11
 enum spa_param_meta {
12
    SPA_PARAM_META_START,
13
-   SPA_PARAM_META_type,    /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */
14
-   SPA_PARAM_META_size,    /**< the expected maximum size the meta (Int) */
15
+   SPA_PARAM_META_type,        /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */
16
+   SPA_PARAM_META_size,        /**< the expected maximum size the meta (Int) */
17
 };
18
 
19
 /** properties for SPA_TYPE_OBJECT_ParamIO */
20
pipewire-1.0.1.tar.bz2/spa/include/spa/param/latency.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/latency.h Changed
55
 
1
@@ -16,14 +16,31 @@
2
 
3
 #include <spa/param/param.h>
4
 
5
-/** properties for SPA_TYPE_OBJECT_ParamLatency */
6
+/**
7
+ * Properties for SPA_TYPE_OBJECT_ParamLatency
8
+ *
9
+ * The latency indicates:
10
+ *
11
+ * - for playback: time delay between start of a graph cycle, and the rendering of
12
+ *   the first sample of that cycle in audio output.
13
+ *
14
+ * - for capture: time delay between start of a graph cycle, and the first sample
15
+ *   of that cycle having occurred in audio input.
16
+ *
17
+ * For physical output/input, the latency is intended to correspond to the
18
+ * rendering/capture of physical audio, including hardware internal rendering delay.
19
+ *
20
+ * The latency values are adjusted by \ref SPA_PROP_latencyOffsetNsec or
21
+ * SPA_PARAM_ProcessLatency, if present. (e.g. for ALSA this is used to adjust for
22
+ * the internal hardware latency).
23
+ */
24
 enum spa_param_latency {
25
    SPA_PARAM_LATENCY_START,
26
    SPA_PARAM_LATENCY_direction,        /**< direction, input/output (Id enum spa_direction) */
27
    SPA_PARAM_LATENCY_minQuantum,       /**< min latency relative to quantum (Float) */
28
    SPA_PARAM_LATENCY_maxQuantum,       /**< max latency relative to quantum (Float) */
29
-   SPA_PARAM_LATENCY_minRate,      /**< min latency (Int) relative to rate */
30
-   SPA_PARAM_LATENCY_maxRate,      /**< max latency (Int) relative to rate */
31
+   SPA_PARAM_LATENCY_minRate,      /**< min latency (Int) relative to graph rate */
32
+   SPA_PARAM_LATENCY_maxRate,      /**< max latency (Int) relative to graph rate */
33
    SPA_PARAM_LATENCY_minNs,        /**< min latency (Long) in nanoseconds */
34
    SPA_PARAM_LATENCY_maxNs,        /**< max latency (Long) in nanoseconds */
35
 };
36
@@ -41,11 +58,16 @@
37
 
38
 #define SPA_LATENCY_INFO(dir,...) ((struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ })
39
 
40
-/** properties for SPA_TYPE_OBJECT_ParamProcessLatency */
41
+/**
42
+ * Properties for SPA_TYPE_OBJECT_ParamProcessLatency
43
+ *
44
+ * The processing latency indicates logical time delay between a sample in an input port,
45
+ * and a corresponding sample in an output port, relative to the graph time.
46
+ */
47
 enum spa_param_process_latency {
48
    SPA_PARAM_PROCESS_LATENCY_START,
49
    SPA_PARAM_PROCESS_LATENCY_quantum,  /**< latency relative to quantum (Float) */
50
-   SPA_PARAM_PROCESS_LATENCY_rate,     /**< latency (Int) relative to rate */
51
+   SPA_PARAM_PROCESS_LATENCY_rate,     /**< latency (Int) relative to graph rate */
52
    SPA_PARAM_PROCESS_LATENCY_ns,       /**< latency (Long) in nanoseconds */
53
 };
54
 
55
pipewire-1.0.1.tar.bz2/spa/include/spa/param/profiler.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/profiler.h Changed
33
 
1
@@ -39,7 +39,8 @@
2
                              *      Long : clock duration,
3
                              *      Long : clock delay,
4
                              *      Double : clock rate_diff,
5
-                             *      Long : clock next_nsec)) */
6
+                             *      Long : clock next_nsec,
7
+                             *      Int : transport_state)) */
8
    SPA_PROFILER_driverBlock,           /**< generic driver info block
9
                              *  (Struct(
10
                              *      Int : driver_id,
11
@@ -48,8 +49,9 @@
12
                              *      Long : driver signal,
13
                              *      Long : driver awake,
14
                              *      Long : driver finish,
15
-                             *      Int : driver status),
16
-                             *      Fraction : latency))  */
17
+                             *      Int : driver status,
18
+                             *      Fraction : latency,
19
+                             *      Int : xrun_count))  */
20
 
21
    SPA_PROFILER_START_Follower = 0x20000,  /**< follower related profiler properties */
22
    SPA_PROFILER_followerBlock,         /**< generic follower info block
23
@@ -61,7 +63,8 @@
24
                              *      Long : awake,
25
                              *      Long : finish,
26
                              *      Int : status,
27
-                             *      Fraction : latency))  */
28
+                             *      Fraction : latency,
29
+                             *      Int : xrun_count))  */
30
 
31
    SPA_PROFILER_START_CUSTOM   = 0x1000000,
32
 };
33
pipewire-1.0.1.tar.bz2/spa/include/spa/param/video/dsp-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/video/dsp-utils.h Changed
10
 
1
@@ -38,7 +38,7 @@
2
 
3
 static inline struct spa_pod *
4
 spa_format_video_dsp_build(struct spa_pod_builder *builder, uint32_t id,
5
-              struct spa_video_info_dsp *info)
6
+              const struct spa_video_info_dsp *info)
7
 {
8
    struct spa_pod_frame f;
9
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
10
pipewire-1.0.1.tar.bz2/spa/include/spa/param/video/format-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/video/format-utils.h Changed
11
 
1
@@ -41,7 +41,8 @@
2
 }
3
 
4
 static inline struct spa_pod *
5
-spa_format_video_build(struct spa_pod_builder *builder, uint32_t id, struct spa_video_info *info)
6
+spa_format_video_build(struct spa_pod_builder *builder, uint32_t id,
7
+              const struct spa_video_info *info)
8
 {
9
    switch (info->media_subtype) {
10
    case SPA_MEDIA_SUBTYPE_raw:
11
pipewire-1.0.1.tar.bz2/spa/include/spa/param/video/h264-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/video/h264-utils.h Changed
10
 
1
@@ -33,7 +33,7 @@
2
 
3
 static inline struct spa_pod *
4
 spa_format_video_h264_build(struct spa_pod_builder *builder, uint32_t id,
5
-              struct spa_video_info_h264 *info)
6
+               const struct spa_video_info_h264 *info)
7
 {
8
    struct spa_pod_frame f;
9
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
10
pipewire-1.0.1.tar.bz2/spa/include/spa/param/video/mjpg-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/video/mjpg-utils.h Changed
10
 
1
@@ -31,7 +31,7 @@
2
 
3
 static inline struct spa_pod *
4
 spa_format_video_mjpg_build(struct spa_pod_builder *builder, uint32_t id,
5
-              struct spa_video_info_mjpg *info)
6
+               const struct spa_video_info_mjpg *info)
7
 {
8
    struct spa_pod_frame f;
9
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
10
pipewire-1.0.1.tar.bz2/spa/include/spa/param/video/raw-utils.h -> pipewire-1.2.0.tar.gz/spa/include/spa/param/video/raw-utils.h Changed
10
 
1
@@ -51,7 +51,7 @@
2
 
3
 static inline struct spa_pod *
4
 spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id,
5
-              struct spa_video_info_raw *info)
6
+              const struct spa_video_info_raw *info)
7
 {
8
    struct spa_pod_frame f;
9
    spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
10
pipewire-1.0.1.tar.bz2/spa/include/spa/pod/builder.h -> pipewire-1.2.0.tar.gz/spa/include/spa/pod/builder.h Changed
10
 
1
@@ -481,7 +481,7 @@
2
        spa_pod_builder_long(builder, va_arg(args, int64_t));       \
3
        break;                              \
4
    case 'f':                               \
5
-       spa_pod_builder_float(builder, va_arg(args, double));       \
6
+       spa_pod_builder_float(builder, (float)va_arg(args, double));    \
7
        break;                              \
8
    case 'd':                               \
9
        spa_pod_builder_double(builder, va_arg(args, double));      \
10
pipewire-1.0.1.tar.bz2/spa/include/spa/pod/compare.h -> pipewire-1.2.0.tar.gz/spa/include/spa/pod/compare.h Changed
44
 
1
@@ -31,16 +31,17 @@
2
    case SPA_TYPE_None:
3
        return 0;
4
    case SPA_TYPE_Bool:
5
+       return SPA_CMP(!!*(int32_t *)r1, !!*(int32_t *)r2);
6
    case SPA_TYPE_Id:
7
-       return *(uint32_t *) r1 == *(uint32_t *) r2 ? 0 : 1;
8
+       return SPA_CMP(*(uint32_t *)r1, *(uint32_t *)r2);
9
    case SPA_TYPE_Int:
10
-       return *(int32_t *) r1 - *(int32_t *) r2;
11
+       return SPA_CMP(*(int32_t *)r1, *(int32_t *)r2);
12
    case SPA_TYPE_Long:
13
-       return *(int64_t *) r1 - *(int64_t *) r2;
14
+       return SPA_CMP(*(int64_t *)r1, *(int64_t *)r2);
15
    case SPA_TYPE_Float:
16
-       return *(float *) r1 - *(float *) r2;
17
+       return SPA_CMP(*(float *)r1, *(float *)r2);
18
    case SPA_TYPE_Double:
19
-       return *(double *) r1 - *(double *) r2;
20
+       return SPA_CMP(*(double *)r1, *(double *)r2);
21
    case SPA_TYPE_String:
22
        return strcmp((char *)r1, (char *)r2);
23
    case SPA_TYPE_Bytes:
24
@@ -60,15 +61,10 @@
25
    {
26
        const struct spa_fraction *f1 = (struct spa_fraction *) r1,
27
            *f2 = (struct spa_fraction *) r2;
28
-       int64_t n1, n2;
29
-       n1 = ((int64_t) f1->num) * f2->denom;
30
-       n2 = ((int64_t) f2->num) * f1->denom;
31
-       if (n1 < n2)
32
-           return -1;
33
-       else if (n1 > n2)
34
-           return 1;
35
-       else
36
-           return 0;
37
+       uint64_t n1, n2;
38
+       n1 = ((uint64_t) f1->num) * f2->denom;
39
+       n2 = ((uint64_t) f2->num) * f1->denom;
40
+       return SPA_CMP(n1, n2);
41
    }
42
    default:
43
        break;
44
pipewire-1.0.1.tar.bz2/spa/include/spa/pod/dynamic.h -> pipewire-1.2.0.tar.gz/spa/include/spa/pod/dynamic.h Changed
10
 
1
@@ -41,7 +41,7 @@
2
        void *data, uint32_t size, uint32_t extend)
3
 {
4
    static const struct spa_pod_builder_callbacks spa_pod_dynamic_builder_callbacks = {
5
-       SPA_VERSION_POD_BUILDER_CALLBACKS,
6
+       .version = SPA_VERSION_POD_BUILDER_CALLBACKS,
7
        .overflow = spa_pod_dynamic_builder_overflow
8
    };
9
    builder->b = SPA_POD_BUILDER_INIT(data, size);
10
pipewire-1.0.1.tar.bz2/spa/include/spa/pod/iter.h -> pipewire-1.2.0.tar.gz/spa/include/spa/pod/iter.h Changed
73
 
1
@@ -28,8 +28,10 @@
2
 
3
 static inline bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter)
4
 {
5
-   return SPA_POD_BODY(iter) <= SPA_PTROFF(pod, size, void) &&
6
-       SPA_PTROFF(iter, SPA_POD_SIZE(iter), void) <= SPA_PTROFF(pod, size, void);
7
+   size_t remaining;
8
+
9
+   return spa_ptr_type_inside(pod, size, iter, struct spa_pod, &remaining) &&
10
+          remaining >= SPA_POD_BODY_SIZE(iter);
11
 }
12
 
13
 static inline void *spa_pod_next(const void *iter)
14
@@ -45,8 +47,10 @@
15
 static inline bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body,
16
        uint32_t size, const struct spa_pod_prop *iter)
17
 {
18
-   return SPA_POD_CONTENTS(struct spa_pod_prop, iter) <= SPA_PTROFF(body, size, void) &&
19
-       SPA_PTROFF(iter, SPA_POD_PROP_SIZE(iter), void) <= SPA_PTROFF(body, size, void);
20
+   size_t remaining;
21
+
22
+   return spa_ptr_type_inside(body, size, iter, struct spa_pod_prop, &remaining) &&
23
+          remaining >= iter->value.size;
24
 }
25
 
26
 static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter)
27
@@ -62,8 +66,10 @@
28
 static inline bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body,
29
        uint32_t size, const struct spa_pod_control *iter)
30
 {
31
-   return SPA_POD_CONTENTS(struct spa_pod_control, iter) <= SPA_PTROFF(body, size, void) &&
32
-       SPA_PTROFF(iter, SPA_POD_CONTROL_SIZE(iter), void) <= SPA_PTROFF(body, size, void);
33
+   size_t remaining;
34
+
35
+   return spa_ptr_type_inside(body, size, iter, struct spa_pod_control, &remaining) &&
36
+          remaining >= iter->value.size;
37
 }
38
 
39
 static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter)
40
@@ -73,7 +79,7 @@
41
 
42
 #define SPA_POD_ARRAY_BODY_FOREACH(body, _size, iter)                          \
43
    for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_array_body), void);    \
44
-        (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void);              \
45
+        (body)->child.size > 0 && spa_ptrinside(body, _size, iter, (body)->child.size, NULL);  \
46
         (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void))
47
 
48
 #define SPA_POD_ARRAY_FOREACH(obj, iter)                           \
49
@@ -81,7 +87,7 @@
50
 
51
 #define SPA_POD_CHOICE_BODY_FOREACH(body, _size, iter)                         \
52
    for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_choice_body), void);   \
53
-        (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void);              \
54
+        (body)->child.size > 0 && spa_ptrinside(body, _size, iter, (body)->child.size, NULL);  \
55
         (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void))
56
 
57
 #define SPA_POD_CHOICE_FOREACH(obj, iter)                          \
58
@@ -437,6 +443,14 @@
59
    return 1;
60
 }
61
 
62
+static inline int spa_pod_object_has_props(const struct spa_pod_object *pod)
63
+{
64
+   struct spa_pod_prop *res;
65
+   SPA_POD_OBJECT_FOREACH(pod, res)
66
+       return 1;
67
+   return 0;
68
+}
69
+
70
 static inline int spa_pod_is_fixated(const struct spa_pod *pod)
71
 {
72
    if (!spa_pod_is_object(pod))
73
pipewire-1.0.1.tar.bz2/spa/include/spa/support/cpu.h -> pipewire-1.2.0.tar.gz/spa/include/spa/support/cpu.h Changed
48
 
1
@@ -87,6 +87,46 @@
2
 #define SPA_CPU_VM_ACRN            (1 << 13)
3
 #define SPA_CPU_VM_POWERVM     (1 << 14)
4
 
5
+static inline const char *spa_cpu_vm_type_to_string(uint32_t vm_type)
6
+{
7
+   switch(vm_type) {
8
+   case SPA_CPU_VM_NONE:
9
+       return NULL;
10
+   case SPA_CPU_VM_KVM:
11
+       return "kvm";
12
+   case SPA_CPU_VM_QEMU:
13
+       return "qemu";
14
+   case SPA_CPU_VM_BOCHS:
15
+       return "bochs";
16
+   case SPA_CPU_VM_XEN:
17
+       return "xen";
18
+   case SPA_CPU_VM_UML:
19
+       return "uml";
20
+   case SPA_CPU_VM_VMWARE:
21
+       return "vmware";
22
+   case SPA_CPU_VM_ORACLE:
23
+       return "oracle";
24
+   case SPA_CPU_VM_MICROSOFT:
25
+       return "microsoft";
26
+   case SPA_CPU_VM_ZVM:
27
+       return "zvm";
28
+   case SPA_CPU_VM_PARALLELS:
29
+       return "parallels";
30
+   case SPA_CPU_VM_BHYVE:
31
+       return "bhyve";
32
+   case SPA_CPU_VM_QNX:
33
+       return "qnx";
34
+   case SPA_CPU_VM_ACRN:
35
+       return "acrn";
36
+   case SPA_CPU_VM_POWERVM:
37
+       return "powervm";
38
+   case SPA_CPU_VM_OTHER:
39
+       return "other";
40
+   default:
41
+       return "unknown";
42
+   }
43
+}
44
+
45
 /**
46
  * methods
47
  */
48
pipewire-1.0.1.tar.bz2/spa/include/spa/support/log-impl.h -> pipewire-1.2.0.tar.gz/spa/include/spa/support/log-impl.h Changed
9
 
1
@@ -108,6 +108,7 @@
2
        spa_log_impl_logv,              \
3
        spa_log_impl_logt,              \
4
        spa_log_impl_logtv,             \
5
+       spa_log_impl_topic_init,            \
6
      } }
7
 
8
 #define SPA_LOG_IMPL(name)         \
9
pipewire-1.0.1.tar.bz2/spa/include/spa/support/log.h -> pipewire-1.2.0.tar.gz/spa/include/spa/support/log.h Changed
101
 
1
@@ -84,6 +84,21 @@
2
    bool has_custom_level;
3
 };
4
 
5
+/**
6
+ * Enumeration of log topics in a plugin
7
+ *
8
+ * \since 1.1.0
9
+ */
10
+struct spa_log_topic_enum {
11
+#define SPA_VERSION_LOG_TOPIC_ENUM 0
12
+   uint32_t version;
13
+   /** Array of pointers to log topics */
14
+   struct spa_log_topic * const * const topics;
15
+   /** End of topics array */
16
+   struct spa_log_topic * const * const topics_end;
17
+};
18
+
19
+
20
 struct spa_log_methods {
21
 #define SPA_VERSION_LOG_METHODS        1
22
    uint32_t version;
23
@@ -185,6 +200,10 @@
24
    /**
25
     * Initializes a \ref spa_log_topic to the correct logging level.
26
     *
27
+    * \deprecated
28
+    * Plugin host should obtain log topics from \ref SPA_LOG_TOPIC_ENUM_NAME
29
+    * and update them itself.
30
+    *
31
     * \since 1
32
     */
33
    void (*topic_init) (void *object, struct spa_log_topic *topic);
34
@@ -282,6 +301,66 @@
35
 #endif
36
 
37
 
38
+/**
39
+ * Name of the symbol indicating a \ref spa_log_topic_enum enumerating
40
+ * the static log topics in a plugin,
41
+ *
42
+ * \since 1.1.0
43
+ */
44
+#define SPA_LOG_TOPIC_ENUM_NAME "spa_log_topic_enum"
45
+
46
+/**
47
+ * Define the symbol for \ref SPA_LOG_TOPIC_ENUM_NAME
48
+ *
49
+ * \since 1.1.0
50
+ */
51
+#define SPA_LOG_TOPIC_ENUM_DEFINE(s, e) \
52
+   SPA_EXPORT struct spa_log_topic_enum spa_log_topic_enum = (struct spa_log_topic_enum) { \
53
+       .version = SPA_VERSION_LOG_TOPIC_ENUM, \
54
+       .topics = (s), \
55
+       .topics_end = (e), \
56
+   }
57
+
58
+/**
59
+ * Magically register a statically defined \ref spa_log_topic into
60
+ * the log topic enumeration for a plugin.
61
+ *
62
+ * \since 1.1.0
63
+ */
64
+#define SPA_LOG_TOPIC_REGISTER(v)            \
65
+   __attribute__((used)) __attribute__((retain)) \
66
+   __attribute__((section("spa_log_topic"))) __attribute__((aligned(__alignof__(struct spa_log_topic *)))) \
67
+   static struct spa_log_topic * const spa_log_topic_export_##v = &v
68
+
69
+/**
70
+ * Define and magically register a \ref spa_log_topic
71
+ *
72
+ * \since 1.1.0
73
+ */
74
+#define SPA_LOG_TOPIC_DEFINE(var,name) \
75
+   struct spa_log_topic var = SPA_LOG_TOPIC(SPA_VERSION_LOG_TOPIC, name); \
76
+   SPA_LOG_TOPIC_REGISTER(var)
77
+
78
+/**
79
+ * Define and magically register a \ref spa_log_topic with static scope
80
+ *
81
+ * \since 1.1.0
82
+ */
83
+#define SPA_LOG_TOPIC_DEFINE_STATIC(var,name) \
84
+   static struct spa_log_topic var = SPA_LOG_TOPIC(SPA_VERSION_LOG_TOPIC, name); \
85
+   SPA_LOG_TOPIC_REGISTER(var)
86
+
87
+/**
88
+ * Do \ref SPA_LOG_TOPIC_ENUM_DEFINE for the auto-registered
89
+ * \ref spa_log_topic in the plugin.
90
+ *
91
+ * \since 1.1.0
92
+ */
93
+#define SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED \
94
+   extern struct spa_log_topic * const __start_spa_log_topic; \
95
+   extern struct spa_log_topic * const __stop_spa_log_topic; \
96
+   SPA_LOG_TOPIC_ENUM_DEFINE(__start_spa_log_topic, __stop_spa_log_topic)
97
+
98
 /** \fn spa_log_error */
99
 
100
 /** keys can be given when initializing the logger handle */
101
pipewire-1.0.1.tar.bz2/spa/include/spa/support/loop.h -> pipewire-1.2.0.tar.gz/spa/include/spa/support/loop.h Changed
62
 
1
@@ -66,19 +66,56 @@
2
 #define SPA_VERSION_LOOP_METHODS   0
3
    uint32_t version;
4
 
5
-   /** add a source to the loop */
6
+   /** Add a source to the loop. Must be called from the loop's own thread.
7
+    *
8
+    * \paramin object The callbacks data.
9
+    * \paramin source The source.
10
+    * \return 0 on success, negative errno-style value on failure.
11
+    */
12
    int (*add_source) (void *object,
13
               struct spa_source *source);
14
 
15
-   /** update the source io mask */
16
+   /** Update the source io mask. Must be called from the loop's own thread.
17
+    *
18
+    * \paramin object The callbacks data.
19
+    * \paramin source The source.
20
+    * \return 0 on success, negative errno-style value on failure.
21
+    */
22
    int (*update_source) (void *object,
23
            struct spa_source *source);
24
 
25
-   /** remove a source from the loop */
26
+   /** Remove a source from the loop. Must be called from the loop's own thread.
27
+    *
28
+    * \paramin object The callbacks data.
29
+    * \paramin source The source.
30
+    * \return 0 on success, negative errno-style value on failure.
31
+    */
32
    int (*remove_source) (void *object,
33
            struct spa_source *source);
34
 
35
-   /** invoke a function in the context of this loop */
36
+   /** Invoke a function in the context of this loop.
37
+    * May be called from any thread and multiple threads at the same time.
38
+    * If called from the loop's thread, all callbacks previously queued with
39
+    * invoke() will be run synchronously, which might cause unexpected
40
+    * reentrancy problems.
41
+    *
42
+    * \paramin object The callbacks data.
43
+    * \param func The function to be invoked.
44
+    * \param seq An opaque sequence number. This will be made
45
+    *            available to func.
46
+    * \paramin data Data that will be copied into the internal ring buffer and made
47
+    *             available to func. Because this data is copied, it is okay to
48
+    *             pass a pointer to a local variable, but do not pass a pointer to
49
+    *             an object that has identity.
50
+    * \param size The size of data to copy.
51
+    * \param block If \true, do not return until func has been called. Otherwise,
52
+    *              returns immediately. Passing \true does not risk a deadlock because
53
+    *              the data thread is never allowed to wait on any other thread.
54
+    * \param user_data An opaque pointer passed to func.
55
+    * \return `-EPIPE` if the internal ring buffer filled up,
56
+    *         if block is \false, 0 if seq was SPA_ID_INVALID or
57
+    *         seq with the ASYNC flag set
58
+    *         or the return value of func otherwise. */
59
    int (*invoke) (void *object,
60
               spa_invoke_func_t func,
61
               uint32_t seq,
62
pipewire-1.0.1.tar.bz2/spa/include/spa/support/thread.h -> pipewire-1.2.0.tar.gz/spa/include/spa/support/thread.h Changed
9
 
1
@@ -117,6 +117,7 @@
2
 
3
 #define SPA_KEY_THREAD_NAME        "thread.name"       /* the thread name */
4
 #define SPA_KEY_THREAD_STACK_SIZE  "thread.stack-size" /* the stack size of the thread */
5
+#define SPA_KEY_THREAD_AFFINITY        "thread.affinity"   /* array of CPUs for this thread */
6
 
7
 /**
8
  * \}
9
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/cleanup.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/cleanup.h Changed
16
 
1
@@ -7,10 +7,10 @@
2
 
3
 #define spa_exchange(var, new_value) \
4
 __extension__ ({ \
5
-   __typeof__(var) *_ptr = &(var); \
6
-   __typeof__(var) _old_value = *_ptr; \
7
-   *_ptr = (new_value); \
8
-   _old_value; \
9
+   __typeof__(var) *_ptr_ = &(var); \
10
+   __typeof__(var) _old_value_ = *_ptr_; \
11
+   *_ptr_ = (new_value); \
12
+   _old_value_; \
13
 })
14
 
15
 /* ========================================================================== */
16
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/defs.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/defs.h Changed
140
 
1
@@ -9,20 +9,31 @@
2
 extern "C" {
3
 # if __cplusplus >= 201103L
4
 #  define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg)
5
+#  define SPA_ALIGNOF alignof
6
 # endif
7
+#elif __STDC_VERSION__ >= 202311L
8
+#  define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg)
9
+#  define SPA_ALIGNOF alignof
10
 #else
11
 # include <stdbool.h>
12
 # if __STDC_VERSION__ >= 201112L
13
 #  define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) _Static_assert(expr, msg)
14
+#  define SPA_ALIGNOF _Alignof
15
 # endif
16
 #endif
17
 #ifndef SPA_STATIC_ASSERT_IMPL
18
 #define SPA_STATIC_ASSERT_IMPL(expr, ...) \
19
    ((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(expr) - 1; }))
20
 #endif
21
+#ifndef SPA_ALIGNOF
22
+#define SPA_ALIGNOF __alignof__
23
+#endif
24
 
25
 #define SPA_STATIC_ASSERT(expr, ...) SPA_STATIC_ASSERT_IMPL(expr, ## __VA_ARGS__, "`" #expr "` evaluated to false")
26
 
27
+#define SPA_CONCAT_NOEXPAND(a, b) a ## b
28
+#define SPA_CONCAT(a, b) SPA_CONCAT_NOEXPAND(a, b)
29
+
30
 #include <inttypes.h>
31
 #include <signal.h>
32
 #include <stdlib.h>
33
@@ -156,6 +167,10 @@
34
 ({                         \
35
    fminf(fmaxf(v, low), high);         \
36
 })
37
+#define SPA_CLAMPD(v,low,high)             \
38
+({                         \
39
+   fmin(fmax(v, low), high);           \
40
+})
41
 
42
 
43
 #define SPA_SWAP(a,b)                  \
44
@@ -171,6 +186,16 @@
45
    x;              \
46
 })
47
 
48
+/** 3-way comparison. NaN > NaN and NaN > finite numbers */
49
+#define SPA_CMP(a, b)                  \
50
+({                         \
51
+   __typeof__(a) _a = (a);             \
52
+   __typeof__(b) _b = (b);             \
53
+   (_a > _b) ? 1 : (_a == _b) ? 0 : (_a < _b) ? -1 \
54
+       : (_a == _a) ? -1 : (_b == _b) ? 1  \
55
+       : 1;                    \
56
+})
57
+
58
 /**
59
  * Return the address (buffer + offset) as pointer of \a type
60
  */
61
@@ -178,7 +203,6 @@
62
 #define SPA_PTROFF_ALIGN(ptr_,offset_,alignment_,type_) \
63
    SPA_PTR_ALIGN(SPA_PTROFF(ptr_,offset_,type_),alignment_,type_)
64
 
65
-
66
 /**
67
  * Deprecated, use SPA_PTROFF and SPA_PTROFF_ALIGN instead
68
  */
69
@@ -189,9 +213,6 @@
70
 
71
 #define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_t)(p2))
72
 
73
-#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
74
-#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
75
-
76
 #define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
77
 #define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
78
 
79
@@ -265,7 +286,7 @@
80
 })
81
 
82
 
83
-#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1))
84
+#define SPA_PTR_ALIGNMENT(p,align) ((uintptr_t)(p) & ((align)-1))
85
 #define SPA_IS_ALIGNED(p,align)        (SPA_PTR_ALIGNMENT(p,align) == 0)
86
 #define SPA_PTR_ALIGN(p,align,type)    ((type*)SPA_ROUND_UP_N((intptr_t)(p), (intptr_t)(align)))
87
 
88
@@ -279,9 +300,51 @@
89
 #endif
90
 #endif
91
 
92
+static inline bool spa_ptrinside(const void *p1, size_t s1, const void *p2, size_t s2,
93
+                                 size_t *remaining)
94
+{
95
+   if (SPA_LIKELY((uintptr_t)p1 <= (uintptr_t)p2 && s2 <= s1 &&
96
+                  (uintptr_t)p2 - (uintptr_t)p1 <= s1 - s2)) {
97
+       if (remaining != NULL)
98
+           *remaining = ((uintptr_t)p1 + s1) - ((uintptr_t)p2 + s2);
99
+       return true;
100
+   } else {
101
+       if (remaining != NULL)
102
+           *remaining = 0;
103
+       return false;
104
+   }
105
+}
106
+
107
+static inline bool spa_ptr_inside_and_aligned(const void *p1, size_t s1,
108
+                                              const void *p2, size_t s2, size_t align,
109
+                                              size_t *remaining)
110
+{
111
+   if (SPA_IS_ALIGNED(p2, align)) {
112
+       return spa_ptrinside(p1, s1, p2, s2, remaining);
113
+   } else {
114
+       if (remaining != NULL)
115
+           *remaining = 0;
116
+       return false;
117
+   }
118
+}
119
+
120
+#define spa_ptr_type_inside(p1, s1, p2, type, remaining) \
121
+   spa_ptr_inside_and_aligned(p1, s1, p2, sizeof(type), SPA_ALIGNOF(type), remaining)
122
+
123
+#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
124
+#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
125
+
126
 #define SPA_STRINGIFY_1(...)   #__VA_ARGS__
127
 #define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__)
128
 
129
+struct spa_error_location {
130
+   int line;
131
+   int col;
132
+   size_t len;
133
+   const char *location;
134
+   const char *reason;
135
+};
136
+
137
 #define spa_return_if_fail(expr)                   \
138
    do {                                \
139
        if (SPA_UNLIKELY(!(expr))) {                \
140
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/enum-types.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/enum-types.h Changed
23
 
1
@@ -10,6 +10,12 @@
2
 #endif
3
 
4
 #include <spa/utils/type.h>
5
+#include <spa/pod/pod.h>
6
+
7
+/**
8
+ * \addtogroup spa_types
9
+ * \{
10
+ */
11
 
12
 #define SPA_TYPE_INFO_Direction            SPA_TYPE_INFO_ENUM_BASE "Direction"
13
 #define SPA_TYPE_INFO_DIRECTION_BASE       SPA_TYPE_INFO_Direction ":"
14
@@ -20,8 +26,6 @@
15
    { 0, 0, NULL, NULL }
16
 };
17
 
18
-#include <spa/pod/pod.h>
19
-
20
 #define SPA_TYPE_INFO_Choice           SPA_TYPE_INFO_ENUM_BASE "Choice"
21
 #define SPA_TYPE_INFO_CHOICE_BASE      SPA_TYPE_INFO_Choice ":"
22
 
23
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/json-pod.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/json-pod.h Changed
23
 
1
@@ -77,17 +77,17 @@
2
            spa_pod_builder_bool(b, val >= 0.5f);
3
            break;
4
        case SPA_TYPE_Id:
5
-           spa_pod_builder_id(b, val);
6
+           spa_pod_builder_id(b, (uint32_t)val);
7
            break;
8
        case SPA_TYPE_Int:
9
-           spa_pod_builder_int(b, val);
10
+           spa_pod_builder_int(b, (int32_t)val);
11
            break;
12
        case SPA_TYPE_Long:
13
-           spa_pod_builder_long(b, val);
14
+           spa_pod_builder_long(b, (int64_t)val);
15
            break;
16
        case SPA_TYPE_Struct:
17
            if (spa_json_is_int(value, len))
18
-               spa_pod_builder_int(b, val);
19
+               spa_pod_builder_int(b, (int32_t)val);
20
            else
21
                spa_pod_builder_float(b, val);
22
            break;
23
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/json.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/json.h Changed
483
 
1
@@ -34,6 +34,7 @@
2
    const char *cur;
3
    const char *end;
4
    struct spa_json *parent;
5
+#define SPA_JSON_ERROR_FLAG    0x100
6
    uint32_t state;
7
    uint32_t depth;
8
 };
9
@@ -44,78 +45,185 @@
10
 {
11
    *iter =  SPA_JSON_INIT(data, size);
12
 }
13
-#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), 0, 0 })
14
+#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xff0, 0 })
15
 
16
 static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub)
17
 {
18
    *sub = SPA_JSON_ENTER(iter);
19
 }
20
 
21
-#define SPA_JSON_SAVE(iter) ((struct spa_json) { (iter)->cur, (iter)->end, })
22
+#define SPA_JSON_SAVE(iter) ((struct spa_json) { (iter)->cur, (iter)->end, NULL, (iter)->state, 0 })
23
 
24
 /** Get the next token. \a value points to the token and the return value
25
- * is the length. */
26
+ * is the length. Returns -1 on parse error, 0 on end of input. */
27
 static inline int spa_json_next(struct spa_json * iter, const char **value)
28
 {
29
-   int utf8_remain = 0;
30
-   enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT };
31
+   int utf8_remain = 0, err = 0;
32
+   enum {
33
+       __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT,
34
+       __ARRAY_FLAG = 0x10,        /* in array context */
35
+       __PREV_ARRAY_FLAG = 0x20,   /* depth=0 array context flag */
36
+       __KEY_FLAG = 0x40,      /* inside object key */
37
+       __SUB_FLAG = 0x80,      /* not at top-level */
38
+       __FLAGS = 0xff0,
39
+       __ERROR_SYSTEM = SPA_JSON_ERROR_FLAG,
40
+       __ERROR_INVALID_ARRAY_SEPARATOR,
41
+       __ERROR_EXPECTED_OBJECT_KEY,
42
+       __ERROR_EXPECTED_OBJECT_VALUE,
43
+       __ERROR_TOO_DEEP_NESTING,
44
+       __ERROR_EXPECTED_ARRAY_CLOSE,
45
+       __ERROR_EXPECTED_OBJECT_CLOSE,
46
+       __ERROR_MISMATCHED_BRACKET,
47
+       __ERROR_ESCAPE_NOT_ALLOWED,
48
+       __ERROR_CHARACTERS_NOT_ALLOWED,
49
+       __ERROR_INVALID_ESCAPE,
50
+       __ERROR_INVALID_STATE,
51
+       __ERROR_UNFINISHED_STRING,
52
+   };
53
+   uint64_t array_stack8 = {0};        /* array context flags of depths 1...512 */
54
 
55
    *value = iter->cur;
56
+
57
+   if (iter->state & SPA_JSON_ERROR_FLAG)
58
+       return -1;
59
+
60
    for (; iter->cur < iter->end; iter->cur++) {
61
        unsigned char cur = (unsigned char)*iter->cur;
62
+       uint32_t flag;
63
+
64
+#define _SPA_ERROR(reason) { err = __ERROR_ ## reason; goto error; }
65
  again:
66
-       switch (iter->state) {
67
+       flag = iter->state & __FLAGS;
68
+       switch (iter->state & ~__FLAGS) {
69
        case __NONE:
70
-           iter->state = __STRUCT;
71
+           flag &= ~(__KEY_FLAG | __PREV_ARRAY_FLAG);
72
+           iter->state = __STRUCT | flag;
73
            iter->depth = 0;
74
            goto again;
75
        case __STRUCT:
76
            switch (cur) {
77
-           case '\0': case '\t': case ' ': case '\r': case '\n': case ':': case '=': case ',':
78
+           case '\0': case '\t': case ' ': case '\r': case '\n': case ',':
79
+               continue;
80
+           case ':': case '=':
81
+               if (flag & __ARRAY_FLAG)
82
+                   _SPA_ERROR(INVALID_ARRAY_SEPARATOR);
83
+               if (!(flag & __KEY_FLAG))
84
+                   _SPA_ERROR(EXPECTED_OBJECT_KEY);
85
+               iter->state |= __SUB_FLAG;
86
                continue;
87
            case '#':
88
-               iter->state = __COMMENT;
89
+               iter->state = __COMMENT | flag;
90
                continue;
91
            case '"':
92
+               if (flag & __KEY_FLAG)
93
+                   flag |= __SUB_FLAG;
94
+               if (!(flag & __ARRAY_FLAG))
95
+                   SPA_FLAG_UPDATE(flag, __KEY_FLAG, !(flag & __KEY_FLAG));
96
                *value = iter->cur;
97
-               iter->state = __STRING;
98
+               iter->state = __STRING | flag;
99
                continue;
100
            case '': case '{':
101
+               if (!(flag & __ARRAY_FLAG)) {
102
+                   /* At top-level we may be either in object context
103
+                    * or in single-item context, and then we need to
104
+                    * accept array/object here.
105
+                    */
106
+                   if ((iter->state & __SUB_FLAG) && !(flag & __KEY_FLAG))
107
+                       _SPA_ERROR(EXPECTED_OBJECT_KEY);
108
+                   SPA_FLAG_CLEAR(flag, __KEY_FLAG);
109
+               }
110
+               iter->state = __STRUCT | __SUB_FLAG | flag;
111
+               SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG, cur == '');
112
+
113
+               /* We need to remember previous array state across calls
114
+                * for depth=0, so store that in state. Others bits go to
115
+                * temporary stack.
116
+                */
117
+               if (iter->depth == 0) {
118
+                   SPA_FLAG_UPDATE(iter->state, __PREV_ARRAY_FLAG, flag & __ARRAY_FLAG);
119
+               } else if (((iter->depth-1) >> 6) < SPA_N_ELEMENTS(array_stack)) {
120
+                   uint64_t mask = 1ULL << ((iter->depth-1) & 0x3f);
121
+                   SPA_FLAG_UPDATE(array_stack(iter->depth-1) >> 6, mask, flag & __ARRAY_FLAG);
122
+               } else {
123
+                   /* too deep */
124
+                   _SPA_ERROR(TOO_DEEP_NESTING);
125
+               }
126
+
127
                *value = iter->cur;
128
                if (++iter->depth > 1)
129
                    continue;
130
                iter->cur++;
131
                return 1;
132
            case '}': case '':
133
+               if ((flag & __ARRAY_FLAG) && cur != '')
134
+                   _SPA_ERROR(EXPECTED_ARRAY_CLOSE);
135
+               if (!(flag & __ARRAY_FLAG) && cur != '}')
136
+                   _SPA_ERROR(EXPECTED_OBJECT_CLOSE);
137
+               if (flag & __KEY_FLAG) {
138
+                   /* incomplete key-value pair */
139
+                   _SPA_ERROR(EXPECTED_OBJECT_VALUE);
140
+               }
141
+               iter->state = __STRUCT | __SUB_FLAG | flag;
142
                if (iter->depth == 0) {
143
                    if (iter->parent)
144
                        iter->parent->cur = iter->cur;
145
+                   else
146
+                       _SPA_ERROR(MISMATCHED_BRACKET);
147
                    return 0;
148
                }
149
                --iter->depth;
150
+               if (iter->depth == 0) {
151
+                   SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG, flag & __PREV_ARRAY_FLAG);
152
+               } else if (((iter->depth-1) >> 6) < SPA_N_ELEMENTS(array_stack)) {
153
+                   uint64_t mask = 1ULL << ((iter->depth-1) & 0x3f);
154
+                   SPA_FLAG_UPDATE(iter->state, __ARRAY_FLAG,
155
+                           SPA_FLAG_IS_SET(array_stack(iter->depth-1) >> 6, mask));
156
+               } else {
157
+                   /* too deep */
158
+                   _SPA_ERROR(TOO_DEEP_NESTING);
159
+               }
160
                continue;
161
+           case '\\':
162
+               /* disallow bare escape */
163
+               _SPA_ERROR(ESCAPE_NOT_ALLOWED);
164
            default:
165
+               /* allow bare ascii */
166
+               if (!(cur >= 32 && cur <= 126))
167
+                   _SPA_ERROR(CHARACTERS_NOT_ALLOWED);
168
+               if (flag & __KEY_FLAG)
169
+                   flag |= __SUB_FLAG;
170
+               if (!(flag & __ARRAY_FLAG))
171
+                   SPA_FLAG_UPDATE(flag, __KEY_FLAG, !(flag & __KEY_FLAG));
172
                *value = iter->cur;
173
-               iter->state = __BARE;
174
+               iter->state = __BARE | flag;
175
            }
176
            continue;
177
        case __BARE:
178
            switch (cur) {
179
+           case '\0':
180
            case '\t': case ' ': case '\r': case '\n':
181
+           case '"': case '#':
182
            case ':': case ',': case '=': case '': case '}':
183
-               iter->state = __STRUCT;
184
+               iter->state = __STRUCT | flag;
185
                if (iter->depth > 0)
186
                    goto again;
187
                return iter->cur - *value;
188
+           case '\\':
189
+               /* disallow bare escape */
190
+               _SPA_ERROR(ESCAPE_NOT_ALLOWED);
191
+           default:
192
+               /* allow bare ascii */
193
+               if (cur >= 32 && cur <= 126)
194
+                   continue;
195
            }
196
-           continue;
197
+           _SPA_ERROR(CHARACTERS_NOT_ALLOWED);
198
        case __STRING:
199
            switch (cur) {
200
            case '\\':
201
-               iter->state = __ESC;
202
+               iter->state = __ESC | flag;
203
                continue;
204
            case '"':
205
-               iter->state = __STRUCT;
206
+               iter->state = __STRUCT | flag;
207
                if (iter->depth > 0)
208
                    continue;
209
                return ++iter->cur - *value;
210
@@ -127,50 +235,134 @@
211
                SPA_FALLTHROUGH;
212
            case 192 ... 223:
213
                utf8_remain++;
214
-               iter->state = __UTF8;
215
+               iter->state = __UTF8 | flag;
216
                continue;
217
            default:
218
-               if (cur >= 32 && cur <= 126)
219
+               if (cur >= 32 && cur <= 127)
220
                    continue;
221
            }
222
-           return -1;
223
+           _SPA_ERROR(CHARACTERS_NOT_ALLOWED);
224
        case __UTF8:
225
            switch (cur) {
226
            case 128 ... 191:
227
                if (--utf8_remain == 0)
228
-                   iter->state = __STRING;
229
+                   iter->state = __STRING | flag;
230
                continue;
231
            }
232
-           return -1;
233
+           _SPA_ERROR(CHARACTERS_NOT_ALLOWED);
234
        case __ESC:
235
            switch (cur) {
236
            case '"': case '\\': case '/': case 'b': case 'f':
237
            case 'n': case 'r': case 't': case 'u':
238
-               iter->state = __STRING;
239
+               iter->state = __STRING | flag;
240
                continue;
241
            }
242
-           return -1;
243
+           _SPA_ERROR(INVALID_ESCAPE);
244
        case __COMMENT:
245
            switch (cur) {
246
            case '\n': case '\r':
247
-               iter->state = __STRUCT;
248
+               iter->state = __STRUCT | flag;
249
            }
250
+           break;
251
+       default:
252
+           _SPA_ERROR(INVALID_STATE);
253
        }
254
 
255
    }
256
-   if (iter->depth != 0)
257
-       return -1;
258
-   if (iter->state != __STRUCT) {
259
-       iter->state = __STRUCT;
260
+   if (iter->depth != 0 || iter->parent)
261
+       _SPA_ERROR(MISMATCHED_BRACKET);
262
+
263
+   switch (iter->state & ~__FLAGS) {
264
+   case __STRING: case __UTF8: case __ESC:
265
+       /* string/escape not closed */
266
+       _SPA_ERROR(UNFINISHED_STRING);
267
+   case __COMMENT:
268
+       /* trailing comment */
269
+       return 0;
270
+   }
271
+
272
+   if ((iter->state & __SUB_FLAG) && (iter->state & __KEY_FLAG)) {
273
+       /* incomplete key-value pair */
274
+       _SPA_ERROR(EXPECTED_OBJECT_VALUE);
275
+   }
276
+
277
+   if ((iter->state & ~__FLAGS) != __STRUCT) {
278
+       iter->state = __STRUCT | (iter->state & __FLAGS);
279
        return iter->cur - *value;
280
    }
281
    return 0;
282
+#undef _SPA_ERROR
283
+
284
+error:
285
+   iter->state = err;
286
+   while (iter->parent) {
287
+       if (iter->parent->state & SPA_JSON_ERROR_FLAG)
288
+           break;
289
+       iter->parent->state = err;
290
+       iter->parent->cur = iter->cur;
291
+       iter = iter->parent;
292
+   }
293
+   return -1;
294
+}
295
+
296
+/**
297
+ * Return it there was a parse error, and its possible location.
298
+ *
299
+ * \since 1.1.0
300
+ */
301
+static inline bool spa_json_get_error(struct spa_json *iter, const char *start,
302
+       struct spa_error_location *loc)
303
+{
304
+   static const char *reasons = {
305
+       "System error",
306
+       "Invalid array separator",
307
+       "Expected object key",
308
+       "Expected object value",
309
+       "Too deep nesting",
310
+       "Expected array close bracket",
311
+       "Expected object close brace",
312
+       "Mismatched bracket",
313
+       "Escape not allowed",
314
+       "Character not allowed",
315
+       "Invalid escape",
316
+       "Invalid state",
317
+       "Unfinished string",
318
+       "Expected key separator",
319
+   };
320
+
321
+   if (!(iter->state & SPA_JSON_ERROR_FLAG))
322
+       return false;
323
+
324
+   if (loc) {
325
+       int linepos = 1, colpos = 1, code;
326
+       const char *p, *l;
327
+
328
+       for (l = p = start; p && p != iter->cur; ++p) {
329
+           if (*p == '\n') {
330
+               linepos++;
331
+               colpos = 1;
332
+               l = p+1;
333
+           } else {
334
+               colpos++;
335
+           }
336
+       }
337
+       code = SPA_CLAMP(iter->state & 0xff, 0u, SPA_N_ELEMENTS(reasons)-1);
338
+       loc->line = linepos;
339
+       loc->col = colpos;
340
+       loc->location = l;
341
+       loc->len = SPA_PTRDIFF(iter->end, loc->location) / sizeof(char);
342
+       loc->reason = code == 0 ? strerror(errno) : reasonscode;
343
+   }
344
+   return true;
345
 }
346
 
347
 static inline int spa_json_enter_container(struct spa_json *iter, struct spa_json *sub, char type)
348
 {
349
    const char *value;
350
-   if (spa_json_next(iter, &value) <= 0 || *value != type)
351
+   int len;
352
+   if ((len = spa_json_next(iter, &value)) <= 0)
353
+       return len;
354
+   if (*value != type)
355
        return -1;
356
    spa_json_enter(iter, sub);
357
    return 1;
358
@@ -181,12 +373,20 @@
359
    return len > 0 && (*val == '{'  || *val == '');
360
 }
361
 
362
+/**
363
+ * Return length of container at current position, starting at \a value.
364
+ *
365
+ * \return Length of container including {} or , or 0 on error.
366
+ */
367
 static inline int spa_json_container_len(struct spa_json *iter, const char *value, int len SPA_UNUSED)
368
 {
369
    const char *val;
370
    struct spa_json sub;
371
+   int res;
372
    spa_json_enter(iter, &sub);
373
-   while (spa_json_next(&sub, &val) > 0);
374
+   while ((res = spa_json_next(&sub, &val)) > 0);
375
+   if (res < 0)
376
+       return 0;
377
    return sub.cur + 1 - value;
378
 }
379
 
380
@@ -219,11 +419,25 @@
381
 /* float */
382
 static inline int spa_json_parse_float(const char *val, int len, float *result)
383
 {
384
+   char buf96;
385
    char *end;
386
-   if (strspn(val, "+-0123456789.Ee") < (size_t)len)
387
+   int pos;
388
+
389
+   if (len <= 0 || len >= (int)sizeof(buf))
390
        return 0;
391
-   *result = spa_strtof(val, &end);
392
-   return len > 0 && end == val + len;
393
+
394
+   for (pos = 0; pos < len; ++pos) {
395
+       switch (valpos) {
396
+       case '+': case '-': case '0' ... '9': case '.': case 'e': case 'E': break;
397
+       default: return 0;
398
+       }
399
+   }
400
+
401
+   memcpy(buf, val, len);
402
+   buflen = '\0';
403
+
404
+   *result = spa_strtof(buf, &end);
405
+   return len > 0 && end == buf + len;
406
 }
407
 
408
 static inline bool spa_json_is_float(const char *val, int len)
409
@@ -236,7 +450,7 @@
410
    const char *value;
411
    int len;
412
    if ((len = spa_json_next(iter, &value)) <= 0)
413
-       return -1;
414
+       return len;
415
    return spa_json_parse_float(value, len, res);
416
 }
417
 
418
@@ -256,9 +470,17 @@
419
 /* int */
420
 static inline int spa_json_parse_int(const char *val, int len, int *result)
421
 {
422
+   char buf64;
423
    char *end;
424
-   *result = strtol(val, &end, 0);
425
-   return len > 0 && end == val + len;
426
+
427
+   if (len <= 0 || len >= (int)sizeof(buf))
428
+       return 0;
429
+
430
+   memcpy(buf, val, len);
431
+   buflen = '\0';
432
+
433
+   *result = strtol(buf, &end, 0);
434
+   return len > 0 && end == buf + len;
435
 }
436
 static inline bool spa_json_is_int(const char *val, int len)
437
 {
438
@@ -270,7 +492,7 @@
439
    const char *value;
440
    int len;
441
    if ((len = spa_json_next(iter, &value)) <= 0)
442
-       return -1;
443
+       return len;
444
    return spa_json_parse_int(value, len, res);
445
 }
446
 
447
@@ -303,7 +525,7 @@
448
    const char *value;
449
    int len;
450
    if ((len = spa_json_next(iter, &value)) <= 0)
451
-       return -1;
452
+       return len;
453
    return spa_json_parse_bool(value, len, res);
454
 }
455
 
456
@@ -339,7 +561,7 @@
457
        return -1;
458
    if (!spa_json_is_string(val, len)) {
459
        if (result != val)
460
-           strncpy(result, val, len);
461
+           memmove(result, val, len);
462
        result += len;
463
    } else {
464
        for (p = val+1; p < val + len; p++) {
465
@@ -372,7 +594,7 @@
466
                            v < 0xdc00 || v > 0xdfff)
467
                            continue;
468
                        p += 6;
469
-                       cp = 0x010000 | ((cp & 0x3ff) << 10) | (v & 0x3ff);
470
+                       cp = 0x010000 + (((cp & 0x3ff) << 10) | (v & 0x3ff));
471
                    } else if (cp >= 0xdc00 && cp <= 0xdfff)
472
                        continue;
473
 
474
@@ -405,7 +627,7 @@
475
    const char *value;
476
    int len;
477
    if ((len = spa_json_next(iter, &value)) <= 0)
478
-       return -1;
479
+       return len;
480
    return spa_json_parse_stringn(value, len, res, maxlen);
481
 }
482
 
483
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/keys.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/keys.h Changed
10
 
1
@@ -78,6 +78,8 @@
2
                                      *  used in open() */
3
 #define SPA_KEY_API_LIBCAMERA_LOCATION "api.libcamera.location"    /**< location of the camera:
4
                                      * "front", "back" or "external" */
5
+#define SPA_KEY_API_LIBCAMERA_ROTATION "api.libcamera.rotation"    /**< rotation of the camera:
6
+                                     * "0", "90", "180" or "270" */
7
 
8
 /** info from libcamera_capability */
9
 #define SPA_KEY_API_LIBCAMERA_CAP_DRIVER   "api.libcamera.cap.driver"  /**< driver from capbility */
10
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/names.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/names.h Changed
12
 
1
@@ -134,6 +134,10 @@
2
                    "api.vulkan.compute.source" /**< a vulkan compute source. */
3
 #define SPA_NAME_API_VULKAN_COMPUTE_FILTER \
4
                    "api.vulkan.compute.filter" /**< a vulkan compute filter. */
5
+#define SPA_NAME_API_VULKAN_BLIT_FILTER    \
6
+                   "api.vulkan.blit.filter"    /**< a vulkan blit filter. */
7
+#define SPA_NAME_API_VULKAN_BLIT_DSP_FILTER    \
8
+                   "api.vulkan.blit.dsp-filter"    /**< a vulkan blit dsp-filter. */
9
 
10
 /**
11
  * \}
12
pipewire-1.0.1.tar.bz2/spa/include/spa/utils/string.h -> pipewire-1.2.0.tar.gz/spa/include/spa/utils/string.h Changed
10
 
1
@@ -367,6 +367,8 @@
2
    buf->buffer = buffer;
3
    buf->maxsize = maxsize;
4
    buf->pos = 0;
5
+   if (maxsize > 0)
6
+       buf->buffer0 = '\0';
7
 }
8
 
9
 SPA_PRINTF_FUNC(2, 3)
10
pipewire-1.0.1.tar.bz2/spa/meson.build -> pipewire-1.2.0.tar.gz/spa/meson.build Changed
59
 
1
@@ -8,6 +8,7 @@
2
 spa_dep = declare_dependency(
3
   include_directories : 
4
     include_directories('include'),
5
+    include_directories('include-private'),
6
   ,
7
   dependencies : atomic_dep,
8
   version : spaversion,
9
@@ -31,11 +32,14 @@
10
 
11
 subdir('include')
12
 
13
+jack_dep = dependency('jack', version : '>= 1.9.10', required: get_option('jack'))
14
+summary({'JACK2': jack_dep.found()}, bool_yn: true, section: 'Backend')
15
+
16
 if get_option('spa-plugins').allowed()
17
   udevrulesdir = get_option('udevrulesdir')
18
   if udevrulesdir == ''
19
     # absolute path, otherwise meson prepends the prefix
20
-    udevrulesdir = '/lib/udev/rules.d'
21
+    udevrulesdir = '/usr/lib/udev/rules.d'
22
   endif
23
 
24
   # plugin-specific dependencies
25
@@ -84,9 +88,8 @@
26
       mm_dep = dependency('ModemManager', version : '>= 1.10.0', required : get_option('bluez5-backend-native-mm'))
27
       summary({'ModemManager': mm_dep.found()}, bool_yn: true, section: 'Bluetooth backends')
28
     endif
29
+    cdata.set('HAVE_LC3', get_option('bluez5-codec-lc3').allowed() and lc3_dep.found())
30
   endif
31
-  jack_dep = dependency('jack', version : '>= 1.9.10', required: get_option('jack'))
32
-  summary({'JACK2': jack_dep.found()}, bool_yn: true, section: 'Backend')
33
 
34
   have_vulkan = false
35
   vulkan_dep = dependency('vulkan', version : '>= 1.2.170', required: get_option('vulkan'))
36
@@ -96,18 +99,19 @@
37
   endif
38
   summary({'Vulkan': have_vulkan}, bool_yn: true, section: 'Misc dependencies')
39
 
40
-  libcamera_dep = dependency('libcamera', required: get_option('libcamera'))
41
+  libcamera_dep = dependency('libcamera', version: '>= 0.2.0', required: get_option('libcamera'))
42
   summary({'libcamera': libcamera_dep.found()}, bool_yn: true, section: 'Backend')
43
-  cdata.set('HAVE_LIBCAMERA_SYSTEM_DEVICES', libcamera_dep.version().version_compare('>= 0.1.0'))
44
 
45
   compress_offload_option = get_option('compress-offload')
46
   summary({'Compress-Offload': compress_offload_option.allowed()}, bool_yn: true, section: 'Backend')
47
   cdata.set('HAVE_ALSA_COMPRESS_OFFLOAD', compress_offload_option.allowed())
48
 
49
   # common dependencies
50
-  libudev_dep = dependency('libudev', required: alsa_dep.found() or get_option('udev').enabled() or get_option('v4l2').enabled())
51
+  libudev_dep = dependency('libudev', required: get_option('udev').enabled())
52
+  cdata.set('HAVE_LIBUDEV', libudev_dep.found())
53
   summary({'Udev': libudev_dep.found()}, bool_yn: true, section: 'Backend')
54
 
55
+  cdata.set('HAVE_SPA_PLUGINS', '1')
56
   subdir('plugins')
57
 endif
58
 
59
pipewire-1.0.1.tar.bz2/spa/plugins/aec/aec-null.c -> pipewire-1.2.0.tar.gz/spa/plugins/aec/aec-null.c Changed
19
 
1
@@ -18,7 +18,7 @@
2
    uint32_t channels;
3
 };
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.aec.null");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.aec.null");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
@@ -137,6 +137,8 @@
11
    impl_enum_interface_info,
12
 };
13
 
14
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
15
+
16
 SPA_EXPORT
17
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
18
 {
19
pipewire-1.0.1.tar.bz2/spa/plugins/aec/aec-webrtc.cpp -> pipewire-1.2.0.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
48
 
1
@@ -35,7 +35,7 @@
2
    std::unique_ptr<float *> play_buffer, rec_buffer, out_buffer;
3
 };
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.eac.webrtc");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.eac.webrtc");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
@@ -171,6 +171,8 @@
11
 #else
12
    webrtc::AudioProcessing::Config config;
13
    config.echo_canceller.enabled = true;
14
+   config.pipeline.multi_channel_capture = rec_info->channels > 1;
15
+   config.pipeline.multi_channel_render = play_info->channels > 1;
16
    // FIXME: Example code enables both gain controllers, but that seems sus
17
    config.gain_controller1.enabled = gain_control;
18
    config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital;
19
@@ -211,7 +213,7 @@
20
    // drift compensation on all sinks and sources linked to this echo-canceler
21
    apm->echo_cancellation()->enable_drift_compensation(false);
22
    apm->echo_cancellation()->Enable(true);
23
-   // TODO: wire up supression levels to args
24
+   // TODO: wire up suppression levels to args
25
    apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kHighSuppression);
26
    apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
27
    apm->noise_suppression()->Enable(noise_suppression);
28
@@ -292,7 +294,7 @@
29
 }
30
 
31
 static const struct spa_audio_aec_methods impl_aec = {
32
-   SPA_VERSION_AUDIO_AEC_METHODS,
33
+   .version = SPA_VERSION_AUDIO_AEC_METHODS,
34
    .add_listener = NULL,
35
    .init = webrtc_init,
36
    .run = webrtc_run,
37
@@ -391,6 +393,10 @@
38
    impl_enum_interface_info,
39
 };
40
 
41
+extern "C" {
42
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
43
+}
44
+
45
 SPA_EXPORT
46
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
47
 {
48
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/90-pipewire-alsa.rules -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/90-pipewire-alsa.rules Changed
10
 
1
@@ -133,6 +133,8 @@
2
 ATTRS{idVendor}=="2f12", ATTRS{idProduct}=="0109", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
3
 # ID 9886:002c is for the Astro A50 Gen4
4
 ATTRS{idVendor}=="9886", ATTRS{idProduct}=="002c", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
5
+# ID 9886:0038 is for the Astro Mixamp Pro TR
6
+ATTRS{idVendor}=="9886", ATTRS{idProduct}=="0038", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
7
 # ID 9886:0045 is for the Astro A20 Gen2
8
 ATTRS{idVendor}=="9886", ATTRS{idProduct}=="0045", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
9
 # ID 1532:0520 is for the Razer Kraken Tournament Edition
10
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp-tool.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp-tool.c Changed
25
 
1
@@ -388,7 +388,7 @@
2
        return -EINVAL;
3
    }
4
    dev_id = atoi(argv1);
5
-   vol = atof(argv2);
6
+   vol = (float)atof(argv2);
7
 
8
    if (dev_id >= card->n_devices)
9
        return -EINVAL;
10
@@ -418,12 +418,12 @@
11
 
12
 static int cmd_inc_volume(struct data *data, const struct command *cmd, int argc, char *argv)
13
 {
14
-   return adjust_volume(data, cmd, argc, argv, 0.2);
15
+   return adjust_volume(data, cmd, argc, argv, 0.2f);
16
 }
17
 
18
 static int cmd_dec_volume(struct data *data, const struct command *cmd, int argc, char *argv)
19
 {
20
-   return adjust_volume(data, cmd, argc, argv, -0.2);
21
+   return adjust_volume(data, cmd, argc, argv, -0.2f);
22
 }
23
 
24
 static int cmd_get_mute(struct data *data, const struct command *cmd, int argc, char *argv)
25
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/acp.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/acp.c Changed
345
 
1
@@ -7,6 +7,7 @@
2
 #include "alsa-ucm.h"
3
 
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/json.h>
6
 
7
 int _acp_log_level = 1;
8
 acp_log_func _acp_log_func;
9
@@ -385,7 +386,7 @@
10
                            devstr, NULL, &m->sample_spec,
11
                            &m->channel_map, SND_PCM_STREAM_PLAYBACK,
12
                            &try_period_size, &try_buffer_size,
13
-                           0, NULL, NULL, false))) {
14
+                           0, NULL, NULL, NULL, NULL, false))) {
15
                pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
16
                pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
17
                pa_proplist_setf(m->output_proplist, "device.profile.pro", "true");
18
@@ -417,7 +418,7 @@
19
                            devstr, NULL, &m->sample_spec,
20
                            &m->channel_map, SND_PCM_STREAM_CAPTURE,
21
                            &try_period_size, &try_buffer_size,
22
-                           0, NULL, NULL, false))) {
23
+                           0, NULL, NULL, NULL, NULL, false))) {
24
                pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
25
                pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
26
                pa_proplist_setf(m->input_proplist, "device.profile.pro", "true");
27
@@ -449,6 +450,24 @@
28
    return 0;
29
 }
30
 
31
+static bool contains_string(const char *arr, const char *str)
32
+{
33
+   struct spa_json it2;
34
+   char v256;
35
+
36
+   if (arr == NULL || str == NULL)
37
+       return false;
38
+
39
+   spa_json_init(&it0, arr, strlen(arr));
40
+        if (spa_json_enter_array(&it0, &it1) <= 0)
41
+                spa_json_init(&it1, arr, strlen(arr));
42
+
43
+   while (spa_json_get_string(&it1, v, sizeof(v)) > 0) {
44
+       if (spa_streq(v, str))
45
+           return true;
46
+   }
47
+   return false;
48
+}
49
 
50
 static void add_profiles(pa_card *impl)
51
 {
52
@@ -459,6 +478,7 @@
53
    pa_alsa_device *dev;
54
    int n_profiles, n_ports, n_devices;
55
    uint32_t idx;
56
+   const char *arr;
57
 
58
    n_devices = 0;
59
    pa_dynarray_init(&impl->out.devices, device_free);
60
@@ -490,8 +510,8 @@
61
                    pa_dynarray_append(&impl->out.devices, dev);
62
                }
63
                if (impl->use_ucm) {
64
-                   if (m->ucm_context.ucm_devices) {
65
-                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
66
+                   if (m->ucm_context.ucm_device) {
67
+                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
68
                            true, impl->ports, ap, NULL);
69
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
70
                            true, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
71
@@ -514,8 +534,8 @@
72
                }
73
 
74
                if (impl->use_ucm) {
75
-                   if (m->ucm_context.ucm_devices) {
76
-                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
77
+                   if (m->ucm_context.ucm_device) {
78
+                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
79
                            false, impl->ports, ap, NULL);
80
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
81
                            false, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
82
@@ -561,7 +581,10 @@
83
        dev->device.ports = dev->port_array.array.data;
84
        dev->device.n_ports = pa_dynarray_size(&dev->port_array);
85
    }
86
+   arr = pa_proplist_gets(impl->proplist, "api.acp.hidden-ports");
87
    PA_HASHMAP_FOREACH(dp, impl->ports, state) {
88
+       if (contains_string(arr, dp->name))
89
+           dp->port.flags |= ACP_PORT_HIDDEN;
90
        dp->port.devices = dp->devices.array.data;
91
        dp->port.n_devices = pa_dynarray_size(&dp->devices);
92
    }
93
@@ -570,7 +593,10 @@
94
 
95
    n_profiles = 0;
96
    pa_dynarray_init(&impl->out.profiles, NULL);
97
+   arr = pa_proplist_gets(impl->proplist, "api.acp.hidden-profiles");
98
    PA_HASHMAP_FOREACH(cp, impl->profiles, state) {
99
+       if (contains_string(arr, cp->name))
100
+           cp->flags |= ACP_PROFILE_HIDDEN;
101
        cp->index = n_profiles++;
102
        pa_dynarray_append(&impl->out.profiles, cp);
103
    }
104
@@ -649,7 +675,7 @@
105
 static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask)
106
 {
107
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
108
-   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
109
+   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
110
    snd_ctl_elem_value_t *elem_value;
111
    bool plugged_in, any_input_port_available;
112
    void *state;
113
@@ -659,6 +685,8 @@
114
    enum acp_available active_available = ACP_AVAILABLE_UNKNOWN;
115
    size_t size;
116
 
117
+   pa_assert(_elem);
118
+   elem = *_elem;
119
 #if 0
120
    /* Changing the jack state may cause a port change, and a port change will
121
     * make the sink or source change the mixer settings. If there are multiple
122
@@ -927,13 +955,17 @@
123
 static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
124
 {
125
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
126
-   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
127
-   int device = snd_hctl_elem_get_device(elem);
128
+   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
129
+   int device;
130
    const char *old_monitor_name;
131
    pa_device_port *p;
132
    pa_hdmi_eld eld;
133
    bool changed = false;
134
 
135
+   pa_assert(_elem);
136
+   elem = *_elem;
137
+   device = snd_hctl_elem_get_device(elem);
138
+
139
    if (mask == SND_CTL_EVENT_MASK_REMOVE)
140
        return 0;
141
 
142
@@ -1021,6 +1053,9 @@
143
    for (i = 0; i < card->n_profiles; i++) {
144
        struct acp_card_profile *p = profilesi;
145
 
146
+       if (SPA_FLAG_IS_SET(p->flags, ACP_PROFILE_HIDDEN))
147
+           continue;
148
+
149
        if (name) {
150
            if (spa_streq(name, p->name))
151
                best = i;
152
@@ -1108,6 +1143,14 @@
153
    uint32_t i;
154
    int res;
155
 
156
+   if (dev->ucm_context) {
157
+       if (!dev->active_port)
158
+           return 0;
159
+       pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(dev->active_port);
160
+       if (pa_alsa_ucm_port_device_status(data) <= 0)
161
+           return 0;
162
+   }
163
+
164
    if (!dev->mixer_handle)
165
        return 0;
166
 
167
@@ -1143,6 +1186,14 @@
168
    if (v != &dev->real_volume)
169
        dev->real_volume = *v;
170
 
171
+   if (dev->ucm_context) {
172
+       if (!dev->active_port)
173
+           return;
174
+       pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(dev->active_port);
175
+       if (pa_alsa_ucm_port_device_status(data) <= 0)
176
+           return;
177
+   }
178
+
179
    if (!dev->mixer_handle)
180
        return;
181
 
182
@@ -1196,6 +1247,14 @@
183
    bool mute;
184
    int res;
185
 
186
+   if (dev->ucm_context) {
187
+       if (!dev->active_port)
188
+           return 0;
189
+       pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(dev->active_port);
190
+       if (pa_alsa_ucm_port_device_status(data) <= 0)
191
+           return 0;
192
+   }
193
+
194
    if (!dev->mixer_handle)
195
        return 0;
196
 
197
@@ -1218,6 +1277,14 @@
198
 {
199
    dev->muted = mute;
200
 
201
+   if (dev->ucm_context) {
202
+       if (!dev->active_port)
203
+           return;
204
+       pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(dev->active_port);
205
+       if (pa_alsa_ucm_port_device_status(data) <= 0)
206
+           return;
207
+   }
208
+
209
    if (!dev->mixer_handle)
210
        return;
211
 
212
@@ -1268,7 +1335,7 @@
213
        pa_log_info("Using hardware volume control. Hardware dB scale %s.",
214
                dev->mixer_path->has_dB ? "supported" : "not supported");
215
    }
216
-   dev->device.base_volume = pa_sw_volume_to_linear(dev->base_volume);
217
+   dev->device.base_volume = (float)pa_sw_volume_to_linear(dev->base_volume);
218
    dev->device.volume_step = 1.0f / dev->n_volume_steps;
219
 
220
    if (impl->soft_mixer || !dev->mixer_path || !dev->mixer_path->has_mute) {
221
@@ -1295,8 +1362,7 @@
222
    * will be NULL, but the UCM device enable sequence will still need to be
223
    * executed. */
224
    if (dev->active_port && dev->ucm_context) {
225
-       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port,
226
-                   dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0)
227
+       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0)
228
            return res;
229
    }
230
 
231
@@ -1441,9 +1507,11 @@
232
    if (new_index >= card->n_profiles)
233
        return -EINVAL;
234
 
235
-   op = old_index != ACP_INVALID_INDEX ? (pa_alsa_profile*)profilesold_index : NULL;
236
    np = (pa_alsa_profile*)profilesnew_index;
237
+   if (SPA_FLAG_IS_SET(np->profile.flags, ACP_PROFILE_HIDDEN))
238
+       return -EINVAL;
239
 
240
+   op = old_index != ACP_INVALID_INDEX ? (pa_alsa_profile*)profilesold_index : NULL;
241
    if (op == np)
242
        return 0;
243
 
244
@@ -1469,10 +1537,19 @@
245
    }
246
 
247
    /* if UCM is available for this card then update the verb */
248
-   if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) {
249
-       if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl,
250
-           np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name,
251
-           op ? op->profile.name : NULL)) < 0) {
252
+   if (impl->use_ucm) {
253
+       if (np->profile.flags & ACP_PROFILE_OFF) {
254
+           if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, NULL, op)) < 0)
255
+               return res;
256
+       } else if (np->profile.flags & ACP_PROFILE_PRO) {
257
+           const char *verb = find_best_verb(impl);
258
+           if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, NULL, op)) < 0)
259
+               return res;
260
+           if ((res = snd_use_case_set(impl->ucm.ucm_mgr, "_verb", verb)) < 0) {
261
+               pa_log_error("error setting verb: %s", snd_strerror(res));
262
+               return res;
263
+           }
264
+       } else if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, np, op)) < 0) {
265
            return res;
266
        }
267
    }
268
@@ -1481,8 +1558,8 @@
269
        PA_IDXSET_FOREACH(am, np->output_mappings, idx) {
270
            if (impl->use_ucm) {
271
                /* Update ports priorities */
272
-               if (am->ucm_context.ucm_devices) {
273
-                   pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context,
274
+               if (am->ucm_context.ucm_device) {
275
+                   pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context,
276
                        true, impl->ports, np, NULL);
277
                }
278
            }
279
@@ -1494,8 +1571,8 @@
280
        PA_IDXSET_FOREACH(am, np->input_mappings, idx) {
281
            if (impl->use_ucm) {
282
                /* Update ports priorities */
283
-               if (am->ucm_context.ucm_devices) {
284
-                   pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context,
285
+               if (am->ucm_context.ucm_device) {
286
+                   pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context,
287
                        false, impl->ports, np, NULL);
288
                }
289
            }
290
@@ -1548,16 +1625,6 @@
291
     pa_hashmap_free(group_counts);
292
 }
293
 
294
-static const char *acp_dict_lookup(const struct acp_dict *dict, const char *key)
295
-{
296
-   const struct acp_dict_item *it;
297
-   acp_dict_for_each(it, dict) {
298
-       if (spa_streq(key, it->key))
299
-           return it->value;
300
-   }
301
-   return NULL;
302
-}
303
-
304
 struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props)
305
 {
306
    pa_card *impl;
307
@@ -1831,6 +1898,9 @@
308
    for (i = 0; i < dev->n_ports; i++) {
309
        struct acp_port *p = portsi;
310
 
311
+       if (SPA_FLAG_IS_SET(p->flags, ACP_PORT_HIDDEN))
312
+           continue;
313
+
314
        if (name) {
315
            if (spa_streq(name, p->name))
316
                best = i;
317
@@ -1870,6 +1940,8 @@
318
    p = (pa_device_port*)impl->card.portsport_index;
319
    if (!pa_hashmap_get(d->ports, p->name))
320
        return -EINVAL;
321
+   if (SPA_FLAG_IS_SET(p->port.flags, ACP_PORT_HIDDEN))
322
+       return -EINVAL;
323
 
324
    p->port.flags = ACP_PORT_ACTIVE | flags;
325
    if (p == old)
326
@@ -1886,8 +1958,7 @@
327
        mixer_volume_init(impl, d);
328
 
329
        sync_mixer(d, p);
330
-       res = pa_alsa_ucm_set_port(d->ucm_context, p,
331
-                   dev->direction == ACP_DIRECTION_PLAYBACK);
332
+       res = pa_alsa_ucm_set_port(d->ucm_context, p);
333
    } else {
334
        pa_alsa_port_data *data;
335
 
336
@@ -1951,7 +2022,7 @@
337
    if (v->channels == 0)
338
        return -EIO;
339
    for (i = 0; i < n_volume; i++)
340
-       volumei = pa_sw_volume_to_linear(v->valuesi % v->channels);
341
+       volumei = (float)pa_sw_volume_to_linear(v->valuesi % v->channels);
342
    return 0;
343
 }
344
 
345
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/acp.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/acp.h Changed
49
 
1
@@ -15,6 +15,7 @@
2
 #include <stdarg.h>
3
 #include <stdint.h>
4
 #include <poll.h>
5
+#include <string.h>
6
 
7
 #ifdef __GNUC__
8
 #define ACP_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
9
@@ -103,6 +104,15 @@
10
         (item) < &(dict)->items(dict)->n_items;        \
11
         (item)++)
12
 
13
+static inline const char *acp_dict_lookup(const struct acp_dict *dict, const char *key)
14
+{
15
+   const struct acp_dict_item *it;
16
+   acp_dict_for_each(it, dict)
17
+       if (strcmp(key, it->key) == 0)
18
+           return it->value;
19
+   return NULL;
20
+}
21
+
22
 enum acp_direction {
23
    ACP_DIRECTION_PLAYBACK = 1,
24
    ACP_DIRECTION_CAPTURE = 2
25
@@ -167,6 +177,7 @@
26
    uint32_t index;         /**< unique index for this port */
27
 #define ACP_PORT_ACTIVE        (1<<0)
28
 #define ACP_PORT_SAVE      (1<<1)  /* if the port needs saving */
29
+#define ACP_PORT_HIDDEN        (1<<2)
30
    uint32_t flags;         /**< extra port flags */
31
 
32
    const char *name;       /**< Name of this port */
33
@@ -190,6 +201,7 @@
34
 #define ACP_DEVICE_HW_MUTE (1<<2)
35
 #define ACP_DEVICE_UCM_DEVICE  (1<<3)
36
 #define ACP_DEVICE_IEC958  (1<<4)
37
+#define ACP_DEVICE_HIDDEN  (1<<5)
38
    uint32_t flags;
39
 
40
    const char *name;
41
@@ -218,6 +230,7 @@
42
 #define ACP_PROFILE_OFF        (1<<1)      /* the Off profile */
43
 #define ACP_PROFILE_SAVE   (1<<2)      /* if the profile needs saving */
44
 #define ACP_PROFILE_PRO        (1<<3)      /* the Pro profile */
45
+#define ACP_PROFILE_HIDDEN (1<<4)      /* don't show the profile */
46
    uint32_t flags;
47
 
48
    const char *name;
49
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-mixer.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c Changed
19
 
1
@@ -1147,7 +1147,7 @@
2
             int rounding;
3
 
4
             if (e->volume_limit >= 0 && value > (e->max_dB * 100))
5
-                value = e->max_dB * 100;
6
+                value = (long) (e->max_dB * 100);
7
 
8
             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
9
                 /* If we call set_playback_volume() without checking first
10
@@ -4972,7 +4972,7 @@
11
     handle = pa_alsa_open_by_template(
12
                               m->device_strings, dev_id, NULL, &try_ss,
13
                               &try_map, mode, &try_period_size,
14
-                              &try_buffer_size, 0, NULL, NULL, exact_channels);
15
+                              &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
16
     if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
17
         char bufPA_CHANNEL_MAP_SNPRINT_MAX;
18
         pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
19
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-mixer.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h Changed
20
 
1
@@ -354,7 +354,7 @@
2
     pa_alsa_device output;
3
     pa_alsa_device input;
4
 
5
-    /* ucm device context*/
6
+    /* ucm device context */
7
     pa_alsa_ucm_mapping_context ucm_context;
8
 };
9
 
10
@@ -381,6 +381,9 @@
11
     pa_idxset *input_mappings;
12
     pa_idxset *output_mappings;
13
 
14
+    /* ucm device context */
15
+    pa_alsa_ucm_profile_context ucm_context;
16
+
17
     struct {
18
    pa_dynarray devices;
19
     } out;
20
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-ucm.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c Changed
1863
 
1
@@ -72,9 +72,8 @@
2
 
3
 
4
 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
5
-                               pa_alsa_ucm_device **devices, unsigned n_devices);
6
+                               pa_alsa_ucm_device *device);
7
 static void ucm_port_data_free(pa_device_port *port);
8
-static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
9
 
10
 static struct ucm_type types = {
11
     {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
12
@@ -170,17 +169,6 @@
13
     return (char *)value;
14
 }
15
 
16
-static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
17
-    pa_alsa_ucm_device *d;
18
-    uint32_t idx;
19
-
20
-    PA_IDXSET_FOREACH(d, idxset, idx)
21
-        if (d == dev)
22
-            return 1;
23
-
24
-    return 0;
25
-}
26
-
27
 static void ucm_add_devices_to_idxset(
28
         pa_idxset *idxset,
29
         pa_alsa_ucm_device *me,
30
@@ -506,10 +494,10 @@
31
     n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
32
     pa_xfree(id);
33
 
34
+    device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
35
     if (n_confdev <= 0)
36
         pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
37
     else {
38
-        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
39
         ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
40
         snd_use_case_free_list(devices, n_confdev);
41
     }
42
@@ -518,10 +506,10 @@
43
     n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
44
     pa_xfree(id);
45
 
46
+    device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
47
     if (n_suppdev <= 0)
48
         pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
49
     else {
50
-        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
51
         ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
52
         snd_use_case_free_list(devices, n_suppdev);
53
     }
54
@@ -530,10 +518,16 @@
55
 };
56
 
57
 /* Create a property list for this ucm modifier */
58
-static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
59
+static int ucm_get_modifier_property(
60
+        pa_alsa_ucm_modifier *modifier,
61
+        snd_use_case_mgr_t *uc_mgr,
62
+        pa_alsa_ucm_verb *verb,
63
+        const char *modifier_name) {
64
     const char *value;
65
     char *id;
66
     int i;
67
+    const char **devices;
68
+    int n_confdev, n_suppdev;
69
 
70
     for (i = 0; itemi.id; i++) {
71
         int err;
72
@@ -550,16 +544,28 @@
73
     }
74
 
75
     id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
76
-    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
77
+    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
78
     pa_xfree(id);
79
-    if (modifier->n_confdev < 0)
80
+
81
+    modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
82
+    if (n_confdev <= 0)
83
         pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
84
+    else {
85
+        ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev);
86
+        snd_use_case_free_list(devices, n_confdev);
87
+    }
88
 
89
     id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
90
-    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
91
+    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
92
     pa_xfree(id);
93
-    if (modifier->n_suppdev < 0)
94
+
95
+    modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
96
+    if (n_suppdev <= 0)
97
         pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
98
+    else {
99
+        ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev);
100
+        snd_use_case_free_list(devices, n_suppdev);
101
+    }
102
 
103
     return 0;
104
 };
105
@@ -596,6 +602,74 @@
106
     return 0;
107
 };
108
 
109
+static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
110
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
111
+    char *devstatus;
112
+    long status = 0;
113
+
114
+    if (!ucm->active_verb) {
115
+        pa_log_error("Failed to get status for UCM device %s: no UCM verb set", dev_name);
116
+        return -1;
117
+    }
118
+
119
+    devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name);
120
+    if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) {
121
+        pa_log_debug("Failed to get status for UCM device %s", dev_name);
122
+        status = -1;
123
+    }
124
+    pa_xfree(devstatus);
125
+
126
+    return status;
127
+}
128
+
129
+static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
130
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
131
+
132
+    if (!ucm->active_verb) {
133
+        pa_log_error("Failed to disable UCM device %s: no UCM verb set", dev_name);
134
+        return -1;
135
+    }
136
+
137
+    /* If any of dev's conflicting devices is enabled, trying to disable
138
+     * dev gives an error despite the fact that it's already disabled.
139
+     * Check that dev is enabled to avoid this error. */
140
+    if (ucm_device_status(ucm, dev) == 0) {
141
+        pa_log_debug("UCM device %s is already disabled", dev_name);
142
+        return 0;
143
+    }
144
+
145
+    pa_log_debug("Disabling UCM device %s", dev_name);
146
+    if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) {
147
+        pa_log("Failed to disable UCM device %s", dev_name);
148
+        return -1;
149
+    }
150
+
151
+    return 0;
152
+}
153
+
154
+static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
155
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
156
+
157
+    if (!ucm->active_verb) {
158
+        pa_log_error("Failed to enable UCM device %s: no UCM verb set", dev_name);
159
+        return -1;
160
+    }
161
+
162
+    /* We don't need to enable devices that are already enabled */
163
+    if (ucm_device_status(ucm, dev) > 0) {
164
+        pa_log_debug("UCM device %s is already enabled", dev_name);
165
+        return 0;
166
+    }
167
+
168
+    pa_log_debug("Enabling UCM device %s", dev_name);
169
+    if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) {
170
+        pa_log("Failed to enable UCM device %s", dev_name);
171
+        return -1;
172
+    }
173
+
174
+    return 0;
175
+}
176
+
177
 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
178
     const char **mod_list;
179
     int num_mod, i;
180
@@ -626,6 +700,72 @@
181
     return 0;
182
 };
183
 
184
+static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
185
+    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
186
+    char *modstatus;
187
+    long status = 0;
188
+
189
+    if (!ucm->active_verb) {
190
+        pa_log_error("Failed to get status for UCM modifier %s: no UCM verb set", mod_name);
191
+        return -1;
192
+    }
193
+
194
+    modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name);
195
+    if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) {
196
+        pa_log_debug("Failed to get status for UCM modifier %s", mod_name);
197
+        status = -1;
198
+    }
199
+    pa_xfree(modstatus);
200
+
201
+    return status;
202
+}
203
+
204
+static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
205
+    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
206
+
207
+    if (!ucm->active_verb) {
208
+        pa_log_error("Failed to disable UCM modifier %s: no UCM verb set", mod_name);
209
+        return -1;
210
+    }
211
+
212
+    /* We don't need to disable modifiers that are already disabled */
213
+    if (ucm_modifier_status(ucm, mod) == 0) {
214
+        pa_log_debug("UCM modifier %s is already disabled", mod_name);
215
+        return 0;
216
+    }
217
+
218
+    pa_log_debug("Disabling UCM modifier %s", mod_name);
219
+    if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
220
+        pa_log("Failed to disable UCM modifier %s", mod_name);
221
+        return -1;
222
+    }
223
+
224
+    return 0;
225
+}
226
+
227
+static int ucm_modifier_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
228
+    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
229
+
230
+    if (!ucm->active_verb) {
231
+        pa_log_error("Failed to enable UCM modifier %s: no UCM verb set", mod_name);
232
+        return -1;
233
+    }
234
+
235
+    /* We don't need to enable modifiers that are already enabled */
236
+    if (ucm_modifier_status(ucm, mod) > 0) {
237
+        pa_log_debug("UCM modifier %s is already enabled", mod_name);
238
+        return 0;
239
+    }
240
+
241
+    pa_log_debug("Enabling UCM modifier %s", mod_name);
242
+    if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
243
+        pa_log("Failed to enable UCM modifier %s", mod_name);
244
+        return -1;
245
+    }
246
+
247
+    return 0;
248
+}
249
+
250
 static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
251
     const char *cur = pa_proplist_gets(dev->proplist, role_name);
252
 
253
@@ -642,27 +782,19 @@
254
                 role_name));
255
 }
256
 
257
-static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
258
-    pa_alsa_ucm_device *d;
259
-
260
-    PA_LLIST_FOREACH(d, list) {
261
-        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
262
-
263
-        if (pa_streq(dev_name, name)) {
264
-            const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
265
-            const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
266
+static void add_media_role(pa_alsa_ucm_device *dev, const char *role_name, const char *role, bool is_sink) {
267
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
268
+    const char *sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
269
+    const char *source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
270
 
271
-            if (is_sink && sink)
272
-                add_role_to_device(d, dev_name, role_name, role);
273
-            else if (!is_sink && source)
274
-                add_role_to_device(d, dev_name, role_name, role);
275
-            break;
276
-        }
277
-    }
278
+    if (is_sink && sink)
279
+        add_role_to_device(dev, dev_name, role_name, role);
280
+    else if (!is_sink && source)
281
+        add_role_to_device(dev, dev_name, role_name, role);
282
 }
283
 
284
 static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
285
-    char *sub = NULL, *tmp;
286
+    char *sub = NULL, *tmp, *pos;
287
 
288
     *is_sink = false;
289
 
290
@@ -672,26 +804,32 @@
291
     } else if (pa_startswith(mod_name, "Capture"))
292
         sub = pa_xstrdup(mod_name + 7);
293
 
294
-    if (!sub || !*sub) {
295
+    pos = sub;
296
+    while (pos && *pos == ' ') pos++;
297
+
298
+    if (!pos || !*pos) {
299
         pa_xfree(sub);
300
         pa_log_warn("Can't match media roles for modifier %s", mod_name);
301
         return NULL;
302
     }
303
 
304
-    tmp = sub;
305
+    tmp = pos;
306
 
307
     do {
308
         *tmp = tolower(*tmp);
309
     } while (*(++tmp));
310
 
311
-    return sub;
312
+    tmp = pa_xstrdup(pos);
313
+    pa_xfree(sub);
314
+    return tmp;
315
 }
316
 
317
-static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
318
-    int i;
319
+static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_name) {
320
+    pa_alsa_ucm_device *dev;
321
     bool is_sink = false;
322
     char *sub = NULL;
323
     const char *role_name;
324
+    uint32_t idx;
325
 
326
     sub = modifier_name_to_role(mod_name, &is_sink);
327
     if (!sub)
328
@@ -701,11 +839,11 @@
329
     modifier->media_role = sub;
330
 
331
     role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
332
-    for (i = 0; i < modifier->n_suppdev; i++) {
333
+    PA_IDXSET_FOREACH(dev, modifier->supported_devices, idx) {
334
         /* if modifier has no specific pcm, we add role intent to its supported devices */
335
         if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
336
                 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
337
-            add_media_role(modifier->supported_devicesi, list, role_name, sub, is_sink);
338
+            add_media_role(dev, role_name, sub, is_sink);
339
     }
340
 }
341
 
342
@@ -713,29 +851,17 @@
343
     uint32_t idx;
344
     pa_alsa_ucm_device *d;
345
 
346
-    if (dev->conflicting_devices) {
347
-        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
348
-            if (!d->conflicting_devices)
349
-                d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
350
-
351
-            if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
352
-                pa_log_warn("Add lost conflicting device %s to %s",
353
-                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
354
-                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
355
-        }
356
-    }
357
-
358
-    if (dev->supported_devices) {
359
-        PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
360
-            if (!d->supported_devices)
361
-                d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
362
+    PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx)
363
+        if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
364
+            pa_log_warn("Add lost conflicting device %s to %s",
365
+                    pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
366
+                    pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
367
 
368
-            if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
369
-                pa_log_warn("Add lost supported device %s to %s",
370
-                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
371
-                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
372
-        }
373
-    }
374
+    PA_IDXSET_FOREACH(d, dev->supported_devices, idx)
375
+        if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
376
+            pa_log_warn("Add lost supported device %s to %s",
377
+                    pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
378
+                    pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
379
 }
380
 
381
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
382
@@ -787,7 +913,7 @@
383
         free((void *)value);
384
     }
385
 
386
-    /* get a list of all UCM verbs (profiles) for this card */
387
+    /* get a list of all UCM verbs for this card */
388
     num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
389
     if (num_verbs < 0) {
390
         pa_log("UCM verb list not found for %s", card_name);
391
@@ -876,11 +1002,11 @@
392
         const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
393
 
394
         /* Modifier properties */
395
-        ucm_get_modifier_property(mod, uc_mgr, mod_name);
396
+        ucm_get_modifier_property(mod, uc_mgr, verb, mod_name);
397
 
398
         /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
399
         pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
400
-        ucm_set_media_roles(mod, verb->devices, mod_name);
401
+        ucm_set_media_roles(mod, mod_name);
402
     }
403
 
404
     *p_verb = verb;
405
@@ -899,43 +1025,28 @@
406
     pa_device_port *port;
407
     pa_alsa_ucm_port_data *data;
408
     pa_alsa_ucm_device *dev;
409
-    const char *eld_mixer_device_name;
410
     void *state;
411
-    int idx, eld_device;
412
 
413
     PA_HASHMAP_FOREACH(port, hash, state) {
414
         data = PA_DEVICE_PORT_DATA(port);
415
-        eld_mixer_device_name = NULL;
416
-        eld_device = -1;
417
-        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
418
-            if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
419
-                if (eld_device >= 0 && eld_device != dev->eld_device) {
420
-                    pa_log_error("The ELD device is already set!");
421
-                } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
422
-                    pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
423
-                } else {
424
-                    eld_mixer_device_name = dev->eld_mixer_device_name;
425
-                    eld_device = dev->eld_device;
426
-                }
427
-            }
428
-        }
429
-        data->eld_device = eld_device;
430
+        dev = data->device;
431
+        data->eld_device = dev->eld_device;
432
         if (data->eld_mixer_device_name)
433
             pa_xfree(data->eld_mixer_device_name);
434
-        data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
435
+        data->eld_mixer_device_name = pa_xstrdup(dev->eld_mixer_device_name);
436
     }
437
 }
438
 
439
-static void update_mixer_paths(pa_hashmap *ports, const char *profile) {
440
+static void update_mixer_paths(pa_hashmap *ports, const char *verb_name) {
441
     pa_device_port *port;
442
     pa_alsa_ucm_port_data *data;
443
     void *state;
444
 
445
     /* select volume controls on ports */
446
     PA_HASHMAP_FOREACH(port, ports, state) {
447
-        pa_log_info("Updating mixer path for %s: %s", profile, port->name);
448
+        pa_log_info("Updating mixer path for %s: %s", verb_name, port->name);
449
         data = PA_DEVICE_PORT_DATA(port);
450
-        data->path = pa_hashmap_get(data->paths, profile);
451
+        data->path = pa_hashmap_get(data->paths, verb_name);
452
     }
453
 }
454
 
455
@@ -945,39 +1056,29 @@
456
     pa_alsa_ucm_port_data *data;
457
     pa_alsa_ucm_device *dev;
458
     snd_mixer_t *mixer_handle;
459
-    const char *profile, *mdev, *mdev2;
460
+    const char *verb_name, *mdev;
461
     void *state, *state2;
462
-    int idx;
463
 
464
     PA_HASHMAP_FOREACH(port, hash, state) {
465
         data = PA_DEVICE_PORT_DATA(port);
466
 
467
-        mdev = NULL;
468
-        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
469
-            mdev2 = get_mixer_device(dev, is_sink);
470
-            if (mdev && mdev2 && !pa_streq(mdev, mdev2)) {
471
-                pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
472
-                goto fail;
473
-            }
474
-            if (mdev2)
475
-                mdev = mdev2;
476
-        }
477
-
478
+        dev = data->device;
479
+        mdev = get_mixer_device(dev, is_sink);
480
         if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
481
             pa_log_error("Failed to find a working mixer device (%s).", mdev);
482
             goto fail;
483
         }
484
 
485
-        PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
486
+        PA_HASHMAP_FOREACH_KV(verb_name, path, data->paths, state2) {
487
             if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
488
                 pa_log_warn("Could not probe path: %s, using s/w volume", path->name);
489
-                pa_hashmap_remove(data->paths, profile);
490
+                pa_hashmap_remove(data->paths, verb_name);
491
             } else if (!path->has_volume && !path->has_mute) {
492
                 pa_log_warn("Path %s is not a volume or mute control", path->name);
493
-                pa_hashmap_remove(data->paths, profile);
494
+                pa_hashmap_remove(data->paths, verb_name);
495
             } else
496
                 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute",
497
-                                path->name, profile, port->name);
498
+                                path->name, verb_name, port->name);
499
         }
500
     }
501
 
502
@@ -1025,91 +1126,141 @@
503
     pa_proplist_sets(port->proplist, "device.icon_name", icon);
504
 }
505
 
506
-static void ucm_add_port_combination(
507
+static char *devset_name(pa_idxset *devices, const char *sep) {
508
+    int i = 0;
509
+    int num = pa_idxset_size(devices);
510
+    pa_alsa_ucm_device *sortednum, *dev;
511
+    char *dev_names = NULL;
512
+    char *tmp = NULL;
513
+    uint32_t idx;
514
+
515
+    PA_IDXSET_FOREACH(dev, devices, idx) {
516
+        sortedi = dev;
517
+        i++;
518
+    }
519
+
520
+    /* Sort by alphabetical order so as to have a deterministic naming scheme */
521
+    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
522
+
523
+    for (i = 0; i < num; i++) {
524
+        dev = sortedi;
525
+        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
526
+
527
+        if (!dev_names) {
528
+            dev_names = pa_xstrdup(dev_name);
529
+        } else {
530
+            tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name);
531
+            pa_xfree(dev_names);
532
+            dev_names = tmp;
533
+        }
534
+    }
535
+
536
+    return dev_names;
537
+}
538
+
539
+PA_UNUSED static char *devset_description(pa_idxset *devices, const char *sep) {
540
+    int i = 0;
541
+    int num = pa_idxset_size(devices);
542
+    pa_alsa_ucm_device *sortednum, *dev;
543
+    char *dev_descs = NULL;
544
+    char *tmp = NULL;
545
+    uint32_t idx;
546
+
547
+    PA_IDXSET_FOREACH(dev, devices, idx) {
548
+        sortedi = dev;
549
+        i++;
550
+    }
551
+
552
+    /* Sort by alphabetical order to match devset_name() */
553
+    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
554
+
555
+    for (i = 0; i < num; i++) {
556
+        dev = sortedi;
557
+        const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
558
+
559
+        if (!dev_descs) {
560
+            dev_descs = pa_xstrdup(dev_desc);
561
+        } else {
562
+            tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc);
563
+            pa_xfree(dev_descs);
564
+            dev_descs = tmp;
565
+        }
566
+    }
567
+
568
+    return dev_descs;
569
+}
570
+
571
+/* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn.
572
+ * This way, the result will always be less than the individual components,
573
+ * yet higher components will lead to higher result. */
574
+static unsigned devset_playback_priority(pa_idxset *devices, bool invert) {
575
+    pa_alsa_ucm_device *dev;
576
+    uint32_t idx;
577
+    double priority = 0;
578
+
579
+    PA_IDXSET_FOREACH(dev, devices, idx) {
580
+        if (dev->playback_priority > 0 && invert)
581
+            priority += 1.0 / dev->playback_priority;
582
+        else
583
+            priority += dev->playback_priority;
584
+    }
585
+
586
+    if (priority > 0 && invert)
587
+        return (unsigned)(1.0 / priority);
588
+
589
+    return (unsigned) priority;
590
+}
591
+
592
+static unsigned devset_capture_priority(pa_idxset *devices, bool invert) {
593
+    pa_alsa_ucm_device *dev;
594
+    uint32_t idx;
595
+    double priority = 0;
596
+
597
+    PA_IDXSET_FOREACH(dev, devices, idx) {
598
+        if (dev->capture_priority > 0 && invert)
599
+            priority += 1.0 / dev->capture_priority;
600
+        else
601
+            priority += dev->capture_priority;
602
+    }
603
+
604
+    if (priority > 0 && invert)
605
+        return (unsigned)(1.0 / priority);
606
+
607
+    return (unsigned) priority;
608
+}
609
+
610
+void pa_alsa_ucm_add_port(
611
         pa_hashmap *hash,
612
         pa_alsa_ucm_mapping_context *context,
613
         bool is_sink,
614
-        pa_alsa_ucm_device **pdevices,
615
-        int num,
616
         pa_hashmap *ports,
617
         pa_card_profile *cp,
618
         pa_core *core) {
619
 
620
     pa_device_port *port;
621
-    int i;
622
     unsigned priority;
623
-    double prio2;
624
     char *name, *desc;
625
     const char *dev_name;
626
     const char *direction;
627
-    const char *profile;
628
-    pa_alsa_ucm_device *sortednum, *dev;
629
+    const char *verb_name;
630
+    pa_alsa_ucm_device *dev;
631
     pa_alsa_ucm_port_data *data;
632
     pa_alsa_ucm_volume *vol;
633
-    pa_alsa_jack *jack, *jack2;
634
-    pa_device_port_type_t type, type2;
635
+    pa_alsa_jack *jack;
636
+    pa_device_port_type_t type;
637
     void *state;
638
 
639
-    for (i = 0; i < num; i++)
640
-        sortedi = pdevicesi;
641
-
642
-    /* Sort by alphabetical order so as to have a deterministic naming scheme
643
-     * for combination ports */
644
-    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
645
+    dev = context->ucm_device;
646
+    if (!dev)
647
+        return;
648
 
649
-    dev = sorted0;
650
     dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
651
-
652
     name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
653
-    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
654
-            : pa_sprintf_malloc("Combination port for %s", dev_name);
655
-
656
+    desc = pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION));
657
     priority = is_sink ? dev->playback_priority : dev->capture_priority;
658
-    prio2 = (priority == 0 ? 0 : 1.0/priority);
659
     jack = ucm_get_jack(context->ucm, dev);
660
     type = dev->type;
661
 
662
-    for (i = 1; i < num; i++) {
663
-        char *tmp;
664
-
665
-        dev = sortedi;
666
-        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
667
-
668
-        tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
669
-        pa_xfree(name);
670
-        name = tmp;
671
-
672
-        tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
673
-        pa_xfree(desc);
674
-        desc = tmp;
675
-
676
-        priority = is_sink ? dev->playback_priority : dev->capture_priority;
677
-        if (priority != 0 && prio2 > 0)
678
-            prio2 += 1.0/priority;
679
-
680
-        jack2 = ucm_get_jack(context->ucm, dev);
681
-        if (jack2) {
682
-            if (jack && jack != jack2)
683
-                pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name);
684
-            jack = jack2;
685
-        }
686
-
687
-        type2 = dev->type;
688
-        if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) {
689
-            if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2)
690
-                pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2);
691
-            type = type2;
692
-        }
693
-    }
694
-
695
-    /* Make combination ports always have lower priority, and use the formula
696
-       1/p = 1/p1 + 1/p2 + ... 1/pn.
697
-       This way, the result will always be less than the individual components,
698
-       yet higher components will lead to higher result. */
699
-
700
-    if (num > 1)
701
-        priority = prio2 > 0 ? 1.0/prio2 : 0;
702
-
703
     port = pa_hashmap_get(ports, name);
704
     if (!port) {
705
         pa_device_port_new_data port_data;
706
@@ -1126,38 +1277,33 @@
707
         pa_device_port_new_data_done(&port_data);
708
 
709
         data = PA_DEVICE_PORT_DATA(port);
710
-        ucm_port_data_init(data, context->ucm, port, pdevices, num);
711
+        ucm_port_data_init(data, context->ucm, port, dev);
712
         port->impl_free = ucm_port_data_free;
713
 
714
         pa_hashmap_put(ports, port->name, port);
715
         pa_log_debug("Add port %s: %s", port->name, port->description);
716
         ucm_add_port_props(port, is_sink);
717
 
718
-        if (num == 1) {
719
-            /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
720
-             * ports. */
721
-            PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
722
-                pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
723
-                                                             is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
724
-
725
-                if (!path)
726
-                    pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
727
-                else {
728
-                    if (vol->master_elem) {
729
-                        pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
730
-                        e->switch_use = PA_ALSA_SWITCH_MUTE;
731
-                        e->volume_use = PA_ALSA_VOLUME_MERGE;
732
-                    }
733
+        PA_HASHMAP_FOREACH_KV(verb_name, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
734
+            pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
735
+                                                         is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
736
+
737
+            if (!path)
738
+                pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
739
+            else {
740
+                if (vol->master_elem) {
741
+                    pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
742
+                    e->switch_use = PA_ALSA_SWITCH_MUTE;
743
+                    e->volume_use = PA_ALSA_VOLUME_MERGE;
744
+                }
745
 
746
-                    pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
747
+                pa_hashmap_put(data->paths, pa_xstrdup(verb_name), path);
748
 
749
-                    /* Add path also to already created empty path set */
750
-                    dev = sorted0;
751
-                    if (is_sink)
752
-                        pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
753
-                    else
754
-                        pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
755
-                }
756
+                /* Add path also to already created empty path set */
757
+                if (is_sink)
758
+                    pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
759
+                else
760
+                    pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
761
             }
762
         }
763
     }
764
@@ -1178,113 +1324,127 @@
765
     if (hash) {
766
         pa_hashmap_put(hash, port->name, port);
767
     }
768
+
769
+    /* ELD devices */
770
+    set_eld_devices(ports);
771
 }
772
 
773
-static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
774
-    int ret = 0;
775
-    const char *r;
776
-    const char *state = NULL;
777
-    size_t len;
778
+static bool devset_supports_device(pa_idxset *devices, pa_alsa_ucm_device *dev) {
779
+    const char *sink, *sink2, *source, *source2;
780
+    pa_alsa_ucm_device *d;
781
+    uint32_t idx;
782
 
783
-    if (!port_name || !dev_name)
784
-        return false;
785
+    pa_assert(devices);
786
+    pa_assert(dev);
787
 
788
-    port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
789
+    /* Can add anything to empty group */
790
+    if (pa_idxset_isempty(devices))
791
+        return true;
792
 
793
-    while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
794
-        if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) {
795
-            ret = 1;
796
-            break;
797
-        }
798
-    }
799
+    /* Device already selected */
800
+    if (pa_idxset_contains(devices, dev))
801
+        return true;
802
 
803
-    return ret;
804
-}
805
+    /* No conflicting device must already be selected */
806
+    if (!pa_idxset_isdisjoint(devices, dev->conflicting_devices))
807
+        return false;
808
 
809
-static int ucm_check_conformance(
810
-        pa_alsa_ucm_mapping_context *context,
811
-        pa_alsa_ucm_device **pdevices,
812
-        int dev_num,
813
-        pa_alsa_ucm_device *dev) {
814
+    /* No already selected device must be unsupported */
815
+    if (!pa_idxset_isempty(dev->supported_devices))
816
+        if (!pa_idxset_issubset(devices, dev->supported_devices))
817
+           return false;
818
 
819
-    uint32_t idx;
820
-    pa_alsa_ucm_device *d;
821
-    int i;
822
+    sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
823
+    source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
824
 
825
-    pa_assert(dev);
826
+    PA_IDXSET_FOREACH(d, devices, idx) {
827
+        /* Must not be unsupported by any selected device */
828
+        if (!pa_idxset_isempty(d->supported_devices))
829
+            if (!pa_idxset_contains(d->supported_devices, dev))
830
+                return false;
831
 
832
-    pa_log_debug("Check device %s conformance with %d other devices",
833
-            pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
834
-    if (dev_num == 0) {
835
-        pa_log_debug("First device in combination, number 1");
836
-        return 1;
837
-    }
838
+        /* PlaybackPCM must not be the same as any selected device */
839
+        sink2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
840
+        if (sink && sink2 && pa_streq(sink, sink2))
841
+            return false;
842
 
843
-    if (dev->conflicting_devices) { /* the device defines conflicting devices */
844
-        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
845
-            for (i = 0; i < dev_num; i++) {
846
-                if (pdevicesi == d) {
847
-                    pa_log_debug("Conflicting device found");
848
-                    return 0;
849
-                }
850
-            }
851
-        }
852
-    } else if (dev->supported_devices) { /* the device defines supported devices */
853
-        for (i = 0; i < dev_num; i++) {
854
-            if (!ucm_device_exists(dev->supported_devices, pdevicesi)) {
855
-                pa_log_debug("Supported device not found");
856
-                return 0;
857
-            }
858
-        }
859
-    } else { /* not support any other devices */
860
-        pa_log_debug("Not support any other devices");
861
-        return 0;
862
+        /* CapturePCM must not be the same as any selected device */
863
+        source2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
864
+        if (source && source2 && pa_streq(source, source2))
865
+            return false;
866
     }
867
 
868
-    pa_log_debug("Device added to combination, number %d", dev_num + 1);
869
-    return 1;
870
+    return true;
871
 }
872
 
873
-static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
874
+/* Iterates nonempty subsets of UCM devices that can be simultaneously
875
+ * used, including subsets of previously returned subsets. At start,
876
+ * *state should be NULL. It's not safe to modify the devices argument
877
+ * until iteration ends. The returned idxsets must be freed by the
878
+ * caller. */
879
+static pa_idxset *iterate_device_subsets(pa_idxset *devices, void **state) {
880
+    uint32_t idx;
881
     pa_alsa_ucm_device *dev;
882
 
883
-    if (*idx == PA_IDXSET_INVALID)
884
-        dev = pa_idxset_first(idxset, idx);
885
-    else
886
-        dev = pa_idxset_next(idxset, idx);
887
+    pa_assert(devices);
888
+    pa_assert(state);
889
 
890
-    return dev;
891
-}
892
+    if (*state == NULL) {
893
+        /* First iteration, start adding from first device */
894
+        *state = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
895
+        dev = pa_idxset_first(devices, &idx);
896
 
897
-static void ucm_add_ports_combination(
898
-        pa_hashmap *hash,
899
-        pa_alsa_ucm_mapping_context *context,
900
-        bool is_sink,
901
-        pa_alsa_ucm_device **pdevices,
902
-        int dev_num,
903
-        uint32_t map_index,
904
-        pa_hashmap *ports,
905
-        pa_card_profile *cp,
906
-        pa_core *core) {
907
+    } else {
908
+        /* Backtrack the most recent device we added and skip it */
909
+        dev = pa_idxset_steal_last(*state, NULL);
910
+        pa_idxset_get_by_data(devices, dev, &idx);
911
+        if (dev)
912
+            dev = pa_idxset_next(devices, &idx);
913
+    }
914
 
915
-    pa_alsa_ucm_device *dev;
916
-    uint32_t idx = map_index;
917
+    /* Try adding devices we haven't decided on yet */
918
+    for (; dev; dev = pa_idxset_next(devices, &idx)) {
919
+        if (devset_supports_device(*state, dev))
920
+            pa_idxset_put(*state, dev, NULL);
921
+    }
922
 
923
-    if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
924
-        return;
925
+    if (pa_idxset_isempty(*state)) {
926
+        /* No more choices to backtrack on, therefore no more subsets to
927
+         * return after this. Don't return the empty set, instead clean
928
+         * up and end iteration. */
929
+        pa_idxset_free(*state, NULL);
930
+        *state = NULL;
931
+        return NULL;
932
+    }
933
+
934
+    return pa_idxset_copy(*state, NULL);
935
+}
936
 
937
-    /* check if device at map_index can combine with existing devices combination */
938
-    if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
939
-        /* add device at map_index to devices combination */
940
-        pdevicesdev_num = dev;
941
-        /* add current devices combination as a new port */
942
-        ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
943
-        /* try more elements combination */
944
-        ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
945
+/* This a wrapper around iterate_device_subsets() that only returns the
946
+ * biggest possible groups and not any of their subsets. */
947
+static pa_idxset *iterate_maximal_device_subsets(pa_idxset *devices, void **state) {
948
+    uint32_t idx;
949
+    pa_alsa_ucm_device *dev;
950
+    pa_idxset *subset;
951
+
952
+    pa_assert(devices);
953
+    pa_assert(state);
954
+
955
+    subset = iterate_device_subsets(devices, state);
956
+    if (!subset)
957
+        return subset;
958
+
959
+    /* Skip this group if it's incomplete, by checking if we can add any
960
+     * other device. If we can, this iteration is a subset of another
961
+     * group that we already returned or eventually return. */
962
+    PA_IDXSET_FOREACH(dev, devices, idx) {
963
+        if (!pa_idxset_contains(subset, dev) && devset_supports_device(subset, dev)) {
964
+            pa_idxset_free(subset, NULL);
965
+            return iterate_maximal_device_subsets(devices, state);
966
+        }
967
     }
968
 
969
-    /* try other device with current elements number */
970
-    ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
971
+    return subset;
972
 }
973
 
974
 static char* merge_roles(const char *cur, const char *add) {
975
@@ -1316,28 +1476,6 @@
976
     return ret;
977
 }
978
 
979
-void pa_alsa_ucm_add_ports_combination(
980
-        pa_hashmap *p,
981
-        pa_alsa_ucm_mapping_context *context,
982
-        bool is_sink,
983
-        pa_hashmap *ports,
984
-        pa_card_profile *cp,
985
-        pa_core *core) {
986
-
987
-    pa_alsa_ucm_device **pdevices;
988
-
989
-    pa_assert(context->ucm_devices);
990
-
991
-    if (pa_idxset_size(context->ucm_devices) > 0) {
992
-        pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
993
-        ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
994
-        pa_xfree(pdevices);
995
-    }
996
-
997
-    /* ELD devices */
998
-    set_eld_devices(ports);
999
-}
1000
-
1001
 void pa_alsa_ucm_add_ports(
1002
         pa_hashmap **p,
1003
         pa_proplist *proplist,
1004
@@ -1347,7 +1485,6 @@
1005
         snd_pcm_t *pcm_handle,
1006
         bool ignore_dB) {
1007
 
1008
-    uint32_t idx;
1009
     char *merged_roles;
1010
     const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
1011
     pa_alsa_ucm_device *dev;
1012
@@ -1358,34 +1495,39 @@
1013
     pa_assert(*p);
1014
 
1015
     /* add ports first */
1016
-    pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
1017
+    pa_alsa_ucm_add_port(*p, context, is_sink, card->ports, NULL, card->core);
1018
 
1019
     /* now set up volume paths if any */
1020
     probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
1021
 
1022
-    /* probe_volumes() removes per-profile paths from ports if probing them
1023
-     * fails. The path for the current profile is cached in
1024
+    /* probe_volumes() removes per-verb paths from ports if probing them
1025
+     * fails. The path for the current verb is cached in
1026
      * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if
1027
      * the path gets removed, so we have to call update_mixer_paths() here to
1028
      * unset the cached path if needed. */
1029
-    if (card->card.active_profile_index < card->card.n_profiles)
1030
-        update_mixer_paths(*p, card->card.profilescard->card.active_profile_index->name);
1031
+    if (context->ucm->active_verb) {
1032
+        const char *verb_name;
1033
+        verb_name = pa_proplist_gets(context->ucm->active_verb->proplist, PA_ALSA_PROP_UCM_NAME);
1034
+        update_mixer_paths(*p, verb_name);
1035
+    }
1036
 
1037
     /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1038
     merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1039
-    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1040
+
1041
+    dev = context->ucm_device;
1042
+    if (dev) {
1043
         const char *roles = pa_proplist_gets(dev->proplist, role_name);
1044
         tmp = merge_roles(merged_roles, roles);
1045
         pa_xfree(merged_roles);
1046
         merged_roles = tmp;
1047
     }
1048
 
1049
-    if (context->ucm_modifiers)
1050
-        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1051
-            tmp = merge_roles(merged_roles, mod->media_role);
1052
-            pa_xfree(merged_roles);
1053
-            merged_roles = tmp;
1054
-        }
1055
+    mod = context->ucm_modifier;
1056
+    if (mod) {
1057
+        tmp = merge_roles(merged_roles, mod->media_role);
1058
+        pa_xfree(merged_roles);
1059
+        merged_roles = tmp;
1060
+    }
1061
 
1062
     if (merged_roles)
1063
         pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1064
@@ -1395,85 +1537,92 @@
1065
 }
1066
 
1067
 /* Change UCM verb and device to match selected card profile */
1068
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1069
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
1070
     int ret = 0;
1071
-    const char *profile;
1072
+    const char *verb_name, *profile_name;
1073
     pa_alsa_ucm_verb *verb;
1074
+    pa_alsa_mapping *map;
1075
+    uint32_t idx;
1076
 
1077
     if (new_profile == old_profile)
1078
-        return ret;
1079
-    else if (new_profile == NULL || old_profile == NULL)
1080
-        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
1081
-    else if (!pa_streq(new_profile, old_profile))
1082
-        profile = new_profile;
1083
-    else
1084
-        return ret;
1085
+        return 0;
1086
 
1087
-    /* change verb */
1088
-    pa_log_info("Set UCM verb to %s", profile);
1089
-    if ((ret = snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
1090
-        pa_log("Failed to set verb %s: %s", profile, snd_strerror(ret));
1091
+    if (new_profile == NULL) {
1092
+        verb = NULL;
1093
+        profile_name = SND_USE_CASE_VERB_INACTIVE;
1094
+        verb_name = SND_USE_CASE_VERB_INACTIVE;
1095
+    } else {
1096
+        verb = new_profile->ucm_context.verb;
1097
+        profile_name = new_profile->name;
1098
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1099
     }
1100
 
1101
-    /* find active verb */
1102
-    ucm->active_verb = NULL;
1103
-    PA_LLIST_FOREACH(verb, ucm->verbs) {
1104
-        const char *verb_name;
1105
-        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1106
-        if (pa_streq(verb_name, profile)) {
1107
-            ucm->active_verb = verb;
1108
-            break;
1109
+    pa_log_info("Set profile to %s", profile_name);
1110
+    if (ucm->active_verb != verb) {
1111
+        /* change verb */
1112
+        pa_log_info("Set UCM verb to %s", verb_name);
1113
+        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
1114
+            pa_log("Failed to set verb %s", verb_name);
1115
+            ret = -1;
1116
         }
1117
+
1118
+    } else if (ucm->active_verb) {
1119
+        /* Disable modifiers not in new profile. Has to be done before
1120
+         * devices, because _dismod fails if a modifier's supported
1121
+         * devices are disabled. */
1122
+        PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1123
+            if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1124
+                if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1125
+                    ret = -1;
1126
+
1127
+        PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1128
+            if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1129
+                if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1130
+                    ret = -1;
1131
+
1132
+        /* Disable devices not in new profile */
1133
+        PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1134
+            if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1135
+                if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1136
+                    ret = -1;
1137
+
1138
+        PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1139
+            if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1140
+                if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1141
+                    ret = -1;
1142
     }
1143
+    ucm->active_verb = verb;
1144
+
1145
+    update_mixer_paths(card->ports, verb_name);
1146
 
1147
-    update_mixer_paths(card->ports, profile);
1148
     return ret;
1149
 }
1150
 
1151
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1152
-    int i;
1153
-    int ret = 0;
1154
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
1155
     pa_alsa_ucm_config *ucm;
1156
-    const char **enable_devs;
1157
-    int enable_num = 0;
1158
-    uint32_t idx;
1159
     pa_alsa_ucm_device *dev;
1160
+    pa_alsa_ucm_port_data *data;
1161
+    const char *dev_name, *ucm_dev_name;
1162
 
1163
     pa_assert(context && context->ucm);
1164
 
1165
     ucm = context->ucm;
1166
     pa_assert(ucm->ucm_mgr);
1167
 
1168
-    enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1169
-
1170
-    /* first disable then enable */
1171
-    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1172
-        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1173
-
1174
-        if (ucm_port_contains(port->name, dev_name, is_sink))
1175
-            enable_devsenable_num++ = dev_name;
1176
-        else {
1177
-            pa_log_debug("Disable ucm device %s", dev_name);
1178
-            if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1179
-                pa_log("Failed to disable ucm device %s", dev_name);
1180
-                ret = -1;
1181
-                break;
1182
-            }
1183
-        }
1184
-    }
1185
+    data = PA_DEVICE_PORT_DATA(port);
1186
+    dev = data->device;
1187
+    pa_assert(dev);
1188
 
1189
-    for (i = 0; i < enable_num; i++) {
1190
-        pa_log_debug("Enable ucm device %s", enable_devsi);
1191
-        if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devsi) < 0) {
1192
-            pa_log("Failed to enable ucm device %s", enable_devsi);
1193
-            ret = -1;
1194
-            break;
1195
+    if (context->ucm_device) {
1196
+        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1197
+        ucm_dev_name = pa_proplist_gets(context->ucm_device->proplist, PA_ALSA_PROP_UCM_NAME);
1198
+        if (!pa_streq(dev_name, ucm_dev_name)) {
1199
+            pa_log_error("Failed to set port %s with wrong UCM context: %s", dev_name, ucm_dev_name);
1200
+            return -1;
1201
         }
1202
     }
1203
 
1204
-    pa_xfree(enable_devs);
1205
-
1206
-    return ret;
1207
+    return ucm_device_enable(ucm, dev);
1208
 }
1209
 
1210
 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1211
@@ -1509,7 +1658,7 @@
1212
     const char *new_desc, *mdev;
1213
     bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1214
 
1215
-    pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1216
+    m->ucm_context.ucm_device = device;
1217
 
1218
     new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1219
     cur_desc = m->description;
1220
@@ -1538,7 +1687,7 @@
1221
     const char *new_desc, *mod_name, *channel_str;
1222
     uint32_t channels = 0;
1223
 
1224
-    pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1225
+    m->ucm_context.ucm_modifier = modifier;
1226
 
1227
     new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1228
     cur_desc = m->description;
1229
@@ -1579,17 +1728,11 @@
1230
         pa_channel_map_init(&m->channel_map);
1231
 }
1232
 
1233
-static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) {
1234
+static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *ucm_name, bool is_sink) {
1235
     pa_alsa_mapping *m;
1236
     char *mapping_name;
1237
-    size_t ucm_alibpref_len = 0;
1238
-
1239
-    /* find private alsa-lib's configuration device prefix */
1240
 
1241
-    if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix))
1242
-        ucm_alibpref_len = strlen(ucm->alib_prefix);
1243
-
1244
-    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source");
1245
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, ucm_name, is_sink ? "sink" : "source");
1246
 
1247
     m = pa_alsa_mapping_get(ps, mapping_name);
1248
 
1249
@@ -1604,7 +1747,6 @@
1250
 static int ucm_create_mapping_direction(
1251
         pa_alsa_ucm_config *ucm,
1252
         pa_alsa_profile_set *ps,
1253
-        pa_alsa_profile *p,
1254
         pa_alsa_ucm_device *device,
1255
         const char *verb_name,
1256
         const char *device_name,
1257
@@ -1614,7 +1756,7 @@
1258
     pa_alsa_mapping *m;
1259
     unsigned priority, rate, channels;
1260
 
1261
-    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1262
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_name, is_sink);
1263
 
1264
     if (!m)
1265
         return -1;
1266
@@ -1625,8 +1767,7 @@
1267
     rate = is_sink ? device->playback_rate : device->capture_rate;
1268
     channels = is_sink ? device->playback_channels : device->capture_channels;
1269
 
1270
-    if (!m->ucm_context.ucm_devices) {   /* new mapping */
1271
-        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1272
+    if (!m->ucm_context.ucm_device) {   /* new mapping */
1273
         m->ucm_context.ucm = ucm;
1274
         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1275
 
1276
@@ -1634,7 +1775,6 @@
1277
         m->device_strings0 = pa_xstrdup(device_str);
1278
         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1279
 
1280
-        ucm_add_mapping(p, m);
1281
         if (rate)
1282
             m->sample_spec.rate = rate;
1283
         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1284
@@ -1656,7 +1796,6 @@
1285
 static int ucm_create_mapping_for_modifier(
1286
         pa_alsa_ucm_config *ucm,
1287
         pa_alsa_profile_set *ps,
1288
-        pa_alsa_profile *p,
1289
         pa_alsa_ucm_modifier *modifier,
1290
         const char *verb_name,
1291
         const char *mod_name,
1292
@@ -1665,16 +1804,14 @@
1293
 
1294
     pa_alsa_mapping *m;
1295
 
1296
-    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1297
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, mod_name, is_sink);
1298
 
1299
     if (!m)
1300
         return -1;
1301
 
1302
     pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
1303
 
1304
-    if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
1305
-        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1306
-        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1307
+    if (!m->ucm_context.ucm_device && !m->ucm_context.ucm_modifier) {   /* new mapping */
1308
         m->ucm_context.ucm = ucm;
1309
         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1310
 
1311
@@ -1683,10 +1820,7 @@
1312
         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1313
         /* Modifier sinks should not be routed to by default */
1314
         m->priority = 0;
1315
-
1316
-        ucm_add_mapping(p, m);
1317
-    } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1318
-        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1319
+    }
1320
 
1321
     alsa_mapping_add_ucm_modifier(m, modifier);
1322
 
1323
@@ -1696,7 +1830,6 @@
1324
 static int ucm_create_mapping(
1325
         pa_alsa_ucm_config *ucm,
1326
         pa_alsa_profile_set *ps,
1327
-        pa_alsa_profile *p,
1328
         pa_alsa_ucm_device *device,
1329
         const char *verb_name,
1330
         const char *device_name,
1331
@@ -1711,9 +1844,9 @@
1332
     }
1333
 
1334
     if (sink)
1335
-        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1336
+        ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, sink, true);
1337
     if (ret == 0 && source)
1338
-        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1339
+        ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, source, false);
1340
 
1341
     return ret;
1342
 }
1343
@@ -1786,27 +1919,28 @@
1344
         pa_alsa_ucm_config *ucm,
1345
         pa_alsa_profile_set *ps,
1346
         pa_alsa_ucm_verb *verb,
1347
-        const char *verb_name,
1348
-        const char *verb_desc) {
1349
+        pa_idxset *mappings,
1350
+        const char *profile_name,
1351
+        const char *profile_desc,
1352
+        unsigned int profile_priority) {
1353
 
1354
     pa_alsa_profile *p;
1355
-    pa_alsa_ucm_device *dev;
1356
-    pa_alsa_ucm_modifier *mod;
1357
-    int i = 0;
1358
-    const char *name, *sink, *source;
1359
-    unsigned int priority;
1360
+    pa_alsa_mapping *map;
1361
+    uint32_t idx;
1362
 
1363
     pa_assert(ps);
1364
 
1365
-    if (pa_hashmap_get(ps->profiles, verb_name)) {
1366
-        pa_log("Verb %s already exists", verb_name);
1367
+    if (pa_hashmap_get(ps->profiles, profile_name)) {
1368
+        pa_log("Profile %s already exists", profile_name);
1369
         return -1;
1370
     }
1371
 
1372
     p = pa_xnew0(pa_alsa_profile, 1);
1373
     p->profile_set = ps;
1374
-    p->name = pa_xstrdup(verb_name);
1375
-    p->description = pa_xstrdup(verb_desc);
1376
+    p->name = pa_xstrdup(profile_name);
1377
+    p->description = pa_xstrdup(profile_desc);
1378
+    p->priority = profile_priority;
1379
+    p->ucm_context.verb = verb;
1380
 
1381
     p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1382
     p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1383
@@ -1814,10 +1948,36 @@
1384
     p->supported = true;
1385
     pa_hashmap_put(ps->profiles, p->name, p);
1386
 
1387
+    PA_IDXSET_FOREACH(map, mappings, idx)
1388
+        ucm_add_mapping(p, map);
1389
+
1390
+    pa_alsa_profile_dump(p);
1391
+
1392
+    return 0;
1393
+}
1394
+
1395
+static int ucm_create_verb_profiles(
1396
+        pa_alsa_ucm_config *ucm,
1397
+        pa_alsa_profile_set *ps,
1398
+        pa_alsa_ucm_verb *verb,
1399
+        const char *verb_name,
1400
+        const char *verb_desc) {
1401
+
1402
+    pa_idxset *verb_devices, *p_devices, *p_mappings;
1403
+    pa_alsa_ucm_device *dev;
1404
+    pa_alsa_ucm_modifier *mod;
1405
+    int i = 0;
1406
+    int n_profiles = 0;
1407
+    const char *name, *sink, *source;
1408
+    char *p_name, *p_desc, *tmp;
1409
+    unsigned int verb_priority, p_priority;
1410
+    uint32_t idx;
1411
+    void *state = NULL;
1412
+
1413
     /* TODO: get profile priority from policy management */
1414
-    priority = verb->priority;
1415
+    verb_priority = verb->priority;
1416
 
1417
-    if (priority == 0) {
1418
+    if (verb_priority == 0) {
1419
         char *verb_cmp, *c;
1420
         c = verb_cmp = pa_xstrdup(verb_name);
1421
         while (*c) {
1422
@@ -1826,15 +1986,13 @@
1423
         }
1424
         for (i = 0; verb_infoi.id; i++) {
1425
             if (strcasecmp(verb_infoi.id, verb_cmp) == 0) {
1426
-                priority = verb_infoi.priority;
1427
+                verb_priority = verb_infoi.priority;
1428
                 break;
1429
             }
1430
         }
1431
         pa_xfree(verb_cmp);
1432
     }
1433
 
1434
-    p->priority = priority;
1435
-
1436
     PA_LLIST_FOREACH(dev, verb->devices) {
1437
         pa_alsa_jack *jack;
1438
         const char *jack_hw_mute;
1439
@@ -1844,7 +2002,7 @@
1440
         sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1441
         source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1442
 
1443
-        ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1444
+        ucm_create_mapping(ucm, ps, dev, verb_name, name, sink, source);
1445
 
1446
         jack = ucm_get_jack(ucm, dev);
1447
         if (jack)
1448
@@ -1895,12 +2053,74 @@
1449
         source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1450
 
1451
         if (sink)
1452
-            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1453
+            ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, sink, true);
1454
         else if (source)
1455
-            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1456
+            ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, source, false);
1457
     }
1458
 
1459
-    pa_alsa_profile_dump(p);
1460
+    verb_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1461
+    PA_LLIST_FOREACH(dev, verb->devices)
1462
+        pa_idxset_put(verb_devices, dev, NULL);
1463
+
1464
+    while ((p_devices = iterate_maximal_device_subsets(verb_devices, &state))) {
1465
+        p_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1466
+
1467
+        /* Add the mappings that include our selected devices */
1468
+        PA_IDXSET_FOREACH(dev, p_devices, idx) {
1469
+            if (dev->playback_mapping)
1470
+                pa_idxset_put(p_mappings, dev->playback_mapping, NULL);
1471
+            if (dev->capture_mapping)
1472
+                pa_idxset_put(p_mappings, dev->capture_mapping, NULL);
1473
+        }
1474
+
1475
+        /* Add mappings only for the modifiers that can work with our
1476
+         * device selection */
1477
+        PA_LLIST_FOREACH(mod, verb->modifiers)
1478
+            if (pa_idxset_isempty(mod->supported_devices) || pa_idxset_issubset(mod->supported_devices, p_devices))
1479
+                if (pa_idxset_isdisjoint(mod->conflicting_devices, p_devices)) {
1480
+                    if (mod->playback_mapping)
1481
+                        pa_idxset_put(p_mappings, mod->playback_mapping, NULL);
1482
+                    if (mod->capture_mapping)
1483
+                        pa_idxset_put(p_mappings, mod->capture_mapping, NULL);
1484
+                }
1485
+
1486
+        /* If we'll have multiple profiles for this verb, their names
1487
+         * must be unique. Use a list of chosen devices to disambiguate
1488
+         * them. If the profile contains all devices of a verb, we'll
1489
+         * generate only onle profile whose name should be the verb
1490
+         * name. GUIs usually show the profile description instead of
1491
+         * the name, add the device names to those as well. */
1492
+        tmp = devset_name(p_devices, ", ");
1493
+        if (pa_idxset_equals(p_devices, verb_devices)) {
1494
+            p_name = pa_xstrdup(verb_name);
1495
+            p_desc = pa_xstrdup(verb_desc);
1496
+        } else {
1497
+            p_name = pa_sprintf_malloc("%s (%s)", verb_name, tmp);
1498
+            p_desc = pa_sprintf_malloc("%s (%s)", verb_desc, tmp);
1499
+        }
1500
+
1501
+        /* Make sure profiles with higher-priority devices are
1502
+         * prioritized. */
1503
+        p_priority = verb_priority + devset_playback_priority(p_devices, false) + devset_capture_priority(p_devices, false);
1504
+
1505
+        if (ucm_create_profile(ucm, ps, verb, p_mappings, p_name, p_desc, p_priority) == 0) {
1506
+            pa_log_debug("Created profile %s for UCM verb %s", p_name, verb_name);
1507
+            n_profiles++;
1508
+        }
1509
+
1510
+        pa_xfree(tmp);
1511
+        pa_xfree(p_name);
1512
+        pa_xfree(p_desc);
1513
+        pa_idxset_free(p_mappings, NULL);
1514
+        pa_idxset_free(p_devices, NULL);
1515
+    }
1516
+
1517
+    pa_idxset_free(verb_devices, NULL);
1518
+
1519
+    if (n_profiles == 0) {
1520
+        pa_log("UCM verb %s created no profiles", verb_name);
1521
+        return -1;
1522
+    }
1523
 
1524
     return 0;
1525
 }
1526
@@ -1909,7 +2129,6 @@
1527
 {
1528
     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1529
     pa_alsa_ucm_device *dev;
1530
-    uint32_t idx;
1531
     char *mdev, *alib_prefix;
1532
     snd_pcm_info_t *info;
1533
     int pcm_card, pcm_device;
1534
@@ -1925,13 +2144,12 @@
1535
 
1536
     alib_prefix = context->ucm->alib_prefix;
1537
 
1538
-    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1539
-       mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1540
-       if (mdev == NULL)
1541
-           continue;
1542
-       dev->eld_mixer_device_name = mdev;
1543
-       dev->eld_device = pcm_device;
1544
-    }
1545
+    dev = context->ucm_device;
1546
+    mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1547
+    if (mdev == NULL)
1548
+        return;
1549
+    dev->eld_mixer_device_name = mdev;
1550
+    dev->eld_device = pcm_device;
1551
 }
1552
 
1553
 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1554
@@ -1953,7 +2171,7 @@
1555
     try_buffer_size = ucm->default_n_fragments * try_period_size;
1556
 
1557
     pcm = pa_alsa_open_by_device_string(m->device_strings0, NULL, &try_ss,
1558
-            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1559
+            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
1560
 
1561
     if (pcm) {
1562
         if (!exact_channels)
1563
@@ -1995,38 +2213,39 @@
1564
     snd_mixer_t *mixer_handle;
1565
     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1566
     pa_alsa_ucm_device *dev;
1567
-    uint32_t idx;
1568
+    bool has_control;
1569
 
1570
-    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1571
-        bool has_control;
1572
-
1573
-        if (!dev->jack || !dev->jack->mixer_device_name)
1574
-            continue;
1575
-
1576
-        mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1577
-        if (!mixer_handle) {
1578
-            pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1579
-            continue;
1580
-        }
1581
+    dev = context->ucm_device;
1582
+    if (!dev->jack || !dev->jack->mixer_device_name)
1583
+        return;
1584
 
1585
-        has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1586
-        pa_alsa_jack_set_has_control(dev->jack, has_control);
1587
-        pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1588
+    mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1589
+    if (!mixer_handle) {
1590
+        pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1591
+        return;
1592
     }
1593
+
1594
+    has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1595
+    pa_alsa_jack_set_has_control(dev->jack, has_control);
1596
+    pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1597
 }
1598
 
1599
 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1600
     void *state;
1601
     pa_alsa_profile *p;
1602
     pa_alsa_mapping *m;
1603
+    const char *verb_name;
1604
     uint32_t idx;
1605
 
1606
     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
1607
+        pa_log_info("Probing profile %s", p->name);
1608
+
1609
         /* change verb */
1610
-        pa_log_info("Set ucm verb to %s", p->name);
1611
+        verb_name = pa_proplist_gets(p->ucm_context.verb->proplist, PA_ALSA_PROP_UCM_NAME);
1612
+        pa_log_info("Set ucm verb to %s", verb_name);
1613
 
1614
-        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
1615
-            pa_log("Failed to set verb %s", p->name);
1616
+        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
1617
+            pa_log("Failed to set verb %s", verb_name);
1618
             p->supported = false;
1619
             continue;
1620
         }
1621
@@ -2096,7 +2315,7 @@
1622
                                        (pa_free_cb_t) pa_alsa_profile_free);
1623
     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1624
 
1625
-    /* create a profile for each verb */
1626
+    /* create profiles for each verb */
1627
     PA_LLIST_FOREACH(verb, ucm->verbs) {
1628
         const char *verb_name;
1629
         const char *verb_desc;
1630
@@ -2108,7 +2327,7 @@
1631
             continue;
1632
         }
1633
 
1634
-        ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
1635
+        ucm_create_verb_profiles(ucm, ps, verb, verb_name, verb_desc);
1636
     }
1637
 
1638
     ucm_probe_profile_set(ucm, ps);
1639
@@ -2137,10 +2356,8 @@
1640
 
1641
         pa_proplist_free(di->proplist);
1642
 
1643
-        if (di->conflicting_devices)
1644
-            pa_idxset_free(di->conflicting_devices, NULL);
1645
-        if (di->supported_devices)
1646
-            pa_idxset_free(di->supported_devices, NULL);
1647
+        pa_idxset_free(di->conflicting_devices, NULL);
1648
+        pa_idxset_free(di->supported_devices, NULL);
1649
 
1650
         pa_xfree(di->eld_mixer_device_name);
1651
 
1652
@@ -2150,10 +2367,8 @@
1653
     PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
1654
         PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
1655
         pa_proplist_free(mi->proplist);
1656
-        if (mi->n_suppdev > 0)
1657
-            snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
1658
-        if (mi->n_confdev > 0)
1659
-            snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
1660
+        pa_idxset_free(mi->conflicting_devices, NULL);
1661
+        pa_idxset_free(mi->supported_devices, NULL);
1662
         pa_xfree(mi->media_role);
1663
         pa_xfree(mi);
1664
     }
1665
@@ -2201,29 +2416,22 @@
1666
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
1667
     pa_alsa_ucm_device *dev;
1668
     pa_alsa_ucm_modifier *mod;
1669
-    uint32_t idx;
1670
 
1671
-    if (context->ucm_devices) {
1672
+    dev = context->ucm_device;
1673
+    if (dev) {
1674
         /* clear ucm device pointer to mapping */
1675
-        PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1676
-            if (context->direction == PA_DIRECTION_OUTPUT)
1677
-                dev->playback_mapping = NULL;
1678
-            else
1679
-                dev->capture_mapping = NULL;
1680
-        }
1681
-
1682
-        pa_idxset_free(context->ucm_devices, NULL);
1683
+        if (context->direction == PA_DIRECTION_OUTPUT)
1684
+            dev->playback_mapping = NULL;
1685
+        else
1686
+            dev->capture_mapping = NULL;
1687
     }
1688
 
1689
-    if (context->ucm_modifiers) {
1690
-        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1691
-            if (context->direction == PA_DIRECTION_OUTPUT)
1692
-                mod->playback_mapping = NULL;
1693
-            else
1694
-                mod->capture_mapping = NULL;
1695
-        }
1696
-
1697
-        pa_idxset_free(context->ucm_modifiers, NULL);
1698
+    mod = context->ucm_modifier;
1699
+    if (mod) {
1700
+        if (context->direction == PA_DIRECTION_OUTPUT)
1701
+            mod->playback_mapping = NULL;
1702
+        else
1703
+            mod->capture_mapping = NULL;
1704
     }
1705
 }
1706
 
1707
@@ -2237,12 +2445,7 @@
1708
     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
1709
         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1710
             if (mod->enabled_counter == 0) {
1711
-                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1712
-
1713
-                pa_log_info("Enable ucm modifier %s", mod_name);
1714
-                if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
1715
-                    pa_log("Failed to enable ucm modifier %s", mod_name);
1716
-                }
1717
+                ucm_modifier_enable(ucm, mod);
1718
             }
1719
 
1720
             mod->enabled_counter++;
1721
@@ -2262,27 +2465,14 @@
1722
         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1723
 
1724
             mod->enabled_counter--;
1725
-            if (mod->enabled_counter == 0) {
1726
-                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1727
-
1728
-                pa_log_info("Disable ucm modifier %s", mod_name);
1729
-                if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
1730
-                    pa_log("Failed to disable ucm modifier %s", mod_name);
1731
-                }
1732
-            }
1733
+            if (mod->enabled_counter == 0)
1734
+                ucm_modifier_disable(ucm, mod);
1735
 
1736
             break;
1737
         }
1738
     }
1739
 }
1740
 
1741
-static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
1742
-    pa_assert(device);
1743
-    pa_assert(port);
1744
-
1745
-    pa_dynarray_append(device->ucm_ports, port);
1746
-}
1747
-
1748
 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
1749
     pa_assert(device);
1750
     pa_assert(jack);
1751
@@ -2315,7 +2505,7 @@
1752
     device->available = available;
1753
 
1754
     PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
1755
-        ucm_port_update_available(port);
1756
+        pa_device_port_set_available(port->core_port, port->device->available);
1757
 }
1758
 
1759
 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
1760
@@ -2339,26 +2529,21 @@
1761
 }
1762
 
1763
 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
1764
-                               pa_alsa_ucm_device **devices, unsigned n_devices) {
1765
-    unsigned i;
1766
-
1767
+                               pa_alsa_ucm_device *device) {
1768
     pa_assert(ucm);
1769
     pa_assert(core_port);
1770
-    pa_assert(devices);
1771
+    pa_assert(device);
1772
 
1773
     port->ucm = ucm;
1774
     port->core_port = core_port;
1775
-    port->devices = pa_dynarray_new(NULL);
1776
     port->eld_device = -1;
1777
 
1778
-    for (i = 0; i < n_devices; i++) {
1779
-        pa_dynarray_append(port->devices, devicesi);
1780
-        device_add_ucm_port(devicesi, port);
1781
-    }
1782
+    port->device = device;
1783
+    pa_dynarray_append(device->ucm_ports, port);
1784
 
1785
     port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, NULL);
1786
 
1787
-    ucm_port_update_available(port);
1788
+    pa_device_port_set_available(port->core_port, port->device->available);
1789
 }
1790
 
1791
 static void ucm_port_data_free(pa_device_port *port) {
1792
@@ -2368,32 +2553,14 @@
1793
 
1794
     ucm_port = PA_DEVICE_PORT_DATA(port);
1795
 
1796
-    if (ucm_port->devices)
1797
-        pa_dynarray_free(ucm_port->devices);
1798
-
1799
     if (ucm_port->paths)
1800
         pa_hashmap_free(ucm_port->paths);
1801
 
1802
     pa_xfree(ucm_port->eld_mixer_device_name);
1803
 }
1804
 
1805
-static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
1806
-    pa_alsa_ucm_device *device;
1807
-    unsigned idx;
1808
-    pa_available_t available = PA_AVAILABLE_YES;
1809
-
1810
-    pa_assert(port);
1811
-
1812
-    PA_DYNARRAY_FOREACH(device, port->devices, idx) {
1813
-        if (device->available == PA_AVAILABLE_UNKNOWN)
1814
-            available = PA_AVAILABLE_UNKNOWN;
1815
-        else if (device->available == PA_AVAILABLE_NO) {
1816
-            available = PA_AVAILABLE_NO;
1817
-            break;
1818
-        }
1819
-    }
1820
-
1821
-    pa_device_port_set_available(port->core_port, available);
1822
+long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data) {
1823
+    return ucm_device_status(data->ucm, data->device);
1824
 }
1825
 
1826
 #else /* HAVE_ALSA_UCM */
1827
@@ -2409,7 +2576,7 @@
1828
     return NULL;
1829
 }
1830
 
1831
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1832
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
1833
     return -1;
1834
 }
1835
 
1836
@@ -2427,7 +2594,7 @@
1837
         bool ignore_dB) {
1838
 }
1839
 
1840
-void pa_alsa_ucm_add_ports_combination(
1841
+void pa_alsa_ucm_add_port(
1842
         pa_hashmap *hash,
1843
         pa_alsa_ucm_mapping_context *context,
1844
         bool is_sink,
1845
@@ -2436,7 +2603,7 @@
1846
         pa_core *core) {
1847
 }
1848
 
1849
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1850
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
1851
     return -1;
1852
 }
1853
 
1854
@@ -2452,4 +2619,8 @@
1855
 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
1856
 }
1857
 
1858
+long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data) {
1859
+    return -1;
1860
+}
1861
+
1862
 #endif
1863
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-ucm.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h Changed
87
 
1
@@ -142,12 +142,13 @@
2
 typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
3
 typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
4
 typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
5
+typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context;
6
 typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
7
 typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
8
 
9
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
10
 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
11
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile);
12
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile);
13
 
14
 int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
15
 
16
@@ -159,14 +160,14 @@
17
         pa_card *card,
18
         snd_pcm_t *pcm_handle,
19
         bool ignore_dB);
20
-void pa_alsa_ucm_add_ports_combination(
21
+void pa_alsa_ucm_add_port(
22
         pa_hashmap *hash,
23
         pa_alsa_ucm_mapping_context *context,
24
         bool is_sink,
25
         pa_hashmap *ports,
26
         pa_card_profile *cp,
27
         pa_core *core);
28
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
29
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port);
30
 
31
 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
32
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
33
@@ -223,11 +224,8 @@
34
 
35
     pa_proplist *proplist;
36
 
37
-    int n_confdev;
38
-    int n_suppdev;
39
-
40
-    const char **conflicting_devices;
41
-    const char **supported_devices;
42
+    pa_idxset *conflicting_devices;
43
+    pa_idxset *supported_devices;
44
 
45
     pa_direction_t action_direction;
46
 
47
@@ -270,21 +268,23 @@
48
     pa_alsa_ucm_config *ucm;
49
     pa_direction_t direction;
50
 
51
-    pa_idxset *ucm_devices;
52
-    pa_idxset *ucm_modifiers;
53
+    pa_alsa_ucm_device *ucm_device;
54
+    pa_alsa_ucm_modifier *ucm_modifier;
55
+};
56
+
57
+struct pa_alsa_ucm_profile_context {
58
+    pa_alsa_ucm_verb *verb;
59
 };
60
 
61
 struct pa_alsa_ucm_port_data {
62
     pa_alsa_ucm_config *ucm;
63
     pa_device_port *core_port;
64
 
65
-    /* A single port will be associated with multiple devices if it represents
66
-     * a combination of devices. */
67
-    pa_dynarray *devices; /* pa_alsa_ucm_device */
68
+    pa_alsa_ucm_device *device;
69
 
70
-    /* profile name -> pa_alsa_path for volume control */
71
+    /* verb name -> pa_alsa_path for volume control */
72
     pa_hashmap *paths;
73
-    /* Current path, set when activating profile */
74
+    /* Current path, set when activating verb */
75
     pa_alsa_path *path;
76
 
77
     /* ELD info */
78
@@ -292,6 +292,8 @@
79
     int eld_device; /* PCM device number */
80
 };
81
 
82
+long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data);
83
+
84
 struct pa_alsa_ucm_volume {
85
     char *mixer_elem;  /* mixer element identifier */
86
     char *master_elem; /* master mixer element identifier */
87
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-util.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-util.c Changed
463
 
1
@@ -505,6 +505,8 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,
4
         bool *use_tsched,
5
+        pa_sample_format_t **query_supported_formats,
6
+        unsigned int **query_supported_rates,
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping) {
9
 
10
@@ -543,6 +545,8 @@
11
                 tsched_size,
12
                 use_mmap,
13
                 use_tsched,
14
+                query_supported_formats,
15
+                query_supported_rates,
16
                 m);
17
 
18
         if (pcm_handle) {
19
@@ -570,6 +574,8 @@
20
                 tsched_size,
21
                 use_mmap,
22
                 use_tsched,
23
+                query_supported_formats,
24
+                query_supported_rates,
25
                 m);
26
 
27
         if (pcm_handle) {
28
@@ -594,6 +600,8 @@
29
             tsched_size,
30
             use_mmap,
31
             use_tsched,
32
+            query_supported_formats,
33
+            query_supported_rates,
34
             false);
35
     pa_xfree(d);
36
 
37
@@ -615,6 +623,8 @@
38
         snd_pcm_uframes_t tsched_size,
39
         bool *use_mmap,
40
         bool *use_tsched,
41
+        pa_sample_format_t **query_supported_formats,
42
+        unsigned int **query_supported_rates,
43
         pa_alsa_mapping *m) {
44
 
45
     snd_pcm_t *pcm_handle;
46
@@ -644,6 +654,8 @@
47
             tsched_size,
48
             use_mmap,
49
             use_tsched,
50
+            query_supported_formats,
51
+            query_supported_rates,
52
             pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
53
 
54
     if (!pcm_handle)
55
@@ -681,6 +693,8 @@
56
         snd_pcm_uframes_t tsched_size,
57
         bool *use_mmap,
58
         bool *use_tsched,
59
+        pa_sample_format_t **query_supported_formats,
60
+        unsigned int **query_supported_rates,
61
         bool require_exact_channel_number) {
62
 
63
     int err;
64
@@ -708,6 +722,12 @@
65
         pa_log_info("ALSA device open '%s' %s: %p", d,
66
            mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle);
67
 
68
+        if (query_supported_formats)
69
+            *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format);
70
+
71
+        if (query_supported_rates)
72
+            *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate);
73
+
74
         if ((err = pa_alsa_set_hw_params(
75
                      pcm_handle,
76
                      ss,
77
@@ -781,6 +801,8 @@
78
         snd_pcm_uframes_t tsched_size,
79
         bool *use_mmap,
80
         bool *use_tsched,
81
+        pa_sample_format_t **query_supported_formats,
82
+        unsigned int **query_supported_rates,
83
         bool require_exact_channel_number) {
84
 
85
     snd_pcm_t *pcm_handle;
86
@@ -802,6 +824,8 @@
87
                 tsched_size,
88
                 use_mmap,
89
                 use_tsched,
90
+                query_supported_formats,
91
+                query_supported_rates,
92
                 require_exact_channel_number);
93
 
94
         pa_xfree(d);
95
@@ -867,6 +891,10 @@
96
 }
97
 #endif
98
 
99
+static PA_PRINTF_FUNC(5,0) void alsa_local_handler(const char *file, int line, const char *function, int err, const char *fmt, va_list arg) {
100
+    pa_log_levelv_meta(PA_LOG_INFO, file, line, function, fmt, arg);
101
+}
102
+
103
 static PA_PRINTF_FUNC(5,6) void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
104
     va_list ap;
105
 //    char *alsa_file;
106
@@ -885,13 +913,17 @@
107
 static int n_error_handler_installed = 0;
108
 
109
 typedef void (*snd_lib2_error_handler_t)(const char *file, int line, const char *function, int err, const char *fmt, ...) PA_PRINTF_FUNC(5,6) /* __attribute__ ((format (printf, 5, 6))) */;
110
+typedef void (*snd_lib2_local_handler_t)(const char *file, int line, const char *function, int err, const char *fmt, va_list args) PA_PRINTF_FUNC(5,0) /* __attribute__ ((format (printf, 5, 0))) */;
111
 
112
 extern int snd_lib_error_set_handler(snd_lib2_error_handler_t handler);
113
+extern snd_local_error_handler_t snd_lib_error_set_local(snd_lib2_local_handler_t handler);
114
 
115
 void pa_alsa_refcnt_inc(void) {
116
     /* This is not really thread safe, but we do our best */
117
-   if (n_error_handler_installed++ == 0)
118
+   if (n_error_handler_installed++ == 0) {
119
         snd_lib_error_set_handler(alsa_error_handler);
120
+        snd_lib_error_set_local(alsa_local_handler);
121
+   }
122
 }
123
 
124
 void pa_alsa_refcnt_dec(void) {
125
@@ -901,6 +933,7 @@
126
 
127
     if (r == 1) {
128
         snd_lib_error_set_handler(NULL);
129
+        snd_lib_error_set_local(NULL);
130
         snd_config_update_free_global();
131
     }
132
 }
133
@@ -929,7 +962,7 @@
134
 }
135
 
136
 void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
137
-    char *cn, *lcn, *dn;
138
+    char *cn, *lcn, *dn, name64;
139
 
140
     pa_assert(p);
141
     pa_assert(card >= 0);
142
@@ -951,6 +984,9 @@
143
         pa_xfree(dn);
144
     }
145
 
146
+    snprintf(name, sizeof(name), "hw:%d", card);
147
+    pa_alsa_init_proplist_ctl(p, name);
148
+
149
 #ifdef HAVE_UDEV
150
     pa_udev_get_info(card, p);
151
 #endif
152
@@ -979,6 +1015,7 @@
153
     snd_pcm_subclass_t subclass;
154
     const char *n, *id, *sdn;
155
     int card;
156
+    snd_pcm_sync_id_t sync_id;
157
 
158
     pa_assert(p);
159
     pa_assert(pcm_info);
160
@@ -1013,6 +1050,10 @@
161
 
162
     if ((card = snd_pcm_info_get_card(pcm_info)) >= 0)
163
         pa_alsa_init_proplist_card(c, p, card);
164
+
165
+    sync_id = snd_pcm_info_get_sync(pcm_info);
166
+    pa_proplist_setf(p, "alsa.sync.id", "%08x:%08x:%08x:%08x",
167
+           sync_id.id320, sync_id.id321, sync_id.id322, sync_id.id323);
168
 }
169
 
170
 void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) {
171
@@ -1037,7 +1078,6 @@
172
         pa_alsa_init_proplist_pcm_info(c, p, info);
173
 }
174
 
175
-#if 0
176
 void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) {
177
     int err;
178
     snd_ctl_t *ctl;
179
@@ -1065,9 +1105,13 @@
180
     if ((t = snd_ctl_card_info_get_components(info)) && *t)
181
         pa_proplist_sets(p, "alsa.components", t);
182
 
183
+    if ((t = snd_ctl_card_info_get_id(info)) && *t)
184
+        pa_proplist_sets(p, "alsa.id", t);
185
+
186
     snd_ctl_close(ctl);
187
 }
188
 
189
+#if 0
190
 int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
191
     snd_pcm_state_t state;
192
     snd_pcm_hw_params_t *hwparams;
193
@@ -1411,6 +1455,24 @@
194
 
195
     return pa_sprintf_malloc("Audio%i", i);
196
 }
197
+#endif
198
+
199
+static void dump_supported_rates(unsigned int* values)
200
+{
201
+    pa_strbuf *buf;
202
+    char *str;
203
+    int i;
204
+
205
+    buf = pa_strbuf_new();
206
+
207
+    for (i = 0; valuesi; i++) {
208
+        pa_strbuf_printf(buf, " %u", valuesi);
209
+    }
210
+
211
+    str = pa_strbuf_to_string_free(buf);
212
+    pa_log_debug("Supported rates:%s", str);
213
+    pa_xfree(str);
214
+}
215
 
216
 unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) {
217
     static unsigned int all_rates = { 8000, 11025, 12000,
218
@@ -1418,7 +1480,8 @@
219
                                         32000, 44100, 48000,
220
                                         64000, 88200, 96000,
221
                                         128000, 176400, 192000,
222
-                                        384000 };
223
+                                        352800, 384000,
224
+                                        705600, 768000 };
225
     bool supportedPA_ELEMENTSOF(all_rates) = { false, };
226
     snd_pcm_hw_params_t *hwparams;
227
     unsigned int i, j, n, *rates = NULL;
228
@@ -1460,39 +1523,40 @@
229
         rates1 = 0;
230
     }
231
 
232
+    dump_supported_rates(rates);
233
     return rates;
234
 }
235
 
236
 pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) {
237
-    static const snd_pcm_format_t format_trans_to_pa = {
238
-        SND_PCM_FORMAT_U8 = PA_SAMPLE_U8,
239
-        SND_PCM_FORMAT_A_LAW = PA_SAMPLE_ALAW,
240
-        SND_PCM_FORMAT_MU_LAW = PA_SAMPLE_ULAW,
241
-        SND_PCM_FORMAT_S16_LE = PA_SAMPLE_S16LE,
242
-        SND_PCM_FORMAT_S16_BE = PA_SAMPLE_S16BE,
243
-        SND_PCM_FORMAT_FLOAT_LE = PA_SAMPLE_FLOAT32LE,
244
-        SND_PCM_FORMAT_FLOAT_BE = PA_SAMPLE_FLOAT32BE,
245
-        SND_PCM_FORMAT_S32_LE = PA_SAMPLE_S32LE,
246
-        SND_PCM_FORMAT_S32_BE = PA_SAMPLE_S32BE,
247
-        SND_PCM_FORMAT_S24_3LE = PA_SAMPLE_S24LE,
248
-        SND_PCM_FORMAT_S24_3BE = PA_SAMPLE_S24BE,
249
-        SND_PCM_FORMAT_S24_LE = PA_SAMPLE_S24_32LE,
250
-        SND_PCM_FORMAT_S24_BE = PA_SAMPLE_S24_32BE,
251
+    static const snd_pcm_format_t format_trans_to_pcm = {
252
+        PA_SAMPLE_U8 = SND_PCM_FORMAT_U8,
253
+        PA_SAMPLE_ALAW = SND_PCM_FORMAT_A_LAW,
254
+        PA_SAMPLE_ULAW = SND_PCM_FORMAT_MU_LAW,
255
+        PA_SAMPLE_S16LE = SND_PCM_FORMAT_S16_LE,
256
+        PA_SAMPLE_S16BE = SND_PCM_FORMAT_S16_BE,
257
+        PA_SAMPLE_FLOAT32LE = SND_PCM_FORMAT_FLOAT_LE,
258
+        PA_SAMPLE_FLOAT32BE = SND_PCM_FORMAT_FLOAT_BE,
259
+        PA_SAMPLE_S32LE = SND_PCM_FORMAT_S32_LE,
260
+        PA_SAMPLE_S32BE = SND_PCM_FORMAT_S32_BE,
261
+        PA_SAMPLE_S24LE = SND_PCM_FORMAT_S24_3LE,
262
+        PA_SAMPLE_S24BE = SND_PCM_FORMAT_S24_3BE,
263
+        PA_SAMPLE_S24_32LE = SND_PCM_FORMAT_S24_LE,
264
+        PA_SAMPLE_S24_32BE = SND_PCM_FORMAT_S24_BE,
265
     };
266
-    static const snd_pcm_format_t all_formats = {
267
-        SND_PCM_FORMAT_U8,
268
-        SND_PCM_FORMAT_A_LAW,
269
-        SND_PCM_FORMAT_MU_LAW,
270
-        SND_PCM_FORMAT_S16_LE,
271
-        SND_PCM_FORMAT_S16_BE,
272
-        SND_PCM_FORMAT_FLOAT_LE,
273
-        SND_PCM_FORMAT_FLOAT_BE,
274
-        SND_PCM_FORMAT_S32_LE,
275
-        SND_PCM_FORMAT_S32_BE,
276
-        SND_PCM_FORMAT_S24_3LE,
277
-        SND_PCM_FORMAT_S24_3BE,
278
-        SND_PCM_FORMAT_S24_LE,
279
-        SND_PCM_FORMAT_S24_BE,
280
+    static const pa_sample_format_t all_formats = {
281
+        PA_SAMPLE_U8,
282
+        PA_SAMPLE_ALAW,
283
+        PA_SAMPLE_ULAW,
284
+        PA_SAMPLE_S16LE,
285
+        PA_SAMPLE_S16BE,
286
+        PA_SAMPLE_FLOAT32LE,
287
+        PA_SAMPLE_FLOAT32BE,
288
+        PA_SAMPLE_S32LE,
289
+        PA_SAMPLE_S32BE,
290
+        PA_SAMPLE_S24LE,
291
+        PA_SAMPLE_S24BE,
292
+        PA_SAMPLE_S24_32LE,
293
+        PA_SAMPLE_S24_32BE,
294
     };
295
     bool supportedPA_ELEMENTSOF(all_formats) = {
296
         false,
297
@@ -1510,7 +1574,7 @@
298
     }
299
 
300
     for (i = 0, n = 0; i < PA_ELEMENTSOF(all_formats); i++) {
301
-        if (snd_pcm_hw_params_test_format(pcm, hwparams, all_formatsi) == 0) {
302
+        if (snd_pcm_hw_params_test_format(pcm, hwparams, format_trans_to_pcmall_formatsi) == 0) {
303
             supportedi = true;
304
             n++;
305
         }
306
@@ -1521,7 +1585,7 @@
307
 
308
         for (i = 0, j = 0; i < PA_ELEMENTSOF(all_formats); i++) {
309
             if (supportedi)
310
-                formatsj++ = format_trans_to_paall_formatsi;
311
+                formatsj++ = all_formatsi;
312
         }
313
 
314
         formatsj = PA_SAMPLE_MAX;
315
@@ -1529,7 +1593,7 @@
316
         formats = pa_xnew(pa_sample_format_t, 2);
317
 
318
         formats0 = fallback_format;
319
-        if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_paformats0)) < 0) {
320
+        if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_pcmformats0)) < 0) {
321
             pa_log_debug("snd_pcm_hw_params_set_format() failed: %s", pa_alsa_strerror(ret));
322
             pa_xfree(formats);
323
             return NULL;
324
@@ -1540,7 +1604,6 @@
325
 
326
     return formats;
327
 }
328
-#endif
329
 
330
 bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
331
     snd_pcm_info_t* info;
332
@@ -1600,14 +1663,16 @@
333
                                             snd_ctl_elem_iface_t iface,
334
                                             const char *name,
335
                                             unsigned int index,
336
-                                            unsigned int device) {
337
+                                            unsigned int device,
338
+                                            unsigned int subdevice) {
339
     snd_mixer_elem_t *elem;
340
 
341
     for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
342
-        snd_hctl_elem_t *helem;
343
+        snd_hctl_elem_t **_helem, *helem;
344
         if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO)
345
             continue;
346
-        helem = snd_mixer_elem_get_private(elem);
347
+        _helem = snd_mixer_elem_get_private(elem);
348
+        helem = *_helem;
349
         if (snd_hctl_elem_get_interface(helem) != iface)
350
             continue;
351
         if (!pa_streq(snd_hctl_elem_get_name(helem), name))
352
@@ -1616,17 +1681,19 @@
353
             continue;
354
         if (snd_hctl_elem_get_device(helem) != device)
355
             continue;
356
+        if (snd_hctl_elem_get_subdevice(helem) != subdevice)
357
+            continue;
358
         return elem;
359
     }
360
     return NULL;
361
 }
362
 
363
 snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) {
364
-    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device);
365
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device, 0);
366
 }
367
 
368
 snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) {
369
-    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device);
370
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device, 0);
371
 }
372
 
373
 static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
374
@@ -1635,36 +1702,79 @@
375
     return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1);
376
 }
377
 
378
+static void mixer_melem_free(snd_mixer_elem_t *elem)
379
+{
380
+    snd_hctl_elem_t **_helem;
381
+    _helem = snd_mixer_elem_get_private(elem);
382
+    pa_xfree(_helem);
383
+}
384
+
385
 static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask,
386
            snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
387
 {
388
     int err;
389
     const char *name = snd_hctl_elem_get_name(helem);
390
+    snd_hctl_elem_t **_helem;
391
     // NOTE: The remove event defined as '~0U`.
392
     if (mask == SND_CTL_EVENT_MASK_REMOVE) {
393
         // NOTE: unless remove pointer to melem from link-list at private_data of helem, hits
394
-   // assersion in alsa-lib since the list is not empty.
395
+   // assertion in alsa-lib since the list is not empty.
396
+        _helem = snd_mixer_elem_get_private(melem);
397
+        *_helem = NULL;
398
         snd_mixer_elem_detach(melem, helem);
399
     } else if (mask & SND_CTL_EVENT_MASK_ADD) {
400
         snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
401
         if (iface == SND_CTL_ELEM_IFACE_CARD || iface == SND_CTL_ELEM_IFACE_PCM) {
402
+            snd_mixer_t *mixer = snd_mixer_class_get_mixer(class);
403
+            snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
404
+            const char *name = snd_hctl_elem_get_name(helem);
405
+            const int index = snd_hctl_elem_get_index(helem);
406
+            const int device = snd_hctl_elem_get_device(helem);
407
+            const int subdevice = snd_hctl_elem_get_subdevice(helem);
408
             snd_mixer_elem_t *new_melem;
409
 
410
-            /* Put the hctl pointer as our private data - it will be useful for callbacks */
411
-            if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) {
412
-                pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
413
-                return 0;
414
+            bool found = true;
415
+
416
+            new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device, subdevice);
417
+            if (!new_melem) {
418
+                _helem = pa_xmalloc(sizeof(snd_hctl_elem_t *));
419
+                *_helem = helem;
420
+                /* Put the hctl pointer as our private data - it will be useful for callbacks */
421
+                if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, _helem, mixer_melem_free)) < 0) {
422
+                    pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
423
+                    return 0;
424
+                }
425
+                found = false;
426
+            } else {
427
+                _helem = snd_mixer_elem_get_private(new_melem);
428
+                if (_helem) {
429
+                    char *s1, *s2;
430
+                    snd_ctl_elem_id_t *id1, *id2;
431
+                    snd_ctl_elem_id_alloca(&id1);
432
+                    snd_ctl_elem_id_alloca(&id2);
433
+                    snd_hctl_elem_get_id(helem, id1);
434
+                    snd_hctl_elem_get_id(*_helem, id2);
435
+                    s1 = snd_ctl_ascii_elem_id_get(id1);
436
+                    s2 = snd_ctl_ascii_elem_id_get(id2);
437
+                    pa_log_warn("mixer_class_event - duplicate mixer controls: %s | %s", s1, s2);
438
+                    free(s2);
439
+                    free(s1);
440
+                    return 0;
441
+                }
442
+                *_helem = helem;
443
             }
444
 
445
             if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) {
446
                 pa_log_warn("snd_mixer_elem_attach failed: %s", pa_alsa_strerror(err));
447
-       snd_mixer_elem_free(melem);
448
+                snd_mixer_elem_free(melem);
449
                 return 0;
450
             }
451
 
452
-            if ((err = snd_mixer_elem_add(new_melem, class)) < 0) {
453
-                pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err));
454
-                return 0;
455
+            if (!found) {
456
+                if ((err = snd_mixer_elem_add(new_melem, class)) < 0) {
457
+                    pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err));
458
+                    return 0;
459
+                }
460
             }
461
         }
462
     }
463
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/alsa-util.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/alsa-util.h Changed
47
 
1
@@ -64,6 +64,8 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,                   /* modified at return */
4
         bool *use_tsched,                 /* modified at return */
5
+        pa_sample_format_t **query_supported_formats, /* modified at return */
6
+        unsigned int **query_supported_rates,         /* modified at return */
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping);       /* modified at return */
9
 #endif
10
@@ -80,6 +82,8 @@
11
         snd_pcm_uframes_t tsched_size,
12
         bool *use_mmap,                   /* modified at return */
13
         bool *use_tsched,                 /* modified at return */
14
+        pa_sample_format_t **query_supported_formats, /* modified at return */
15
+        unsigned int **query_supported_rates,         /* modified at return */
16
         pa_alsa_mapping *mapping);
17
 
18
 /* Opens the explicit ALSA device */
19
@@ -94,6 +98,8 @@
20
         snd_pcm_uframes_t tsched_size,
21
         bool *use_mmap,                   /* modified at return */
22
         bool *use_tsched,                 /* modified at return */
23
+        pa_sample_format_t **query_supported_formats, /* modified at return */
24
+        unsigned int **query_supported_rates,         /* modified at return */
25
         bool require_exact_channel_number);
26
 
27
 /* Opens the explicit ALSA device with a fallback list */
28
@@ -109,6 +115,8 @@
29
         snd_pcm_uframes_t tsched_size,
30
         bool *use_mmap,                   /* modified at return */
31
         bool *use_tsched,                 /* modified at return */
32
+        pa_sample_format_t **query_supported_formats, /* modified at return */
33
+        unsigned int **query_supported_rates,        /* modified at return */
34
         bool require_exact_channel_number);
35
 
36
 #if 0
37
@@ -123,9 +131,7 @@
38
 void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
39
 void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
40
 void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm);
41
-#if 0
42
 void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
43
-#endif
44
 bool pa_alsa_init_description(pa_proplist *p, pa_card *card);
45
 
46
 #if 0
47
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/compat.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/compat.h Changed
50
 
1
@@ -35,6 +35,7 @@
2
 #include <stdlib.h>
3
 #include <unistd.h>
4
 #include <math.h>
5
+#include <limits.h>
6
 
7
 #include <spa/utils/string.h>
8
 
9
@@ -47,10 +48,12 @@
10
 #define PA_LIKELY(x) (__builtin_expect(!!(x),1))
11
 #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
12
 #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
13
+#define PA_UNUSED __attribute__ ((unused))
14
 #else
15
 #define PA_LIKELY(x) (x)
16
 #define PA_UNLIKELY(x) (x)
17
 #define PA_PRINTF_FUNC(fmt, arg1)
18
+#define PA_UNUSED
19
 #endif
20
 
21
 #define PA_MIN(a,b)                    \
22
@@ -96,7 +99,7 @@
23
    PA_AVAILABLE_YES = 2,
24
 } pa_available_t;
25
 
26
-#define PA_RATE_MAX (48000U*8U)
27
+#define PA_RATE_MAX (48000U*16U)
28
 
29
 typedef enum pa_sample_format {
30
    PA_SAMPLE_U8,       /**< Unsigned 8 Bit PCM */
31
@@ -660,6 +663,8 @@
32
         ssize_t n;
33
 
34
         c = pa_xmalloc(l);
35
+        if (c == NULL)
36
+            return NULL;
37
 
38
         if ((n = readlink(p, c, l-1)) < 0) {
39
             pa_xfree(c);
40
@@ -672,6 +677,9 @@
41
         }
42
 
43
         pa_xfree(c);
44
+
45
+        if (l >= (size_t)(INT_MAX / 2))
46
+            return NULL;
47
         l *= 2;
48
     }
49
 #else
50
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/acp/idxset.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/acp/idxset.h Changed
126
 
1
@@ -130,13 +130,25 @@
2
    return count;
3
 }
4
 
5
-static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx)
6
+static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx)
7
 {
8
         pa_idxset_item *item;
9
    for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
10
         pa_array_check(&s->array, item); item++, (*idx)++) {
11
        if (item->ptr != NULL)
12
-           return item->ptr;
13
+           return item;
14
+   }
15
+   *idx = PA_IDXSET_INVALID;
16
+   return NULL;
17
+}
18
+
19
+static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx)
20
+{
21
+        pa_idxset_item *item;
22
+   for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
23
+        pa_array_check(&s->array, item); item--, (*idx)--) {
24
+       if (item->ptr != NULL)
25
+           return item;
26
    }
27
    *idx = PA_IDXSET_INVALID;
28
    return NULL;
29
@@ -144,29 +156,93 @@
30
 
31
 static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx)
32
 {
33
+   pa_idxset_item *item;
34
    (*idx)++;;
35
-   return pa_idxset_search(s, idx);
36
+   item = pa_idxset_search(s, idx);
37
+   return item ? item->ptr : NULL;
38
 }
39
 
40
 static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx)
41
 {
42
    uint32_t i = 0;
43
-   void *ptr = pa_idxset_search(s, &i);
44
+   pa_idxset_item *item = pa_idxset_search(s, &i);
45
    if (idx)
46
        *idx = i;
47
+   return item ? item->ptr : NULL;
48
+}
49
+
50
+static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx)
51
+{
52
+   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
53
+   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
54
+   if (idx)
55
+       *idx = i;
56
+   return item ? item->ptr : NULL;
57
+}
58
+
59
+static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx)
60
+{
61
+   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
62
+   void *ptr = NULL;
63
+   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
64
+   if (idx)
65
+       *idx = i;
66
+   if (item) {
67
+       ptr = item->ptr;
68
+       item->ptr = NULL;
69
+       pa_array_remove(&s->array, item);
70
+   }
71
    return ptr;
72
 }
73
 
74
 static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx)
75
 {
76
    pa_idxset_item *item = pa_idxset_find(s, p);
77
-   if (item == NULL)
78
+   if (item == NULL) {
79
+       if (idx)
80
+           *idx = PA_IDXSET_INVALID;
81
        return NULL;
82
+   }
83
    if (idx)
84
        *idx = item - (pa_idxset_item*)s->array.data;
85
    return item->ptr;
86
 }
87
 
88
+static inline bool pa_idxset_contains(pa_idxset *s, const void *p)
89
+{
90
+   return pa_idxset_get_by_data(s, p, NULL) == p;
91
+}
92
+
93
+static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t)
94
+{
95
+   pa_idxset_item *item;
96
+   pa_array_for_each(item, &s->array) {
97
+       if (item->ptr && pa_idxset_contains(t, item->ptr))
98
+           return false;
99
+   }
100
+   return true;
101
+}
102
+
103
+static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t)
104
+{
105
+   pa_idxset_item *item;
106
+   pa_array_for_each(item, &s->array) {
107
+       if (item->ptr && !pa_idxset_contains(t, item->ptr))
108
+           return false;
109
+   }
110
+   return true;
111
+}
112
+
113
+static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t)
114
+{
115
+    return pa_idxset_issubset(t, s);
116
+}
117
+
118
+static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t)
119
+{
120
+    return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t);
121
+}
122
+
123
 static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx)
124
 {
125
         pa_idxset_item *item;
126
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-acp-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-acp-device.c Changed
230
 
1
@@ -12,6 +12,7 @@
2
 #include <alsa/asoundlib.h>
3
 
4
 #include <spa/node/node.h>
5
+#include <spa/node/keys.h>
6
 #include <spa/utils/type.h>
7
 #include <spa/utils/keys.h>
8
 #include <spa/utils/names.h>
9
@@ -130,12 +131,12 @@
10
    struct spa_dict_item *items;
11
    const struct acp_dict_item *it;
12
    uint32_t n_items, i;
13
-   char device_name128, path180, channels16, ch12, routes16;
14
-   char card_index16, *p;
15
+   char device_name128, path210, channels16, ch12, routes16;
16
+   char card_index16, card_name64, *p;
17
    char positionsSPA_AUDIO_MAX_CHANNELS * 12;
18
    struct spa_device_object_info info;
19
    struct acp_card *card = this->card;
20
-   const char *stream, *devstr;
21
+   const char *stream, *devstr, *card_id;
22
 
23
    info = SPA_DEVICE_OBJECT_INFO_INIT();
24
    info.type = SPA_TYPE_INTERFACE_Node;
25
@@ -150,10 +151,12 @@
26
 
27
    info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
28
 
29
-   items = alloca((dev->props.n_items + 8) * sizeof(*items));
30
+   items = alloca((dev->props.n_items + 9) * sizeof(*items));
31
    n_items = 0;
32
 
33
    snprintf(card_index, sizeof(card_index), "%d", card->index);
34
+   card_id = acp_dict_lookup(&card->props, "alsa.id");
35
+   snprintf(card_name, sizeof(card_name), "%s", card_id ? card_id : card_index);
36
 
37
    devstr = dev->device_strings0;
38
    p = strstr(devstr, "%f");
39
@@ -164,13 +167,15 @@
40
    } else {
41
        snprintf(device_name, sizeof(device_name), "%s", devstr);
42
    }
43
-   snprintf(path, sizeof(path), "alsa:pcm:%s:%s:%s", card_index, device_name, stream);
44
+
45
+   snprintf(path, sizeof(path), "alsa:acp:%s:%d:%s", card_name, dev->index, stream);
46
    itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_OBJECT_PATH, path);
47
    itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, device_name);
48
    if (dev->flags & ACP_DEVICE_UCM_DEVICE)
49
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_OPEN_UCM, "true");
50
    itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_CARD, card_index);
51
    itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_STREAM, stream);
52
+   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, stream);
53
 
54
    snprintf(channels, sizeof(channels), "%d", dev->format.channels);
55
    itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels);
56
@@ -204,6 +209,7 @@
57
    struct acp_card *card = this->card;
58
    char path128;
59
    uint64_t old = full ? this->info.change_mask : 0;
60
+   const char *card_id;
61
 
62
    if (full)
63
        this->info.change_mask = this->info_all;
64
@@ -211,11 +217,16 @@
65
        n_items = card->props.n_items + 4;
66
        items = alloca(n_items * sizeof(*items));
67
 
68
+       card_id = acp_dict_lookup(&card->props, "alsa.id");
69
+
70
        n_items = 0;
71
 #define ADD_ITEM(key, value) itemsn_items++ = SPA_DICT_ITEM_INIT(key, value)
72
-       snprintf(path, sizeof(path), "alsa:pcm:%d", card->index);
73
+       if (card_id)
74
+           snprintf(path, sizeof(path), "alsa:acp:%s", card_id);
75
+       else
76
+           snprintf(path, sizeof(path), "alsa:acp:%d", card->index);
77
        ADD_ITEM(SPA_KEY_OBJECT_PATH, path);
78
-       ADD_ITEM(SPA_KEY_DEVICE_API, "alsa:pcm");
79
+       ADD_ITEM(SPA_KEY_DEVICE_API, "alsa:acp");
80
        ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Audio/Device");
81
        ADD_ITEM(SPA_KEY_API_ALSA_PATH, (char *)this->props.device);
82
        acp_dict_for_each(it, &card->props)
83
@@ -516,14 +527,17 @@
84
            return 0;
85
 
86
        pr = card->profilesresult.index;
87
+       if (SPA_FLAG_IS_SET(pr->flags, ACP_PROFILE_HIDDEN))
88
+           goto next;
89
        param = build_profile(&b.b, id, pr, false);
90
        break;
91
 
92
    case SPA_PARAM_Profile:
93
        if (result.index > 0 || card->active_profile_index >= card->n_profiles)
94
            return 0;
95
-
96
        pr = card->profilescard->active_profile_index;
97
+       if (SPA_FLAG_IS_SET(pr->flags, ACP_PROFILE_HIDDEN))
98
+           goto next;
99
        param = build_profile(&b.b, id, pr, true);
100
        break;
101
 
102
@@ -532,6 +546,8 @@
103
            return 0;
104
 
105
        p = card->portsresult.index;
106
+       if (SPA_FLAG_IS_SET(p->flags, ACP_PORT_HIDDEN))
107
+           goto next;
108
        param = build_route(&b.b, id, p, NULL, SPA_ID_INVALID);
109
        break;
110
 
111
@@ -541,6 +557,8 @@
112
                return 0;
113
 
114
            dev = card->devicesresult.index;
115
+           if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HIDDEN))
116
+               goto next;
117
            if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_ACTIVE) &&
118
                (p = find_port_for_device(card, dev)) != NULL)
119
                break;
120
@@ -548,6 +566,8 @@
121
            result.index++;
122
        }
123
        result.next = result.index + 1;
124
+       if (SPA_FLAG_IS_SET(p->flags, ACP_PORT_HIDDEN))
125
+           goto next;
126
        param = build_route(&b.b, id, p, dev, card->active_profile_index);
127
        if (param == NULL)
128
            return -errno;
129
@@ -699,6 +719,26 @@
130
    return changed;
131
 }
132
 
133
+static uint32_t find_profile_by_name(struct acp_card *card, const char *name)
134
+{
135
+   uint32_t i;
136
+   for (i = 0; i < card->n_profiles; i++) {
137
+       if (spa_streq(card->profilesi->name, name))
138
+           return i;
139
+   }
140
+   return SPA_ID_INVALID;
141
+}
142
+
143
+static uint32_t find_route_by_name(struct acp_card *card, const char *name)
144
+{
145
+   uint32_t i;
146
+   for (i = 0; i < card->n_ports; i++) {
147
+       if (spa_streq(card->portsi->name, name))
148
+           return i;
149
+   }
150
+   return SPA_ID_INVALID;
151
+}
152
+
153
 static int impl_set_param(void *object,
154
              uint32_t id, uint32_t flags,
155
              const struct spa_pod *param)
156
@@ -711,7 +751,8 @@
157
    switch (id) {
158
    case SPA_PARAM_Profile:
159
    {
160
-       uint32_t idx;
161
+       uint32_t idx = SPA_ID_INVALID;
162
+       const char *name = NULL;
163
        bool save = false;
164
 
165
        if (param == NULL) {
166
@@ -719,20 +760,31 @@
167
            save = true;
168
        } else if ((res = spa_pod_parse_object(param,
169
                SPA_TYPE_OBJECT_ParamProfile, NULL,
170
-               SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx),
171
+               SPA_PARAM_PROFILE_index, SPA_POD_OPT_Int(&idx),
172
+               SPA_PARAM_PROFILE_name, SPA_POD_OPT_String(&name),
173
                SPA_PARAM_PROFILE_save, SPA_POD_OPT_Bool(&save))) < 0) {
174
            spa_log_warn(this->log, "can't parse profile");
175
            spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
176
            return res;
177
        }
178
-
179
+       if (idx == SPA_ID_INVALID && name == NULL) {
180
+           spa_log_warn(this->log, "profile needs name or index");
181
+           return -EINVAL;
182
+       }
183
+       if (idx == SPA_ID_INVALID)
184
+           idx = find_profile_by_name(this->card, name);
185
+       if (idx == SPA_ID_INVALID) {
186
+           spa_log_warn(this->log, "unknown profile %s", name);
187
+           return -EINVAL;
188
+       }
189
        acp_card_set_profile(this->card, idx, save ? ACP_PROFILE_SAVE : 0);
190
        emit_info(this, false);
191
        break;
192
    }
193
    case SPA_PARAM_Route:
194
    {
195
-       uint32_t idx, device;
196
+       uint32_t idx = SPA_ID_INVALID, device;
197
+       const char *name = NULL;
198
        struct spa_pod *props = NULL;
199
        struct acp_device *dev;
200
        bool save = false;
201
@@ -742,7 +794,8 @@
202
 
203
        if ((res = spa_pod_parse_object(param,
204
                SPA_TYPE_OBJECT_ParamRoute, NULL,
205
-               SPA_PARAM_ROUTE_index, SPA_POD_Int(&idx),
206
+               SPA_PARAM_ROUTE_index, SPA_POD_OPT_Int(&idx),
207
+               SPA_PARAM_ROUTE_name, SPA_POD_OPT_String(&name),
208
                SPA_PARAM_ROUTE_device, SPA_POD_Int(&device),
209
                SPA_PARAM_ROUTE_props, SPA_POD_OPT_Pod(&props),
210
                SPA_PARAM_ROUTE_save, SPA_POD_OPT_Bool(&save))) < 0) {
211
@@ -752,8 +805,18 @@
212
        }
213
        if (device >= this->card->n_devices)
214
            return -EINVAL;
215
+       if (idx == SPA_ID_INVALID && name == NULL)
216
+           return -EINVAL;
217
 
218
        dev = this->card->devicesdevice;
219
+       if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HIDDEN))
220
+           return -EINVAL;
221
+
222
+       if (idx == SPA_ID_INVALID)
223
+           idx = find_route_by_name(this->card, name);
224
+       if (idx == SPA_ID_INVALID)
225
+           return -EINVAL;
226
+
227
        acp_device_set_port(dev, idx, save ? ACP_PORT_SAVE : 0);
228
        if (props)
229
            apply_device_props(this, dev, props);
230
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-compress-offload-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-compress-offload-device.c Changed
189
 
1
@@ -47,6 +47,13 @@
2
 
3
    struct spa_log *log;
4
 
5
+   uint32_t info_all;
6
+   struct spa_device_info device_info;
7
+
8
+#define IDX_EnumProfile        0
9
+#define IDX_Profile        1
10
+   struct spa_param_info params2;
11
+
12
    struct spa_hook_list hooks;
13
 
14
    struct props props;
15
@@ -215,6 +222,9 @@
16
    this->n_playback = n_play;
17
    this->n_nodes = i;
18
 
19
+   this->device_info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
20
+   this->paramsIDX_Profile.user++;
21
+
22
 finish:
23
    if (snd_dir != NULL)
24
        closedir(snd_dir);
25
@@ -234,56 +244,58 @@
26
    uint32_t n_items = 0;
27
    snd_ctl_t *ctl_hndl;
28
    snd_ctl_card_info_t *info;
29
-   struct spa_device_info dinfo;
30
-   struct spa_param_info params2;
31
    char path128;
32
    char device_name200;
33
    char device_desc200;
34
 
35
-   spa_log_debug(this->log, "open card %s", this->props.device);
36
-   if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
37
-       spa_log_error(this->log, "can't open control for card %s: %s",
38
-                     this->props.device, snd_strerror(err));
39
-       return err;
40
-   }
41
-
42
-   snd_ctl_card_info_alloca(&info);
43
-   if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) {
44
-       spa_log_error(this->log, "error hardware info: %s", snd_strerror(err));
45
-       goto finish;
46
-   }
47
-
48
-   dinfo = SPA_DEVICE_INFO_INIT();
49
-
50
-   dinfo.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS;
51
-
52
-   snprintf(path, sizeof(path), "alsa:compressed:%s", snd_ctl_card_info_get_id(info));
53
-   snprintf(device_name, sizeof(device_name), "comprC%u", this->props.card_nr);
54
-   snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", this->props.card_nr);
55
+   if (full)
56
+       this->device_info.change_mask = this->info_all;
57
 
58
-   ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH,              path);
59
-   ADD_DICT_ITEM(SPA_KEY_DEVICE_API,               "alsa:compressed");
60
-   ADD_DICT_ITEM(SPA_KEY_DEVICE_NICK,              "alsa:compressed");
61
-   ADD_DICT_ITEM(SPA_KEY_DEVICE_NAME,              device_name);
62
-   ADD_DICT_ITEM(SPA_KEY_DEVICE_DESCRIPTION,       device_desc);
63
-   ADD_DICT_ITEM(SPA_KEY_MEDIA_CLASS,              "Audio/Device");
64
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH,            (char *)this->props.device);
65
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_ID,         snd_ctl_card_info_get_id(info));
66
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info));
67
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER,     snd_ctl_card_info_get_driver(info));
68
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_NAME,       snd_ctl_card_info_get_name(info));
69
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME,   snd_ctl_card_info_get_longname(info));
70
-   ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME,  snd_ctl_card_info_get_mixername(info));
71
-
72
-   dinfo.props = &SPA_DICT_INIT(items, n_items);
73
+   if (this->device_info.change_mask) {
74
+       spa_log_debug(this->log, "open card %s", this->props.device);
75
+       if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
76
+           spa_log_error(this->log, "can't open control for card %s: %s",
77
+                         this->props.device, snd_strerror(err));
78
+           return err;
79
+       }
80
 
81
-   dinfo.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
82
-   params0 = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
83
-   params1 = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
84
-   dinfo.n_params = SPA_N_ELEMENTS(params);
85
-   dinfo.params = params;
86
+       snd_ctl_card_info_alloca(&info);
87
+       if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) {
88
+           spa_log_error(this->log, "error hardware info: %s", snd_strerror(err));
89
+           goto finish;
90
+       }
91
 
92
-   spa_device_emit_info(&this->hooks, &dinfo);
93
+       snprintf(path, sizeof(path), "alsa:compressed:%s", snd_ctl_card_info_get_id(info));
94
+       snprintf(device_name, sizeof(device_name), "comprC%u", this->props.card_nr);
95
+       snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", this->props.card_nr);
96
+
97
+       ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH,              path);
98
+       ADD_DICT_ITEM(SPA_KEY_DEVICE_API,               "alsa:compressed");
99
+       ADD_DICT_ITEM(SPA_KEY_DEVICE_NICK,              "alsa:compressed");
100
+       ADD_DICT_ITEM(SPA_KEY_DEVICE_NAME,              device_name);
101
+       ADD_DICT_ITEM(SPA_KEY_DEVICE_DESCRIPTION,       device_desc);
102
+       ADD_DICT_ITEM(SPA_KEY_MEDIA_CLASS,              "Audio/Device");
103
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH,            (char *)this->props.device);
104
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_ID,         snd_ctl_card_info_get_id(info));
105
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info));
106
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER,     snd_ctl_card_info_get_driver(info));
107
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_NAME,       snd_ctl_card_info_get_name(info));
108
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME,   snd_ctl_card_info_get_longname(info));
109
+       ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME,  snd_ctl_card_info_get_mixername(info));
110
+
111
+       this->device_info.props = &SPA_DICT_INIT(items, n_items);
112
+
113
+       if (this->device_info.change_mask & SPA_DEVICE_CHANGE_MASK_PARAMS) {
114
+           SPA_FOR_EACH_ELEMENT_VAR(this->params, p) {
115
+               if (p->user > 0) {
116
+                   p->flags ^= SPA_PARAM_INFO_SERIAL;
117
+                   p->user = 0;
118
+               }
119
+           }
120
+       }
121
+       spa_device_emit_info(&this->hooks, &this->device_info);
122
+       this->device_info.change_mask = 0;
123
+   }
124
 
125
 finish:
126
    spa_log_debug(this->log, "close card %s", this->props.device);
127
@@ -429,6 +441,15 @@
128
    return 0;
129
 }
130
 
131
+static uint32_t find_profile_by_name(const char *name)
132
+{
133
+   if (spa_streq(name, "off"))
134
+       return 0;
135
+   else if (spa_streq(name, "on"))
136
+       return 1;
137
+   return SPA_ID_INVALID;
138
+}
139
+
140
 static int impl_set_param(void *object,
141
                           uint32_t id, uint32_t flags,
142
                           const struct spa_pod *param)
143
@@ -441,17 +462,26 @@
144
    switch (id) {
145
    case SPA_PARAM_Profile:
146
    {
147
-       uint32_t idx;
148
+       uint32_t idx = SPA_ID_INVALID;
149
+       const char *name = NULL;
150
 
151
        if ((res = spa_pod_parse_object(param,
152
-                                       SPA_TYPE_OBJECT_ParamProfile, NULL,
153
-                                       SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx))) < 0) {
154
+                       SPA_TYPE_OBJECT_ParamProfile, NULL,
155
+                       SPA_PARAM_PROFILE_index, SPA_POD_OPT_Int(&idx),
156
+                                       SPA_PARAM_PROFILE_name, SPA_POD_OPT_String(&name))) < 0) {
157
            spa_log_warn(this->log, "can't parse profile");
158
            spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
159
            return res;
160
        }
161
+       if (idx == SPA_ID_INVALID && name == NULL)
162
+           return -EINVAL;
163
+       if (idx == SPA_ID_INVALID)
164
+           idx = find_profile_by_name(name);
165
+       if (idx == SPA_ID_INVALID)
166
+           return -EINVAL;
167
 
168
        set_profile(this, idx);
169
+       emit_info(this, false);
170
        break;
171
    }
172
    default:
173
@@ -545,6 +575,15 @@
174
        }
175
    }
176
 
177
+   this->device_info = SPA_DEVICE_INFO_INIT();
178
+   this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS |
179
+       SPA_DEVICE_CHANGE_MASK_PARAMS;
180
+
181
+   this->paramsIDX_EnumProfile = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
182
+   this->paramsIDX_Profile = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
183
+   this->device_info.params = this->params;
184
+   this->device_info.n_params = SPA_N_ELEMENTS(this->params);
185
+
186
    return 0;
187
 }
188
 
189
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-compress-offload-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-compress-offload-sink.c Changed
55
 
1
@@ -227,7 +227,6 @@
2
 static int do_start(struct impl *this);
3
 static void do_stop(struct impl *this);
4
 static int write_queued_output_buffers(struct impl *this);
5
-static const char * spa_command_to_string(const struct spa_command *command);
6
 
7
 /* Node and port functions */
8
 
9
@@ -962,7 +961,7 @@
10
 
11
    /* In here, we write as much data as possible. The device may
12
     * initially not have sufficient space, but it is possible
13
-    * that due to ongoing data consumption, it can accomodate
14
+    * that due to ongoing data consumption, it can accommodate
15
     * for more data in a next attempt, hence the "again" label.
16
     *
17
     * If during the write attempts, only a portion of a chunk
18
@@ -1051,26 +1050,6 @@
19
 }
20
 
21
 
22
-static const char * spa_command_to_string(const struct spa_command *command)
23
-{
24
-   switch (SPA_NODE_COMMAND_ID(command)) {
25
-   case SPA_NODE_COMMAND_Suspend: return "Suspend";
26
-   case SPA_NODE_COMMAND_Pause: return "Pause";
27
-   case SPA_NODE_COMMAND_Start: return "Start";
28
-   case SPA_NODE_COMMAND_Enable: return "Enable";
29
-   case SPA_NODE_COMMAND_Disable: return "Disable";
30
-   case SPA_NODE_COMMAND_Flush: return "Flush";
31
-   case SPA_NODE_COMMAND_Drain: return "Drain";
32
-   case SPA_NODE_COMMAND_Marker: return "Marker";
33
-   case SPA_NODE_COMMAND_ParamBegin: return "ParamBegin";
34
-   case SPA_NODE_COMMAND_ParamEnd: return "ParamEnd";
35
-   case SPA_NODE_COMMAND_RequestProcess: return "RequestProcess";
36
-   default: return "<unknown>";
37
-   }
38
-}
39
-
40
-
41
-
42
 /* Node and port functions */
43
 
44
 static const struct spa_dict_item node_info_items = {
45
@@ -1341,7 +1320,8 @@
46
    spa_return_val_if_fail(this != NULL, -EINVAL);
47
    spa_return_val_if_fail(command != NULL, -EINVAL);
48
 
49
-   spa_log_debug(this->log, "%p: got new command: %s", this, spa_command_to_string(command));
50
+   spa_log_debug(this->log, "%p: got new command: %s", this,
51
+       spa_debug_type_find_name(spa_type_node_command_id, SPA_NODE_COMMAND_ID(command)));
52
 
53
    switch (SPA_NODE_COMMAND_ID(command)) {
54
    case SPA_NODE_COMMAND_ParamBegin:
55
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-pcm-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-pcm-device.c Changed
183
 
1
@@ -48,6 +48,13 @@
2
 
3
    struct spa_log *log;
4
 
5
+   uint32_t info_all;
6
+   struct spa_device_info device_info;
7
+
8
+#define IDX_EnumProfile        0
9
+#define IDX_Profile        1
10
+   struct spa_param_info params2;
11
+
12
    struct spa_hook_list hooks;
13
 
14
    struct props props;
15
@@ -229,6 +236,9 @@
16
    spa_log_debug(this->log, "done enumerating PCM nodes for card %s", this->props.device);
17
    snd_ctl_close(ctl_hndl);
18
 
19
+   this->device_info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
20
+   this->paramsIDX_Profile.user++;
21
+
22
    return err;
23
 }
24
 
25
@@ -239,51 +249,54 @@
26
    uint32_t n_items = 0;
27
    snd_ctl_t *ctl_hndl;
28
    snd_ctl_card_info_t *info;
29
-   struct spa_device_info dinfo;
30
-   struct spa_param_info params2;
31
    char path128;
32
 
33
-   spa_log_debug(this->log, "open card %s", this->props.device);
34
-   if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
35
-       spa_log_error(this->log, "can't open control for card %s: %s",
36
-               this->props.device, snd_strerror(err));
37
-       return err;
38
-   }
39
-
40
-   snd_ctl_card_info_alloca(&info);
41
-   if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) {
42
-       spa_log_error(this->log, "error hardware info: %s", snd_strerror(err));
43
-       goto exit;
44
-   }
45
+   if (full)
46
+       this->device_info.change_mask = this->info_all;
47
 
48
-   dinfo = SPA_DEVICE_INFO_INIT();
49
+   if (this->device_info.change_mask) {
50
+       spa_log_debug(this->log, "open card %s", this->props.device);
51
+       if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
52
+           spa_log_error(this->log, "can't open control for card %s: %s",
53
+                   this->props.device, snd_strerror(err));
54
+           return err;
55
+       }
56
 
57
-   dinfo.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS;
58
+       snd_ctl_card_info_alloca(&info);
59
+       if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) {
60
+           spa_log_error(this->log, "error hardware info: %s", snd_strerror(err));
61
+           goto exit;
62
+       }
63
 
64
 #define ADD_ITEM(key, value) itemsn_items++ = SPA_DICT_ITEM_INIT(key, value)
65
-   snprintf(path, sizeof(path), "alsa:pcm:%s", snd_ctl_card_info_get_id(info));
66
-   ADD_ITEM(SPA_KEY_OBJECT_PATH, path);
67
-   ADD_ITEM(SPA_KEY_DEVICE_API, "alsa:pcm");
68
-   ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Audio/Device");
69
-   ADD_ITEM(SPA_KEY_API_ALSA_PATH, (char *)this->props.device);
70
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_ID, snd_ctl_card_info_get_id(info));
71
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info));
72
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER, snd_ctl_card_info_get_driver(info));
73
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_NAME, snd_ctl_card_info_get_name(info));
74
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME, snd_ctl_card_info_get_longname(info));
75
-   ADD_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME, snd_ctl_card_info_get_mixername(info));
76
-   dinfo.props = &SPA_DICT_INIT(items, n_items);
77
+       snprintf(path, sizeof(path), "alsa:pcm:%s", snd_ctl_card_info_get_id(info));
78
+       ADD_ITEM(SPA_KEY_OBJECT_PATH, path);
79
+       ADD_ITEM(SPA_KEY_DEVICE_API, "alsa:pcm");
80
+       ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Audio/Device");
81
+       ADD_ITEM(SPA_KEY_API_ALSA_PATH, (char *)this->props.device);
82
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_ID, snd_ctl_card_info_get_id(info));
83
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info));
84
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER, snd_ctl_card_info_get_driver(info));
85
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_NAME, snd_ctl_card_info_get_name(info));
86
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME, snd_ctl_card_info_get_longname(info));
87
+       ADD_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME, snd_ctl_card_info_get_mixername(info));
88
+       this->device_info.props = &SPA_DICT_INIT(items, n_items);
89
 #undef ADD_ITEM
90
 
91
-   dinfo.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
92
-   params0 = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
93
-   params1 = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
94
-   dinfo.n_params = SPA_N_ELEMENTS(params);
95
-   dinfo.params = params;
96
+       if (this->device_info.change_mask & SPA_DEVICE_CHANGE_MASK_PARAMS) {
97
+           SPA_FOR_EACH_ELEMENT_VAR(this->params, p) {
98
+               if (p->user > 0) {
99
+                   p->flags ^= SPA_PARAM_INFO_SERIAL;
100
+                   p->user = 0;
101
+               }
102
+           }
103
+       }
104
 
105
-   spa_device_emit_info(&this->hooks, &dinfo);
106
+       spa_device_emit_info(&this->hooks, &this->device_info);
107
+       this->device_info.change_mask = 0;
108
+   }
109
 
110
-      exit:
111
+exit:
112
    spa_log_debug(this->log, "close card %s", this->props.device);
113
    snd_ctl_close(ctl_hndl);
114
    return err;
115
@@ -427,6 +440,15 @@
116
    return 0;
117
 }
118
 
119
+static uint32_t find_profile_by_name(const char *name)
120
+{
121
+   if (spa_streq(name, "off"))
122
+       return 0;
123
+   else if (spa_streq(name, "on"))
124
+       return 1;
125
+   return SPA_ID_INVALID;
126
+}
127
+
128
 static int impl_set_param(void *object,
129
              uint32_t id, uint32_t flags,
130
              const struct spa_pod *param)
131
@@ -439,17 +461,32 @@
132
    switch (id) {
133
    case SPA_PARAM_Profile:
134
    {
135
-       uint32_t idx;
136
+       uint32_t idx = SPA_ID_INVALID;
137
+       const char *name = NULL;
138
 
139
-       if ((res = spa_pod_parse_object(param,
140
+       if (param == NULL) {
141
+           idx = 1;
142
+       } else if ((res = spa_pod_parse_object(param,
143
                SPA_TYPE_OBJECT_ParamProfile, NULL,
144
-               SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx))) < 0) {
145
+               SPA_PARAM_PROFILE_index, SPA_POD_OPT_Int(&idx),
146
+               SPA_PARAM_PROFILE_name, SPA_POD_OPT_String(&name))) < 0) {
147
            spa_log_warn(this->log, "can't parse profile");
148
            spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
149
            return res;
150
        }
151
+       if (idx == SPA_ID_INVALID && name == NULL) {
152
+           spa_log_warn(this->log, "profile needs name or index");
153
+           return -EINVAL;
154
+       }
155
+       if (idx == SPA_ID_INVALID)
156
+           idx = find_profile_by_name(name);
157
+       if (idx == SPA_ID_INVALID) {
158
+           spa_log_warn(this->log, "unknown profile %s", name);
159
+           return -EINVAL;
160
+       }
161
 
162
        set_profile(this, idx);
163
+       emit_info(this, false);
164
        break;
165
    }
166
    default:
167
@@ -529,6 +566,15 @@
168
    if (info && (str = spa_dict_lookup(info, SPA_KEY_API_ALSA_PATH)))
169
        snprintf(this->props.device, 64, "%s", str);
170
 
171
+   this->device_info = SPA_DEVICE_INFO_INIT();
172
+   this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS |
173
+       SPA_DEVICE_CHANGE_MASK_PARAMS;
174
+
175
+   this->paramsIDX_EnumProfile = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
176
+   this->paramsIDX_Profile = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
177
+   this->device_info.params = this->params;
178
+   this->device_info.n_params = SPA_N_ELEMENTS(this->params);
179
+
180
    return 0;
181
 }
182
 
183
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c Changed
30
 
1
@@ -458,14 +458,19 @@
2
        break;
3
 
4
    case SPA_PARAM_Buffers:
5
+   {
6
+       uint32_t min_buffers;
7
+
8
        if (!this->have_format)
9
            return -EIO;
10
        if (result.index > 0)
11
            return 0;
12
 
13
+       min_buffers = (this->quantum_limit * 4 * this->frame_scale) > this->buffer_frames ?  2 : 1;
14
+
15
        param = spa_pod_builder_add_object(&b.b,
16
            SPA_TYPE_OBJECT_ParamBuffers, id,
17
-           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS),
18
+           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(min_buffers, min_buffers, MAX_BUFFERS),
19
            SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(this->blocks),
20
            SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(
21
                            this->quantum_limit * this->frame_size * this->frame_scale,
22
@@ -473,6 +478,7 @@
23
                            INT32_MAX),
24
            SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(this->frame_size));
25
        break;
26
+   }
27
 
28
    case SPA_PARAM_Meta:
29
        switch (result.index) {
30
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-pcm.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
958
 
1
@@ -133,6 +133,8 @@
2
        state->multi_rate = spa_atob(s);
3
    } else if (spa_streq(k, "api.alsa.htimestamp")) {
4
        state->htimestamp = spa_atob(s);
5
+   } else if (spa_streq(k, "api.alsa.htimestamp.max-errors")) {
6
+       state->htimestamp_max_errors = atoi(s);
7
    } else if (spa_streq(k, "api.alsa.auto-link")) {
8
        state->auto_link = spa_atob(s);
9
    } else if (spa_streq(k, "latency.internal.rate")) {
10
@@ -411,9 +413,17 @@
11
            SPA_PROP_INFO_type, SPA_POD_String(state->clock_name),
12
            SPA_PROP_INFO_params, SPA_POD_Bool(true));
13
        break;
14
+   case 18:
15
+       param = spa_pod_builder_add_object(b,
16
+           SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
17
+           SPA_PROP_INFO_name, SPA_POD_String("api.alsa.htimestamp.max-errors"),
18
+           SPA_PROP_INFO_description, SPA_POD_String("Max errors before disabling htimestamp"),
19
+           SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(state->htimestamp_max_errors, 0, INT32_MAX),
20
+           SPA_PROP_INFO_params, SPA_POD_Bool(true));
21
+       break;
22
    // While adding params here, update the math in default too
23
    default:
24
-       idx -= 17;
25
+       idx -= 18;
26
        if (idx <= state->num_bind_ctls)
27
            param = enum_bind_ctl_propinfo(state, idx - 1, b);
28
        else
29
@@ -425,36 +435,53 @@
30
 static void add_bind_ctl_param(struct state *state, const snd_ctl_elem_value_t *elem, const snd_ctl_elem_info_t *info,
31
        struct spa_pod_builder *b)
32
 {
33
+   struct spa_pod_frame f1;
34
    char param_name1024;
35
+   unsigned int count = snd_ctl_elem_info_get_count(info);
36
+   int type = snd_ctl_elem_info_get_type(info);
37
+   bool is_array = count > 1 && type != SND_CTL_ELEM_TYPE_BYTES;
38
 
39
    snprintf(param_name, sizeof(param_name), "api.alsa.bind-ctl.%s",
40
            snd_ctl_elem_info_get_name(info));
41
    spa_pod_builder_string(b, param_name);
42
 
43
-   switch (snd_ctl_elem_info_get_type(info)) {
44
+   if (is_array)
45
+       spa_pod_builder_push_array(b, &f0);
46
+
47
+   switch (type) {
48
        case SND_CTL_ELEM_TYPE_BOOLEAN:
49
-           spa_pod_builder_bool(b, snd_ctl_elem_value_get_boolean(elem, 0));
50
+           for (unsigned int i = 0; i < count; i++)
51
+               spa_pod_builder_bool(b, snd_ctl_elem_value_get_boolean(elem, i));
52
            break;
53
 
54
        case SND_CTL_ELEM_TYPE_INTEGER:
55
-           spa_pod_builder_int(b, snd_ctl_elem_value_get_integer(elem, 0));
56
+           for (unsigned int i = 0; i < count; i++)
57
+               spa_pod_builder_int(b, snd_ctl_elem_value_get_integer(elem, i));
58
            break;
59
 
60
        case SND_CTL_ELEM_TYPE_INTEGER64:
61
-           spa_pod_builder_long(b, snd_ctl_elem_value_get_integer64(elem, 0));
62
+           for (unsigned int i = 0; i < count; i++)
63
+               spa_pod_builder_long(b, snd_ctl_elem_value_get_integer64(elem, i));
64
            break;
65
 
66
        case SND_CTL_ELEM_TYPE_ENUMERATED:
67
-           spa_pod_builder_int(b, snd_ctl_elem_value_get_enumerated(elem, 0));
68
+           for (unsigned int i = 0; i < count; i++)
69
+               spa_pod_builder_int(b, snd_ctl_elem_value_get_enumerated(elem, i));
70
+           break;
71
+
72
+       case SND_CTL_ELEM_TYPE_BYTES:
73
+           spa_pod_builder_bytes(b, snd_ctl_elem_value_get_bytes(elem), count);
74
            break;
75
 
76
        default:
77
-           // FIXME: we can probably support bytes but the length seems unknown in the API
78
            spa_log_warn(state->log, "%s ctl '%s' not supported",
79
                    snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(info)),
80
                    snd_ctl_elem_info_get_name(info));
81
            break;
82
    }
83
+
84
+   if (is_array)
85
+       spa_pod_builder_pop(b, &f0);
86
 }
87
 
88
 static void add_bind_ctl_params(struct state *state, struct spa_pod_builder *b)
89
@@ -462,6 +489,10 @@
90
    int err;
91
 
92
    for (unsigned int i = 0; i < state->num_bind_ctls; i++) {
93
+
94
+       if(!state->bound_ctlsi.value || !state->bound_ctlsi.info)
95
+           continue;
96
+
97
        err = snd_ctl_elem_read(state->ctl, state->bound_ctlsi.value);
98
        if (err < 0) {
99
            spa_log_warn(state->log, "Could not read elem value for '%s': %s",
100
@@ -530,6 +561,9 @@
101
    spa_pod_builder_string(b, "api.alsa.htimestamp");
102
    spa_pod_builder_bool(b, state->htimestamp);
103
 
104
+   spa_pod_builder_string(b, "api.alsa.htimestamp.max-errors");
105
+   spa_pod_builder_int(b, state->htimestamp_max_errors);
106
+
107
    spa_pod_builder_string(b, "latency.internal.rate");
108
    spa_pod_builder_int(b, state->process_latency.rate);
109
 
110
@@ -629,62 +663,193 @@
111
 
112
 static void bind_ctl_event(struct spa_source *source)
113
 {
114
-   // We don't know if a bound element changed or not, so let's find out
115
    struct state *state = source->data;
116
+   snd_ctl_event_t *ev;
117
+   snd_ctl_elem_id_t *id, *bound_id;
118
    snd_ctl_elem_value_t *old_value;
119
-   bool changed = false;
120
+   unsigned short revents;
121
+   int err;
122
 
123
+   // Do the same demangling of revents we do for PCM pollfds
124
+   for (int i = 0; i < state->ctl_n_fds; i++) {
125
+       state->ctl_pfdsi.revents = state->ctl_sourcesi.rmask;
126
+       state->ctl_sourcesi.rmask = 0;
127
+   }
128
+
129
+   err = snd_ctl_poll_descriptors_revents(state->ctl, state->ctl_pfds, state->ctl_n_fds, &revents);
130
+   if (SPA_UNLIKELY(err < 0)) {
131
+       spa_log_warn(state->log, "Could not read ctl revents: %s", snd_strerror(err));
132
+       return;
133
+   }
134
+
135
+   if (!revents) {
136
+       spa_log_trace(state->log, "Got a bind ctl wakeup but no actual event");
137
+       return;
138
+   }
139
+
140
+   snd_ctl_event_alloca(&ev);
141
+   snd_ctl_elem_id_alloca(&id);
142
+   snd_ctl_elem_id_alloca(&bound_id);
143
    snd_ctl_elem_value_alloca(&old_value);
144
 
145
+   while ((err = snd_ctl_read(state->ctl, ev) > 0)) {
146
+       bool changed = false;
147
+
148
+       if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM)
149
+           continue;
150
+
151
+       snd_ctl_event_elem_get_id(ev, id);
152
+
153
+       for (unsigned int i = 0; i < state->num_bind_ctls; i++) {
154
+           int err;
155
+
156
+           if(!state->bound_ctlsi.value || !state->bound_ctlsi.info)
157
+               continue;
158
+
159
+           // Check if we have the right element
160
+           snd_ctl_elem_value_get_id(state->bound_ctlsi.value, bound_id);
161
+           if (snd_ctl_elem_id_compare_set(id, bound_id) ||
162
+                   snd_ctl_elem_id_compare_numid(id, bound_id)) {
163
+               continue;
164
+           }
165
+
166
+           snd_ctl_elem_value_copy(old_value, state->bound_ctlsi.value);
167
+
168
+           err = snd_ctl_elem_read(state->ctl, state->bound_ctlsi.value);
169
+           if (err < 0) {
170
+               spa_log_warn(state->log, "Could not read ctl '%s': %s",
171
+                       state->bound_ctlsi.name, snd_strerror(err));
172
+               continue;
173
+           }
174
+
175
+           if (snd_ctl_elem_value_compare(old_value, state->bound_ctlsi.value) != 0) {
176
+               // We don't need to check all the ctls, if one changed,
177
+               // we'll emit a notification and they'll be read when
178
+               // the props are read
179
+               spa_log_debug(state->log, "bound ctl '%s' has changed", state->bound_ctlsi.name);
180
+               changed = true;
181
+               break;
182
+           }
183
+       }
184
+
185
+       if (changed) {
186
+           state->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
187
+           state->paramsNODE_Props.user++;
188
+           spa_alsa_emit_node_info(state, false);
189
+       }
190
+   }
191
+
192
+   if (err < 0 && err != -EAGAIN)
193
+       spa_log_warn(state->log, "Could not read ctl: %s", snd_strerror(err));
194
+}
195
+
196
+static void fetch_bind_ctls(struct state *state)
197
+{
198
+   snd_ctl_elem_list_t* element_list;
199
+   unsigned int elem_count = 0;
200
+   int err;
201
+
202
+   if (!state->num_bind_ctls)
203
+       return;
204
+
205
+   snd_ctl_elem_list_alloca(&element_list);
206
+
207
+   /* Get number of elements */
208
+   err = snd_ctl_elem_list(state->ctl, element_list);
209
+   if (SPA_UNLIKELY(err < 0)) {
210
+       spa_log_warn(state->log, "Couldn't get elem list count. Error: %s",
211
+                snd_strerror(err));
212
+       return;
213
+   }
214
+
215
+   elem_count = snd_ctl_elem_list_get_count(element_list);
216
+   err = snd_ctl_elem_list_alloc_space(element_list, elem_count);
217
+   if (SPA_UNLIKELY(err < 0)) {
218
+       spa_log_error(state->log, "Couldn't allocate elem_list space. Error: %s",
219
+                 snd_strerror(err));
220
+       return;
221
+   }
222
+
223
+   /* Get identifiers */
224
+   err = snd_ctl_elem_list(state->ctl, element_list);
225
+   if (SPA_UNLIKELY(err < 0)) {
226
+       spa_log_warn(state->log, "Couldn't get elem list. Error: %s",
227
+                snd_strerror(err));
228
+       goto cleanup;
229
+   }
230
+
231
+
232
    for (unsigned int i = 0; i < state->num_bind_ctls; i++) {
233
-       int err;
234
+       unsigned int numid = 0;
235
 
236
-       snd_ctl_elem_value_copy(old_value, state->bound_ctlsi.value);
237
+       for (unsigned int j = 0; j < elem_count; j++) {
238
+           const char* element_name = snd_ctl_elem_list_get_name(element_list, j);
239
 
240
-       err = snd_ctl_elem_read(state->ctl, state->bound_ctlsi.value);
241
-       if (err < 0) {
242
-           spa_log_warn(state->log, "Could not read ctl '%s': %s",
243
-                   state->bound_ctlsi.name, snd_strerror(err));
244
+           if (!strcmp(element_name, state->bound_ctlsi.name)) {
245
+               numid = snd_ctl_elem_list_get_numid(element_list, j);
246
+               break;
247
+           }
248
+       }
249
+
250
+       /* zero = invalid numid */
251
+       if (SPA_UNLIKELY(!numid)) {
252
+           spa_log_warn(state->log, "Didn't find ctl: '%s', count: %u",
253
+                    state->bound_ctlsi.name, elem_count);
254
            continue;
255
        }
256
 
257
-       if (snd_ctl_elem_value_compare(old_value, state->bound_ctlsi.value) != 0) {
258
-           // We don't need to check all the ctls, if one changed,
259
-           // we'll emit a notification and they'll be read when
260
-           // the props are read
261
-           spa_log_debug(state->log, "bound ctl '%s' has changed", state->bound_ctlsi.name);
262
-           changed = true;
263
-           break;
264
+       snd_ctl_elem_info_malloc(&state->bound_ctlsi.info);
265
+       snd_ctl_elem_info_set_numid(state->bound_ctlsi.info, numid);
266
+
267
+       err = snd_ctl_elem_info(state->ctl, state->bound_ctlsi.info);
268
+       if (SPA_UNLIKELY(err < 0)) {
269
+           spa_log_warn(state->log, "Could not read elem info for '%s': %s",
270
+                    state->bound_ctlsi.name, snd_strerror(err));
271
+
272
+           snd_ctl_elem_info_free(state->bound_ctlsi.info);
273
+           state->bound_ctlsi.info = NULL;
274
+           continue;
275
        }
276
+
277
+       snd_ctl_elem_value_malloc(&state->bound_ctlsi.value);
278
+       snd_ctl_elem_value_set_numid(state->bound_ctlsi.value, numid);
279
+
280
+       spa_log_debug(state->log, "Binding ctl for '%s'",
281
+                 snd_ctl_elem_info_get_name(state->bound_ctlsi.info));
282
    }
283
 
284
-   if (changed) {
285
-       state->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
286
-       state->paramsNODE_Props.user++;
287
-       spa_alsa_emit_node_info(state, false);
288
+cleanup:
289
+   snd_ctl_elem_list_free_space(element_list);
290
+}
291
+
292
+int open_card_ctl(struct state *state)
293
+{
294
+   int err;
295
+   char card_name256;
296
+
297
+   snprintf(card_name, sizeof(card_name), "hw:%d", state->card_index);
298
+
299
+   err = snd_ctl_open(&state->ctl, card_name, SND_CTL_NONBLOCK);
300
+   if (err < 0) {
301
+       spa_log_info(state->log, "%s could not find ctl card: %s",
302
+               card_name, snd_strerror(err));
303
+       return err;
304
    }
305
+
306
+   return 0;
307
 }
308
 
309
 static void bind_ctls_for_params(struct state *state)
310
 {
311
-   struct pollfd pfds16;
312
    int err;
313
 
314
    if (state->num_bind_ctls == 0)
315
        return;
316
 
317
    if (!state->ctl) {
318
-       char device_name256;
319
-
320
-       fill_device_name(state, NULL, device_name, sizeof(device_name));
321
-
322
-       err = snd_ctl_open(&state->ctl, device_name, SND_CTL_NONBLOCK);
323
-       if (err < 0) {
324
-           spa_log_info(state->log, "%s could not find ctl device: %s",
325
-                   state->props.device, snd_strerror(err));
326
-           state->ctl = NULL;
327
+       err = open_card_ctl(state);
328
+       if (err < 0)
329
            return;
330
-       }
331
    }
332
 
333
    state->ctl_n_fds = snd_ctl_poll_descriptors_count(state->ctl);
334
@@ -693,7 +858,7 @@
335
        state->ctl_n_fds = SPA_N_ELEMENTS(state->ctl_sources);
336
    }
337
 
338
-   if ((err = snd_ctl_poll_descriptors(state->ctl, pfds, state->ctl_n_fds)) < 0) {
339
+   if ((err = snd_ctl_poll_descriptors(state->ctl, state->ctl_pfds, state->ctl_n_fds)) < 0) {
340
        spa_log_warn(state->log, "Could not get poll descriptors: %s", snd_strerror(err));
341
        return;
342
    }
343
@@ -703,38 +868,13 @@
344
    for (int i = 0; i < state->ctl_n_fds; i++) {
345
        state->ctl_sourcesi.func = bind_ctl_event;
346
        state->ctl_sourcesi.data = state;
347
-       state->ctl_sourcesi.fd = pfdsi.fd;
348
+       state->ctl_sourcesi.fd = state->ctl_pfdsi.fd;
349
        state->ctl_sourcesi.mask = SPA_IO_IN;
350
        state->ctl_sourcesi.rmask = 0;
351
        spa_loop_add_source(state->main_loop, &state->ctl_sourcesi);
352
    }
353
 
354
-   for (unsigned int i = 0; i < state->num_bind_ctls; i++) {
355
-       snd_ctl_elem_id_t *id;
356
-
357
-       snd_ctl_elem_id_alloca(&id);
358
-       snd_ctl_elem_id_set_name(id, state->bound_ctlsi.name);
359
-       snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
360
-
361
-       snd_ctl_elem_info_malloc(&state->bound_ctlsi.info);
362
-       snd_ctl_elem_info_set_id(state->bound_ctlsi.info, id);
363
-
364
-       err = snd_ctl_elem_info(state->ctl, state->bound_ctlsi.info);
365
-       if (err < 0) {
366
-           spa_log_warn(state->log, "Could not read elem info for '%s': %s",
367
-                   state->bound_ctlsi.name, snd_strerror(err));
368
-
369
-           snd_ctl_elem_info_free(state->bound_ctlsi.info);
370
-           state->bound_ctlsi.info = NULL;
371
-           continue;
372
-       }
373
-
374
-       snd_ctl_elem_value_malloc(&state->bound_ctlsi.value);
375
-       snd_ctl_elem_value_set_id(state->bound_ctlsi.value, id);
376
-
377
-       spa_log_debug(state->log, "Binding ctl for '%s'",
378
-               snd_ctl_elem_info_get_name(state->bound_ctlsi.info));
379
-   }
380
+   fetch_bind_ctls(state);
381
 }
382
 
383
 int spa_alsa_init(struct state *state, const struct spa_dict *info)
384
@@ -753,6 +893,7 @@
385
 
386
    state->multi_rate = true;
387
    state->htimestamp = false;
388
+   state->htimestamp_max_errors = MAX_HTIMESTAMP_ERROR;
389
    for (i = 0; info && i < info->n_items; i++) {
390
        const char *k = info->itemsi.key;
391
        const char *s = info->itemsi.value;
392
@@ -762,6 +903,8 @@
393
            state->card_index = atoi(s);
394
        } else if (spa_streq(k, SPA_KEY_API_ALSA_OPEN_UCM)) {
395
            state->open_ucm = spa_atob(s);
396
+           if (state->open_ucm)
397
+               state->props.use_chmap = true;
398
        } else if (spa_streq(k, "clock.quantum-limit")) {
399
            spa_atou32(s, &state->quantum_limit, 0);
400
        } else if (spa_streq(k, SPA_KEY_API_ALSA_BIND_CTLS)) {
401
@@ -861,7 +1004,7 @@
402
    return err;
403
 }
404
 
405
-static int probe_pitch_ctl(struct state *state, const char* device_name)
406
+static int probe_pitch_ctl(struct state *state)
407
 {
408
    snd_ctl_elem_id_t *id;
409
    /* TODO: Add configuration params for the control name and units */
410
@@ -875,13 +1018,10 @@
411
    snd_lib_error_set_handler(silence_error_handler);
412
 
413
    if (!state->ctl) {
414
-       err = snd_ctl_open(&state->ctl, device_name, SND_CTL_NONBLOCK);
415
-       if (err < 0) {
416
-           spa_log_info(state->log, "%s could not find ctl device: %s",
417
-                   device_name, snd_strerror(err));
418
-           state->ctl = NULL;
419
+       err = open_card_ctl(state);
420
+       if (err < 0)
421
            goto error;
422
-       }
423
+
424
        opened = true;
425
    }
426
 
427
@@ -894,8 +1034,8 @@
428
 
429
    err = snd_ctl_elem_read(state->ctl, state->pitch_elem);
430
    if (err < 0) {
431
-       spa_log_debug(state->log, "%s: did not find ctl %s: %s",
432
-               device_name, elem_name, snd_strerror(err));
433
+       spa_log_debug(state->log, "%s: did not find ctl: %s",
434
+                elem_name, snd_strerror(err));
435
 
436
        snd_ctl_elem_value_free(state->pitch_elem);
437
        state->pitch_elem = NULL;
438
@@ -912,7 +1052,7 @@
439
    CHECK(snd_ctl_elem_write(state->ctl, state->pitch_elem), "snd_ctl_elem_write");
440
    state->last_rate = 1.0;
441
 
442
-   spa_log_info(state->log, "%s: found ctl %s", device_name, elem_name);
443
+   spa_log_info(state->log, "found ctl %s", elem_name);
444
    err = 0;
445
 error:
446
    snd_lib_error_set_handler(NULL);
447
@@ -979,7 +1119,7 @@
448
    state->sample_count = 0;
449
    state->sample_time = 0;
450
 
451
-   probe_pitch_ctl(state, device_name);
452
+   probe_pitch_ctl(state);
453
 
454
    return 0;
455
 
456
@@ -1353,6 +1493,8 @@
457
            goto skip_channels;
458
        }
459
 
460
+       spa_log_debug(state->log, "%p: using chmap", state);
461
+
462
        sanitize_map(map);
463
        spa_pod_builder_int(b, map->channels);
464
 
465
@@ -1385,10 +1527,13 @@
466
        spa_pod_builder_pop(b, &f0);
467
 
468
        if (min == max) {
469
-           if (state->default_pos.channels == min)
470
+           if (state->default_pos.channels == min) {
471
                map = &state->default_pos;
472
-           else if (min == max && min <= 8)
473
+               spa_log_debug(state->log, "%p: using provided default", state);
474
+           } else if (min <= 8) {
475
                map = &default_mapmin;
476
+               spa_log_debug(state->log, "%p: using default %d channel map", state, min);
477
+           }
478
        }
479
        if (map) {
480
            spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
481
@@ -1789,7 +1934,10 @@
482
        if (state->stream == SND_PCM_STREAM_CAPTURE)
483
            state->headroom = SPA_MAX(state->headroom, 32u);
484
    }
485
-   state->headroom = SPA_MIN(state->headroom, state->buffer_frames);
486
+   if (SPA_LIKELY(state->buffer_frames >= state->threshold))
487
+       state->headroom = SPA_MIN(state->headroom, state->buffer_frames - state->threshold);
488
+   else
489
+       state->headroom = 0;
490
 
491
    latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom));
492
    if (rate != 0 && state->rate != 0)
493
@@ -1817,7 +1965,7 @@
494
            state->have_format, state->started);
495
 
496
    state->use_mmap = !state->disable_mmap;
497
-   state->force_position = false;
498
+   state->force_rate = false;
499
 
500
    switch (fmt->media_subtype) {
501
    case SPA_MEDIA_SUBTYPE_raw:
502
@@ -1880,7 +2028,7 @@
503
                IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
504
                IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
505
                0, aes3);
506
-       state->force_position = true;
507
+       state->force_rate = true;
508
        break;
509
    }
510
    case SPA_MEDIA_SUBTYPE_dsd:
511
@@ -2135,11 +2283,11 @@
512
     * means that to adjust the playback rate, we need to apply the inverse
513
     * of the given rate. */
514
    if (state->stream == SND_PCM_STREAM_CAPTURE) {
515
-       pitch = 1000000 * state->rate_match->rate;
516
-       last_pitch = 1000000 * state->last_rate;
517
+       pitch = (uint64_t)(1000000 * state->rate_match->rate);
518
+       last_pitch = (uint64_t)(1000000 * state->last_rate);
519
    } else {
520
-       pitch = 1000000 / state->rate_match->rate;
521
-       last_pitch = 1000000 / state->last_rate;
522
+       pitch = (uint64_t)(1000000 / state->rate_match->rate);
523
+       last_pitch = (uint64_t)(1000000 / state->last_rate);
524
    }
525
 
526
    /* The pitch adjustment is limited to 1 ppm */
527
@@ -2183,14 +2331,15 @@
528
    CHECK(snd_pcm_sw_params_set_start_threshold(hndl, params, LONG_MAX), "set_start_threshold");
529
 
530
    if (state->disable_tsched) {
531
-       snd_pcm_uframes_t avail_min;
532
+       snd_pcm_uframes_t avail_min = 0;
533
 
534
        if (state->stream == SND_PCM_STREAM_PLAYBACK) {
535
            /* wake up when buffer has target frames or less data (will underrun soon) */
536
-           avail_min = state->buffer_frames - state->threshold;
537
+           if (state->buffer_frames >= (state->threshold + state->headroom))
538
+               avail_min = state->buffer_frames - (state->threshold + state->headroom);
539
        } else {
540
            /* wake up when there's target frames or more (enough for us to read and push a buffer) */
541
-           avail_min = state->threshold;
542
+           avail_min = SPA_MIN(state->threshold + state->headroom, state->buffer_frames);
543
        }
544
 
545
        CHECK(snd_pcm_sw_params_set_avail_min(hndl, params, avail_min), "set_avail_min");
546
@@ -2343,7 +2492,7 @@
547
    return 0;
548
 }
549
 
550
-static inline int check_position_config(struct state *state);
551
+static inline int check_position_config(struct state *state, bool starting);
552
 
553
 static int alsa_recover(struct state *state)
554
 {
555
@@ -2410,7 +2559,7 @@
556
    spa_list_for_each(follower, &driver->rt.followers, rt.driver_link) {
557
        if (follower != driver && follower->linked) {
558
            do_drop(follower);
559
-           check_position_config(follower);
560
+           check_position_config(follower, false);
561
        }
562
    }
563
    do_prepare(driver);
564
@@ -2429,7 +2578,7 @@
565
 static inline snd_pcm_sframes_t alsa_avail(struct state *state)
566
 {
567
    snd_pcm_sframes_t avail;
568
-   if (state->disable_tsched && !state->resample)
569
+   if (!state->matching && state->disable_tsched && !state->resample)
570
        avail = snd_pcm_avail_update(state->hndl);
571
    else
572
        avail = snd_pcm_avail(state->hndl);
573
@@ -2483,8 +2632,8 @@
574
            if (SPA_ABS(diff) < state->threshold * 3) {
575
                *delay += SPA_CLAMP(diff, -((int64_t)state->threshold), (int64_t)state->threshold);
576
                state->htimestamp_error = 0;
577
-           } else {
578
-               if (++state->htimestamp_error > MAX_HTIMESTAMP_ERROR) {
579
+           } else if (state->htimestamp_max_errors) {
580
+               if (++state->htimestamp_error > state->htimestamp_max_errors) {
581
                    spa_log_error(state->log, "%s: wrong htimestamps from driver, disabling",
582
                        state->name);
583
                    state->htimestamp_error = 0;
584
@@ -2578,7 +2727,7 @@
585
        corr = 1.0;
586
 
587
    if (diff < 0)
588
-       state->next_time += diff / corr * 1e9 / state->rate;
589
+       state->next_time += (uint64_t)(diff / corr * 1e9 / state->rate);
590
 
591
    if (SPA_UNLIKELY((state->next_time - state->base_time) > BW_PERIOD)) {
592
        state->base_time = state->next_time;
593
@@ -2602,7 +2751,7 @@
594
            SPA_FLAG_UPDATE(state->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, state->matching);
595
    }
596
 
597
-   state->next_time += state->threshold / corr * 1e9 / state->rate;
598
+   state->next_time += (uint64_t)(state->threshold / corr * 1e9 / state->rate);
599
 
600
    if (SPA_LIKELY(!follower && state->clock)) {
601
        state->clock->nsec = current_time;
602
@@ -2655,7 +2804,7 @@
603
    }
604
 }
605
 
606
-static inline int check_position_config(struct state *state)
607
+static inline int check_position_config(struct state *state, bool starting)
608
 {
609
    uint64_t target_duration;
610
    struct spa_fraction target_rate;
611
@@ -2664,15 +2813,19 @@
612
    if (SPA_UNLIKELY((pos = state->position) == NULL))
613
        return 0;
614
 
615
-   if (state->force_position ||
616
-       (state->disable_tsched && state->started && !state->following)) {
617
+   if (state->disable_tsched && (starting || state->started) && !state->following) {
618
        target_duration = state->period_frames;
619
        target_rate = SPA_FRACTION(1, state->rate);
620
        pos->clock.target_duration = target_duration;
621
        pos->clock.target_rate = target_rate;
622
    } else {
623
        target_duration = pos->clock.target_duration;
624
-       target_rate = pos->clock.target_rate;
625
+       if (state->force_rate && !state->following) {
626
+           target_rate = SPA_FRACTION(1, state->rate);
627
+           pos->clock.target_rate = target_rate;
628
+       } else {
629
+           target_rate = pos->clock.target_rate;
630
+       }
631
    }
632
    if (target_duration == 0 || target_rate.denom == 0)
633
        return -EIO;
634
@@ -2701,12 +2854,12 @@
635
    snd_pcm_uframes_t avail, delay, target;
636
    bool following = state->following;
637
 
638
-   if (SPA_UNLIKELY((res = check_position_config(state)) < 0))
639
+   if (SPA_UNLIKELY((res = check_position_config(state, false)) < 0))
640
        return res;
641
 
642
    if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) {
643
        spa_log_error(state->log, "get_status error: %s", spa_strerror(res));
644
-       state->next_time += state->threshold * 1e9 / state->rate;
645
+       state->next_time += (uint64_t)(state->threshold * 1e9 / state->rate);
646
        return res;
647
    }
648
 
649
@@ -2962,12 +3115,12 @@
650
    if (SPA_UNLIKELY(!state->alsa_started))
651
        return 0;
652
 
653
-   if (SPA_UNLIKELY((res = check_position_config(state)) < 0))
654
+   if (SPA_UNLIKELY((res = check_position_config(state, false)) < 0))
655
        return res;
656
 
657
    if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) {
658
        spa_log_error(state->log, "get_status error: %s", spa_strerror(res));
659
-       state->next_time += state->threshold * 1e9 / state->rate;
660
+       state->next_time += (uint64_t)(state->threshold * 1e9 / state->rate);
661
        return res;
662
    }
663
 
664
@@ -3174,56 +3327,10 @@
665
    return SPA_TIMESPEC_TO_NSEC(&now);
666
 }
667
 
668
-static void alsa_wakeup_event(struct spa_source *source)
669
+static inline int alsa_do_wakeup_work(struct state *state, uint64_t current_time)
670
 {
671
-   struct state *state = source->data, *follower;
672
-   uint64_t expire, current_time;
673
-   int res, suppressed;
674
-
675
-   if (SPA_UNLIKELY(state->disable_tsched)) {
676
-       /* ALSA poll fds need to be "demangled" to know whether it's a real wakeup */
677
-       int err;
678
-       unsigned short revents;
679
-
680
-       current_time = get_time_ns(state);
681
-
682
-       for (int i = 0; i < state->n_fds; i++) {
683
-           state->pfdsi.revents = state->sourcei.rmask;
684
-           /* Reset so that we only handle all our sources' events once */
685
-           state->sourcei.rmask = 0;
686
-       }
687
-
688
-       if (SPA_UNLIKELY(err = snd_pcm_poll_descriptors_revents(state->hndl,
689
-                       state->pfds, state->n_fds, &revents))) {
690
-           spa_log_error(state->log, "Could not look up revents: %s",
691
-                   snd_strerror(err));
692
-           return;
693
-       }
694
-
695
-       if (!revents) {
696
-           spa_log_trace_fp(state->log, "Woken up with no work to do");
697
-           return;
698
-       }
699
-       if (revents & POLLERR) {
700
-           spa_log_trace_fp(state->log, "poll error");
701
-           if ((res = alsa_recover(state)) < 0)
702
-               return;
703
-       }
704
-   } else {
705
-       if (SPA_LIKELY(state->started)) {
706
-           if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system,
707
-                       state->timerfd, &expire)) < 0)) {
708
-           /* we can get here when the timer is changed since the last
709
-                * timerfd wakeup, for example by do_reassign_follower() executed
710
-                * in the same epoll wakeup cycle */
711
-               if (res != -EAGAIN)
712
-                   spa_log_warn(state->log, "%p: error reading timerfd: %s",
713
-                           state, spa_strerror(res));
714
-               return;
715
-           }
716
-       }
717
-       current_time = state->next_time;
718
-   }
719
+   struct state *follower;
720
+   int res;
721
 
722
    /* first do all the sync */
723
    if (state->stream == SND_PCM_STREAM_CAPTURE)
724
@@ -3232,7 +3339,7 @@
725
        res = alsa_write_sync(state, current_time);
726
    /* we can get -EAGAIN when we need to wait some more */
727
    if (SPA_UNLIKELY(res == -EAGAIN))
728
-       goto done;
729
+       return res;
730
 
731
    spa_list_for_each(follower, &state->rt.followers, rt.driver_link) {
732
        if (follower == state)
733
@@ -3255,21 +3362,89 @@
734
    else
735
        capture_ready(state);
736
 
737
-done:
738
-   if (!state->disable_tsched) {
739
-       if (state->next_time > current_time + SPA_NSEC_PER_SEC ||
740
-           current_time > state->next_time + SPA_NSEC_PER_SEC) {
741
-           if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
742
-               spa_log_error(state->log, "%s: impossible timeout %"
743
-                   PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)",
744
-                   state->name, current_time, state->next_time,
745
-                   state->next_time - current_time, state->threshold,
746
-                   state->sample_count, suppressed);
747
-           }
748
-           state->next_time = current_time + state->threshold * 1e9 / state->rate;
749
+   return 0;
750
+}
751
+
752
+static void alsa_irq_wakeup_event(struct spa_source *source)
753
+{
754
+   struct state *state = source->data;
755
+   uint64_t current_time;
756
+   int res, err;
757
+   unsigned short revents;
758
+   snd_pcm_uframes_t havail;
759
+   snd_htimestamp_t tstamp;
760
+
761
+   // First, take a snapshot of the wakeup time
762
+   current_time = get_time_ns(state);
763
+   // If the hi-res timestamps are working, we will get a timestamp that
764
+   // is earlier then current_time
765
+   if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) == 0) {
766
+       uint64_t htime = SPA_TIMESPEC_TO_NSEC(&tstamp);
767
+       if (htime < current_time) {
768
+           current_time = htime;
769
        }
770
-       set_timeout(state, state->next_time);
771
    }
772
+
773
+   for (int i = 0; i < state->n_fds; i++) {
774
+       state->pfdsi.revents = state->sourcei.rmask;
775
+       /* Reset so that we only handle all our sources' events once */
776
+       state->sourcei.rmask = 0;
777
+   }
778
+
779
+   /* ALSA poll fds need to be "demangled" to know whether it's a real wakeup */
780
+   if (SPA_UNLIKELY(err = snd_pcm_poll_descriptors_revents(state->hndl,
781
+                   state->pfds, state->n_fds, &revents))) {
782
+       spa_log_error(state->log, "Could not look up revents: %s",
783
+               snd_strerror(err));
784
+       return;
785
+   }
786
+
787
+   if (!revents) {
788
+       spa_log_trace_fp(state->log, "Woken up with no work to do");
789
+       return;
790
+   }
791
+   if (revents & POLLERR) {
792
+       spa_log_trace_fp(state->log, "poll error");
793
+       if ((res = alsa_recover(state)) < 0)
794
+           return;
795
+   }
796
+   alsa_do_wakeup_work(state, current_time);
797
+}
798
+
799
+static void alsa_timer_wakeup_event(struct spa_source *source)
800
+{
801
+   struct state *state = source->data;
802
+   uint64_t expire, current_time;
803
+   int res, suppressed;
804
+
805
+   if (SPA_LIKELY(state->started)) {
806
+       if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system,
807
+                   state->timerfd, &expire)) < 0)) {
808
+       /* we can get here when the timer is changed since the last
809
+            * timerfd wakeup, for example by do_reassign_follower() executed
810
+            * in the same epoll wakeup cycle */
811
+           if (res != -EAGAIN)
812
+               spa_log_warn(state->log, "%p: error reading timerfd: %s",
813
+                       state, spa_strerror(res));
814
+           return;
815
+       }
816
+   }
817
+   current_time = state->next_time;
818
+
819
+   alsa_do_wakeup_work(state, current_time);
820
+
821
+   if (state->next_time > current_time + SPA_NSEC_PER_SEC ||
822
+       current_time > state->next_time + SPA_NSEC_PER_SEC) {
823
+       if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
824
+           spa_log_error(state->log, "%s: impossible timeout %"
825
+               PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)",
826
+               state->name, current_time, state->next_time,
827
+               state->next_time - current_time, state->threshold,
828
+               state->sample_count, suppressed);
829
+       }
830
+       state->next_time = (uint64_t)(current_time + state->threshold * 1e9 / state->rate);
831
+   }
832
+   set_timeout(state, state->next_time);
833
 }
834
 
835
 static void remove_sources(struct state *state)
836
@@ -3344,7 +3519,7 @@
837
    if (state->prepared)
838
        return 0;
839
 
840
-   if (check_position_config(state) < 0) {
841
+   if (check_position_config(state, true) < 0) {
842
        spa_log_error(state->log, "%s: invalid position config", state->name);
843
        return -EIO;
844
    }
845
@@ -3379,7 +3554,7 @@
846
 
847
    if (!state->disable_tsched) {
848
        /* Timer-based scheduling */
849
-       state->source0.func = alsa_wakeup_event;
850
+       state->source0.func = alsa_timer_wakeup_event;
851
        state->source0.data = state;
852
        state->source0.fd = state->timerfd;
853
        state->source0.mask = SPA_IO_IN;
854
@@ -3408,7 +3583,7 @@
855
        /* We only add the source to the data loop if we're driving.
856
         * This is done in setup_sources() */
857
        for (int i = 0; i < state->n_fds; i++) {
858
-           state->sourcei.func = alsa_wakeup_event;
859
+           state->sourcei.func = alsa_irq_wakeup_event;
860
            state->sourcei.data = state;
861
            state->sourcei.fd = state->pfdsi.fd;
862
            state->sourcei.mask = state->pfdsi.events;
863
@@ -3427,16 +3602,17 @@
864
            return err;
865
    }
866
 
867
-   state->started = true;
868
-   spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
869
-
870
    /* playback will start after first write. Without tsched, we start
871
     * right away so that the fds become active in poll right away. */
872
    if (state->stream == SND_PCM_STREAM_PLAYBACK) {
873
-       if (state->disable_tsched)
874
+       if (state->disable_tsched || state->start_delay > 0)
875
            if ((err = do_start(state)) < 0)
876
                return err;
877
    }
878
+
879
+   state->started = true;
880
+   spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
881
+
882
    return 0;
883
 }
884
 
885
@@ -3476,8 +3652,8 @@
886
    if (following != state->following) {
887
        spa_log_debug(state->log, "%p: reassign follower %d->%d", state, state->following, following);
888
        state->following = following;
889
-       setup_matching(state);
890
    }
891
+   setup_matching(state);
892
    if (state->started)
893
        spa_loop_invoke(state->data_loop, do_state_sync, 0, NULL, 0, true, state);
894
 
895
@@ -3527,28 +3703,37 @@
896
    if (state->info.change_mask) {
897
        struct spa_dict_item items7;
898
        uint32_t i, n_items = 0;
899
-       char latency64, period64, nperiods64, headroom64;
900
+       char latency64 = "", period64 = "", nperiods64 = "", headroom64 = "";
901
 
902
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa");
903
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS,
904
                state->stream == SND_PCM_STREAM_PLAYBACK ? "Audio/Sink" : "Audio/Source");
905
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true");
906
-       if (state->have_format) {
907
-           snprintf(latency, sizeof(latency), "%lu/%d", state->buffer_frames / 2, state->rate);
908
-           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency);
909
+
910
+       if (state->have_format)
911
+           snprintf(latency, sizeof(latency), "%lu/%d",
912
+                   state->buffer_frames / (2 * state->frame_scale), state->rate);
913
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency0 ? latency : NULL);
914
+
915
+       if (state->have_format)
916
            snprintf(period, sizeof(period), "%lu", state->period_frames);
917
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period);
918
+       else if (state->default_period_size)
919
+           snprintf(period, sizeof(period), "%u", state->default_period_size);
920
+       itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", period0 ? period : NULL);
921
+
922
+       if (state->have_format)
923
            snprintf(nperiods, sizeof(nperiods), "%lu",
924
                    state->period_frames != 0 ? state->buffer_frames / state->period_frames : 0);
925
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods);
926
+       else if (state->default_period_num)
927
+           snprintf(nperiods, sizeof(nperiods), "%u", state->default_period_size);
928
+       itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", nperiods0 ? nperiods : NULL);
929
+
930
+       if (state->have_format)
931
            snprintf(headroom, sizeof(headroom), "%u", state->headroom);
932
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom);
933
-       } else {
934
-           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, NULL);
935
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-size", NULL);
936
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.period-num", NULL);
937
-           itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", NULL);
938
-       }
939
+       else if (state->default_headroom)
940
+           snprintf(headroom, sizeof(headroom), "%u", state->default_headroom);
941
+       itemsn_items++ = SPA_DICT_ITEM_INIT("api.alsa.headroom", headroom0 ? headroom : NULL);
942
+
943
        state->info.props = &SPA_DICT_INIT(items, n_items);
944
 
945
        if (state->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
946
@@ -3573,7 +3758,10 @@
947
        state->port_info.change_mask = state->port_info_all;
948
    if (state->port_info.change_mask) {
949
        uint32_t i;
950
-
951
+       static const struct spa_dict_item info_items = {
952
+           { SPA_KEY_PORT_GROUP, "stream.0" },
953
+       };
954
+       state->port_info.props = &SPA_DICT_INIT_ARRAY(info_items);
955
        if (state->port_info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
956
            for (i = 0; i < state->port_info.n_params; i++) {
957
                if (state->port_paramsi.user > 0) {
958
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-pcm.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-pcm.h Changed
34
 
1
@@ -40,6 +40,7 @@
2
 #define DEFAULT_PERIOD     1024u
3
 #define DEFAULT_RATE       48000u
4
 #define DEFAULT_CHANNELS   2u
5
+/* CHMAP defaults to true when using UCM */
6
 #define DEFAULT_USE_CHMAP  false
7
 
8
 #define MAX_HTIMESTAMP_ERROR   64
9
@@ -202,6 +203,7 @@
10
    uint32_t min_delay;
11
    uint32_t max_delay;
12
    uint32_t htimestamp_error;
13
+   uint32_t htimestamp_max_errors;
14
 
15
    struct spa_fraction driver_rate;
16
    uint32_t driver_duration;
17
@@ -226,7 +228,7 @@
18
    unsigned int auto_link:1;
19
    unsigned int linked:1;
20
    unsigned int is_batch:1;
21
-   unsigned int force_position:1;
22
+   unsigned int force_rate:1;
23
 
24
    uint64_t iec958_codecs;
25
 
26
@@ -257,6 +259,7 @@
27
    /* ALSA ctls exposed as params */
28
    unsigned int num_bind_ctls;
29
    struct bound_ctl bound_ctls16;
30
+   struct pollfd ctl_pfdsMAX_POLL;
31
    struct spa_source ctl_sourcesMAX_POLL;
32
    int ctl_n_fds;
33
 
34
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-seq-bridge.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c Changed
97
 
1
@@ -225,7 +225,7 @@
2
    if (full)
3
        port->info.change_mask = port->info_all;
4
    if (port->info.change_mask) {
5
-       struct spa_dict_item items5;
6
+       struct spa_dict_item items6;
7
        uint32_t n_items = 0;
8
        int id;
9
        snd_seq_port_info_t *info;
10
@@ -234,6 +234,7 @@
11
        char name256;
12
        char path128;
13
        char alias128;
14
+       char stream32;
15
 
16
        snd_seq_port_info_alloca(&info);
17
        snd_seq_get_any_port_info(this->sys.hndl,
18
@@ -273,9 +274,12 @@
19
        }
20
        clean_name(name);
21
 
22
-       snprintf(path, sizeof(path), "alsa:seq:%s:client_%d:%s_%d",
23
+       snprintf(stream, sizeof(stream), "client_%d", port->addr.client);
24
+       clean_name(stream);
25
+
26
+       snprintf(path, sizeof(path), "alsa:seq:%s:%s:%s_%d",
27
                this->props.device,
28
-               port->addr.client,
29
+               stream,
30
                port->direction == SPA_DIRECTION_OUTPUT ? "capture" : "playback",
31
                port->addr.port);
32
        clean_name(path);
33
@@ -285,10 +289,12 @@
34
                snd_seq_port_info_get_name(info));
35
        clean_name(alias);
36
 
37
+
38
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi");
39
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_OBJECT_PATH, path);
40
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, name);
41
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, alias);
42
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, stream);
43
        if ((id = snd_seq_client_info_get_card(client_info)) != -1) {
44
            snprintf(card, sizeof(card), "%d", id);
45
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card);
46
@@ -563,7 +569,7 @@
47
            SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
48
            SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
49
            SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(
50
-                           4096, 4096, INT32_MAX),
51
+                           this->quantum_limit, this->quantum_limit, INT32_MAX),
52
            SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(1));
53
        break;
54
 
55
@@ -927,6 +933,10 @@
56
    this->info.n_params = N_NODE_PARAMS;
57
    reset_props(&this->props);
58
 
59
+   this->quantum_limit = 8192;
60
+   this->min_pool_size = 500;
61
+   this->max_pool_size = 2000;
62
+
63
    for (i = 0; info && i < info->n_items; i++) {
64
        const char *k = info->itemsi.key;
65
        const char *s = info->itemsi.value;
66
@@ -936,8 +946,14 @@
67
        } else if (spa_streq(k, "clock.name")) {
68
            spa_scnprintf(this->props.clock_name,
69
                    sizeof(this->props.clock_name), "%s", s);
70
+       } else if (spa_streq(k, "clock.quantum-limit")) {
71
+           spa_atou32(s, &this->quantum_limit, 0);
72
        } else if (spa_streq(k, SPA_KEY_API_ALSA_DISABLE_LONGNAME)) {
73
            this->props.disable_longname = spa_atob(s);
74
+       } else if (spa_streq(k, "api.alsa.seq.min-pool")) {
75
+           spa_atou32(s, &this->min_pool_size, 0);
76
+       } else if (spa_streq(k, "api.alsa.seq.max-pool")) {
77
+           spa_atou32(s, &this->max_pool_size, 0);
78
        }
79
    }
80
 
81
@@ -974,7 +990,14 @@
82
 static const struct spa_dict_item info_items = {
83
    { SPA_KEY_FACTORY_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
84
    { SPA_KEY_FACTORY_DESCRIPTION, "Bridge midi ports with the alsa sequencer API" },
85
-   { SPA_KEY_FACTORY_USAGE, ""SPA_KEY_API_ALSA_PATH"=<device>" },
86
+   { SPA_KEY_FACTORY_USAGE,
87
+       ""SPA_KEY_API_ALSA_PATH"=<device, default \"default\"> "
88
+       " clock.name=<clock name, default \"clock.system.monotonic\"> "
89
+       " clock.quantum-limit=<limit, default 8192> "
90
+       ""SPA_KEY_API_ALSA_DISABLE_LONGNAME"=<bool, default false> "
91
+       " api.alsa.seq.min-pool=<min-pool, default 500> "
92
+       " api.alsa.seq.max-pool=<max-pool, default 2000>"
93
+   },
94
 };
95
 
96
 static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items);
97
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-seq.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-seq.c Changed
209
 
1
@@ -250,7 +250,9 @@
2
    snd_seq_port_subscribe_t *sub;
3
    snd_seq_addr_t addr;
4
    snd_seq_queue_timer_t *timer;
5
+   snd_seq_client_pool_t *pool;
6
    struct seq_conn reserve16;
7
+   size_t pool_size;
8
 
9
    if (state->opened)
10
        return 0;
11
@@ -319,6 +321,31 @@
12
        spa_log_warn(state->log, "failed to set queue timer: %s", snd_strerror(res));
13
    }
14
 
15
+   /* Increase client pool sizes. This determines the max sysex message that
16
+    * can be received. */
17
+   snd_seq_client_pool_alloca(&pool);
18
+   if ((res = snd_seq_get_client_pool(state->event.hndl, pool)) < 0) {
19
+       spa_log_warn(state->log, "failed to get pool: %s", snd_strerror(res));
20
+   } else {
21
+       /* make sure we at least use the default size */
22
+       pool_size = snd_seq_client_pool_get_output_pool(pool);
23
+       pool_size = SPA_MAX(pool_size, snd_seq_client_pool_get_input_pool(pool));
24
+
25
+       /* The pool size is in cells, which are about 24 bytes long. Try to
26
+        * make sure we can fit sysex of at least twice the quantum limit. */
27
+       pool_size = SPA_MAX(pool_size, state->quantum_limit * 2 / 24);
28
+       /* The kernel ignores values larger than 2000 (by default) so clamp
29
+        * this here. It's configurable in case the kernel was modified. */
30
+       pool_size = SPA_CLAMP(pool_size, state->min_pool_size, state->max_pool_size);
31
+
32
+       snd_seq_client_pool_set_input_pool(pool, pool_size);
33
+       snd_seq_client_pool_set_output_pool(pool, pool_size);
34
+
35
+       if ((res = snd_seq_set_client_pool(state->event.hndl, pool)) < 0) {
36
+           spa_log_warn(state->log, "failed to set pool: %s", snd_strerror(res));
37
+       }
38
+   }
39
+
40
    init_ports(state);
41
 
42
    if ((res = spa_system_timerfd_create(state->data_system,
43
@@ -503,7 +530,7 @@
44
    int res;
45
 
46
    /* copy all new midi events into their port buffers */
47
-   while (snd_seq_event_input(state->event.hndl, &ev) > 0) {
48
+   while ((res = snd_seq_event_input(state->event.hndl, &ev)) > 0) {
49
        const snd_seq_addr_t *addr = &ev->source;
50
        struct seq_port *port;
51
        uint64_t ev_time, diff;
52
@@ -546,14 +573,23 @@
53
        else
54
            offset = 0;
55
 
56
-       spa_log_trace_fp(state->log, "event time:%"PRIu64" offset:%d size:%ld port:%d.%d",
57
-               ev_time, offset, size, addr->client, addr->port);
58
+       spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
59
+               ev->type, ev_time, offset, size, addr->client, addr->port);
60
 
61
        spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_Midi);
62
        spa_pod_builder_bytes(&port->builder, data, size);
63
 
64
        snd_seq_free_event(ev);
65
+
66
+       /* make sure we can fit at least one control event of max size otherwise
67
+        * we keep the event in the queue and try to copy it in the next cycle */
68
+       if (port->builder.state.offset +
69
+           sizeof(struct spa_pod_control) +
70
+           MAX_EVENT_SIZE > port->buffer->buf->datas0.maxsize)
71
+           break;
72
         }
73
+   if (res < 0 && res != -EAGAIN)
74
+       spa_log_warn(state->log, "event read failed: %s", snd_strerror(res));
75
 
76
    /* prepare a buffer on each port, some ports might have their
77
     * buffer filled above */
78
@@ -571,6 +607,12 @@
79
            port->buffer->buf->datas0.chunk->offset = 0;
80
            port->buffer->buf->datas0.chunk->size = port->builder.state.offset;
81
 
82
+           if (port->builder.state.offset > port->buffer->buf->datas0.maxsize) {
83
+               spa_log_warn(state->log, "control overflow: %d > %d",
84
+                       port->builder.state.offset,
85
+                       port->buffer->buf->datas0.maxsize);
86
+           }
87
+
88
            /* move buffer to ready queue */
89
            spa_list_remove(&port->buffer->link);
90
            SPA_FLAG_SET(port->buffer->flags, BUFFER_FLAG_OUT);
91
@@ -620,6 +662,7 @@
92
        snd_seq_event_t ev;
93
        uint64_t out_time;
94
        snd_seq_real_time_t out_rt;
95
+       long size = 0;
96
 
97
        if (!port->valid || io == NULL)
98
            continue;
99
@@ -643,37 +686,53 @@
100
        }
101
 
102
        SPA_POD_SEQUENCE_FOREACH(pod, c) {
103
-           long size;
104
+           long s, body_size;
105
+           uint8_t *body;
106
 
107
            if (c->type != SPA_CONTROL_Midi)
108
                continue;
109
 
110
-           snd_seq_ev_clear(&ev);
111
-
112
-           snd_midi_event_reset_encode(stream->codec);
113
-           if ((size = snd_midi_event_encode(stream->codec,
114
-                       SPA_POD_BODY(&c->value),
115
-                       SPA_POD_BODY_SIZE(&c->value), &ev)) <= 0) {
116
-               spa_log_warn(state->log, "failed to encode event: %s",
117
-                       snd_strerror(size));
118
-                           continue;
119
-           }
120
-
121
-           snd_seq_ev_set_source(&ev, state->event.addr.port);
122
-           snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port);
123
-
124
-           out_time = state->queue_time + NSEC_FROM_CLOCK(&state->rate, c->offset);
125
-
126
-           out_rt.tv_nsec = out_time % SPA_NSEC_PER_SEC;
127
-           out_rt.tv_sec = out_time / SPA_NSEC_PER_SEC;
128
-           snd_seq_ev_schedule_real(&ev, state->event.queue_id, 0, &out_rt);
129
-
130
-           spa_log_trace_fp(state->log, "event time:%"PRIu64" offset:%d size:%ld port:%d.%d",
131
-               out_time, c->offset, size, port->addr.client, port->addr.port);
132
-
133
-           if ((err = snd_seq_event_output(state->event.hndl, &ev)) < 0) {
134
-               spa_log_warn(state->log, "failed to output event: %s",
135
-                       snd_strerror(err));
136
+           body = SPA_POD_BODY(&c->value);
137
+           body_size = SPA_POD_BODY_SIZE(&c->value);
138
+
139
+           while (body_size > 0) {
140
+               if (size == 0)
141
+                   /* only reset when we start decoding a new message */
142
+                   snd_seq_ev_clear(&ev);
143
+
144
+               if ((s = snd_midi_event_encode(stream->codec,
145
+                           body, body_size, &ev)) < 0) {
146
+                   spa_log_warn(state->log, "failed to encode event: %s",
147
+                           snd_strerror(s));
148
+                   snd_midi_event_reset_encode(stream->codec);
149
+                   size = 0;
150
+                   break;
151
+               }
152
+               body += s;
153
+               body_size -= s;
154
+               size += s;
155
+               if (ev.type == SND_SEQ_EVENT_NONE)
156
+                   /* this can happen when the event is not complete yet, like
157
+                    * a sysex message and we need to encode some more data. */
158
+                   break;
159
+
160
+               snd_seq_ev_set_source(&ev, state->event.addr.port);
161
+               snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port);
162
+
163
+               out_time = state->queue_time + NSEC_FROM_CLOCK(&state->rate, c->offset);
164
+
165
+               out_rt.tv_nsec = out_time % SPA_NSEC_PER_SEC;
166
+               out_rt.tv_sec = out_time / SPA_NSEC_PER_SEC;
167
+               snd_seq_ev_schedule_real(&ev, state->event.queue_id, 0, &out_rt);
168
+
169
+               spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
170
+                   ev.type, out_time, c->offset, size, port->addr.client, port->addr.port);
171
+
172
+               if ((err = snd_seq_event_output(state->event.hndl, &ev)) < 0) {
173
+                   spa_log_warn(state->log, "failed to output event: %s",
174
+                           snd_strerror(err));
175
+               }
176
+               size = 0;
177
            }
178
        }
179
    }
180
@@ -738,9 +797,9 @@
181
     * use the rate correction, else we will use the rate correction only for the new
182
     * timeout. */
183
    if (state->following)
184
-       state->queue_next += state->threshold * corr * 1e9 / state->rate.denom;
185
+       state->queue_next += (uint64_t)(state->threshold * corr * 1e9 / state->rate.denom);
186
    else
187
-       state->queue_next += state->threshold * 1e9 / state->rate.denom;
188
+       state->queue_next += (uint64_t)(state->threshold * 1e9 / state->rate.denom);
189
 
190
    if ((state->next_time - state->base_time) > BW_PERIOD) {
191
        state->base_time = state->next_time;
192
@@ -748,14 +807,14 @@
193
                state, follower, corr, state->dll.bw, err,
194
                state->dll.z1, state->dll.z2, state->dll.z3);
195
    }
196
-   state->next_time += state->threshold / corr * 1e9 / state->rate.denom;
197
+   state->next_time += (uint64_t)(state->threshold / corr * 1e9 / state->rate.denom);
198
 
199
    if (!follower && state->clock) {
200
        state->clock->nsec = nsec;
201
        state->clock->rate = state->rate;
202
        state->clock->position += state->clock->duration;
203
        state->clock->duration = state->duration;
204
-       state->clock->delay = state->duration * corr;
205
+       state->clock->delay = (int64_t)(state->duration * corr);
206
        state->clock->rate_diff = corr;
207
        state->clock->next_nsec = state->next_time;
208
    }
209
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-seq.h -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-seq.h Changed
21
 
1
@@ -35,7 +35,7 @@
2
    bool disable_longname;
3
 };
4
 
5
-#define MAX_EVENT_SIZE 1024
6
+#define MAX_EVENT_SIZE 256
7
 #define MAX_PORTS 256
8
 #define MAX_BUFFERS 32
9
 
10
@@ -131,6 +131,10 @@
11
    struct spa_io_clock *clock;
12
    struct spa_io_position *position;
13
 
14
+   uint32_t quantum_limit;
15
+   uint32_t min_pool_size;
16
+   uint32_t max_pool_size;
17
+
18
    int rate_denom;
19
    uint32_t duration;
20
    uint32_t threshold;
21
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa-udev.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa-udev.c Changed
239
 
1
@@ -31,9 +31,10 @@
2
 
3
 #define MAX_CARDS  64
4
 
5
-#define ACTION_ADD 0
6
-#define ACTION_REMOVE  1
7
-#define ACTION_DISABLE 2
8
+enum action {
9
+   ACTION_CHANGE,
10
+   ACTION_REMOVE,
11
+};
12
 
13
 /* Used for unavailable devices in the card structure. */
14
 #define ID_DEVICE_NOT_SUPPORTED 0
15
@@ -92,6 +93,7 @@
16
    struct spa_source source;
17
    struct spa_source notify;
18
    unsigned int use_acp:1;
19
+   unsigned int expose_busy:1;
20
 };
21
 
22
 static int impl_udev_open(struct impl *this)
23
@@ -381,6 +383,8 @@
24
     */
25
 
26
    res = 0;
27
+   if (this->expose_busy)
28
+       return res;
29
 
30
    spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", card->card_nr);
31
 
32
@@ -715,57 +719,48 @@
33
    return card->accessible;
34
 }
35
 
36
-static void process_card(struct impl *this, uint32_t action, struct udev_device *udev_device)
37
+static void process_card(struct impl *this, enum action action, struct card *card)
38
 {
39
-   unsigned int card_nr;
40
-   struct card *card;
41
-   bool emitted;
42
-   int res;
43
-
44
-   if ((card_nr = get_card_nr(this, udev_device)) == SPA_ID_INVALID)
45
-       return;
46
-
47
-   card = find_card(this, card_nr);
48
-   if (card && card->ignored)
49
+   if (card->ignored)
50
        return;
51
 
52
    switch (action) {
53
-   case ACTION_ADD:
54
-       if (card == NULL)
55
-           card = add_card(this, card_nr, udev_device);
56
-       if (card == NULL)
57
-           return;
58
-       if (!check_access(this, card))
59
-           return;
60
-       res = emit_added_object_info(this, card);
61
-       if (res < 0) {
62
-           if (card->ignored)
63
-               spa_log_info(this->log, "ALSA card %u unavailable (%s): it is ignored",
64
-                       card->card_nr, spa_strerror(res));
65
-           else if (!card->unavailable)
66
-               spa_log_info(this->log, "ALSA card %u unavailable (%s): wait for it",
67
-                       card->card_nr, spa_strerror(res));
68
-           else
69
-               spa_log_debug(this->log, "ALSA card %u still unavailable (%s)",
70
-                       card->card_nr, spa_strerror(res));
71
-           card->unavailable = true;
72
-       } else {
73
-           if (card->unavailable)
74
-               spa_log_info(this->log, "ALSA card %u now available",
75
-                       card->card_nr);
76
-           card->unavailable = false;
77
+   case ACTION_CHANGE: {
78
+       check_access(this, card);
79
+       if (card->accessible && !card->emitted) {
80
+           int res = emit_added_object_info(this, card);
81
+           if (res < 0) {
82
+               if (card->ignored)
83
+                   spa_log_info(this->log, "ALSA card %u unavailable (%s): it is ignored",
84
+                           card->card_nr, spa_strerror(res));
85
+               else if (!card->unavailable)
86
+                   spa_log_info(this->log, "ALSA card %u unavailable (%s): wait for it",
87
+                           card->card_nr, spa_strerror(res));
88
+               else
89
+                   spa_log_debug(this->log, "ALSA card %u still unavailable (%s)",
90
+                           card->card_nr, spa_strerror(res));
91
+               card->unavailable = true;
92
+           } else {
93
+               if (card->unavailable)
94
+                   spa_log_info(this->log, "ALSA card %u now available",
95
+                           card->card_nr);
96
+               card->unavailable = false;
97
+           }
98
+       } else if (!card->accessible && card->emitted) {
99
+           card->emitted = false;
100
+
101
+           if (card->pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
102
+               spa_device_emit_object_info(&this->hooks, card->pcm_device_id, NULL);
103
+           if (card->compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED)
104
+               spa_device_emit_object_info(&this->hooks, card->compress_offload_device_id, NULL);
105
        }
106
        break;
107
-
108
+   }
109
    case ACTION_REMOVE: {
110
-       uint32_t pcm_device_id, compress_offload_device_id;
111
+       uint32_t pcm_device_id = card->pcm_device_id;
112
+       uint32_t compress_offload_device_id = card->compress_offload_device_id;
113
+       bool emitted = card->emitted;
114
 
115
-       if (card == NULL)
116
-           return;
117
-
118
-       emitted = card->emitted;
119
-       pcm_device_id = card->pcm_device_id;
120
-       compress_offload_device_id = card->compress_offload_device_id;
121
        remove_card(this, card);
122
 
123
        if (emitted) {
124
@@ -776,25 +771,25 @@
125
        }
126
        break;
127
    }
128
+   }
129
+}
130
 
131
-   case ACTION_DISABLE:
132
-       if (card == NULL)
133
-           return;
134
-       if (card->emitted) {
135
-           uint32_t pcm_device_id, compress_offload_device_id;
136
+static void process_udev_device(struct impl *this, enum action action, struct udev_device *udev_device)
137
+{
138
+   unsigned int card_nr;
139
+   struct card *card;
140
 
141
-           pcm_device_id = card->pcm_device_id;
142
-           compress_offload_device_id = card->compress_offload_device_id;
143
+   if ((card_nr = get_card_nr(this, udev_device)) == SPA_ID_INVALID)
144
+       return;
145
 
146
-           card->emitted = false;
147
+   card = find_card(this, card_nr);
148
+   if (action == ACTION_CHANGE && !card)
149
+       card = add_card(this, card_nr, udev_device);
150
 
151
-           if (pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
152
-               spa_device_emit_object_info(&this->hooks, pcm_device_id, NULL);
153
-           if (compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED)
154
-               spa_device_emit_object_info(&this->hooks, compress_offload_device_id, NULL);
155
-       }
156
-       break;
157
-   }
158
+   if (!card)
159
+       return;
160
+
161
+   process_card(this, action, card);
162
 }
163
 
164
 static int stop_inotify(struct impl *this)
165
@@ -823,8 +818,6 @@
166
        void *p, *e;
167
 
168
        len = read(source->fd, &buf, sizeof(buf));
169
-       if (len < 0 && errno != EAGAIN)
170
-           break;
171
        if (len <= 0)
172
            break;
173
 
174
@@ -842,21 +835,16 @@
175
 
176
            /* card becomes accessible or not busy */
177
            if ((event->mask & (IN_ATTRIB | IN_CLOSE_WRITE))) {
178
-               bool access;
179
                if (sscanf(event->name, "controlC%u", &card_nr) != 1 &&
180
                    sscanf(event->name, "pcmC%uD", &card_nr) != 1)
181
                    continue;
182
                if ((card = find_card(this, card_nr)) == NULL)
183
                    continue;
184
 
185
-               access = check_access(this, card);
186
-               if (access && !card->emitted)
187
-                   process_card(this, ACTION_ADD, card->udev_device);
188
-               else if (!access && card->emitted)
189
-                   process_card(this, ACTION_DISABLE, card->udev_device);
190
+               process_card(this, ACTION_CHANGE, card);
191
            }
192
            /* /dev/snd/ might have been removed */
193
-           if ((event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)))
194
+           if ((event->mask & (IN_IGNORED | IN_MOVE_SELF)))
195
                deleted = true;
196
        }
197
    }
198
@@ -875,7 +863,7 @@
199
        return -errno;
200
 
201
    res = inotify_add_watch(notify_fd, "/dev/snd",
202
-           IN_ATTRIB | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MOVE_SELF);
203
+           IN_ATTRIB | IN_CLOSE_WRITE | IN_MOVE_SELF);
204
    if (res < 0) {
205
        res = -errno;
206
        close(notify_fd);
207
@@ -915,10 +903,10 @@
208
 
209
    start_inotify(this);
210
 
211
-   if (spa_streq(action, "change")) {
212
-       process_card(this, ACTION_ADD, udev_device);
213
+   if (spa_streq(action, "add") || spa_streq(action, "change")) {
214
+       process_udev_device(this, ACTION_CHANGE, udev_device);
215
    } else if (spa_streq(action, "remove")) {
216
-       process_card(this, ACTION_REMOVE, udev_device);
217
+       process_udev_device(this, ACTION_REMOVE, udev_device);
218
    }
219
    udev_device_unref(udev_device);
220
 }
221
@@ -989,7 +977,7 @@
222
        if (udev_device == NULL)
223
            continue;
224
 
225
-       process_card(this, ACTION_ADD, udev_device);
226
+       process_udev_device(this, ACTION_CHANGE, udev_device);
227
 
228
        udev_device_unref(udev_device);
229
    }
230
@@ -1141,6 +1129,8 @@
231
    if (info) {
232
        if ((str = spa_dict_lookup(info, "alsa.use-acp")) != NULL)
233
            this->use_acp = spa_atob(str);
234
+       else if ((str = spa_dict_lookup(info, "alsa.udev.expose-busy")) != NULL)
235
+           this->expose_busy = spa_atob(str);
236
    }
237
 
238
    return 0;
239
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/alsa.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/alsa.c Changed
26
 
1
@@ -22,7 +22,9 @@
2
 extern const struct spa_handle_factory spa_alsa_compress_offload_device_factory;
3
 #endif
4
 
5
-struct spa_log_topic alsa_log_topic = SPA_LOG_TOPIC(0, "spa.alsa");
6
+SPA_LOG_TOPIC_DEFINE(alsa_log_topic, "spa.alsa");
7
+
8
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
9
 
10
 SPA_EXPORT
11
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
12
@@ -38,8 +40,13 @@
13
        *factory = &spa_alsa_sink_factory;
14
        break;
15
    case 2:
16
+#ifdef HAVE_LIBUDEV
17
        *factory = &spa_alsa_udev_factory;
18
        break;
19
+#else
20
+       (*index)++;
21
+       SPA_FALLTHROUGH;
22
+#endif
23
    case 3:
24
        *factory = &spa_alsa_pcm_device_factory;
25
        break;
26
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/meson.build Changed
26
 
1
@@ -1,11 +1,10 @@
2
 subdir('acp')
3
 subdir('mixer')
4
 
5
-spa_alsa_dependencies =  spa_dep, alsa_dep, libudev_dep, mathlib, epoll_shim_dep, libinotify_dep 
6
+spa_alsa_dependencies =  spa_dep, alsa_dep, mathlib, epoll_shim_dep, libinotify_dep 
7
 
8
 spa_alsa_sources = 'alsa.c',
9
                 'alsa.h',
10
-                'alsa-udev.c',
11
                 'alsa-acp-device.c',
12
                 'alsa-pcm-device.c',
13
                 'alsa-pcm-sink.c',
14
@@ -14,6 +13,11 @@
15
                 'alsa-seq-bridge.c',
16
                 'alsa-seq.c'
17
 
18
+if libudev_dep.found()
19
+spa_alsa_sources +=  'alsa-udev.c' 
20
+spa_alsa_dependencies +=  libudev_dep 
21
+endif
22
+
23
 if compress_offload_option.allowed()
24
   spa_alsa_sources += 'alsa-compress-offload-sink.c',
25
                        'alsa-compress-offload-device.c',
26
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-0.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-0.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-1.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-1.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-10.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-10.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-2.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-2.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-3.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-3.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-4.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-4.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-5.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-5.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-6.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-6.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-7.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-7.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-8.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-8.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/paths/hdmi-output-9.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/paths/hdmi-output-9.conf Changed
9
 
1
@@ -10,3 +10,7 @@
2
 Jack HDMI/DP
3
 append-pcm-to-name = yes
4
 required = ignore
5
+
6
+Jack HDMI
7
+append-pcm-to-name = no
8
+required = ignore
9
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/mixer/profile-sets/default.conf -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/mixer/profile-sets/default.conf Changed
166
 
1
@@ -237,6 +237,14 @@
2
 priority = 6
3
 direction = output
4
 
5
+Mapping hdmi-ac3-surround
6
+description = Digital Surround 5.1 (HDMI/AC3)
7
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,3'"}
8
+paths-output = hdmi-output-0
9
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
10
+priority = 6
11
+direction = output
12
+
13
 Mapping hdmi-stereo-extra1
14
 description = Digital Stereo (HDMI 2)
15
 device-strings = hdmi:%f,1
16
@@ -269,6 +277,14 @@
17
 priority = 6
18
 direction = output
19
 
20
+Mapping hdmi-ac3-surround-extra1
21
+description = Digital Surround 5.1 (HDMI 2/AC3)
22
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,7'"}
23
+paths-output = hdmi-output-1
24
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
25
+priority = 6
26
+direction = output
27
+
28
 Mapping hdmi-stereo-extra2
29
 description = Digital Stereo (HDMI 3)
30
 device-strings = hdmi:%f,2
31
@@ -301,6 +317,14 @@
32
 priority = 6
33
 direction = output
34
 
35
+Mapping hdmi-ac3-surround-extra2
36
+description = Digital Surround 5.1 (HDMI 3/AC3)
37
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,8'"}
38
+paths-output = hdmi-output-2
39
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
40
+priority = 6
41
+direction = output
42
+
43
 Mapping hdmi-stereo-extra3
44
 description = Digital Stereo (HDMI 4)
45
 device-strings = hdmi:%f,3
46
@@ -333,6 +357,14 @@
47
 priority = 6
48
 direction = output
49
 
50
+Mapping hdmi-ac3-surround-extra3
51
+description = Digital Surround 5.1 (HDMI 4/AC3)
52
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,9'"}
53
+paths-output = hdmi-output-3
54
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
55
+priority = 6
56
+direction = output
57
+
58
 Mapping hdmi-stereo-extra4
59
 description = Digital Stereo (HDMI 5)
60
 device-strings = hdmi:%f,4
61
@@ -365,6 +397,14 @@
62
 priority = 6
63
 direction = output
64
 
65
+Mapping hdmi-ac3-surround-extra4
66
+description = Digital Surround 5.1 (HDMI 5/AC3)
67
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,10'"}
68
+paths-output = hdmi-output-4
69
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
70
+priority = 6
71
+direction = output
72
+
73
 Mapping hdmi-stereo-extra5
74
 description = Digital Stereo (HDMI 6)
75
 device-strings = hdmi:%f,5
76
@@ -397,6 +437,14 @@
77
 priority = 6
78
 direction = output
79
 
80
+Mapping hdmi-ac3-surround-extra5
81
+description = Digital Surround 5.1 (HDMI 6/AC3)
82
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,11'"}
83
+paths-output = hdmi-output-5
84
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
85
+priority = 6
86
+direction = output
87
+
88
 Mapping hdmi-stereo-extra6
89
 description = Digital Stereo (HDMI 7)
90
 device-strings = hdmi:%f,6
91
@@ -429,6 +477,14 @@
92
 priority = 6
93
 direction = output
94
 
95
+Mapping hdmi-ac3-surround-extra6
96
+description = Digital Surround 5.1 (HDMI 7/AC3)
97
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,12'"}
98
+paths-output = hdmi-output-6
99
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
100
+priority = 6
101
+direction = output
102
+
103
 Mapping hdmi-stereo-extra7
104
 description = Digital Stereo (HDMI 8)
105
 device-strings = hdmi:%f,7
106
@@ -461,6 +517,14 @@
107
 priority = 6
108
 direction = output
109
 
110
+Mapping hdmi-ac3-surround-extra7
111
+description = Digital Surround 5.1 (HDMI 8/AC3)
112
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,13'"}
113
+paths-output = hdmi-output-7
114
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
115
+priority = 6
116
+direction = output
117
+
118
 Mapping hdmi-stereo-extra8
119
 description = Digital Stereo (HDMI 9)
120
 device-strings = hdmi:%f,8
121
@@ -493,6 +557,14 @@
122
 priority = 6
123
 direction = output
124
 
125
+Mapping hdmi-ac3-surround-extra8
126
+description = Digital Surround 5.1 (HDMI 9/AC3)
127
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,14'"}
128
+paths-output = hdmi-output-8
129
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
130
+priority = 6
131
+direction = output
132
+
133
 Mapping hdmi-stereo-extra9
134
 description = Digital Stereo (HDMI 10)
135
 device-strings = hdmi:%f,9
136
@@ -525,6 +597,14 @@
137
 priority = 6
138
 direction = output
139
 
140
+Mapping hdmi-ac3-surround-extra9
141
+description = Digital Surround 5.1 (HDMI 10/AC3)
142
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,15'"}
143
+paths-output = hdmi-output-9
144
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
145
+priority = 6
146
+direction = output
147
+
148
 Mapping hdmi-stereo-extra10
149
 description = Digital Stereo (HDMI 11)
150
 device-strings = hdmi:%f,10
151
@@ -557,6 +637,14 @@
152
 priority = 6
153
 direction = output
154
 
155
+Mapping hdmi-ac3-surround-extra10
156
+description = Digital Surround 5.1 (HDMI 11/AC3)
157
+device-strings = plug:{SLAVE="a52:%f,'hw:%f,16'"}
158
+paths-output = hdmi-output-10
159
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
160
+priority = 6
161
+direction = output
162
+
163
 Mapping multichannel-output
164
 device-strings = hw:%f
165
 channel-map = left,right,rear-left,rear-right
166
pipewire-1.0.1.tar.bz2/spa/plugins/alsa/test-timer.c -> pipewire-1.2.0.tar.gz/spa/plugins/alsa/test-timer.c Changed
34
 
1
@@ -16,7 +16,7 @@
2
 
3
 #define DEFAULT_DEVICE "hw:0"
4
 
5
-#define M_PI_M2 (M_PI + M_PI)
6
+#define M_PI_M2f (float)(M_PI+M_PI)
7
 
8
 #define BW_PERIOD  (SPA_NSEC_PER_SEC * 3)
9
 
10
@@ -63,10 +63,10 @@
11
    type *samples, v;                                       \
12
    samples = (type*)((uint8_t*)areas0.addr + (areas0.first + offset*areas0.step) / 8); \
13
    for (i = 0; i < frames; i++) {                                  \
14
-       state->accumulator += M_PI_M2 * 440 / state->rate;                  \
15
-       if (state->accumulator >= M_PI_M2)                          \
16
-           state->accumulator -= M_PI_M2;                          \
17
-       v = sin(state->accumulator) * scale;                            \
18
+       state->accumulator += M_PI_M2f * 440.0f / state->rate;                  \
19
+       if (state->accumulator >= M_PI_M2f)                         \
20
+           state->accumulator -= M_PI_M2f;                         \
21
+       v = (type)(sin(state->accumulator) * scale);                        \
22
        for (j = 0; j < state->channels; j++)                           \
23
            *samples++ = v;                                 \
24
    }                                               \
25
@@ -135,7 +135,7 @@
26
    /* set our new adjusted timeout. alternatively, this value can
27
     * instead be used to drive a resampler if this device is
28
     * slaved. */
29
-   state->next_time += state->period / corr * 1e9 / state->rate;
30
+   state->next_time += (uint64_t)(state->period / corr * 1e9 / state->rate);
31
    set_timeout(state, state->next_time);
32
 
33
    if (state->next_time - state->prev_time > BW_PERIOD) {
34
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/audioadapter.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/audioadapter.c Changed
300
 
1
@@ -13,6 +13,7 @@
2
 #include <spa/utils/names.h>
3
 #include <spa/utils/result.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/json.h>
6
 #include <spa/buffer/alloc.h>
7
 #include <spa/pod/parser.h>
8
 #include <spa/pod/filter.h>
9
@@ -27,7 +28,7 @@
10
 
11
 #undef SPA_LOG_TOPIC_DEFAULT
12
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
13
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.audioadapter");
14
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioadapter");
15
 
16
 #define DEFAULT_ALIGN  16
17
 
18
@@ -88,6 +89,7 @@
19
 
20
    unsigned int add_listener:1;
21
    unsigned int have_format:1;
22
+   unsigned int recheck_format:1;
23
    unsigned int started:1;
24
    unsigned int ready:1;
25
    unsigned int async:1;
26
@@ -301,6 +303,18 @@
27
    if (full)
28
        this->info.change_mask = this->info_all;
29
    if (this->info.change_mask) {
30
+       struct spa_dict_item *items;
31
+       uint32_t n_items = 0;
32
+
33
+       if (this->info.props)
34
+           n_items = this->info.props->n_items;
35
+       items = alloca((n_items + 2) * sizeof(struct spa_dict_item));
36
+       for (i = 0; i < n_items; i++)
37
+           itemsi = this->info.props->itemsi;
38
+       itemsn_items++ = SPA_DICT_ITEM_INIT("adapter.auto-port-config", NULL);
39
+       itemsn_items++ = SPA_DICT_ITEM_INIT("audio.adapt.follower", NULL);
40
+       this->info.props = &SPA_DICT_INIT(items, n_items);
41
+
42
        if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
43
            for (i = 0; i < this->info.n_params; i++) {
44
                if (this->paramsi.user > 0) {
45
@@ -313,6 +327,7 @@
46
        }
47
        spa_node_emit_info(&this->hooks, &this->info);
48
        this->info.change_mask = old;
49
+       spa_zero(this->info.props);
50
    }
51
 }
52
 
53
@@ -472,6 +487,13 @@
54
    return 0;
55
 }
56
 
57
+static void clear_buffers(struct impl *this)
58
+{
59
+   free(this->buffers);
60
+   this->buffers = NULL;
61
+   this->n_buffers = 0;
62
+}
63
+
64
 static int configure_format(struct impl *this, uint32_t flags, const struct spa_pod *format)
65
 {
66
    uint8_t buffer4096;
67
@@ -515,11 +537,11 @@
68
    }
69
 
70
    this->have_format = format != NULL;
71
-   if (format == NULL) {
72
-       this->n_buffers = 0;
73
-   } else if (this->target != this->follower) {
74
+   clear_buffers(this);
75
+
76
+   if (format != NULL && this->target != this->follower)
77
        res = negotiate_buffers(this);
78
-   }
79
+
80
    return res;
81
 }
82
 
83
@@ -846,11 +868,14 @@
84
    struct spa_pod_builder b = { 0 };
85
    int res;
86
 
87
-   spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format);
88
+   spa_log_debug(this->log, "%p: have_format:%d recheck:%d", this, this->have_format,
89
+           this->recheck_format);
90
 
91
-   if (this->have_format)
92
+   if (this->have_format && !this->recheck_format)
93
        return 0;
94
 
95
+   this->recheck_format = false;
96
+
97
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
98
 
99
    spa_node_send_command(this->follower,
100
@@ -1040,6 +1065,9 @@
101
    uint32_t i;
102
    int res;
103
 
104
+   if (info == NULL)
105
+       return;
106
+
107
    spa_log_debug(this->log, "%p: convert port info %s %p %08"PRIx64, this,
108
            this->direction == SPA_DIRECTION_INPUT ?
109
                "Input" : "Output", info, info->change_mask);
110
@@ -1217,6 +1245,9 @@
111
    uint32_t i;
112
    int res;
113
 
114
+   if (info == NULL)
115
+       return;
116
+
117
    if (this->follower_removing) {
118
          spa_node_emit_port_info(&this->hooks, direction, port_id, NULL);
119
          return;
120
@@ -1279,6 +1310,7 @@
121
            if (idx == IDX_EnumFormat) {
122
                spa_log_debug(this->log, "new formats");
123
                /* we will renegotiate when restarting */
124
+               this->recheck_format = true;
125
            }
126
 
127
            this->paramsidx.user++;
128
@@ -1632,11 +1664,7 @@
129
                break;
130
 
131
            done = (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED));
132
-
133
-           /* when not async, we can return the data when we are done.
134
-            * In async mode we might first need to wake up the follower
135
-            * to asynchronously provide more data for the next round. */
136
-           if (!this->async && done)
137
+           if (done)
138
                break;
139
 
140
            if (status & SPA_STATUS_NEED_DATA) {
141
@@ -1652,10 +1680,6 @@
142
                if ((fstatus & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) == 0)
143
                    break;
144
            }
145
-           /* converter produced something or is drained and we
146
-            * scheduled the follower above, we can stop now*/
147
-           if (done)
148
-               break;
149
        }
150
        if (!done)
151
            spa_node_call_xrun(&this->callbacks, 0, 0, NULL);
152
@@ -1689,6 +1713,115 @@
153
    .process = impl_node_process,
154
 };
155
 
156
+static int do_auto_port_config(struct impl *this, const char *str)
157
+{
158
+   uint32_t state = 0, i;
159
+   uint8_t buffer4096;
160
+   struct spa_pod_builder b;
161
+#define POSITION_PRESERVE 0
162
+#define POSITION_AUX 1
163
+#define POSITION_UNKNOWN 2
164
+   int res, position = POSITION_PRESERVE;
165
+   struct spa_pod *param;
166
+   uint32_t media_type, media_subtype;
167
+   bool have_format = false, monitor = false, control = false;
168
+   struct spa_audio_info format = { 0, };
169
+   enum spa_param_port_config_mode mode = SPA_PARAM_PORT_CONFIG_MODE_none;
170
+   struct spa_json it2;
171
+   char key1024, val256;
172
+
173
+   spa_json_init(&it0, str, strlen(str));
174
+   if (spa_json_enter_object(&it0, &it1) <= 0)
175
+       return -EINVAL;
176
+
177
+   while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
178
+       if (spa_json_get_string(&it1, val, sizeof(val)) <= 0)
179
+           break;
180
+
181
+       if (spa_streq(key, "mode")) {
182
+           mode = spa_debug_type_find_type_short(spa_type_param_port_config_mode, val);
183
+           if (mode == SPA_ID_INVALID)
184
+               mode = SPA_PARAM_PORT_CONFIG_MODE_none;
185
+       } else if (spa_streq(key, "monitor")) {
186
+           monitor = spa_atob(val);
187
+       } else if (spa_streq(key, "control")) {
188
+           control = spa_atob(val);
189
+       } else if (spa_streq(key, "position")) {
190
+           if (spa_streq(val, "unknown"))
191
+               position = POSITION_UNKNOWN;
192
+           else if (spa_streq(val, "aux"))
193
+               position = POSITION_AUX;
194
+           else
195
+               position = POSITION_PRESERVE;
196
+       }
197
+        }
198
+
199
+   while (true) {
200
+       struct spa_audio_info info = { 0, };
201
+       struct spa_pod *position = NULL;
202
+       uint32_t n_position = 0;
203
+
204
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
205
+       if ((res = spa_node_port_enum_params_sync(this->follower,
206
+                   this->direction, 0,
207
+                   SPA_PARAM_EnumFormat, &state,
208
+                   NULL, &param, &b)) != 1)
209
+           break;
210
+
211
+       if ((res = spa_format_parse(param, &media_type, &media_subtype)) < 0)
212
+           continue;
213
+
214
+       if (media_type != SPA_MEDIA_TYPE_audio ||
215
+           media_subtype != SPA_MEDIA_SUBTYPE_raw)
216
+           continue;
217
+
218
+       spa_pod_object_fixate((struct spa_pod_object*)param);
219
+
220
+       if (spa_pod_parse_object(param,
221
+               SPA_TYPE_OBJECT_Format, NULL,
222
+               SPA_FORMAT_AUDIO_format,        SPA_POD_Id(&info.info.raw.format),
223
+               SPA_FORMAT_AUDIO_rate,          SPA_POD_Int(&info.info.raw.rate),
224
+               SPA_FORMAT_AUDIO_channels,      SPA_POD_Int(&info.info.raw.channels),
225
+               SPA_FORMAT_AUDIO_position,      SPA_POD_OPT_Pod(&position)) < 0)
226
+           continue;
227
+
228
+       if (position != NULL)
229
+           n_position = spa_pod_copy_array(position, SPA_TYPE_Id,
230
+                   info.info.raw.position, SPA_AUDIO_MAX_CHANNELS);
231
+       if (n_position == 0 || n_position != info.info.raw.channels)
232
+           SPA_FLAG_SET(info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED);
233
+
234
+       if (format.info.raw.channels >= info.info.raw.channels)
235
+           continue;
236
+
237
+       format = info;
238
+       have_format = true;
239
+   }
240
+   if (!have_format)
241
+       return -ENOENT;
242
+
243
+   if (position == POSITION_AUX) {
244
+       for (i = 0; i < format.info.raw.channels; i++)
245
+           format.info.raw.positioni = SPA_AUDIO_CHANNEL_START_Aux + i;
246
+   } else if (position == POSITION_UNKNOWN) {
247
+       for (i = 0; i < format.info.raw.channels; i++)
248
+           format.info.raw.positioni = SPA_AUDIO_CHANNEL_UNKNOWN;
249
+   }
250
+
251
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
252
+   param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &format.info.raw);
253
+   param = spa_pod_builder_add_object(&b,
254
+       SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
255
+       SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(this->direction),
256
+       SPA_PARAM_PORT_CONFIG_mode,      SPA_POD_Id(mode),
257
+       SPA_PARAM_PORT_CONFIG_monitor,   SPA_POD_Bool(monitor),
258
+       SPA_PARAM_PORT_CONFIG_control,   SPA_POD_Bool(control),
259
+       SPA_PARAM_PORT_CONFIG_format,    SPA_POD_Pod(param));
260
+   impl_node_set_param(this, SPA_PARAM_PortConfig, 0, param);
261
+
262
+   return 0;
263
+}
264
+
265
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
266
 {
267
    struct impl *this;
268
@@ -1719,10 +1852,7 @@
269
 
270
    spa_handle_clear(this->hnd_convert);
271
 
272
-   if (this->buffers)
273
-       free(this->buffers);
274
-   this->buffers = NULL;
275
-
276
+   clear_buffers(this);
277
    return 0;
278
 }
279
 
280
@@ -1794,6 +1924,7 @@
281
    this->target = this->convert;
282
 
283
    this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
284
+       SPA_NODE_CHANGE_MASK_PROPS |
285
        SPA_NODE_CHANGE_MASK_PARAMS;
286
    this->info = SPA_NODE_INFO_INIT();
287
    this->info.flags = SPA_NODE_FLAG_RT |
288
@@ -1817,7 +1948,10 @@
289
    spa_node_add_listener(this->convert,
290
            &this->convert_listener, &convert_node_events, this);
291
 
292
-   configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
293
+   if (info && (str = spa_dict_lookup(info, "adapter.auto-port-config")) != NULL)
294
+       do_auto_port_config(this, str);
295
+   else
296
+       configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
297
 
298
    link_io(this);
299
 
300
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/audioconvert.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/audioconvert.c Changed
407
 
1
@@ -37,7 +37,7 @@
2
 
3
 #undef SPA_LOG_TOPIC_DEFAULT
4
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.audioconvert");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
7
 
8
 #define DEFAULT_RATE       48000
9
 #define DEFAULT_CHANNELS   2
10
@@ -151,6 +151,7 @@
11
    unsigned int have_latency:1;
12
 
13
    struct spa_audio_info format;
14
+   unsigned int valid:1;
15
    unsigned int have_format:1;
16
    unsigned int is_dsp:1;
17
    unsigned int is_monitor:1;
18
@@ -237,6 +238,9 @@
19
    unsigned int drained:1;
20
    unsigned int rate_adjust:1;
21
    unsigned int port_ignore_latency:1;
22
+   unsigned int monitor_passthrough:1;
23
+
24
+   char group_name128;
25
 
26
    uint32_t scratch_size;
27
    uint32_t scratch_ports;
28
@@ -285,7 +289,7 @@
29
    if (full)
30
        port->info.change_mask = port->info_all;
31
    if (port->info.change_mask) {
32
-       struct spa_dict_item items4;
33
+       struct spa_dict_item items5;
34
        uint32_t n_items = 0;
35
 
36
        if (PORT_IS_DSP(this, port->direction, port->id)) {
37
@@ -299,6 +303,8 @@
38
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "control");
39
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi");
40
        }
41
+       if (this->group_name0 != '\0')
42
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, this->group_name);
43
        port->info.props = &SPA_DICT_INIT(items, n_items);
44
 
45
        if (port->info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
46
@@ -370,6 +376,7 @@
47
        port->blocks = 1;
48
        port->stride = 1;
49
    }
50
+   port->valid = true;
51
    spa_list_init(&port->queue);
52
 
53
    spa_log_debug(this->log, "%p: add port %d:%d position:%s %d %d %d",
54
@@ -380,6 +387,16 @@
55
    return 0;
56
 }
57
 
58
+static int deinit_port(struct impl *this, enum spa_direction direction, uint32_t port_id)
59
+{
60
+   struct port *port = GET_PORT(this, direction, port_id);
61
+   if (port == NULL || !port->valid)
62
+       return -ENOENT;
63
+   port->valid = false;
64
+   spa_node_emit_port_info(&this->hooks, direction, port_id, NULL);
65
+   return 0;
66
+}
67
+
68
 static int impl_node_enum_params(void *object, int seq,
69
                 uint32_t id, uint32_t start, uint32_t num,
70
                 const struct spa_pod *filter)
71
@@ -1274,9 +1291,9 @@
72
            control, mode, dir->n_ports);
73
 
74
    for (i = 0; i < dir->n_ports; i++) {
75
-       spa_node_emit_port_info(&this->hooks, direction, i, NULL);
76
+       deinit_port(this, direction, i);
77
        if (this->monitor && direction == SPA_DIRECTION_INPUT)
78
-           spa_node_emit_port_info(&this->hooks, SPA_DIRECTION_OUTPUT, i+1, NULL);
79
+           deinit_port(this, SPA_DIRECTION_OUTPUT, i+1);
80
    }
81
 
82
    this->monitor = monitor;
83
@@ -1325,6 +1342,11 @@
84
        i = dir->n_ports++;
85
        init_port(this, direction, i, 0, false, false, true);
86
    }
87
+   /* when output is convert mode, we are in OUTPUT (merge) mode, we always output all
88
+    * the incoming data to output. When output is DSP, we need to output quantum size
89
+    * chunks. */
90
+   this->direction = this->dirSPA_DIRECTION_OUTPUT.mode == SPA_PARAM_PORT_CONFIG_MODE_convert ?
91
+       SPA_DIRECTION_OUTPUT : SPA_DIRECTION_INPUT;
92
 
93
    this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS;
94
    this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE;
95
@@ -1813,10 +1835,56 @@
96
    return 0;
97
 }
98
 
99
+static uint32_t resample_update_rate_match(struct impl *this, bool passthrough, uint32_t size, uint32_t queued)
100
+{
101
+   uint32_t delay, match_size;
102
+
103
+   if (passthrough) {
104
+       delay = 0;
105
+       match_size = size;
106
+   } else {
107
+       double rate = this->rate_scale / this->props.rate;
108
+       if (this->io_rate_match &&
109
+           SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
110
+           rate *= this->io_rate_match->rate;
111
+       resample_update_rate(&this->resample, rate);
112
+       delay = resample_delay(&this->resample);
113
+       if (this->direction == SPA_DIRECTION_INPUT)
114
+           match_size = resample_in_len(&this->resample, size);
115
+       else
116
+           match_size = resample_out_len(&this->resample, size);
117
+   }
118
+   match_size -= SPA_MIN(match_size, queued);
119
+
120
+   spa_log_trace_fp(this->log, "%p: next match %u %u %u", this, match_size, size, queued);
121
+
122
+   if (this->io_rate_match) {
123
+       this->io_rate_match->delay = delay + queued;
124
+       this->io_rate_match->size = match_size;
125
+   }
126
+   return match_size;
127
+}
128
+
129
+static inline bool resample_is_passthrough(struct impl *this)
130
+{
131
+   if (this->props.resample_disabled)
132
+       return true;
133
+   if (this->resample.i_rate != this->resample.o_rate)
134
+       return false;
135
+   if (this->rate_scale != 1.0)
136
+       return false;
137
+   if (this->rate_adjust)
138
+       return false;
139
+   if (this->io_rate_match != NULL &&
140
+       SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
141
+       return false;
142
+   return true;
143
+}
144
+
145
 static int setup_convert(struct impl *this)
146
 {
147
    struct dir *in, *out;
148
-   uint32_t i, rate, maxsize, maxports;
149
+   uint32_t i, rate, maxsize, maxports, duration;
150
    struct port *p;
151
    int res;
152
 
153
@@ -1832,7 +1900,13 @@
154
    if (!in->have_format || !out->have_format)
155
        return -EINVAL;
156
 
157
-   rate = this->io_position ? this->io_position->clock.target_rate.denom : DEFAULT_RATE;
158
+   if (this->io_position != NULL) {
159
+       rate = this->io_position->clock.target_rate.denom;
160
+       duration = this->io_position->clock.target_duration;
161
+   } else {
162
+       rate = DEFAULT_RATE;
163
+       duration = this->quantum_limit;
164
+   }
165
 
166
    /* in DSP mode we always convert to the DSP rate */
167
    if (in->mode == SPA_PARAM_PORT_CONFIG_MODE_dsp)
168
@@ -1879,6 +1953,8 @@
169
    if ((res = ensure_tmp(this, maxsize, maxports)) < 0)
170
        return res;
171
 
172
+   resample_update_rate_match(this, resample_is_passthrough(this), duration, 0);
173
+
174
    this->setup = true;
175
 
176
    emit_node_info(this, false);
177
@@ -1934,6 +2010,7 @@
178
    struct impl *this = object;
179
    uint32_t i;
180
    struct spa_hook_list save;
181
+   struct port *p;
182
 
183
    spa_return_val_if_fail(this != NULL, -EINVAL);
184
 
185
@@ -1942,10 +2019,12 @@
186
 
187
    emit_node_info(this, true);
188
    for (i = 0; i < this->dirSPA_DIRECTION_INPUT.n_ports; i++) {
189
-       emit_port_info(this, GET_IN_PORT(this, i), true);
190
+       if ((p = GET_IN_PORT(this, i)) && p->valid)
191
+           emit_port_info(this, p, true);
192
    }
193
    for (i = 0; i < this->dirSPA_DIRECTION_OUTPUT.n_ports; i++) {
194
-       emit_port_info(this, GET_OUT_PORT(this, i), true);
195
+       if ((p = GET_OUT_PORT(this, i)) && p->valid)
196
+           emit_port_info(this, p, true);
197
    }
198
    spa_hook_list_join(&this->hooks, &save);
199
 
200
@@ -2176,8 +2255,6 @@
201
        case 0: case 1:
202
        {
203
            uint32_t idx = result.index;
204
-           if (port->is_monitor)
205
-               idx = idx ^ 1;
206
            param = spa_latency_build(&b, id, &port->latencyidx);
207
            break;
208
        }
209
@@ -2243,9 +2320,6 @@
210
            this, direction, port_id, latency);
211
 
212
    port = GET_PORT(this, direction, port_id);
213
-   if (port->is_monitor)
214
-       return 0;
215
-
216
    if (latency == NULL) {
217
        info = SPA_LATENCY_INFO(other);
218
        have_latency = false;
219
@@ -2267,32 +2341,50 @@
220
            info.min_rate, info.max_rate,
221
            info.min_ns, info.max_ns);
222
 
223
-   spa_latency_info_combine_start(&info, other);
224
-   for (i = 0; i < this->dirdirection.n_ports; i++) {
225
-       oport = GET_PORT(this, direction, i);
226
-       if (oport->is_monitor || !oport->have_latency)
227
-           continue;
228
-       spa_log_debug(this->log, "%p: combine %d", this, i);
229
-       spa_latency_info_combine(&info, &oport->latencyother);
230
-   }
231
-   spa_latency_info_combine_finish(&info);
232
-
233
-   spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
234
-           info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
235
-           info.min_quantum, info.max_quantum,
236
-           info.min_rate, info.max_rate,
237
-           info.min_ns, info.max_ns);
238
-
239
-   for (i = 0; i < this->dirother.n_ports; i++) {
240
-       oport = GET_PORT(this, other, i);
241
+   if (this->monitor_passthrough) {
242
+       if (port->is_monitor)
243
+           oport = GET_PORT(this, other, port_id-1);
244
+       else if (this->monitor && direction == SPA_DIRECTION_INPUT)
245
+           oport = GET_PORT(this, other, port_id+1);
246
+       else
247
+           return 0;
248
 
249
-       spa_log_debug(this->log, "%p: change %d", this, i);
250
-       if (spa_latency_info_compare(&info, &oport->latencyother) != 0) {
251
+       if (oport != NULL &&
252
+           spa_latency_info_compare(&info, &oport->latencyother) != 0) {
253
            oport->latencyother = info;
254
            oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
255
            oport->paramsIDX_Latency.user++;
256
            emit_port_info(this, oport, false);
257
        }
258
+   } else {
259
+       spa_latency_info_combine_start(&info, other);
260
+       for (i = 0; i < this->dirdirection.n_ports; i++) {
261
+           oport = GET_PORT(this, direction, i);
262
+           if ((oport->is_monitor) || !oport->have_latency)
263
+               continue;
264
+           spa_log_debug(this->log, "%p: combine %d", this, i);
265
+           spa_latency_info_combine(&info, &oport->latencyother);
266
+       }
267
+       spa_latency_info_combine_finish(&info);
268
+
269
+       spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
270
+               info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
271
+               info.min_quantum, info.max_quantum,
272
+               info.min_rate, info.max_rate,
273
+               info.min_ns, info.max_ns);
274
+
275
+       for (i = 0; i < this->dirother.n_ports; i++) {
276
+           oport = GET_PORT(this, other, i);
277
+           if (oport->is_monitor)
278
+               continue;
279
+           spa_log_debug(this->log, "%p: change %d", this, i);
280
+           if (spa_latency_info_compare(&info, &oport->latencyother) != 0) {
281
+               oport->latencyother = info;
282
+               oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
283
+               oport->paramsIDX_Latency.user++;
284
+               emit_port_info(this, oport, false);
285
+           }
286
+       }
287
    }
288
    if (emit) {
289
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
290
@@ -2317,7 +2409,7 @@
291
            this, direction, port_id, tag);
292
 
293
    port = GET_PORT(this, direction, port_id);
294
-   if (port->is_monitor)
295
+   if (port->is_monitor && !this->monitor_passthrough)
296
        return 0;
297
 
298
    if (tag != NULL) {
299
@@ -2736,49 +2828,6 @@
300
    return match_size;
301
 }
302
 
303
-static uint32_t resample_update_rate_match(struct impl *this, bool passthrough, uint32_t out_size, uint32_t in_queued)
304
-{
305
-   uint32_t delay, match_size;
306
-
307
-   if (passthrough) {
308
-       delay = 0;
309
-       match_size = out_size;
310
-   } else {
311
-       double rate = this->rate_scale / this->props.rate;
312
-       if (this->io_rate_match &&
313
-           SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
314
-           rate *= this->io_rate_match->rate;
315
-       resample_update_rate(&this->resample, rate);
316
-       delay = resample_delay(&this->resample);
317
-       match_size = resample_in_len(&this->resample, out_size);
318
-   }
319
-   match_size -= SPA_MIN(match_size, in_queued);
320
-
321
-   spa_log_trace_fp(this->log, "%p: next match %u", this, match_size);
322
-
323
-   if (this->io_rate_match) {
324
-       this->io_rate_match->delay = delay + in_queued;
325
-       this->io_rate_match->size = match_size;
326
-   }
327
-   return match_size;
328
-}
329
-
330
-static inline bool resample_is_passthrough(struct impl *this)
331
-{
332
-   if (this->props.resample_disabled)
333
-       return true;
334
-   if (this->resample.i_rate != this->resample.o_rate)
335
-       return false;
336
-   if (this->rate_scale != 1.0)
337
-       return false;
338
-   if (this->rate_adjust)
339
-       return false;
340
-   if (this->io_rate_match != NULL &&
341
-       SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
342
-       return false;
343
-   return true;
344
-}
345
-
346
 static uint64_t get_time_ns(struct impl *impl)
347
 {
348
    struct timespec now;
349
@@ -3007,6 +3056,9 @@
350
                        volume *= this->props.channel.mute ? 0.0f :
351
                            this->props.channel.volumesremap;
352
 
353
+                   volume = SPA_CLAMPF(volume, this->props.min_volume,
354
+                           this->props.max_volume);
355
+
356
                    mon_max = SPA_MIN(bd->maxsize / port->stride, max_in);
357
 
358
                    volume_process(&this->volume, bd->data, src_datasremap,
359
@@ -3243,10 +3295,20 @@
360
            spa_log_trace_fp(this->log, "%p: no output buffer", this);
361
        }
362
    }
363
-   if (resample_update_rate_match(this, resample_passthrough,
364
-           max_out - this->out_offset,
365
-           max_in - this->in_offset) > 0)
366
-       res |= SPA_STATUS_NEED_DATA;
367
+   {
368
+       uint32_t size, queued;
369
+
370
+       if (this->direction == SPA_DIRECTION_INPUT) {
371
+           size = max_out - this->out_offset;
372
+           queued = max_in - this->in_offset;
373
+       } else {
374
+           size = quant_samples;
375
+           queued = 0;
376
+       }
377
+       if (resample_update_rate_match(this, resample_passthrough,
378
+                   size, queued) > 0)
379
+           res |= SPA_STATUS_NEED_DATA;
380
+   }
381
 
382
    return res;
383
 }
384
@@ -3401,18 +3463,16 @@
385
        else if (spa_streq(k, "resample.prefill"))
386
            SPA_FLAG_UPDATE(this->resample.options,
387
                RESAMPLE_OPTION_PREFILL, spa_atob(s));
388
-       else if (spa_streq(k, "factory.mode")) {
389
-           if (spa_streq(s, "merge"))
390
-               this->direction = SPA_DIRECTION_OUTPUT;
391
-           else
392
-               this->direction = SPA_DIRECTION_INPUT;
393
-       }
394
        else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
395
            if (s != NULL)
396
                            this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s));
397
        }
398
        else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY))
399
            this->port_ignore_latency = spa_atob(s);
400
+       else if (spa_streq(k, SPA_KEY_PORT_GROUP))
401
+           spa_scnprintf(this->group_name, sizeof(this->group_name), "%s", s);
402
+       else if (spa_streq(k, "monitor.passthrough"))
403
+           this->monitor_passthrough = spa_atob(s);
404
        else
405
            audioconvert_set_param(this, k, s);
406
    }
407
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/biquad.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/biquad.c Changed
18
 
1
@@ -26,11 +26,11 @@
2
                double a0, double a1, double a2)
3
 {
4
    double a0_inv = 1 / a0;
5
-   bq->b0 = b0 * a0_inv;
6
-   bq->b1 = b1 * a0_inv;
7
-   bq->b2 = b2 * a0_inv;
8
-   bq->a1 = a1 * a0_inv;
9
-   bq->a2 = a2 * a0_inv;
10
+   bq->b0 = (float)(b0 * a0_inv);
11
+   bq->b1 = (float)(b1 * a0_inv);
12
+   bq->b2 = (float)(b2 * a0_inv);
13
+   bq->a1 = (float)(a1 * a0_inv);
14
+   bq->a2 = (float)(a2 * a0_inv);
15
 }
16
 
17
 static void biquad_lowpass(struct biquad *bq, double cutoff)
18
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/channelmix-ops.c Changed
19
 
1
@@ -649,7 +649,7 @@
2
                        spa_debug_type_find_short_name(spa_type_audio_channel, j + _SH));
3
 
4
            mix->matrix_origicjc++ = matrixij;
5
-           sum += fabs(matrixij);
6
+           sum += fabsf(matrixij);
7
 
8
            if (matrixij == 0.0f)
9
                spa_strbuf_append(&sb1, "      ");
10
@@ -772,7 +772,7 @@
11
    mix->process = info->process;
12
    mix->set_volume = impl_channelmix_set_volume;
13
    mix->cpu_flags = info->cpu_flags;
14
-   mix->delay = mix->rear_delay * mix->freq / 1000.0f;
15
+   mix->delay = (uint32_t)(mix->rear_delay * mix->freq / 1000.0f);
16
    mix->func_name = info->name;
17
 
18
    spa_log_debug(mix->log, "selected %s delay:%d options:%08x", info->name, mix->delay,
19
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/fmt-ops-sse2.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c Changed
26
 
1
@@ -969,7 +969,8 @@
2
        s += 4*n_channels;
3
    }
4
    for(; n < n_samples; n++) {
5
-       d0n = bswap_32(*s);
6
+       uint32_t *di = (uint32_t*)&d0n, *si = (uint32_t*)s;
7
+       *di = bswap_32(*si);
8
        s += n_channels;
9
    }
10
 }
11
@@ -1011,10 +1012,10 @@
12
        s += 4 * n_channels;
13
    }
14
    for(; n < n_samples; n++) {
15
-       d0n = bswap_32(s0);
16
-       d1n = bswap_32(s1);
17
-       d2n = bswap_32(s2);
18
-       d3n = bswap_32(s3);
19
+       *((uint32_t*)&d0n) = bswap_32(*((uint32_t*)&s0));
20
+       *((uint32_t*)&d1n) = bswap_32(*((uint32_t*)&s1));
21
+       *((uint32_t*)&d2n) = bswap_32(*((uint32_t*)&s2));
22
+       *((uint32_t*)&d3n) = bswap_32(*((uint32_t*)&s3));
23
        s += n_channels;
24
    }
25
 }
26
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/fmt-ops.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/fmt-ops.c Changed
34
 
1
@@ -468,6 +468,8 @@
2
    const struct noise_info *ninfo;
3
    uint32_t i, conv_flags, data_size3;
4
 
5
+   /* we generate int32 bits of random values. With this scale
6
+    * factor, we bring this in the -1.0, 1.0 range */
7
    conv->scale = 1.0f / (float)(INT32_MAX);
8
 
9
    /* disable dither if not needed */
10
@@ -483,6 +485,9 @@
11
        switch (conv->noise_method) {
12
        case NOISE_METHOD_NONE:
13
            conv->noise_method = NOISE_METHOD_PATTERN;
14
+           /* the pattern method does not use a random number
15
+            * but flips the noise between -(1<<(N-1)), 0 every
16
+            * 1024 samples. */
17
            conv->scale = -1.0f * (1 << (conv->noise_bits-1));
18
            break;
19
        case NOISE_METHOD_RECTANGULAR:
20
@@ -490,10 +495,13 @@
21
            SPA_FALLTHROUGH;
22
        case NOISE_METHOD_TRIANGULAR:
23
        case NOISE_METHOD_TRIANGULAR_HF:
24
+           /* Amplify the random noise, with additional
25
+            * N-1 bits of noise. */
26
            conv->scale *= (1 << (conv->noise_bits-1));
27
            break;
28
        }
29
    }
30
+   /* RECTANGULAR dither goes from -0.5, 0.5 */
31
    if (conv->noise_method < NOISE_METHOD_TRIANGULAR)
32
        conv->scale *= 0.5f;
33
 
34
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/hilbert.h -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/hilbert.h Changed
28
 
1
@@ -13,13 +13,14 @@
2
 #include <stddef.h>
3
 #include <math.h>
4
 
5
+
6
 static inline void blackman_window(float *taps, int n_taps)
7
 {
8
    int n;
9
    for (n = 0; n < n_taps; n++) {
10
-       float w = 2 * M_PI * n / (n_taps-1);
11
-       tapsn = 0.3635819 - 0.4891775 * cos(w)
12
-           + 0.1365995 * cos(2 * w) - 0.0106411 * cos(3 * w);
13
+       float w = 2.0f * (float)M_PI * n / (n_taps-1);
14
+       tapsn = 0.3635819f - 0.4891775f * cosf(w)
15
+           + 0.1365995f * cosf(2 * w) - 0.0106411f * cosf(3 * w);
16
    }
17
 }
18
 
19
@@ -33,7 +34,7 @@
20
    for (i = 0; i < n_taps; i++) {
21
        int k = -(n_taps / 2) + i;
22
        if (k & 1) {
23
-           float pk = M_PI * k;
24
+           float pk = (float)M_PI * k;
25
            tapsi *= (1.0f - cosf(pk)) / pk;
26
        } else {
27
            tapsi = 0.0f;
28
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/plugin.c Changed
15
 
1
@@ -5,10 +5,13 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_audioconvert_factory;
8
 extern const struct spa_handle_factory spa_audioadapter_factory;
9
 
10
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
11
+
12
 SPA_EXPORT
13
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
14
 {
15
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/resample-native-avx.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/resample-native-avx.c Changed
50
 
1
@@ -16,18 +16,18 @@
2
    uint32_t n_taps4 = n_taps & ~0xf;
3
 
4
    for (; i < n_taps4; i += 16) {
5
-       ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 0));
6
+       ty = _mm256_loadu_ps(s + i + 0);
7
        sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 0), sy0);
8
-       ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 8));
9
+       ty = _mm256_loadu_ps(s + i + 8);
10
        sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(taps + i + 8), sy1);
11
    }
12
    sy0 = _mm256_add_ps(sy1, sy0);
13
    sx1 = _mm256_extractf128_ps(sy0, 1);
14
    sx0 = _mm256_extractf128_ps(sy0, 0);
15
    for (; i < n_taps; i += 8) {
16
-       tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 0));
17
+       tx = _mm_loadu_ps(s + i + 0);
18
        sx0 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 0), sx0);
19
-       tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 4));
20
+       tx = _mm_loadu_ps(s + i + 4);
21
        sx1 = _mm_fmadd_ps(tx, _mm_load_ps(taps + i + 4), sx1);
22
    }
23
    sx0 = _mm_add_ps(sx0, sx1);
24
@@ -45,10 +45,10 @@
25
    uint32_t i, n_taps4 = n_taps & ~0xf;
26
 
27
    for (i = 0; i < n_taps4; i += 16) {
28
-       ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 0));
29
+       ty = _mm256_loadu_ps(s + i + 0);
30
        sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 0), sy0);
31
        sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 0), sy1);
32
-       ty = (__m256)_mm256_lddqu_si256((__m256i*)(s + i + 8));
33
+       ty = _mm256_loadu_ps(s + i + 8);
34
        sy0 = _mm256_fmadd_ps(ty, _mm256_load_ps(t0 + i + 8), sy0);
35
        sy1 = _mm256_fmadd_ps(ty, _mm256_load_ps(t1 + i + 8), sy1);
36
    }
37
@@ -56,10 +56,10 @@
38
    sx1 = _mm_add_ps(_mm256_extractf128_ps(sy1, 0), _mm256_extractf128_ps(sy1, 1));
39
 
40
    for (; i < n_taps; i += 8) {
41
-       tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 0));
42
+       tx = _mm_loadu_ps(s + i + 0);
43
        sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 0), sx0);
44
        sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 0), sx1);
45
-       tx = (__m128)_mm_lddqu_si128((__m128i*)(s + i + 4));
46
+       tx = _mm_loadu_ps(s + i + 4);
47
        sx0 = _mm_fmadd_ps(tx, _mm_load_ps(t0 + i + 4), sx0);
48
        sx1 = _mm_fmadd_ps(tx, _mm_load_ps(t1 + i + 4), sx1);
49
    }
50
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/resample-native-impl.h -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/resample-native-impl.h Changed
44
 
1
@@ -29,7 +29,7 @@
2
    uint32_t n_phases;
3
    uint32_t in_rate;
4
    uint32_t out_rate;
5
-   uint32_t phase;
6
+   float phase;
7
    uint32_t inc;
8
    uint32_t frac;
9
    uint32_t filter_stride;
10
@@ -99,7 +99,7 @@
11
        float *d = dstc;                        \
12
                                        \
13
        index = ioffs;                          \
14
-       phase = data->phase;                        \
15
+       phase = (uint32_t)data->phase;                  \
16
                                        \
17
        for (o = ooffs; o < olen && index + n_taps <= ilen; o++) {  \
18
            inner_product_##arch(&do, &sindex,          \
19
@@ -117,11 +117,12 @@
20
 DEFINE_RESAMPLER(inter,arch)                           \
21
 {                                      \
22
    struct native_data *data = r->data;                 \
23
-   uint32_t index, phase, stride = data->filter_stride;            \
24
+   uint32_t index, stride = data->filter_stride;               \
25
    uint32_t n_phases = data->n_phases, out_rate = data->out_rate;      \
26
    uint32_t n_taps = data->n_taps;                     \
27
    uint32_t c, o, olen = *out_len, ilen = *in_len;             \
28
    uint32_t inc = data->inc, frac = data->frac;                \
29
+   float phase;                                \
30
                                        \
31
    if (r->channels == 0)                           \
32
        return;                             \
33
@@ -134,8 +135,8 @@
34
        phase = data->phase;                        \
35
                                        \
36
        for (o = ooffs; o < olen && index + n_taps <= ilen; o++) {  \
37
-           float ph = (float)phase * n_phases / out_rate;      \
38
-           uint32_t offset = floorf(ph);               \
39
+           float ph = phase * n_phases / out_rate;         \
40
+           uint32_t offset = (uint32_t)floorf(ph);         \
41
            inner_product_ip_##arch(&do, &sindex,       \
42
                    &data->filter(offset + 0) * stride, \
43
                    &data->filter(offset + 1) * stride, \
44
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/resample-native.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/resample-native.c Changed
93
 
1
@@ -72,8 +72,8 @@
2
        for (j = 0; j < n_taps12; j++, t += 1.0) {
3
            /* exploit symmetry in filter taps */
4
            taps(n_phases - i) * stride + n_taps12 + j =
5
-               tapsi * stride + (n_taps12 - j - 1) =
6
-                   cutoff * sinc(t * cutoff) * window(t, n_taps);
7
+               tapsi * stride + (n_taps12 - j - 1) = (float)
8
+                   (cutoff * sinc(t * cutoff) * window(t, n_taps));
9
        }
10
    }
11
    return 0;
12
@@ -134,13 +134,14 @@
13
 static void impl_native_update_rate(struct resample *r, double rate)
14
 {
15
    struct native_data *data = r->data;
16
-   uint32_t in_rate, out_rate, phase, gcd, old_out_rate;
17
+   uint32_t in_rate, out_rate, gcd, old_out_rate;
18
+   float phase;
19
 
20
    if (SPA_LIKELY(data->rate == rate))
21
        return;
22
 
23
    old_out_rate = data->out_rate;
24
-   in_rate = r->i_rate / rate;
25
+   in_rate = (uint32_t)(r->i_rate / rate);
26
    out_rate = r->o_rate;
27
    phase = data->phase;
28
 
29
@@ -149,14 +150,14 @@
30
    out_rate /= gcd;
31
 
32
    data->rate = rate;
33
-   data->phase = phase * out_rate / old_out_rate;
34
+   data->phase = phase * out_rate / (float)old_out_rate;
35
    data->in_rate = in_rate;
36
    data->out_rate = out_rate;
37
 
38
    data->inc = data->in_rate / data->out_rate;
39
    data->frac = data->in_rate % data->out_rate;
40
 
41
-   if (data->in_rate == data->out_rate) {
42
+   if (data->in_rate == data->out_rate && rate == 1.0) {
43
        data->func = data->info->process_copy;
44
        r->func_name = data->info->copy_name;
45
    }
46
@@ -169,7 +170,7 @@
47
        r->func_name = data->info->inter_name;
48
    }
49
 
50
-   spa_log_trace_fp(r->log, "native %p: rate:%f in:%d out:%d gcd:%d phase:%d inc:%d frac:%d", r,
51
+   spa_log_trace_fp(r->log, "native %p: rate:%f in:%d out:%d gcd:%d phase:%f inc:%d frac:%d", r,
52
            rate, r->i_rate, r->o_rate, gcd, data->phase, data->inc, data->frac);
53
 
54
 }
55
@@ -179,7 +180,7 @@
56
    struct native_data *data = r->data;
57
    uint32_t in_len;
58
 
59
-   in_len = (data->phase + out_len * data->frac) / data->out_rate;
60
+   in_len = (uint32_t)((data->phase + out_len * data->frac) / data->out_rate);
61
    in_len += out_len * data->inc + (data->n_taps - data->hist);
62
 
63
    spa_log_trace_fp(r->log, "native %p: hist:%d %d->%d", r, data->hist, out_len, in_len);
64
@@ -187,6 +188,20 @@
65
    return in_len;
66
 }
67
 
68
+static uint32_t impl_native_out_len(struct resample *r, uint32_t in_len)
69
+{
70
+   struct native_data *data = r->data;
71
+   uint32_t out_len;
72
+
73
+   in_len = in_len - SPA_MIN(in_len, (data->n_taps - data->hist) + 1);
74
+   out_len = (uint32_t)(in_len * data->out_rate - data->phase);
75
+   out_len = (out_len + data->in_rate - 1) / data->in_rate;
76
+
77
+   spa_log_trace_fp(r->log, "native %p: hist:%d %d->%d", r, data->hist, in_len, out_len);
78
+
79
+   return out_len;
80
+}
81
+
82
 static void impl_native_process(struct resample *r,
83
        const void * SPA_RESTRICT src, uint32_t *in_len,
84
        void * SPA_RESTRICT dst, uint32_t *out_len)
85
@@ -309,6 +324,7 @@
86
    r->free = impl_native_free;
87
    r->update_rate = impl_native_update_rate;
88
    r->in_len = impl_native_in_len;
89
+   r->out_len = impl_native_out_len;
90
    r->process = impl_native_process;
91
    r->reset = impl_native_reset;
92
    r->delay = impl_native_delay;
93
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/resample-peaks.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/resample-peaks.c Changed
21
 
1
@@ -89,6 +89,11 @@
2
    return out_len;
3
 }
4
 
5
+static uint32_t impl_peaks_out_len(struct resample *r, uint32_t in_len)
6
+{
7
+   return in_len;
8
+}
9
+
10
 static void impl_peaks_reset (struct resample *r)
11
 {
12
    struct peaks_data *d = r->data;
13
@@ -119,6 +124,7 @@
14
    r->reset = impl_peaks_reset;
15
    r->delay = impl_peaks_delay;
16
    r->in_len = impl_peaks_in_len;
17
+   r->out_len = impl_peaks_out_len;
18
 
19
    spa_log_debug(r->log, "peaks %p: in:%d out:%d features:%08x:%08x", r,
20
            r->i_rate, r->o_rate, r->cpu_flags, d->peaks.cpu_flags);
21
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/test-channelmix.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/test-channelmix.c Changed
52
 
1
@@ -18,14 +18,14 @@
2
 
3
 SPA_LOG_IMPL(logger);
4
 
5
-#define MATRIX(...) (float) { __VA_ARGS__ }
6
+#define MATRIX(...) (double) { __VA_ARGS__ }
7
 
8
 #include "test-helper.h"
9
 #include "channelmix-ops.c"
10
 
11
 #define CLOSE_ENOUGH(a,b)  (fabs((a)-(b)) < 0.000001f)
12
 
13
-static void dump_matrix(struct channelmix *mix, float *coeff)
14
+static void dump_matrix(struct channelmix *mix, double *coeff)
15
 {
16
    uint32_t i, j;
17
 
18
@@ -33,13 +33,13 @@
19
        for (j = 0; j < mix->src_chan; j++) {
20
            float v = mix->matrixij;
21
            spa_log_debug(mix->log, "%d %d: %f <-> %f", i, j, v, *coeff);
22
-           spa_assert_se(CLOSE_ENOUGH(v, *coeff));
23
+           spa_assert_se(CLOSE_ENOUGH(v, (float)*coeff));
24
            coeff++;
25
        }
26
    }
27
 }
28
 
29
-static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, uint32_t dst_mask, uint32_t options, float *coeff)
30
+static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, uint32_t dst_mask, uint32_t options, double *coeff)
31
 {
32
    struct channelmix mix;
33
 
34
@@ -336,7 +336,7 @@
35
 
36
    for (i = 0; i < 16; i++) {
37
        for (j = 0; j < N_SAMPLES; j++)
38
-           src_dataij = (drand48() - 0.5f) * 2.5f;
39
+           src_dataij = (float)((drand48() - 0.5f) * 2.5f);
40
        srci = src_datai;
41
    }
42
 
43
@@ -360,7 +360,7 @@
44
    /* random matrix */
45
    for (i = 0; i < mix.dst_chan; i++) {
46
        for (j = 0; j < mix.src_chan; j++) {
47
-           mix.matrix_origij = drand48() - 0.5f;
48
+           mix.matrix_origij = (float)(drand48() - 0.5f);
49
        }
50
    }
51
    channelmix_set_volume(&mix, 1.0f, false, 0, NULL);
52
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/test-fmt-ops.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/test-fmt-ops.c Changed
10
 
1
@@ -321,7 +321,7 @@
2
 static void test_s32_f32(void)
3
 {
4
    static const int32_t in = { 0, 0x7fffff00, 0x80000000, 0x40000000, 0xc0000000 };
5
-   static const float out = { 0.0f, 0.999999880791, -1.0f, 0.5, -0.5, };
6
+   static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5, -0.5, };
7
 
8
    run_test("test_s32_f32d", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out),
9
            true, false, conv_s32_to_f32d_c);
10
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/test-peaks.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/test-peaks.c Changed
10
 
1
@@ -30,7 +30,7 @@
2
    float min2 = { 0.0f, 0.0f }, max2 = { 0.0f, 0.0f }, absmax2 = { 0.0f, 0.0f };
3
 
4
    for (i = 0; i < SPA_N_ELEMENTS(vals); i++)
5
-       valsi = (drand48() - 0.5f) * 2.5f;
6
+       valsi = (float)((drand48() - 0.5f) * 2.5f);
7
 
8
    peaks_min_max_c(&peaks, &vals1, SPA_N_ELEMENTS(vals) - 1, &min0, &max0);
9
    printf("c peaks min:%f max:%f\n", min0, max0);
10
pipewire-1.0.1.tar.bz2/spa/plugins/audioconvert/test-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/audioconvert/test-source.c Changed
99
 
1
@@ -20,7 +20,9 @@
2
 #include <spa/pod/filter.h>
3
 #include <spa/debug/types.h>
4
 
5
-#define NAME "test-source"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.test-source");
9
 
10
 #define DEFAULT_RATE       44100
11
 #define DEFAULT_CHANNELS   2
12
@@ -135,7 +137,7 @@
13
 
14
    spa_return_val_if_fail(this != NULL, -EINVAL);
15
 
16
-   spa_log_trace(this->log, NAME" %p: add listener %p", this, listener);
17
+   spa_log_trace(this->log, "%p: add listener %p", this, listener);
18
    spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
19
 
20
    emit_info(this, true);
21
@@ -494,7 +496,7 @@
22
 static int clear_buffers(struct impl *this, struct port *port)
23
 {
24
    if (port->n_buffers > 0) {
25
-       spa_log_debug(this->log, NAME " %p: clear buffers %p", this, port);
26
+       spa_log_debug(this->log, "%p: clear buffers %p", this, port);
27
        port->n_buffers = 0;
28
        spa_list_init(&port->queue);
29
    }
30
@@ -571,7 +573,7 @@
31
        port->have_format = true;
32
        port->format = info;
33
 
34
-       spa_log_debug(this->log, NAME " %p: set format on port %d %d", this, port_id, res);
35
+       spa_log_debug(this->log, "%p: set format on port %d %d", this, port_id, res);
36
    }
37
 
38
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
39
@@ -599,7 +601,7 @@
40
    spa_return_val_if_fail(object != NULL, -EINVAL);
41
    spa_return_val_if_fail(CHECK_PORT(object, direction, port_id), -EINVAL);
42
 
43
-   spa_log_debug(this->log, NAME" %p: set param %d", this, id);
44
+   spa_log_debug(this->log, "%p: set param %d", this, id);
45
 
46
    switch (id) {
47
    case SPA_PARAM_Format:
48
@@ -616,7 +618,7 @@
49
    if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
50
        spa_list_append(&port->queue, &b->link);
51
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
52
-       spa_log_trace_fp(this->log, NAME " %p: recycle buffer %d", this, b->id);
53
+       spa_log_trace_fp(this->log, "%p: recycle buffer %d", this, b->id);
54
    }
55
 }
56
 
57
@@ -637,7 +639,7 @@
58
 
59
    spa_return_val_if_fail(port->have_format, -EIO);
60
 
61
-   spa_log_debug(this->log, NAME " %p: use buffers %d on port %d", this, n_buffers, port_id);
62
+   spa_log_debug(this->log, "%p: use buffers %d on port %d", this, n_buffers, port_id);
63
 
64
    clear_buffers(this, port);
65
 
66
@@ -659,12 +661,12 @@
67
                return -EINVAL;
68
 
69
            if (dj.data == NULL) {
70
-               spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
71
+               spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
72
                          buffersi);
73
                return -EINVAL;
74
            }
75
            if (!SPA_IS_ALIGNED(dj.data, 16)) {
76
-               spa_log_warn(this->log, NAME " %p: memory %d on buffer %d not aligned",
77
+               spa_log_warn(this->log, "%p: memory %d on buffer %d not aligned",
78
                        this, j, i);
79
            }
80
        }
81
@@ -742,7 +744,7 @@
82
    if ((io = port->io) == NULL)
83
        return -EIO;
84
 
85
-   spa_log_trace_fp(this->log, NAME " %p: status %d", this, io->status);
86
+   spa_log_trace_fp(this->log, "%p: status %d", this, io->status);
87
 
88
    if (io->status == SPA_STATUS_HAVE_DATA)
89
        goto done;
90
@@ -839,7 +841,7 @@
91
            spa_atou32(s, &this->quantum_limit, 0);
92
    }
93
 
94
-   spa_log_debug(this->log, NAME " %p: init", this);
95
+   spa_log_debug(this->log, "%p: init", this);
96
    spa_hook_list_init(&this->hooks);
97
 
98
    this->node.iface = SPA_INTERFACE_INIT(
99
pipewire-1.0.1.tar.bz2/spa/plugins/audiomixer/audiomixer.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiomixer/audiomixer.c Changed
201
 
1
@@ -24,7 +24,7 @@
2
 
3
 #undef SPA_LOG_TOPIC_DEFAULT
4
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.audiomixer");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audiomixer");
7
 
8
 #define DEFAULT_RATE       48000
9
 #define DEFAULT_CHANNELS   2
10
@@ -65,7 +65,7 @@
11
 
12
    struct port_props props;
13
 
14
-   struct spa_io_buffers *io;
15
+   struct spa_io_buffers *io2;
16
 
17
    uint64_t info_all;
18
    struct spa_port_info info;
19
@@ -100,6 +100,8 @@
20
    struct spa_node_info info;
21
    struct spa_param_info params8;
22
 
23
+   struct spa_io_position *position;
24
+
25
    struct spa_hook_list hooks;
26
 
27
    uint32_t port_count;
28
@@ -120,13 +122,16 @@
29
 };
30
 
31
 #define PORT_VALID(p)                ((p) != NULL && (p)->valid)
32
+#define CHECK_ANY_IN(this,d,p)       ((d) == SPA_DIRECTION_INPUT && (p) == SPA_ID_INVALID)
33
 #define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && !PORT_VALID(this->in_ports(p)))
34
 #define CHECK_IN_PORT(this,d,p)      ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && PORT_VALID(this->in_ports(p)))
35
 #define CHECK_OUT_PORT(this,d,p)     ((d) == SPA_DIRECTION_OUTPUT && (p) == 0)
36
 #define CHECK_PORT(this,d,p)         (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p))
37
+#define CHECK_PORT_ANY(this,d,p)     (CHECK_ANY_IN(this,d,p) || CHECK_PORT(this,d,p))
38
 #define GET_IN_PORT(this,p)          (this->in_portsp)
39
 #define GET_OUT_PORT(this,p)         (&this->out_portsp)
40
 #define GET_PORT(this,d,p)           (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p))
41
+#define GET_PORT_ANY(this,d,p)       (CHECK_ANY_IN(this,d,p) ? NULL : GET_PORT(this,d,p))
42
 
43
 static int impl_node_enum_params(void *object, int seq,
44
            uint32_t id, uint32_t start, uint32_t num,
45
@@ -143,7 +148,16 @@
46
 
47
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
48
 {
49
-   return -ENOTSUP;
50
+   struct impl *this = object;
51
+
52
+   switch (id) {
53
+   case SPA_IO_Position:
54
+       this->position = data;
55
+       break;
56
+   default:
57
+       return -ENOTSUP;
58
+   }
59
+   return 0;
60
 }
61
 
62
 static int impl_node_send_command(void *object, const struct spa_command *command)
63
@@ -307,8 +321,7 @@
64
    return 0;
65
 }
66
 
67
-static int port_enum_formats(void *object,
68
-                enum spa_direction direction, uint32_t port_id,
69
+static int port_enum_formats(void *object, struct port *port,
70
                 uint32_t index,
71
                 struct spa_pod **param,
72
                 struct spa_pod_builder *builder)
73
@@ -372,9 +385,9 @@
74
 
75
    spa_return_val_if_fail(this != NULL, -EINVAL);
76
    spa_return_val_if_fail(num != 0, -EINVAL);
77
-   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
78
+   spa_return_val_if_fail(CHECK_PORT_ANY(this, direction, port_id), -EINVAL);
79
 
80
-   port = GET_PORT(this, direction, port_id);
81
+   port = GET_PORT_ANY(this, direction, port_id);
82
 
83
    result.id = id;
84
    result.next = start;
85
@@ -385,12 +398,12 @@
86
 
87
    switch (id) {
88
    case SPA_PARAM_EnumFormat:
89
-       if ((res = port_enum_formats(this, direction, port_id, result.index, &param, &b)) <= 0)
90
+       if ((res = port_enum_formats(this, port, result.index, &param, &b)) <= 0)
91
            return res;
92
        break;
93
 
94
    case SPA_PARAM_Format:
95
-       if (!port->have_format)
96
+       if (port == NULL || !port->have_format)
97
            return -EIO;
98
        if (result.index > 0)
99
            return 0;
100
@@ -399,7 +412,7 @@
101
        break;
102
 
103
    case SPA_PARAM_Buffers:
104
-       if (!port->have_format)
105
+       if (port == NULL || !port->have_format)
106
            return -EIO;
107
        if (result.index > 0)
108
            return 0;
109
@@ -434,6 +447,12 @@
110
                SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_Buffers),
111
                SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
112
            break;
113
+       case 1:
114
+           param = spa_pod_builder_add_object(&b,
115
+               SPA_TYPE_OBJECT_ParamIO, id,
116
+               SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_AsyncBuffers),
117
+               SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_async_buffers)));
118
+           break;
119
        default:
120
            return 0;
121
        }
122
@@ -681,13 +700,24 @@
123
 struct io_info {
124
    struct port *port;
125
    void *data;
126
+   size_t size;
127
 };
128
 
129
 static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
130
        const void *data, size_t size, void *user_data)
131
 {
132
    struct io_info *info = user_data;
133
-   info->port->io = info->data;
134
+   if (info->size >= sizeof(struct spa_io_async_buffers)) {
135
+       struct spa_io_async_buffers *ab = info->data;
136
+       info->port->io0 = &ab->buffersinfo->port->direction;
137
+       info->port->io1 = &ab->buffersinfo->port->direction^1;
138
+   } else if (info->size >= sizeof(struct spa_io_buffers)) {
139
+       info->port->io0 = info->data;
140
+       info->port->io1 = info->data;
141
+   } else {
142
+       info->port->io0 = NULL;
143
+       info->port->io1 = NULL;
144
+   }
145
    return 0;
146
 }
147
 
148
@@ -710,9 +740,11 @@
149
    port = GET_PORT(this, direction, port_id);
150
    info.port = port;
151
    info.data = data;
152
+   info.size = size;
153
 
154
    switch (id) {
155
    case SPA_IO_Buffers:
156
+   case SPA_IO_AsyncBuffers:
157
        spa_loop_invoke(this->data_loop,
158
                                do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
159
        break;
160
@@ -746,11 +778,12 @@
161
    struct buffer **buffers;
162
    struct buffer *outb;
163
    const void **datas;
164
+   uint32_t cycle = this->position->clock.cycle & 1;
165
 
166
    spa_return_val_if_fail(this != NULL, -EINVAL);
167
 
168
    outport = GET_OUT_PORT(this, 0);
169
-   if ((outio = outport->io) == NULL)
170
+   if ((outio = outport->iocycle) == NULL)
171
        return -EIO;
172
 
173
    spa_log_trace_fp(this->log, "%p: status %p %d %d",
174
@@ -778,16 +811,17 @@
175
        struct spa_data *bd;
176
        uint32_t size, offs;
177
 
178
-       if (SPA_UNLIKELY(!PORT_VALID(inport) ||
179
-           (inio = inport->io) == NULL ||
180
-           inio->buffer_id >= inport->n_buffers ||
181
-           inio->status != SPA_STATUS_HAVE_DATA)) {
182
-           spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d "
183
+       if (SPA_UNLIKELY(!PORT_VALID(inport) || (inio = inport->iocycle) == NULL)) {
184
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d io:%p/%p/%d",
185
+                   this, i, PORT_VALID(inport),
186
+                   inport->io0, inport->io1, cycle);
187
+           continue;
188
+       }
189
+       if (inio->buffer_id >= inport->n_buffers ||
190
+           inio->status != SPA_STATUS_HAVE_DATA) {
191
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d "
192
                    "io:%p status:%d buf_id:%d n_buffers:%d", this,
193
-               i, PORT_VALID(inport), inio,
194
-               inio ? inio->status : -1,
195
-               inio ? inio->buffer_id : SPA_ID_INVALID,
196
-               inport->n_buffers);
197
+               i, inio, inio->status, inio->buffer_id, inport->n_buffers);
198
            continue;
199
        }
200
 
201
pipewire-1.0.1.tar.bz2/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiomixer/mixer-dsp.c Changed
212
 
1
@@ -24,7 +24,7 @@
2
 
3
 #undef SPA_LOG_TOPIC_DEFAULT
4
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.mixer-dsp");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.mixer-dsp");
7
 
8
 #define MAX_BUFFERS    64
9
 #define MAX_PORTS  512
10
@@ -61,7 +61,7 @@
11
 
12
    struct port_props props;
13
 
14
-   struct spa_io_buffers *io;
15
+   struct spa_io_buffers *io2;
16
 
17
    uint64_t info_all;
18
    struct spa_port_info info;
19
@@ -96,6 +96,8 @@
20
    struct spa_node_info info;
21
    struct spa_param_info params8;
22
 
23
+   struct spa_io_position *position;
24
+
25
    struct spa_hook_list hooks;
26
 
27
    uint32_t port_count;
28
@@ -115,13 +117,16 @@
29
 };
30
 
31
 #define PORT_VALID(p)                ((p) != NULL && (p)->valid)
32
+#define CHECK_ANY_IN(this,d,p)       ((d) == SPA_DIRECTION_INPUT && (p) == SPA_ID_INVALID)
33
 #define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && !PORT_VALID(this->in_ports(p)))
34
 #define CHECK_IN_PORT(this,d,p)      ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && PORT_VALID(this->in_ports(p)))
35
 #define CHECK_OUT_PORT(this,d,p)     ((d) == SPA_DIRECTION_OUTPUT && (p) == 0)
36
 #define CHECK_PORT(this,d,p)         (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p))
37
+#define CHECK_PORT_ANY(this,d,p)     (CHECK_ANY_IN(this,d,p) || CHECK_PORT(this,d,p))
38
 #define GET_IN_PORT(this,p)          (this->in_portsp)
39
 #define GET_OUT_PORT(this,p)         (&this->out_portsp)
40
 #define GET_PORT(this,d,p)           (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p))
41
+#define GET_PORT_ANY(this,d,p)       (CHECK_ANY_IN(this,d,p) ? NULL : GET_PORT(this,d,p))
42
 
43
 static int impl_node_enum_params(void *object, int seq,
44
                 uint32_t id, uint32_t start, uint32_t num,
45
@@ -138,7 +143,16 @@
46
 
47
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
48
 {
49
-   return -ENOTSUP;
50
+   struct impl *this = object;
51
+
52
+   switch (id) {
53
+   case SPA_IO_Position:
54
+       this->position = data;
55
+       break;
56
+   default:
57
+       return -ENOTSUP;
58
+   }
59
+   return 0;
60
 }
61
 
62
 static int impl_node_send_command(void *object, const struct spa_command *command)
63
@@ -303,8 +317,7 @@
64
    return 0;
65
 }
66
 
67
-static int port_enum_formats(void *object,
68
-                enum spa_direction direction, uint32_t port_id,
69
+static int port_enum_formats(void *object, struct port *port,
70
                 uint32_t index,
71
                 struct spa_pod **param,
72
                 struct spa_pod_builder *builder)
73
@@ -347,9 +360,9 @@
74
 
75
    spa_return_val_if_fail(this != NULL, -EINVAL);
76
    spa_return_val_if_fail(num != 0, -EINVAL);
77
-   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
78
+   spa_return_val_if_fail(CHECK_PORT_ANY(this, direction, port_id), -EINVAL);
79
 
80
-   port = GET_PORT(this, direction, port_id);
81
+   port = GET_PORT_ANY(this, direction, port_id);
82
 
83
    result.id = id;
84
    result.next = start;
85
@@ -360,12 +373,12 @@
86
 
87
    switch (id) {
88
    case SPA_PARAM_EnumFormat:
89
-       if ((res = port_enum_formats(this, direction, port_id, result.index, &param, &b)) <= 0)
90
+       if ((res = port_enum_formats(this, port, result.index, &param, &b)) <= 0)
91
            return res;
92
        break;
93
 
94
    case SPA_PARAM_Format:
95
-       if (!port->have_format)
96
+       if (port == NULL || !port->have_format)
97
            return -EIO;
98
        if (result.index > 0)
99
            return 0;
100
@@ -374,7 +387,7 @@
101
        break;
102
 
103
    case SPA_PARAM_Buffers:
104
-       if (!port->have_format)
105
+       if (port == NULL || !port->have_format)
106
            return -EIO;
107
        if (result.index > 0)
108
            return 0;
109
@@ -411,6 +424,12 @@
110
                SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_Buffers),
111
                SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
112
            break;
113
+       case 1:
114
+           param = spa_pod_builder_add_object(&b,
115
+               SPA_TYPE_OBJECT_ParamIO, id,
116
+               SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_AsyncBuffers),
117
+               SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_async_buffers)));
118
+           break;
119
        default:
120
            return 0;
121
        }
122
@@ -616,13 +635,24 @@
123
 struct io_info {
124
    struct port *port;
125
    void *data;
126
+   size_t size;
127
 };
128
 
129
 static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
130
        const void *data, size_t size, void *user_data)
131
 {
132
    struct io_info *info = user_data;
133
-   info->port->io = info->data;
134
+   if (info->size >= sizeof(struct spa_io_async_buffers)) {
135
+       struct spa_io_async_buffers *ab = info->data;
136
+       info->port->io0 = &ab->buffersinfo->port->direction;
137
+       info->port->io1 = &ab->buffersinfo->port->direction^1;
138
+   } else if (info->size >= sizeof(struct spa_io_buffers)) {
139
+       info->port->io0 = info->data;
140
+       info->port->io1 = info->data;
141
+   } else {
142
+       info->port->io0 = NULL;
143
+       info->port->io1 = NULL;
144
+   }
145
    return 0;
146
 }
147
 
148
@@ -645,9 +675,11 @@
149
    port = GET_PORT(this, direction, port_id);
150
    info.port = port;
151
    info.data = data;
152
+   info.size = size;
153
 
154
    switch (id) {
155
    case SPA_IO_Buffers:
156
+   case SPA_IO_AsyncBuffers:
157
        spa_loop_invoke(this->data_loop,
158
                                do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
159
        break;
160
@@ -681,11 +713,12 @@
161
    struct buffer **buffers;
162
    struct buffer *outb;
163
    const void **datas;
164
+   uint32_t cycle = this->position->clock.cycle & 1;
165
 
166
    spa_return_val_if_fail(this != NULL, -EINVAL);
167
 
168
    outport = GET_OUT_PORT(this, 0);
169
-   if ((outio = outport->io) == NULL)
170
+   if ((outio = outport->iocycle) == NULL)
171
        return -EIO;
172
 
173
    spa_log_trace_fp(this->log, "%p: status %p %d %d",
174
@@ -713,16 +746,17 @@
175
        struct spa_data *bd;
176
        uint32_t size, offs;
177
 
178
-       if (SPA_UNLIKELY(!PORT_VALID(inport) ||
179
-           (inio = inport->io) == NULL ||
180
-           inio->buffer_id >= inport->n_buffers ||
181
-           inio->status != SPA_STATUS_HAVE_DATA)) {
182
-           spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d "
183
+       if (SPA_UNLIKELY(!PORT_VALID(inport) || (inio = inport->iocycle) == NULL)) {
184
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d io:%p/%p/%d",
185
+                   this, i, PORT_VALID(inport),
186
+                   inport->io0, inport->io1, cycle);
187
+           continue;
188
+       }
189
+       if (inio->buffer_id >= inport->n_buffers ||
190
+           inio->status != SPA_STATUS_HAVE_DATA) {
191
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d "
192
                    "io:%p status:%d buf_id:%d n_buffers:%d", this,
193
-               i, PORT_VALID(inport), inio,
194
-               inio ? inio->status : -1,
195
-               inio ? inio->buffer_id : SPA_ID_INVALID,
196
-               inport->n_buffers);
197
+               i, inio, inio->status, inio->buffer_id, inport->n_buffers);
198
            continue;
199
        }
200
 
201
@@ -733,8 +767,8 @@
202
        size = SPA_MIN(bd->maxsize - offs, bd->chunk->size);
203
        maxsize = SPA_MIN(maxsize, size);
204
 
205
-       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d %d:%d/%d %u", this,
206
-               i, inio, outio, inio->status, inio->buffer_id,
207
+       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d/%d %d:%d/%d %u", this,
208
+               i, inio, outio, inio->status, inio->buffer_id, inport->n_buffers,
209
                offs, size, (int)sizeof(float),
210
                bd->chunk->flags);
211
 
212
pipewire-1.0.1.tar.bz2/spa/plugins/audiomixer/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiomixer/plugin.c Changed
15
 
1
@@ -5,10 +5,13 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_audiomixer_factory;
8
 extern const struct spa_handle_factory spa_mixer_dsp_factory;
9
 
10
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
11
+
12
 SPA_EXPORT
13
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
14
 {
15
pipewire-1.0.1.tar.bz2/spa/plugins/audiotestsrc/audiotestsrc.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiotestsrc/audiotestsrc.c Changed
84
 
1
@@ -25,7 +25,9 @@
2
 #include <spa/pod/filter.h>
3
 #include <spa/control/control.h>
4
 
5
-#define NAME "audiotestsrc"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audiotestsrc");
9
 
10
 #define SAMPLES_TO_TIME(this,s)   ((s) * SPA_NSEC_PER_SEC / (port)->current_format.info.raw.rate)
11
 #define BYTES_TO_SAMPLES(this,b)  ((b)/(port)->bpf)
12
@@ -338,7 +340,7 @@
13
        if ((res = spa_system_timerfd_read(this->data_system,
14
                this->timer_source.fd, &expirations)) < 0) {
15
            if (res != -EAGAIN)
16
-               spa_log_error(this->log, NAME " %p: timerfd error: %s",
17
+               spa_log_error(this->log, "%p: timerfd error: %s",
18
                        this, spa_strerror(res));
19
        }
20
    }
21
@@ -369,7 +371,7 @@
22
 
23
    if (spa_list_is_empty(&port->empty)) {
24
        set_timer(this, false);
25
-       spa_log_error(this->log, NAME " %p: out of buffers", this);
26
+       spa_log_error(this->log, "%p: out of buffers", this);
27
        return -EPIPE;
28
    }
29
    b = spa_list_first(&port->empty, struct buffer, link);
30
@@ -382,7 +384,7 @@
31
 
32
    n_bytes = maxsize;
33
 
34
-   spa_log_trace(this->log, NAME " %p: dequeue buffer %d %d %d", this, b->id,
35
+   spa_log_trace(this->log, "%p: dequeue buffer %d %d %d", this, b->id,
36
              maxsize, n_bytes);
37
 
38
    filled = 0;
39
@@ -704,7 +706,7 @@
40
 static int clear_buffers(struct impl *this, struct port *port)
41
 {
42
    if (port->n_buffers > 0) {
43
-       spa_log_info(this->log, NAME " %p: clear buffers", this);
44
+       spa_log_info(this->log, "%p: clear buffers", this);
45
        port->n_buffers = 0;
46
        spa_list_init(&port->empty);
47
        this->started = false;
48
@@ -837,7 +839,7 @@
49
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
50
 
51
        if (d0.data == NULL) {
52
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
53
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
54
                      buffersi);
55
            return -EINVAL;
56
        }
57
@@ -882,7 +884,7 @@
58
    struct buffer *b = &port->buffersid;
59
    spa_return_if_fail(b->outstanding);
60
 
61
-   spa_log_trace(this->log, NAME " %p: reuse buffer %d", this, id);
62
+   spa_log_trace(this->log, "%p: reuse buffer %d", this, id);
63
 
64
    b->outstanding = false;
65
    spa_list_append(&port->empty, &b->link);
66
@@ -1102,7 +1104,7 @@
67
    port->info.n_params = 5;
68
    spa_list_init(&port->empty);
69
 
70
-   spa_log_info(this->log, NAME " %p: initialized", this);
71
+   spa_log_info(this->log, "%p: initialized", this);
72
 
73
    return 0;
74
 }
75
@@ -1141,7 +1143,7 @@
76
 
77
 const struct spa_handle_factory spa_audiotestsrc_factory = {
78
    SPA_VERSION_HANDLE_FACTORY,
79
-   NAME,
80
+   "audiotestsrc",
81
    &info,
82
    impl_get_size,
83
    impl_init,
84
pipewire-1.0.1.tar.bz2/spa/plugins/audiotestsrc/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiotestsrc/plugin.c Changed
14
 
1
@@ -5,9 +5,12 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_audiotestsrc_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/audiotestsrc/render.c -> pipewire-1.2.0.tar.gz/spa/plugins/audiotestsrc/render.c Changed
42
 
1
@@ -4,7 +4,7 @@
2
 
3
 #include <math.h>
4
 
5
-#define M_PI_M2 ( M_PI + M_PI )
6
+#define M_PI_M2f (float)(M_PI+M_PI)
7
 
8
 #define DEFINE_SINE(type,scale)                                \
9
 static void                                        \
10
@@ -17,24 +17,24 @@
11
    float volume = this->props.volume;                      \
12
                                            \
13
    channels = this->port.current_format.info.raw.channels;             \
14
-   step = M_PI_M2 * freq / this->port.current_format.info.raw.rate;        \
15
+   step = M_PI_M2f * freq / this->port.current_format.info.raw.rate;       \
16
    amp = volume * scale;                               \
17
                                            \
18
    for (i = 0; i < n_samples; i++) {                       \
19
        type val;                               \
20
        this->port.accumulator += step;                     \
21
-       if (this->port.accumulator >= M_PI_M2)                  \
22
-           this->port.accumulator -= M_PI_M2;              \
23
+       if (this->port.accumulator >= M_PI_M2f)                 \
24
+           this->port.accumulator -= M_PI_M2f;             \
25
        val = (type) (sin (this->port.accumulator) * amp);          \
26
        for (c = 0; c < channels; ++c)                      \
27
            *samples++ = val;                       \
28
    }                                       \
29
 }
30
 
31
-DEFINE_SINE(int16_t, 32767.0);
32
-DEFINE_SINE(int32_t, 2147483647.0);
33
-DEFINE_SINE(float, 1.0);
34
-DEFINE_SINE(double, 1.0);
35
+DEFINE_SINE(int16_t, 32767.0f);
36
+DEFINE_SINE(int32_t, 2147483647.0f);
37
+DEFINE_SINE(float, 1.0f);
38
+DEFINE_SINE(double, 1.0f);
39
 
40
 static const render_func_t sine_funcs = {
41
    (render_func_t) audio_test_src_create_sine_int16_t,
42
pipewire-1.0.1.tar.bz2/spa/plugins/avb/avb-pcm-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/avb/avb-pcm-sink.c Changed
10
 
1
@@ -557,7 +557,7 @@
2
 {
3
    struct state *this = object;
4
    struct port *port;
5
-   int res;
6
+   int res = 0;
7
 
8
    spa_return_val_if_fail(this != NULL, -EINVAL);
9
 
10
pipewire-1.0.1.tar.bz2/spa/plugins/avb/avb-pcm-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/avb/avb-pcm-source.c Changed
10
 
1
@@ -557,7 +557,7 @@
2
 {
3
    struct state *this = object;
4
    struct port *port;
5
-   int res;
6
+   int res = 0;
7
 
8
    spa_return_val_if_fail(this != NULL, -EINVAL);
9
 
10
pipewire-1.0.1.tar.bz2/spa/plugins/avb/avb.c -> pipewire-1.2.0.tar.gz/spa/plugins/avb/avb.c Changed
12
 
1
@@ -12,7 +12,9 @@
2
 extern const struct spa_handle_factory spa_avb_sink_factory;
3
 extern const struct spa_handle_factory spa_avb_source_factory;
4
 
5
-struct spa_log_topic avb_log_topic = SPA_LOG_TOPIC(0, "spa.avb");
6
+SPA_LOG_TOPIC_DEFINE(avb_log_topic, "spa.avb");
7
+
8
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
9
 
10
 SPA_EXPORT
11
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
12
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/a2dp-codec-aac.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c Changed
177
 
1
@@ -17,9 +17,6 @@
2
 #include "media-codecs.h"
3
 
4
 static struct spa_log *log;
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs.aac");
6
-#undef SPA_LOG_TOPIC_DEFAULT
7
-#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
 
9
 #define DEFAULT_AAC_BITRATE    320000
10
 #define MIN_AAC_BITRATE        64000
11
@@ -45,15 +42,41 @@
12
    int samplesize;
13
 };
14
 
15
+static bool eld_supported(void)
16
+{
17
+   static bool supported = false, checked = false;
18
+   HANDLE_AACENCODER aacenc = NULL;
19
+
20
+   if (checked)
21
+       return supported;
22
+
23
+   if (aacEncOpen(&aacenc, 0, 2) != AACENC_OK)
24
+       goto done;
25
+   if (aacEncoder_SetParam(aacenc, AACENC_AOT, AOT_ER_AAC_ELD) != AACENC_OK)
26
+       goto done;
27
+   if (aacEncoder_SetParam(aacenc,  AACENC_SBR_MODE, 1) != AACENC_OK)
28
+       goto done;
29
+
30
+   supported = true;
31
+
32
+done:
33
+   if (aacenc)
34
+       aacEncClose(&aacenc);
35
+   checked = true;
36
+   spa_log_debug(log, "FDK-AAC AAC-ELD support:%d", (int)supported);
37
+   return supported;
38
+}
39
+
40
 static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
41
        uint8_t capsA2DP_MAX_CAPS_SIZE)
42
 {
43
-   static const a2dp_aac_t a2dp_aac = {
44
+   const a2dp_aac_t a2dp_aac = {
45
        .object_type =
46
            /* NOTE: AAC Long Term Prediction and AAC Scalable are
47
             *       not supported by the FDK-AAC library. */
48
            AAC_OBJECT_TYPE_MPEG2_AAC_LC |
49
-           AAC_OBJECT_TYPE_MPEG4_AAC_LC,
50
+           AAC_OBJECT_TYPE_MPEG4_AAC_LC |
51
+           (eld_supported() ? AAC_OBJECT_TYPE_MPEG4_AAC_ELD : 0),
52
        AAC_INIT_FREQUENCY(
53
            AAC_SAMPLING_FREQ_8000 |
54
            AAC_SAMPLING_FREQ_11025 |
55
@@ -123,16 +146,26 @@
56
 
57
    conf = *(a2dp_aac_t*)caps;
58
 
59
-   if (conf.object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
60
-       conf.object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC;
61
-   else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
62
-       conf.object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC;
63
-   else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
64
-       return -ENOTSUP;  /* Not supported by FDK-AAC */
65
-   else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
66
-       return -ENOTSUP;  /* Not supported by FDK-AAC */
67
-   else
68
-       return -ENOTSUP;
69
+   if (codec->id == SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD) {
70
+       if (!eld_supported())
71
+           return -ENOTSUP;
72
+           
73
+       if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD)
74
+           conf.object_type = AAC_OBJECT_TYPE_MPEG4_AAC_ELD;
75
+       else
76
+           return -ENOTSUP;
77
+   } else {
78
+       if (conf.object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
79
+           conf.object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC;
80
+       else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
81
+           conf.object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC;
82
+       else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
83
+           return -ENOTSUP;  /* Not supported by FDK-AAC */
84
+       else if (conf.object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
85
+           return -ENOTSUP;  /* Not supported by FDK-AAC */
86
+       else
87
+           return -ENOTSUP;
88
+   }
89
 
90
    if ((i = media_codec_select_config(aac_frequencies,
91
                      SPA_N_ELEMENTS(aac_frequencies),
92
@@ -250,7 +283,8 @@
93
     * a BlueZ bug, but we can be lax here and below in codec_init.
94
     */
95
    if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
96
-                   AAC_OBJECT_TYPE_MPEG4_AAC_LC)))
97
+                   AAC_OBJECT_TYPE_MPEG4_AAC_LC |
98
+                   AAC_OBJECT_TYPE_MPEG4_AAC_ELD)))
99
        return -EINVAL;
100
    j = 0;
101
    SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) {
102
@@ -330,16 +364,24 @@
103
    if (res != AACENC_OK)
104
        goto error;
105
 
106
-   if (!(conf->object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
107
-                   AAC_OBJECT_TYPE_MPEG4_AAC_LC))) {
108
+   if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD) {
109
+       res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_ER_AAC_ELD);
110
+       if (res != AACENC_OK)
111
+           goto error;
112
+
113
+       res = aacEncoder_SetParam(this->aacenc,  AACENC_SBR_MODE, 1);
114
+       if (res != AACENC_OK)
115
+           goto error;
116
+   } else if (conf->object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
117
+                       AAC_OBJECT_TYPE_MPEG4_AAC_LC)) {
118
+       res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
119
+       if (res != AACENC_OK)
120
+           goto error;
121
+   } else {        
122
        res = -EINVAL;
123
        goto error;
124
    }
125
 
126
-   res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
127
-   if (res != AACENC_OK)
128
-       goto error;
129
-
130
    res = aacEncoder_SetParam(this->aacenc, AACENC_SAMPLERATE, this->rate);
131
    if (res != AACENC_OK)
132
        goto error;
133
@@ -608,7 +650,7 @@
134
 static void codec_set_log(struct spa_log *global_log)
135
 {
136
    log = global_log;
137
-   spa_log_topic_init(log, &log_topic);
138
+   spa_log_topic_init(log, &codec_plugin_log_topic);
139
 }
140
 
141
 const struct media_codec a2dp_codec_aac = {
142
@@ -635,7 +677,33 @@
143
    .set_log = codec_set_log,
144
 };
145
 
146
+const struct media_codec a2dp_codec_aac_eld = {
147
+   .id = SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD,
148
+   .codec_id = A2DP_CODEC_MPEG24,
149
+   .name = "aac_eld",
150
+   .description = "AAC-ELD",
151
+   .endpoint_name = "aac",
152
+   .fill_caps = NULL,
153
+   .select_config = codec_select_config,
154
+   .enum_config = codec_enum_config,
155
+   .validate_config = codec_validate_config,
156
+   .init_props = codec_init_props,
157
+   .clear_props = codec_clear_props,
158
+   .init = codec_init,
159
+   .deinit = codec_deinit,
160
+   .get_block_size = codec_get_block_size,
161
+   .start_encode = codec_start_encode,
162
+   .encode = codec_encode,
163
+   .start_decode = codec_start_decode,
164
+   .decode = codec_decode,
165
+   .abr_process = codec_abr_process,
166
+   .reduce_bitpool = codec_reduce_bitpool,
167
+   .increase_bitpool = codec_increase_bitpool,
168
+   .set_log = codec_set_log,
169
+};
170
+
171
 MEDIA_CODEC_EXPORT_DEF(
172
    "aac",
173
-   &a2dp_codec_aac
174
+   &a2dp_codec_aac,
175
+   &a2dp_codec_aac_eld
176
 );
177
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c Changed
39
 
1
@@ -300,8 +300,17 @@
2
        void *config, size_t config_len, const struct spa_audio_info *info,
3
        void *props, size_t mtu)
4
 {
5
-   struct impl *this;
6
+   struct impl *this = NULL;
7
+   a2dp_aptx_t conf;
8
    int res;
9
+   int frequency;
10
+
11
+   if (config_len < sizeof(conf)) {
12
+       res = -EINVAL;
13
+       goto error;
14
+   }
15
+
16
+   memcpy(&conf, config, sizeof(conf));
17
 
18
    if ((this = calloc(1, sizeof(struct impl))) == NULL)
19
        goto error_errno;
20
@@ -322,10 +331,17 @@
21
    this->frame_length = this->hd ? 6 : 4;
22
    this->codesize = 4 * 3 * 2;
23
 
24
+   frequency = media_codec_get_config(aptx_frequencies, SPA_N_ELEMENTS(aptx_frequencies), conf.frequency);
25
+   if (frequency < 0) {
26
+       res = -EINVAL;
27
+       goto error;
28
+   }
29
+
30
    if (this->hd)
31
        this->max_frames = (this->mtu - sizeof(struct rtp_header)) / this->frame_length;
32
    else if (codec_is_ll(codec))
33
-       this->max_frames = SPA_MIN(256u, this->mtu) / this->frame_length;
34
+       /* try to make 7.5ms packets */
35
+       this->max_frames = SPA_MIN((unsigned)frequency * 75u/10000u / 4u, this->mtu / this->frame_length);
36
    else
37
        this->max_frames = this->mtu / this->frame_length;
38
 
39
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/a2dp-codec-caps.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-caps.h Changed
49
 
1
@@ -101,6 +101,7 @@
2
 #define AAC_OBJECT_TYPE_MPEG4_AAC_LC   0x40
3
 #define AAC_OBJECT_TYPE_MPEG4_AAC_LTP  0x20
4
 #define AAC_OBJECT_TYPE_MPEG4_AAC_SCA  0x10
5
+#define AAC_OBJECT_TYPE_MPEG4_AAC_ELD  0x02
6
 
7
 #define AAC_SAMPLING_FREQ_8000     0x0800
8
 #define AAC_SAMPLING_FREQ_11025        0x0400
9
@@ -281,6 +282,29 @@
10
 #define OPUS_05_SET_BITRATE(a, v) OPUS_05_SET_UINT16(a, bitrate, v)
11
 
12
 
13
+#define OPUS_G_VENDOR_ID   0x000000e0
14
+#define OPUS_G_CODEC_ID        0x0001
15
+
16
+#define OPUS_G_FREQUENCY_MASK      0x80
17
+#define OPUS_G_FREQUENCY_48000     0x80
18
+
19
+#define OPUS_G_DURATION_MASK       0x18
20
+#define OPUS_G_DURATION_100        0x08
21
+#define OPUS_G_DURATION_200        0x10
22
+
23
+#define OPUS_G_CHANNELS_MASK       0x07
24
+#define OPUS_G_CHANNELS_MONO       0x01
25
+#define OPUS_G_CHANNELS_STEREO     0x02
26
+#define OPUS_G_CHANNELS_MONO_2     0x04
27
+
28
+#define OPUS_G_GET_FREQUENCY(a) ((a).data & OPUS_G_FREQUENCY_MASK)
29
+#define OPUS_G_GET_DURATION(a) ((a).data & OPUS_G_DURATION_MASK)
30
+#define OPUS_G_GET_CHANNELS(a) ((a).data & OPUS_G_CHANNELS_MASK)
31
+
32
+#define OPUS_G_SET(a, freq, dur, ch) \
33
+   (a).data = ((freq) & OPUS_G_FREQUENCY_MASK) | ((dur) & OPUS_G_DURATION_MASK) | ((ch) & OPUS_G_CHANNELS_MASK)
34
+
35
+
36
 typedef struct {
37
    uint32_t vendor_id;
38
    uint16_t codec_id;
39
@@ -457,4 +481,9 @@
40
    a2dp_opus_05_direction_t bidi;
41
 } __attribute__ ((packed)) a2dp_opus_05_t;
42
 
43
+typedef struct {
44
+   a2dp_vendor_codec_t info;
45
+   uint8_t data;
46
+} __attribute__ ((packed)) a2dp_opus_g_t;
47
+
48
 #endif
49
pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-opus-g.c Added
528
 
1
@@ -0,0 +1,526 @@
2
+/* Spa A2DP Opus Codec */
3
+/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2022 Pauli Virtanen */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <unistd.h>
8
+#include <string.h>
9
+#include <stddef.h>
10
+#include <errno.h>
11
+#include <arpa/inet.h>
12
+#if __BYTE_ORDER != __LITTLE_ENDIAN
13
+#include <byteswap.h>
14
+#endif
15
+
16
+#include <spa/debug/types.h>
17
+#include <spa/param/audio/type-info.h>
18
+#include <spa/param/audio/raw.h>
19
+#include <spa/utils/string.h>
20
+#include <spa/utils/dict.h>
21
+#include <spa/param/audio/format.h>
22
+#include <spa/param/audio/format-utils.h>
23
+
24
+#include <opus.h>
25
+
26
+#include "rtp.h"
27
+#include "media-codecs.h"
28
+
29
+static struct spa_log *log;
30
+
31
+struct dec_data {
32
+};
33
+
34
+struct enc_data {
35
+   struct rtp_header *header;
36
+   struct rtp_payload *payload;
37
+
38
+   int samples;
39
+   int codesize;
40
+   int frame_dms;
41
+   int bitrate;
42
+   int packet_size;
43
+};
44
+
45
+struct impl {
46
+   OpusEncoder *enc;
47
+   OpusDecoder *dec;
48
+
49
+   int mtu;
50
+   int samplerate;
51
+   int channels;
52
+   int application;
53
+
54
+   struct dec_data d;
55
+   struct enc_data e;
56
+};
57
+
58
+static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
59
+       uint8_t capsA2DP_MAX_CAPS_SIZE)
60
+{
61
+   a2dp_opus_g_t conf = {
62
+       .info = codec->vendor,
63
+   };
64
+
65
+   OPUS_G_SET(conf,
66
+           OPUS_G_FREQUENCY_48000,
67
+           OPUS_G_DURATION_100 | OPUS_G_DURATION_200,
68
+           OPUS_G_CHANNELS_MONO | OPUS_G_CHANNELS_STEREO | OPUS_G_CHANNELS_MONO_2);
69
+
70
+   memcpy(caps, &conf, sizeof(conf));
71
+   return sizeof(conf);
72
+}
73
+
74
+static int codec_select_config(const struct media_codec *codec, uint32_t flags,
75
+       const void *caps, size_t caps_size,
76
+       const struct media_codec_audio_info *info,
77
+       const struct spa_dict *global_settings, uint8_t configA2DP_MAX_CAPS_SIZE)
78
+{
79
+   a2dp_opus_g_t conf;
80
+   int frequency, duration, channels;
81
+
82
+   if (caps_size < sizeof(conf))
83
+       return -EINVAL;
84
+
85
+   memcpy(&conf, caps, sizeof(conf));
86
+
87
+   if (codec->vendor.vendor_id != conf.info.vendor_id ||
88
+       codec->vendor.codec_id != conf.info.codec_id)
89
+       return -ENOTSUP;
90
+
91
+   if (OPUS_G_GET_FREQUENCY(conf) & OPUS_G_FREQUENCY_48000)
92
+       frequency = OPUS_G_FREQUENCY_48000;
93
+   else
94
+       return -EINVAL;
95
+
96
+   if (OPUS_G_GET_DURATION(conf) & OPUS_G_DURATION_200)
97
+       duration = OPUS_G_DURATION_200;
98
+   else if (OPUS_G_GET_DURATION(conf) & OPUS_G_DURATION_100)
99
+       duration = OPUS_G_DURATION_100;
100
+   else
101
+       return -EINVAL;
102
+
103
+   if (OPUS_G_GET_CHANNELS(conf) & OPUS_G_CHANNELS_STEREO)
104
+       channels = OPUS_G_CHANNELS_STEREO;
105
+   else if (OPUS_G_GET_CHANNELS(conf) & OPUS_G_CHANNELS_MONO)
106
+       channels = OPUS_G_CHANNELS_MONO;
107
+   else if (OPUS_G_GET_CHANNELS(conf) & OPUS_G_CHANNELS_MONO_2)
108
+       channels = OPUS_G_CHANNELS_MONO_2;
109
+   else
110
+       return -EINVAL;
111
+
112
+   OPUS_G_SET(conf, frequency, duration, channels);
113
+
114
+   memcpy(config, &conf, sizeof(conf));
115
+
116
+   return sizeof(conf);
117
+}
118
+
119
+static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size,
120
+       const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info,
121
+       const struct spa_dict *global_settings)
122
+{
123
+   a2dp_opus_g_t conf1, conf2, cap1, cap2;
124
+   a2dp_opus_g_t *conf;
125
+   int res1, res2;
126
+   int a, b;
127
+
128
+   /* Order selected configurations by preference */
129
+   res1 = codec->select_config(codec, flags, caps1, caps1_size, info, global_settings, (uint8_t *)&conf1);
130
+   res2 = codec->select_config(codec, flags, caps2, caps2_size, info, global_settings, (uint8_t *)&conf2);
131
+
132
+#define PREFER_EXPR(expr)          \
133
+       do {                \
134
+           conf = &conf1;      \
135
+           a = (expr);     \
136
+           conf = &conf2;      \
137
+           b = (expr);     \
138
+           if (a != b)     \
139
+               return b - a;   \
140
+       } while (0)
141
+
142
+#define PREFER_BOOL(expr)  PREFER_EXPR((expr) ? 1 : 0)
143
+
144
+   /* Prefer valid */
145
+   a = (res1 > 0 && (size_t)res1 == sizeof(a2dp_opus_g_t)) ? 1 : 0;
146
+   b = (res2 > 0 && (size_t)res2 == sizeof(a2dp_opus_g_t)) ? 1 : 0;
147
+   if (!a || !b)
148
+       return b - a;
149
+
150
+   memcpy(&cap1, caps1, sizeof(cap1));
151
+   memcpy(&cap2, caps2, sizeof(cap2));
152
+
153
+   PREFER_EXPR(OPUS_G_GET_CHANNELS(*conf) & OPUS_G_CHANNELS_STEREO);
154
+
155
+   return 0;
156
+
157
+#undef PREFER_EXPR
158
+#undef PREFER_BOOL
159
+}
160
+
161
+static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
162
+       const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
163
+       struct spa_pod_builder *b, struct spa_pod **param)
164
+{
165
+   a2dp_opus_g_t conf;
166
+   struct spa_pod_frame f1;
167
+   uint32_t positionSPA_AUDIO_MAX_CHANNELS;
168
+   int channels;
169
+
170
+   if (caps_size < sizeof(conf))
171
+       return -EINVAL;
172
+
173
+   memcpy(&conf, caps, sizeof(conf));
174
+
175
+   if (idx > 0)
176
+       return 0;
177
+
178
+   switch (OPUS_G_GET_CHANNELS(conf)) {
179
+   case OPUS_G_CHANNELS_STEREO:
180
+       channels = 2;
181
+       position0 = SPA_AUDIO_CHANNEL_FL;
182
+       position1 = SPA_AUDIO_CHANNEL_FR;
183
+       break;
184
+   case OPUS_G_CHANNELS_MONO:
185
+       channels = 1;
186
+       position0 = SPA_AUDIO_CHANNEL_MONO;
187
+       break;
188
+   case OPUS_G_CHANNELS_MONO_2:
189
+       channels = 2;
190
+       position0 = SPA_AUDIO_CHANNEL_AUX0;
191
+       position1 = SPA_AUDIO_CHANNEL_AUX1;
192
+       break;
193
+   default:
194
+       return -EINVAL;
195
+   }
196
+
197
+   spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_Format, id);
198
+   spa_pod_builder_add(b,
199
+           SPA_FORMAT_mediaType,      SPA_POD_Id(SPA_MEDIA_TYPE_audio),
200
+           SPA_FORMAT_mediaSubtype,   SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
201
+           SPA_FORMAT_AUDIO_format,   SPA_POD_Id(SPA_AUDIO_FORMAT_F32),
202
+           SPA_FORMAT_AUDIO_rate,     SPA_POD_CHOICE_ENUM_Int(6,
203
+                   48000, 48000, 24000, 16000, 12000, 8000),
204
+           SPA_FORMAT_AUDIO_channels, SPA_POD_Int(channels),
205
+           SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
206
+                   SPA_TYPE_Id, channels, position),
207
+           0);
208
+
209
+   *param = spa_pod_builder_pop(b, &f0);
210
+   return *param == NULL ? -EIO : 1;
211
+}
212
+
213
+static int codec_validate_config(const struct media_codec *codec, uint32_t flags,
214
+           const void *caps, size_t caps_size,
215
+           struct spa_audio_info *info)
216
+{
217
+   a2dp_opus_g_t conf;
218
+
219
+   if (caps == NULL || caps_size < sizeof(conf))
220
+       return -EINVAL;
221
+
222
+   memcpy(&conf, caps, sizeof(conf));
223
+
224
+   spa_zero(*info);
225
+   info->media_type = SPA_MEDIA_TYPE_audio;
226
+   info->media_subtype = SPA_MEDIA_SUBTYPE_raw;
227
+
228
+   info->info.raw.format = SPA_AUDIO_FORMAT_F32;
229
+   info->info.raw.rate = 0;  /* not specified by config */
230
+
231
+   switch (OPUS_G_GET_FREQUENCY(conf)) {
232
+   case OPUS_G_FREQUENCY_48000:
233
+       break;
234
+   default:
235
+       return -EINVAL;
236
+   }
237
+
238
+   switch (OPUS_G_GET_DURATION(conf)) {
239
+   case OPUS_G_DURATION_100:
240
+   case OPUS_G_DURATION_200:
241
+       break;
242
+   default:
243
+       return -EINVAL;
244
+   }
245
+
246
+   switch (OPUS_G_GET_CHANNELS(conf)) {
247
+   case OPUS_G_CHANNELS_STEREO:
248
+       info->info.raw.channels = 2;
249
+       info->info.raw.position0 = SPA_AUDIO_CHANNEL_FL;
250
+       info->info.raw.position1 = SPA_AUDIO_CHANNEL_FR;
251
+       break;
252
+   case OPUS_G_CHANNELS_MONO:
253
+       info->info.raw.channels = 1;
254
+       info->info.raw.position0 = SPA_AUDIO_CHANNEL_MONO;
255
+       break;
256
+   case OPUS_G_CHANNELS_MONO_2:
257
+       info->info.raw.channels = 2;
258
+       info->info.raw.position0 = SPA_AUDIO_CHANNEL_AUX0;
259
+       info->info.raw.position1 = SPA_AUDIO_CHANNEL_AUX1;
260
+       break;
261
+   default:
262
+       return -EINVAL;
263
+   }
264
+
265
+   return 0;
266
+}
267
+
268
+static int parse_frame_dms(int value)
269
+{
270
+   switch (value) {
271
+   case OPUS_G_DURATION_100:
272
+       return 100;
273
+   case OPUS_G_DURATION_200:
274
+       return 200;
275
+   default:
276
+       return -EINVAL;
277
+   }
278
+}
279
+
280
+static void *codec_init(const struct media_codec *codec, uint32_t flags,
281
+       void *config, size_t config_len, const struct spa_audio_info *info,
282
+       void *props, size_t mtu)
283
+{
284
+   a2dp_opus_g_t conf;
285
+   struct impl *this = NULL;
286
+   struct spa_audio_info config_info;
287
+   int res;
288
+
289
+   if (config_len < sizeof(conf)) {
290
+       res = -EINVAL;
291
+       goto error;
292
+   }
293
+   memcpy(&conf, config, sizeof(conf));
294
+
295
+   if (info->media_type != SPA_MEDIA_TYPE_audio ||
296
+       info->media_subtype != SPA_MEDIA_SUBTYPE_raw ||
297
+       info->info.raw.format != SPA_AUDIO_FORMAT_F32) {
298
+       res = -EINVAL;
299
+       goto error;
300
+   }
301
+
302
+   if ((this = calloc(1, sizeof(struct impl))) == NULL)
303
+       goto error_errno;
304
+
305
+   if ((res = codec_validate_config(codec, flags, config, config_len, &config_info)) < 0)
306
+       goto error;
307
+   if (config_info.info.raw.channels != info->info.raw.channels) {
308
+       res = -EINVAL;
309
+       goto error;
310
+   }
311
+
312
+   this->mtu = mtu;
313
+   this->samplerate = info->info.raw.rate;
314
+   this->channels = config_info.info.raw.channels;
315
+   this->application = OPUS_APPLICATION_AUDIO;
316
+
317
+   /*
318
+    * Setup encoder
319
+    */
320
+   this->enc = opus_encoder_create(this->samplerate, this->channels, this->application, &res);
321
+   if (this->enc == NULL) {
322
+       res = -EINVAL;
323
+       goto error;
324
+   }
325
+
326
+   if ((this->e.frame_dms = parse_frame_dms(OPUS_G_GET_DURATION(conf))) < 0) {
327
+       res = -EINVAL;
328
+       goto error;
329
+   }
330
+
331
+   this->e.samples = this->e.frame_dms * this->samplerate / 10000;
332
+   this->e.codesize = this->e.samples * (int)this->channels * sizeof(float);
333
+
334
+   int header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
335
+   this->e.bitrate = SPA_MIN(128000 * this->channels,
336
+           (int64_t)8 * (this->mtu - header_size) * 10000 / this->e.frame_dms);
337
+
338
+   opus_encoder_ctl(this->enc, OPUS_SET_BITRATE(this->e.bitrate));
339
+
340
+   /*
341
+    * Setup decoder
342
+    */
343
+   this->dec = opus_decoder_create(this->samplerate, this->channels, &res);
344
+   if (this->dec == NULL) {
345
+       res = -EINVAL;
346
+       goto error;
347
+   }
348
+
349
+   return this;
350
+
351
+error_errno:
352
+   res = -errno;
353
+   goto error;
354
+
355
+error:
356
+   if (this && this->enc)
357
+       opus_encoder_destroy(this->enc);
358
+   if (this && this->dec)
359
+       opus_decoder_destroy(this->dec);
360
+   free(this);
361
+   errno = -res;
362
+   return NULL;
363
+}
364
+
365
+static void codec_deinit(void *data)
366
+{
367
+   struct impl *this = data;
368
+
369
+   opus_encoder_destroy(this->enc);
370
+   opus_decoder_destroy(this->dec);
371
+   free(this);
372
+}
373
+
374
+static int codec_get_block_size(void *data)
375
+{
376
+   struct impl *this = data;
377
+   return this->e.codesize;
378
+}
379
+
380
+static int codec_start_encode (void *data,
381
+       void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp)
382
+{
383
+   struct impl *this = data;
384
+   size_t header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
385
+
386
+   if (dst_size <= header_size)
387
+       return -EINVAL;
388
+
389
+   this->e.header = (struct rtp_header *)dst;
390
+   this->e.payload = SPA_PTROFF(dst, sizeof(struct rtp_header), struct rtp_payload);
391
+   memset(dst, 0, header_size);
392
+
393
+   this->e.payload->frame_count = 0;
394
+   this->e.header->v = 2;
395
+   this->e.header->pt = 96;
396
+   this->e.header->sequence_number = htons(seqnum);
397
+   this->e.header->timestamp = htonl(timestamp);
398
+   this->e.header->ssrc = htonl(1);
399
+
400
+   this->e.packet_size = header_size;
401
+   return this->e.packet_size;
402
+}
403
+
404
+static int codec_encode(void *data,
405
+       const void *src, size_t src_size,
406
+       void *dst, size_t dst_size,
407
+       size_t *dst_out, int *need_flush)
408
+{
409
+   struct impl *this = data;
410
+   int res;
411
+
412
+   if (src_size < (size_t)this->e.codesize) {
413
+       *dst_out = 0;
414
+       return 0;
415
+   }
416
+   if (this->e.packet_size >= this->mtu)
417
+       return -EINVAL;
418
+
419
+   dst_size = SPA_MIN(dst_size, (size_t)(this->mtu - this->e.packet_size));
420
+
421
+   res = opus_encode_float(this->enc, src, this->e.samples, dst, dst_size);
422
+   if (res < 0)
423
+       return -EINVAL;
424
+
425
+   *dst_out = res;
426
+
427
+   this->e.packet_size += res;
428
+   this->e.payload->frame_count++;
429
+
430
+   *need_flush = NEED_FLUSH_ALL;
431
+
432
+   return this->e.codesize;
433
+}
434
+
435
+static int codec_start_decode (void *data,
436
+       const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp)
437
+{
438
+   struct impl SPA_UNUSED *this = data;
439
+   const struct rtp_header *header = src;
440
+   const struct rtp_payload *payload = SPA_PTROFF(src, sizeof(struct rtp_header), void);
441
+   size_t header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
442
+
443
+   spa_return_val_if_fail (src_size > header_size, -EINVAL);
444
+
445
+   if (seqnum)
446
+       *seqnum = ntohs(header->sequence_number);
447
+   if (timestamp)
448
+       *timestamp = ntohl(header->timestamp);
449
+
450
+   if (payload->is_fragmented)
451
+       return -EINVAL;  /* fragmentation not supported */
452
+   if (payload->frame_count != 1)
453
+       return -EINVAL;  /* wrong number of frames in packet */
454
+
455
+   return header_size;
456
+}
457
+
458
+static int codec_decode(void *data,
459
+       const void *src, size_t src_size,
460
+       void *dst, size_t dst_size,
461
+       size_t *dst_out)
462
+{
463
+   struct impl SPA_UNUSED *this = data;
464
+   int consumed = src_size;
465
+   int res;
466
+   int dst_samples;
467
+
468
+   dst_samples = dst_size / (sizeof(float) * this->channels);
469
+   res = opus_decode_float(this->dec, src, src_size, dst, dst_samples, 0);
470
+   if (res < 0)
471
+       return -EINVAL;
472
+
473
+   *dst_out = (size_t)res * this->channels * sizeof(float);
474
+
475
+   return consumed;
476
+}
477
+
478
+static int codec_abr_process (void *data, size_t unsent)
479
+{
480
+   return -ENOTSUP;
481
+}
482
+
483
+static int codec_reduce_bitpool(void *data)
484
+{
485
+   return 0;
486
+}
487
+
488
+static int codec_increase_bitpool(void *data)
489
+{
490
+   return 0;
491
+}
492
+
493
+static void codec_set_log(struct spa_log *global_log)
494
+{
495
+   log = global_log;
496
+   spa_log_topic_init(log, &codec_plugin_log_topic);
497
+}
498
+
499
+const struct media_codec a2dp_codec_opus_g = {
500
+   .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
501
+   .codec_id = A2DP_CODEC_VENDOR,
502
+   .vendor = { .vendor_id = OPUS_G_VENDOR_ID,
503
+           .codec_id = OPUS_G_CODEC_ID },
504
+   .select_config = codec_select_config,
505
+   .enum_config = codec_enum_config,
506
+   .validate_config = codec_validate_config,
507
+   .caps_preference_cmp = codec_caps_preference_cmp,
508
+   .init = codec_init,
509
+   .deinit = codec_deinit,
510
+   .get_block_size = codec_get_block_size,
511
+   .start_encode = codec_start_encode,
512
+   .encode = codec_encode,
513
+   .abr_process = codec_abr_process,
514
+   .reduce_bitpool = codec_reduce_bitpool,
515
+   .increase_bitpool = codec_increase_bitpool,
516
+   .set_log = codec_set_log,
517
+   .start_decode = codec_start_decode,
518
+   .decode = codec_decode,
519
+   .name = "opus_g",
520
+   .description = "Opus",
521
+   .fill_caps = codec_fill_caps,
522
+};
523
+
524
+MEDIA_CODEC_EXPORT_DEF(
525
+   "opus-g",
526
+   &a2dp_codec_opus_g
527
+);
528
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/a2dp-codec-opus.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c Changed
82
 
1
@@ -27,9 +27,6 @@
2
 #include "media-codecs.h"
3
 
4
 static struct spa_log *log;
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs.opus");
6
-#undef SPA_LOG_TOPIC_DEFAULT
7
-#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
 
9
 #define BUFSIZE_FROM_BITRATE(frame_dms,bitrate)    ((bitrate)/8 * (frame_dms) / 10000 * 5/4)  /* estimate */
10
 
11
@@ -182,7 +179,7 @@
12
 #define BT_AUDIO_LOCATION_BC   0x00200000  /* Bottom Front Center */
13
 #define BT_AUDIO_LOCATION_BLC  0x00400000  /* Bottom Front Left */
14
 #define BT_AUDIO_LOCATION_BRC  0x00800000  /* Bottom Front Right */
15
-#define BT_AUDIO_LOCATION_FLW  0x01000000  /* Fron Left Wide */
16
+#define BT_AUDIO_LOCATION_FLW  0x01000000  /* Front Left Wide */
17
 #define BT_AUDIO_LOCATION_FRW  0x02000000  /* Front Right Wide */
18
 #define BT_AUDIO_LOCATION_SSL  0x04000000  /* Left Surround */
19
 #define BT_AUDIO_LOCATION_SSR  0x08000000  /* Right Surround */
20
@@ -1333,7 +1330,7 @@
21
 static void codec_set_log(struct spa_log *global_log)
22
 {
23
    log = global_log;
24
-   spa_log_topic_init(log, &log_topic);
25
+   spa_log_topic_init(log, &codec_plugin_log_topic);
26
 }
27
 
28
 #define OPUS_05_COMMON_DEFS                    \
29
@@ -1363,7 +1360,7 @@
30
    OPUS_05_COMMON_FULL_DEFS,
31
    .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05,
32
    .name = "opus_05",
33
-   .description = "Opus",
34
+   .description = "Opus 05",
35
    .fill_caps = codec_fill_caps,
36
 };
37
 
38
@@ -1371,7 +1368,7 @@
39
    OPUS_05_COMMON_DEFS,
40
    .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_51,
41
    .name = "opus_05_51",
42
-   .description = "Opus 5.1 Surround",
43
+   .description = "Opus 05 5.1 Surround",
44
    .endpoint_name = "opus_05",
45
    .fill_caps = NULL,
46
 };
47
@@ -1380,7 +1377,7 @@
48
    OPUS_05_COMMON_DEFS,
49
    .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71,
50
    .name = "opus_05_71",
51
-   .description = "Opus 7.1 Surround",
52
+   .description = "Opus 05 7.1 Surround",
53
    .endpoint_name = "opus_05",
54
    .fill_caps = NULL,
55
 };
56
@@ -1390,14 +1387,14 @@
57
    OPUS_05_COMMON_FULL_DEFS,
58
    .id = 0,
59
    .name = "opus_05_duplex_bidi",
60
-   .description = "Opus Duplex Bidi channel",
61
+   .description = "Opus 05 Duplex Bidi channel",
62
 };
63
 
64
 const struct media_codec a2dp_codec_opus_05_duplex = {
65
    OPUS_05_COMMON_FULL_DEFS,
66
    .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX,
67
    .name = "opus_05_duplex",
68
-   .description = "Opus Duplex",
69
+   .description = "Opus 05 Duplex",
70
    .duplex_codec = &a2dp_codec_opus_05_return,
71
    .fill_caps = codec_fill_caps,
72
 };
73
@@ -1406,7 +1403,7 @@
74
    OPUS_05_COMMON_DEFS,
75
    .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO,
76
    .name = "opus_05_pro",
77
-   .description = "Opus Pro Audio",
78
+   .description = "Opus 05 Pro Audio",
79
    .init_props = codec_init_props,
80
    .clear_props = codec_clear_props,
81
    .duplex_codec = &a2dp_codec_opus_05_return,
82
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/a2dp-codec-sbc.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/a2dp-codec-sbc.c Changed
11
 
1
@@ -645,7 +645,8 @@
2
    .codec_id = A2DP_CODEC_SBC,
3
    .name = "sbc_xq",
4
    .description = "SBC-XQ",
5
-   .fill_caps = codec_fill_caps,
6
+   .endpoint_name = "sbc",
7
+   .fill_caps = NULL,
8
    .select_config = codec_select_config,
9
    .enum_config = codec_enum_config,
10
    .validate_config = codec_validate_config,
11
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/backend-hsphfpd.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/backend-hsphfpd.c Changed
15
 
1
@@ -16,11 +16,11 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/type.h>
4
 #include <spa/param/audio/raw.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "defs.h"
8
-#include "dbus-helpers.h"
9
 
10
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.hsphfpd");
11
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.hsphfpd");
12
 #undef SPA_LOG_TOPIC_DEFAULT
13
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
14
 
15
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/backend-native.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/backend-native.c Changed
982
 
1
@@ -26,6 +26,7 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/param/audio/raw.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "defs.h"
8
 
9
@@ -33,11 +34,10 @@
10
 #include <libusb.h>
11
 #endif
12
 
13
-#include "dbus-helpers.h"
14
 #include "modemmanager.h"
15
 #include "upower.h"
16
 
17
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.native");
18
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native");
19
 #undef SPA_LOG_TOPIC_DEFAULT
20
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
21
 
22
@@ -161,6 +161,7 @@
23
    unsigned int slc_configured:1;
24
    unsigned int codec_negotiation_supported:1;
25
    unsigned int msbc_supported_by_hfp:1;
26
+   unsigned int lc3_supported_by_hfp:1;
27
    unsigned int hfp_ag_switching_codec:1;
28
    unsigned int hfp_ag_initial_codec_setup:2;
29
    unsigned int cind_call_active:1;
30
@@ -199,20 +200,26 @@
31
 
32
 static const struct spa_bt_transport_implementation sco_transport_impl;
33
 
34
-static struct spa_bt_transport *_transport_create(struct rfcomm *rfcomm)
35
+static int rfcomm_new_transport(struct rfcomm *rfcomm)
36
 {
37
    struct impl *backend = rfcomm->backend;
38
    struct spa_bt_transport *t = NULL;
39
    struct transport_data *td;
40
    char* pathfd;
41
 
42
+   if (rfcomm->transport) {
43
+       spa_hook_remove(&rfcomm->transport_listener);
44
+       spa_bt_transport_free(rfcomm->transport);
45
+       rfcomm->transport = NULL;
46
+   }
47
+
48
    if ((pathfd = spa_aprintf("%s/fd%d", rfcomm->path, rfcomm->source.fd)) == NULL)
49
-       return NULL;
50
+       goto fail;
51
 
52
    t = spa_bt_transport_create(backend->monitor, pathfd, sizeof(struct transport_data));
53
    if (t == NULL) {
54
        free(pathfd);
55
-       return NULL;
56
+       goto fail;
57
    }
58
    spa_bt_transport_set_implementation(t, &sco_transport_impl, t);
59
 
60
@@ -222,6 +229,7 @@
61
    t->backend = &backend->this;
62
    t->n_channels = 1;
63
    t->channels0 = SPA_AUDIO_CHANNEL_MONO;
64
+   t->codec = HFP_AUDIO_CODEC_CVSD;
65
 
66
    td = t->user_data;
67
    td->rfcomm = rfcomm;
68
@@ -238,13 +246,18 @@
69
        t->volumesi.active = rfcomm->volumesi.active;
70
        t->volumesi.hw_volume_max = SPA_BT_VOLUME_HS_MAX;
71
        if (rfcomm->volumesi.active && rfcomm->volumesi.hw_volume != SPA_BT_VOLUME_INVALID)
72
-           t->volumesi.volume =
73
+           t->volumesi.volume = (float)
74
                spa_bt_volume_hw_to_linear(rfcomm->volumesi.hw_volume, t->volumesi.hw_volume_max);
75
    }
76
 
77
    spa_bt_transport_add_listener(t, &rfcomm->transport_listener, &transport_events, rfcomm);
78
 
79
-   return t;
80
+   rfcomm->transport = t;
81
+   return 0;
82
+
83
+fail:
84
+   spa_log_warn(backend->log, "failed to create transport");
85
+   return -ENOMEM;
86
 }
87
 
88
 static int codec_switch_stop_timer(struct rfcomm *rfcomm);
89
@@ -411,7 +424,7 @@
90
    for (int i = 0; i < SPA_BT_VOLUME_ID_TERM ; ++i) {
91
        t_volume = &rfcomm->transport->volumesi;
92
        t_volume->active = rfcomm->volumesi.active;
93
-       t_volume->volume =
94
+       t_volume->volume = (float)
95
            spa_bt_volume_hw_to_linear(rfcomm->volumesi.hw_volume, t_volume->hw_volume_max);
96
    }
97
 
98
@@ -422,7 +435,7 @@
99
 static bool rfcomm_hsp_ag(struct rfcomm *rfcomm, char* buf)
100
 {
101
    struct impl *backend = rfcomm->backend;
102
-   unsigned int gain, dummy;
103
+   unsigned int gain;
104
 
105
    /* There are only three HSP AT commands:
106
     * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain.
107
@@ -445,8 +458,9 @@
108
            rfcomm_send_reply(rfcomm, "ERROR");
109
            spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", buf);
110
        }
111
-   } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
112
+   } else if (spa_strstartswith(buf, "AT+CKPD=200") == 1) {
113
        rfcomm_send_reply(rfcomm, "OK");
114
+       spa_bt_device_emit_switch_profile(rfcomm->device);
115
    } else {
116
        return false;
117
    }
118
@@ -495,19 +509,19 @@
119
     *   or when the gain is changed on the AG side.
120
     * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because
121
     *   it does not expect a reply. */
122
-   if (sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) {
123
+   if (sscanf(buf, "+VGS=%d", &gain) == 1) {
124
        if (gain <= SPA_BT_VOLUME_HS_MAX) {
125
            rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_RX, gain);
126
        } else {
127
            spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", buf);
128
        }
129
-   } else if (sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) {
130
+   } else if (sscanf(buf, "+VGM=%d", &gain) == 1) {
131
        if (gain <= SPA_BT_VOLUME_HS_MAX) {
132
            rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_TX, gain);
133
        } else {
134
            spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", buf);
135
        }
136
-   } else if (spa_strstartswith(buf, "\r\nOK\r\n")) {
137
+   } else if (spa_streq(buf, "OK")) {
138
        if (rfcomm->hs_state == hsp_hs_init2) {
139
            if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
140
                rfcomm->hs_state = hsp_hs_vgs;
141
@@ -614,28 +628,48 @@
142
 
143
 #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
144
 
145
-static bool device_supports_required_mSBC_transport_modes(
146
-       struct impl *backend, struct spa_bt_device *device)
147
+static bool device_supports_codec(struct impl *backend, struct spa_bt_device *device, int codec)
148
 {
149
    int res;
150
-   bool msbc_ok, msbc_alt1_ok;
151
+   bool alt6_ok = true, alt1_ok = true;
152
+   bool msbc_alt6_ok = true, msbc_alt1_ok = true;
153
    uint32_t bt_features;
154
 
155
    if (device->adapter == NULL)
156
        return false;
157
 
158
    if (backend->quirks && spa_bt_quirks_get_features(backend->quirks, device->adapter, device, &bt_features) == 0) {
159
-       msbc_ok = bt_features & SPA_BT_FEATURE_MSBC;
160
-       msbc_alt1_ok = bt_features & (SPA_BT_FEATURE_MSBC_ALT1 | SPA_BT_FEATURE_MSBC_ALT1_RTL);
161
-   } else {
162
-       msbc_ok = true;
163
-       msbc_alt1_ok = true;
164
+       msbc_alt1_ok = (bt_features & (SPA_BT_FEATURE_MSBC_ALT1 | SPA_BT_FEATURE_MSBC_ALT1_RTL));
165
+       msbc_alt6_ok = (bt_features & SPA_BT_FEATURE_MSBC);
166
+   }
167
+
168
+   switch (codec) {
169
+   case HFP_AUDIO_CODEC_CVSD:
170
+       return true;
171
+   case HFP_AUDIO_CODEC_MSBC:
172
+       alt1_ok = msbc_alt1_ok;
173
+       alt6_ok = msbc_alt6_ok;
174
+       break;
175
+   case HFP_AUDIO_CODEC_LC3_SWB:
176
+#ifdef HAVE_LC3
177
+       /* LC3-SWB has same transport requirements as msbc.
178
+        * However, ALT1/ALT5 modes don't appear to work, seem
179
+        * to lose frame sync so output is garbled.
180
+        */
181
+       alt1_ok = false;
182
+       alt6_ok = msbc_alt6_ok;
183
+       break;
184
+#else
185
+       return false;
186
+#endif
187
+   default:
188
+       return false;
189
    }
190
 
191
    spa_log_info(backend->log,
192
-           "bluez-monitor/hardware.conf: msbc:%d msbc-alt1:%d", (int)msbc_ok, (int)msbc_alt1_ok);
193
+           "bluez-monitor/hardware.conf: alt6:%d alt1/5:%d", (int)alt6_ok, (int)alt1_ok);
194
 
195
-   if (!msbc_ok && !msbc_alt1_ok)
196
+   if (!alt6_ok && !alt1_ok)
197
        return false;
198
 
199
    res = spa_bt_adapter_has_msbc(device->adapter);
200
@@ -655,26 +689,26 @@
201
    }
202
 
203
    /* Check if USB ALT6 is really available on the device */
204
-   if (device->adapter->bus_type == BUS_TYPE_USB && !msbc_alt1_ok && msbc_ok) {
205
+   if (device->adapter->bus_type == BUS_TYPE_USB && !alt1_ok && alt6_ok) {
206
 #ifdef HAVE_LIBUSB
207
        if (device->adapter->source_id == SOURCE_ID_USB) {
208
-           msbc_ok = check_usb_altsetting_6(backend, device->adapter->vendor_id,
209
+           alt6_ok = check_usb_altsetting_6(backend, device->adapter->vendor_id,
210
                    device->adapter->product_id);
211
        } else {
212
-           msbc_ok = false;
213
+           alt6_ok = false;
214
        }
215
-       if (!msbc_ok)
216
+       if (!alt6_ok)
217
            spa_log_info(backend->log, "bluetooth host adapter does not support USB ALT6");
218
 #else
219
        spa_log_info(backend->log,
220
-           "compiled without libusb; can't check if bluetooth adapter has USB ALT6");
221
-       msbc_ok = false;
222
+           "compiled without libusb; can't check if bluetooth adapter has USB ALT6, assuming no");
223
+       alt6_ok = false;
224
 #endif
225
    }
226
    if (device->adapter->bus_type != BUS_TYPE_USB)
227
-       msbc_alt1_ok = false;
228
+       alt1_ok = false;
229
 
230
-   return msbc_ok || msbc_alt1_ok;
231
+   return alt6_ok || alt1_ok;
232
 }
233
 
234
 static int codec_switch_start_timer(struct rfcomm *rfcomm, int timeout_msec);
235
@@ -776,14 +810,10 @@
236
    int xapl_product;
237
    int xapl_features;
238
 
239
-   spa_debug_log_mem(backend->log, SPA_LOG_LEVEL_DEBUG, 2, buf, strlen(buf));
240
-
241
-   /* Some devices send initial \n: be permissive */
242
-   while (*buf == '\n')
243
-       ++buf;
244
-
245
    if (sscanf(buf, "AT+BRSF=%u", &features) == 1) {
246
        unsigned int ag_features = SPA_BT_HFP_AG_FEATURE_NONE;
247
+       bool codecs = device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_MSBC) ||
248
+           device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_LC3_SWB);
249
 
250
        /*
251
         * Determine device volume control. Some headsets only support control of
252
@@ -792,10 +822,9 @@
253
         */
254
        rfcomm->has_volume = (features & SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL);
255
 
256
-       /* Decide if we want to signal that the computer supports mSBC negotiation
257
+       /* Decide if we want to signal that the computer supports codec negotiation
258
           This should be done when the computers bluetooth adapter supports the necessary transport mode */
259
-       if (device_supports_required_mSBC_transport_modes(backend, rfcomm->device)) {
260
-
261
+       if (codecs) {
262
            /* set the feature bit that indicates AG (=computer) supports codec negotiation */
263
            ag_features |= SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION;
264
 
265
@@ -807,6 +836,7 @@
266
                /* Prepare reply: Audio Gateway (=computer) supports codec negotiation */
267
                rfcomm->codec_negotiation_supported = true;
268
                rfcomm->msbc_supported_by_hfp = false;
269
+               rfcomm->lc3_supported_by_hfp = false;
270
            } else {
271
                /* Codec negotiation not supported */
272
                spa_log_debug(backend->log,
273
@@ -815,6 +845,7 @@
274
 
275
                rfcomm->codec_negotiation_supported = false;
276
                rfcomm->msbc_supported_by_hfp = false;
277
+               rfcomm->lc3_supported_by_hfp = false;
278
            }
279
        }
280
 
281
@@ -837,14 +868,22 @@
282
            /* skip token 0 i.e. the "AT+BAC=" part */
283
            if (cntr > 0 && sscanf(token, "%u", &codec_id) == 1) {
284
                spa_log_debug(backend->log, "RFCOMM AT+BAC found codec %u", codec_id);
285
-               if (codec_id == HFP_AUDIO_CODEC_MSBC) {
286
-                   rfcomm->msbc_supported_by_hfp = true;
287
-                   spa_log_debug(backend->log, "RFCOMM headset supports mSBC codec");
288
-               }
289
+
290
+               if (codec_id == HFP_AUDIO_CODEC_MSBC)
291
+                   rfcomm->msbc_supported_by_hfp =
292
+                       device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_MSBC);
293
+               else if (codec_id == HFP_AUDIO_CODEC_LC3_SWB)
294
+                   rfcomm->lc3_supported_by_hfp =
295
+                       device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_LC3_SWB);
296
            }
297
            cntr++;
298
        }
299
 
300
+       if (rfcomm->msbc_supported_by_hfp)
301
+           spa_log_debug(backend->log, "mSBC codec is supported");
302
+       if (rfcomm->lc3_supported_by_hfp)
303
+           spa_log_debug(backend->log, "LC3 codec is supported");
304
+
305
        rfcomm_send_reply(rfcomm, "OK");
306
    } else if (spa_strstartswith(buf, "AT+CIND=?")) {
307
        rfcomm_send_reply(rfcomm, "+CIND:%s", CIND_INDICATORS);
308
@@ -856,6 +895,7 @@
309
        rfcomm_send_reply(rfcomm, "OK");
310
    } else if (spa_strstartswith(buf, "AT+CMER")) {
311
        int mode, keyp, disp, ind;
312
+       bool have_codecs = rfcomm->msbc_supported_by_hfp || rfcomm->lc3_supported_by_hfp;
313
 
314
        rfcomm->slc_configured = true;
315
        rfcomm_send_reply(rfcomm, "OK");
316
@@ -867,15 +907,16 @@
317
            rfcomm->cind_call_notify = false;
318
 
319
        /* switch codec to mSBC by sending unsolicited +BCS message */
320
-       if (rfcomm->codec_negotiation_supported && rfcomm->msbc_supported_by_hfp) {
321
+       if (rfcomm->codec_negotiation_supported && have_codecs) {
322
            spa_log_debug(backend->log, "RFCOMM initial codec setup");
323
            rfcomm->hfp_ag_initial_codec_setup = HFP_AG_INITIAL_CODEC_SETUP_SEND;
324
-           rfcomm_send_reply(rfcomm, "+BCS: 2");
325
+           if (rfcomm->lc3_supported_by_hfp)
326
+               rfcomm_send_reply(rfcomm, "+BCS: 3");
327
+           else
328
+               rfcomm_send_reply(rfcomm, "+BCS: 2");
329
            codec_switch_start_timer(rfcomm, HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC);
330
        } else {
331
-           rfcomm->transport = _transport_create(rfcomm);
332
-           if (rfcomm->transport == NULL) {
333
-               spa_log_warn(backend->log, "can't create transport: %m");
334
+           if (rfcomm_new_transport(rfcomm) < 0) {
335
                // TODO: We should manage the missing transport
336
            } else {
337
                rfcomm->transport->codec = HFP_AUDIO_CODEC_CVSD;
338
@@ -883,7 +924,7 @@
339
                rfcomm_emit_volume_changed(rfcomm, -1, SPA_BT_VOLUME_INVALID);
340
            }
341
        }
342
-   } else if (spa_streq(buf, "\r")) {
343
+   } else if (spa_streq(buf, "")) {
344
        /* No commands, reply OK (ITU-T Rec. V.250 Sec. 5.2.1 & 5.6) */
345
        rfcomm_send_reply(rfcomm, "OK");
346
    } else if (!rfcomm->slc_configured) {
347
@@ -903,7 +944,8 @@
348
        codec_switch_stop_timer(rfcomm);
349
        volume_sync_stop_timer(rfcomm);
350
 
351
-       if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC) {
352
+       if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC &&
353
+               selected_codec != HFP_AUDIO_CODEC_LC3_SWB) {
354
            spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec);
355
            rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
356
            if (was_switching_codec)
357
@@ -916,12 +958,7 @@
358
        spa_log_debug(backend->log, "RFCOMM selected_codec = %i", selected_codec);
359
 
360
        /* Recreate transport, since previous connection may now be invalid */
361
-       if (rfcomm->transport)
362
-           spa_bt_transport_free(rfcomm->transport);
363
-
364
-       rfcomm->transport = _transport_create(rfcomm);
365
-       if (rfcomm->transport == NULL) {
366
-           spa_log_warn(backend->log, "can't create transport: %m");
367
+       if (rfcomm_new_transport(rfcomm) < 0) {
368
            // TODO: We should manage the missing transport
369
            rfcomm_send_error(rfcomm, CMEE_AG_FAILURE);
370
            if (was_switching_codec)
371
@@ -1115,7 +1152,7 @@
372
 
373
    /* *****
374
     * Following commands requires a Service Level Connection
375
-    * and acces to a modem
376
+    * and access to a modem
377
     * ***** */
378
 
379
    } else if (!backend->modem.network_has_service) {
380
@@ -1125,7 +1162,7 @@
381
 
382
    /* *****
383
     * Following commands requires a Service Level Connection,
384
-    * acces to a modem and to the network
385
+    * access to a modem and to the network
386
     * ***** */
387
 
388
    } else if (spa_strstartswith(buf, "ATA")) {
389
@@ -1177,147 +1214,148 @@
390
    return true;
391
 }
392
 
393
-static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* buf)
394
+static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
395
 {
396
    struct impl *backend = rfcomm->backend;
397
    unsigned int features, gain, selected_codec, indicator, value;
398
-   char* token;
399
-
400
-   while ((token = strsep(&buf, "\r\n"))) {
401
-       if (sscanf(token, "+BRSF:%u", &features) == 1) {
402
-           if (((features & (SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION)) != 0) &&
403
-               rfcomm->msbc_supported_by_hfp)
404
-               rfcomm->codec_negotiation_supported = true;
405
-       } else if (sscanf(token, "+BCS:%u", &selected_codec) == 1 && rfcomm->codec_negotiation_supported) {
406
-           if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC) {
407
-               spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec);
408
-           } else {
409
-               spa_log_debug(backend->log, "RFCOMM selected_codec = %i", selected_codec);
410
 
411
-               /* send codec selection to AG */
412
-               rfcomm_send_cmd(rfcomm, "AT+BCS=%u", selected_codec);
413
+   if (sscanf(token, "+BRSF:%u", &features) == 1) {
414
+       if (((features & (SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION)) != 0) &&
415
+               (rfcomm->msbc_supported_by_hfp || rfcomm->lc3_supported_by_hfp))
416
+           rfcomm->codec_negotiation_supported = true;
417
+   } else if (sscanf(token, "+BCS:%u", &selected_codec) == 1 && rfcomm->codec_negotiation_supported) {
418
+       if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC &&
419
+               selected_codec != HFP_AUDIO_CODEC_LC3_SWB) {
420
+           spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec);
421
+       } else {
422
+           spa_log_debug(backend->log, "RFCOMM selected_codec = %i", selected_codec);
423
 
424
-               rfcomm->hf_state = hfp_hf_bcs;
425
+           /* send codec selection to AG */
426
+           rfcomm_send_cmd(rfcomm, "AT+BCS=%u", selected_codec);
427
 
428
-               if (!rfcomm->transport || (rfcomm->transport->codec != selected_codec) ) {
429
-                   if (rfcomm->transport)
430
-                       spa_bt_transport_free(rfcomm->transport);
431
+           rfcomm->hf_state = hfp_hf_bcs;
432
 
433
-                   rfcomm->transport = _transport_create(rfcomm);
434
-                   if (rfcomm->transport == NULL) {
435
-                       spa_log_warn(backend->log, "can't create transport: %m");
436
-                       // TODO: We should manage the missing transport
437
-                   } else {
438
-                       rfcomm->transport->codec = selected_codec;
439
-                       spa_bt_device_connect_profile(rfcomm->device, rfcomm->profile);
440
-                   }
441
+           if (!rfcomm->transport || (rfcomm->transport->codec != selected_codec) ) {
442
+               if (rfcomm_new_transport(rfcomm) < 0) {
443
+                   // TODO: We should manage the missing transport
444
+               } else {
445
+                   rfcomm->transport->codec = selected_codec;
446
+                   spa_bt_device_connect_profile(rfcomm->device, rfcomm->profile);
447
                }
448
            }
449
-       } else if (sscanf(token, "+VGM%*1:=%u", &gain) == 1) {
450
-           if (gain <= SPA_BT_VOLUME_HS_MAX) {
451
-               rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_TX, gain);
452
-           } else {
453
-               spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", token);
454
+       }
455
+   } else if (sscanf(token, "+VGM%*1:=%u", &gain) == 1) {
456
+       if (gain <= SPA_BT_VOLUME_HS_MAX) {
457
+           rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_TX, gain);
458
+       } else {
459
+           spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", token);
460
+       }
461
+   } else if (sscanf(token, "+VGS%*1:=%u", &gain) == 1) {
462
+       if (gain <= SPA_BT_VOLUME_HS_MAX) {
463
+           rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_RX, gain);
464
+       } else {
465
+           spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", token);
466
+       }
467
+   } else if (spa_strstartswith(token, "+CIND: (")) {
468
+       uint8_t i = 1;
469
+       while (strstr(token, "\"")) {
470
+           token += strcspn(token, "\"") + 1;
471
+           tokenstrcspn(token, "\"") = 0;
472
+           rfcomm->hf_indicatorsi = strdup(token);
473
+           token += strcspn(token, "\"") + 1;
474
+           i++;
475
+           if (i == MAX_HF_INDICATORS) {
476
+               break;
477
            }
478
-       } else if (sscanf(token, "+VGS%*1:=%u", &gain) == 1) {
479
-           if (gain <= SPA_BT_VOLUME_HS_MAX) {
480
-               rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_RX, gain);
481
-           } else {
482
-               spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", token);
483
+       }
484
+   } else if (spa_strstartswith(token, "+CIND: ")) {
485
+       tokenstrcspn(token, "\r") = 0;
486
+       tokenstrcspn(token, "\n") = 0;
487
+       token += strlen("+CIND: ");
488
+       uint8_t i = 1;
489
+       while (strlen(token)) {
490
+           if (i >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsi) {
491
+               break;
492
            }
493
-       } else if (spa_strstartswith(token, "+CIND: (")) {
494
-           uint8_t i = 1;
495
-           while (strstr(token, "\"")) {
496
-               token += strcspn(token, "\"") + 1;
497
-               tokenstrcspn(token, "\"") = 0;
498
-               rfcomm->hf_indicatorsi = strdup(token);
499
-               token += strcspn(token, "\"") + 1;
500
-               i++;
501
-               if (i == MAX_HF_INDICATORS) {
502
-                   break;
503
-               }
504
+           tokenstrcspn(token, ",") = 0;
505
+           spa_log_info(backend->log, "AG indicator state: %s = %i", rfcomm->hf_indicatorsi, atoi(token));
506
+
507
+           if (spa_streq(rfcomm->hf_indicatorsi, "battchg")) {
508
+               spa_bt_device_report_battery_level(rfcomm->device, atoi(token) * 100 / 5);
509
            }
510
-       } else if (spa_strstartswith(token, "+CIND: ")) {
511
-           tokenstrcspn(token, "\r") = 0;
512
-           tokenstrcspn(token, "\n") = 0;
513
-           token += strlen("+CIND: ");
514
-           uint8_t i = 1;
515
-           while (strlen(token)) {
516
-               if (i >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsi) {
517
-                   break;
518
-               }
519
-               tokenstrcspn(token, ",") = 0;
520
-               spa_log_info(backend->log, "AG indicator state: %s = %i", rfcomm->hf_indicatorsi, atoi(token));
521
 
522
-               if (spa_streq(rfcomm->hf_indicatorsi, "battchg")) {
523
-                   spa_bt_device_report_battery_level(rfcomm->device, atoi(token) * 100 / 5);
524
-               }
525
+           token += strcspn(token, "\0") + 1;
526
+           i++;
527
+       }
528
+   } else if (sscanf(token, "+CIEV: %u,%u", &indicator, &value) == 2) {
529
+       if (indicator >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsindicator) {
530
+           spa_log_warn(backend->log, "indicator %u has not been registered, ignoring", indicator);
531
+       } else {
532
+           spa_log_info(backend->log, "AG indicator update: %s = %u", rfcomm->hf_indicatorsindicator, value);
533
 
534
-               token += strcspn(token, "\0") + 1;
535
-               i++;
536
+           if (spa_streq(rfcomm->hf_indicatorsindicator, "battchg")) {
537
+               spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5);
538
            }
539
-       } else if (sscanf(token, "+CIEV: %u,%u", &indicator, &value) == 2) {
540
-           if (indicator >= MAX_HF_INDICATORS || !rfcomm->hf_indicatorsindicator) {
541
-               spa_log_warn(backend->log, "indicator %u has not been registered, ignoring", indicator);
542
+       }
543
+   } else if (spa_strstartswith(token, "OK")) {
544
+       switch(rfcomm->hf_state) {
545
+       case hfp_hf_brsf:
546
+           if (rfcomm->codec_negotiation_supported) {
547
+               char buf64;
548
+               struct spa_strbuf str;
549
+
550
+               spa_strbuf_init(&str, buf, sizeof(buf));
551
+               spa_strbuf_append(&str, "1");
552
+               if (rfcomm->msbc_supported_by_hfp)
553
+                   spa_strbuf_append(&str, ",2");
554
+               if (rfcomm->lc3_supported_by_hfp)
555
+                   spa_strbuf_append(&str, ",3");
556
+
557
+               rfcomm_send_cmd(rfcomm, "AT+BAC=%s", buf);
558
+               rfcomm->hf_state = hfp_hf_bac;
559
            } else {
560
-               spa_log_info(backend->log, "AG indicator update: %s = %u", rfcomm->hf_indicatorsindicator, value);
561
-
562
-               if (spa_streq(rfcomm->hf_indicatorsindicator, "battchg")) {
563
-                   spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5);
564
-               }
565
+               rfcomm_send_cmd(rfcomm, "AT+CIND=?");
566
+               rfcomm->hf_state = hfp_hf_cind1;
567
            }
568
-       } else if (spa_strstartswith(token, "OK")) {
569
-           switch(rfcomm->hf_state) {
570
-               case hfp_hf_brsf:
571
-                   if (rfcomm->codec_negotiation_supported) {
572
-                       rfcomm_send_cmd(rfcomm, "AT+BAC=1,2");
573
-                       rfcomm->hf_state = hfp_hf_bac;
574
-                   } else {
575
-                       rfcomm_send_cmd(rfcomm, "AT+CIND=?");
576
-                       rfcomm->hf_state = hfp_hf_cind1;
577
-                   }
578
-                   break;
579
-               case hfp_hf_bac:
580
-                   rfcomm_send_cmd(rfcomm, "AT+CIND=?");
581
-                   rfcomm->hf_state = hfp_hf_cind1;
582
-                   break;
583
-               case hfp_hf_cind1:
584
-                   rfcomm_send_cmd(rfcomm, "AT+CIND?");
585
-                   rfcomm->hf_state = hfp_hf_cind2;
586
-                   break;
587
-               case hfp_hf_cind2:
588
-                   rfcomm_send_cmd(rfcomm, "AT+CMER=3,0,0,1");
589
-                   rfcomm->hf_state = hfp_hf_cmer;
590
-                   break;
591
-               case hfp_hf_cmer:
592
-                   rfcomm->hf_state = hfp_hf_slc1;
593
-                   rfcomm->slc_configured = true;
594
-                   if (!rfcomm->codec_negotiation_supported) {
595
-                       rfcomm->transport = _transport_create(rfcomm);
596
-                       if (rfcomm->transport == NULL) {
597
-                           spa_log_warn(backend->log, "can't create transport: %m");
598
-                           // TODO: We should manage the missing transport
599
-                       } else {
600
-                           rfcomm->transport->codec = HFP_AUDIO_CODEC_CVSD;
601
-                           spa_bt_device_connect_profile(rfcomm->device, rfcomm->profile);
602
-                       }
603
-                   }
604
-                   /* Report volume on SLC establishment */
605
-                   if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
606
-                       rfcomm->hf_state = hfp_hf_vgs;
607
-                   break;
608
-               case hfp_hf_slc2:
609
-                   if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
610
-                       rfcomm->hf_state = hfp_hf_vgs;
611
-                   break;
612
-               case hfp_hf_vgs:
613
-                   rfcomm->hf_state = hfp_hf_slc1;
614
-                   if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX))
615
-                       rfcomm->hf_state = hfp_hf_vgm;
616
-                   break;
617
-               default:
618
-                   break;
619
+           break;
620
+       case hfp_hf_bac:
621
+           rfcomm_send_cmd(rfcomm, "AT+CIND=?");
622
+           rfcomm->hf_state = hfp_hf_cind1;
623
+           break;
624
+       case hfp_hf_cind1:
625
+           rfcomm_send_cmd(rfcomm, "AT+CIND?");
626
+           rfcomm->hf_state = hfp_hf_cind2;
627
+           break;
628
+       case hfp_hf_cind2:
629
+           rfcomm_send_cmd(rfcomm, "AT+CMER=3,0,0,1");
630
+           rfcomm->hf_state = hfp_hf_cmer;
631
+           break;
632
+       case hfp_hf_cmer:
633
+           rfcomm->hf_state = hfp_hf_slc1;
634
+           rfcomm->slc_configured = true;
635
+           if (!rfcomm->codec_negotiation_supported) {
636
+               if (rfcomm_new_transport(rfcomm) < 0) {
637
+                   // TODO: We should manage the missing transport
638
+               } else {
639
+                   rfcomm->transport->codec = HFP_AUDIO_CODEC_CVSD;
640
+                   spa_bt_device_connect_profile(rfcomm->device, rfcomm->profile);
641
+               }
642
            }
643
+           /* Report volume on SLC establishment */
644
+           if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
645
+               rfcomm->hf_state = hfp_hf_vgs;
646
+           break;
647
+       case hfp_hf_slc2:
648
+           if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
649
+               rfcomm->hf_state = hfp_hf_vgs;
650
+           break;
651
+       case hfp_hf_vgs:
652
+           rfcomm->hf_state = hfp_hf_slc1;
653
+           if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX))
654
+               rfcomm->hf_state = hfp_hf_vgm;
655
+           break;
656
+       default:
657
+           break;
658
        }
659
    }
660
 
661
@@ -1326,6 +1364,35 @@
662
 
663
 #endif
664
 
665
+static void rfcomm_process_events(struct rfcomm *rfcomm, char *buf, bool ag, bool (*handler)(struct rfcomm *, char *))
666
+{
667
+   struct impl *backend = rfcomm->backend;
668
+   char *token;
669
+
670
+   /* Relaxed parsing of both <COMMAND>\r (AG) and \r\n<REPLY>\r\n (HF) */
671
+
672
+   while ((token = strsep(&buf, "\r"))) {
673
+       size_t len;
674
+
675
+       /* Skip leading and trailing \n */
676
+       while (*token == '\n')
677
+           ++token;
678
+       for (len = strlen(token); len > 0 && tokenlen - 1 == '\n'; --len)
679
+           tokenlen - 1 = '\0';
680
+
681
+       /* Skip empty (only last one if AG) */
682
+       if (*token == '\0' && (buf == NULL || !ag))
683
+           continue;
684
+
685
+       spa_log_debug(backend->log, "RFCOMM event: %s", token);
686
+
687
+       if (!handler(rfcomm, token)) {
688
+           spa_log_debug(backend->log, "RFCOMM received unsupported event: %s", token);
689
+           rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_SUPPORTED);
690
+       }
691
+   }
692
+}
693
+
694
 static void rfcomm_event(struct spa_source *source)
695
 {
696
    struct rfcomm *rfcomm = source->data;
697
@@ -1340,45 +1407,41 @@
698
    if (source->rmask & SPA_IO_IN) {
699
        char buf512;
700
        ssize_t len;
701
-       bool res = false;
702
 
703
-       len = read(source->fd, buf, 511);
704
+       len = read(source->fd, buf, sizeof(buf) - 1);
705
        if (len < 0) {
706
            spa_log_error(backend->log, "RFCOMM read error: %s", strerror(errno));
707
            return;
708
        }
709
        buflen = 0;
710
+
711
        spa_log_debug(backend->log, "RFCOMM << %s", buf);
712
+       spa_debug_log_mem(backend->log, SPA_LOG_LEVEL_DEBUG, 2, buf, strlen(buf));
713
 
714
        switch (rfcomm->profile) {
715
 #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
716
        case SPA_BT_PROFILE_HSP_HS:
717
-           res = rfcomm_hsp_ag(rfcomm, buf);
718
+           rfcomm_process_events(rfcomm, buf, true, rfcomm_hsp_ag);
719
            break;
720
        case SPA_BT_PROFILE_HSP_AG:
721
-           res = rfcomm_hsp_hs(rfcomm, buf);
722
+           rfcomm_process_events(rfcomm, buf, false, rfcomm_hsp_hs);
723
            break;
724
 #endif
725
 #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
726
        case SPA_BT_PROFILE_HFP_HF:
727
-           res = rfcomm_hfp_ag(rfcomm, buf);
728
+           rfcomm_process_events(rfcomm, buf, true, rfcomm_hfp_ag);
729
            break;
730
        case SPA_BT_PROFILE_HFP_AG:
731
-           res = rfcomm_hfp_hf(rfcomm, buf);
732
+           rfcomm_process_events(rfcomm, buf, false, rfcomm_hfp_hf);
733
            break;
734
 #endif
735
        default:
736
            break;
737
        }
738
-
739
-       if (!res) {
740
-           spa_log_debug(backend->log, "RFCOMM received unsupported command: %s", buf);
741
-           rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_SUPPORTED);
742
-       }
743
    }
744
 }
745
 
746
-static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapter, bool msbc)
747
+static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapter, bool transparent)
748
 {
749
    struct sockaddr_sco addr;
750
    socklen_t len;
751
@@ -1402,9 +1465,9 @@
752
        return -1;
753
    }
754
 
755
-   spa_log_debug(backend->log, "msbc=%d", (int)msbc);
756
-   if (msbc) {
757
-       /* set correct socket options for mSBC */
758
+   spa_log_debug(backend->log, "transparent=%d", (int)transparent);
759
+   if (transparent) {
760
+       /* set correct socket options for mSBC/LC3 */
761
        struct bt_voice voice_config;
762
        memset(&voice_config, 0, sizeof(voice_config));
763
        voice_config.setting = BT_VOICE_TRANSPARENT;
764
@@ -1438,7 +1501,8 @@
765
    str2ba(d->address, &addr.sco_bdaddr);
766
 
767
    for (int retry = 2;;) {
768
-       spa_autoclose int sock = sco_create_socket(backend, d->adapter, (t->codec == HFP_AUDIO_CODEC_MSBC));
769
+       bool transparent = (t->codec == HFP_AUDIO_CODEC_MSBC || t->codec == HFP_AUDIO_CODEC_LC3_SWB);
770
+       spa_autoclose int sock = sco_create_socket(backend, d->adapter, transparent);
771
        if (sock < 0)
772
            return -1;
773
 
774
@@ -1451,12 +1515,13 @@
775
        } else if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
776
            spa_log_error(backend->log, "connect(): %s", strerror(errno));
777
 #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
778
-           if (errno == EOPNOTSUPP && t->codec == HFP_AUDIO_CODEC_MSBC &&
779
-                   td->rfcomm->msbc_supported_by_hfp) {
780
-               /* Adapter doesn't support msbc. Renegotiate. */
781
+           if (errno == EOPNOTSUPP && t->codec != HFP_AUDIO_CODEC_CVSD &&
782
+                   td->rfcomm->codec_negotiation_supported) {
783
+               /* Adapter doesn't support msbc/lc3. Renegotiate. */
784
                d->adapter->msbc_probed = true;
785
                d->adapter->has_msbc = false;
786
                td->rfcomm->msbc_supported_by_hfp = false;
787
+               td->rfcomm->lc3_supported_by_hfp = false;
788
                if (t->profile == SPA_BT_PROFILE_HFP_HF) {
789
                    td->rfcomm->hfp_ag_switching_codec = true;
790
                    rfcomm_send_reply(td->rfcomm, "+BCS: 1");
791
@@ -1668,8 +1733,8 @@
792
        }
793
    }
794
 
795
-   if (source->rmask & SPA_IO_IN) {
796
-       SPA_FLAG_UPDATE(source->mask, SPA_IO_IN, false);
797
+   if (source->rmask & (SPA_IO_OUT | SPA_IO_IN)) {
798
+       SPA_FLAG_CLEAR(source->mask, SPA_IO_OUT | SPA_IO_IN);
799
        spa_loop_update_source(backend->main_loop, source);
800
        sco_ready(t);
801
    }
802
@@ -1685,15 +1750,25 @@
803
 
804
    td->err = -EINPROGRESS;
805
 
806
-   /*
807
-    * We on purpose wait for POLLIN when connecting (not POLLOUT as usual), to
808
-    * indicate ready only after we are sure the device is sending data.
809
-    */
810
    td->sco.func = sco_event;
811
    td->sco.data = t;
812
    td->sco.fd = t->fd;
813
-   td->sco.mask = SPA_IO_HUP | SPA_IO_ERR | SPA_IO_IN;
814
+   td->sco.mask = SPA_IO_HUP | SPA_IO_ERR;
815
    td->sco.rmask = 0;
816
+
817
+   switch (t->device->adapter->bus_type) {
818
+   case BUS_TYPE_USB:
819
+       /* With USB controllers, we have to determine packet size from incoming
820
+        * packets before we can send. Wait for POLLIN when connecting (not
821
+        * POLLOUT as usual).
822
+        */
823
+       td->sco.mask |= SPA_IO_IN;
824
+       break;
825
+   default:
826
+       td->sco.mask |= SPA_IO_OUT;
827
+       break;
828
+   }
829
+
830
    spa_loop_add_source(backend->main_loop, &td->sco);
831
 }
832
 
833
@@ -1736,10 +1811,12 @@
834
 
835
    /* Find transport for local and remote address */
836
    spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
837
-       if (rfcomm->transport && spa_streq(rfcomm->transport->device->address, remote_address) &&
838
-           spa_streq(rfcomm->transport->device->adapter->address, local_address)) {
839
-                   t = rfcomm->transport;
840
-                   break;
841
+       if ((rfcomm->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) &&
842
+               rfcomm->transport &&
843
+               spa_streq(rfcomm->device->address, remote_address) &&
844
+               spa_streq(rfcomm->device->adapter->address, local_address)) {
845
+           t = rfcomm->transport;
846
+           break;
847
        }
848
    }
849
    if (!t) {
850
@@ -1748,12 +1825,7 @@
851
        return;
852
    }
853
 
854
-   /* The Synchronous Connection shall always be established by the AG, i.e. the remote profile
855
-      should be a HSP AG or HFP AG profile */
856
-   if ((t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) == 0) {
857
-       spa_log_debug(backend->log, "transport %p: Rejecting incoming audio connection to an AG profile", t);
858
-       return;
859
-   }
860
+   spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
861
 
862
    if (t->fd >= 0) {
863
        spa_log_debug(backend->log, "transport %p: Rejecting, audio already connected", t);
864
@@ -1767,8 +1839,8 @@
865
         * accepted socket. */
866
        char buff;
867
 
868
-       if (t->codec == HFP_AUDIO_CODEC_MSBC) {
869
-           /* set correct socket options for mSBC */
870
+       if (t->codec == HFP_AUDIO_CODEC_MSBC || t->codec == HFP_AUDIO_CODEC_LC3_SWB) {
871
+           /* set correct socket options for mSBC/LC3 */
872
            struct bt_voice voice_config;
873
            memset(&voice_config, 0, sizeof(voice_config));
874
            voice_config.setting = BT_VOICE_TRANSPARENT;
875
@@ -1939,11 +2011,15 @@
876
    if (codec == HFP_AUDIO_CODEC_CVSD)
877
        return 1;
878
 
879
-   return (codec == HFP_AUDIO_CODEC_MSBC &&
880
-           (rfcomm->profile == SPA_BT_PROFILE_HFP_AG ||
881
-            rfcomm->profile == SPA_BT_PROFILE_HFP_HF) &&
882
-           rfcomm->msbc_supported_by_hfp &&
883
-           rfcomm->codec_negotiation_supported) ? 1 : 0;
884
+   if (!rfcomm->codec_negotiation_supported)
885
+       return 0;
886
+
887
+   if (codec == HFP_AUDIO_CODEC_MSBC)
888
+       return rfcomm->msbc_supported_by_hfp;
889
+   else if (codec == HFP_AUDIO_CODEC_LC3_SWB)
890
+       return rfcomm->lc3_supported_by_hfp;
891
+
892
+   return 0;
893
 #else
894
    return -ENOTSUP;
895
 #endif
896
@@ -2046,10 +2122,7 @@
897
        /* Failure, try falling back to CVSD. */
898
        rfcomm->hfp_ag_initial_codec_setup = HFP_AG_INITIAL_CODEC_SETUP_NONE;
899
        if (rfcomm->transport == NULL) {
900
-           rfcomm->transport = _transport_create(rfcomm);
901
-           if (rfcomm->transport == NULL) {
902
-               spa_log_warn(backend->log, "can't create transport: %m");
903
-           } else {
904
+           if (rfcomm_new_transport(rfcomm) == 0) {
905
                rfcomm->transport->codec = HFP_AUDIO_CODEC_CVSD;
906
                spa_bt_device_connect_profile(rfcomm->device, rfcomm->profile);
907
            }
908
@@ -2168,7 +2241,6 @@
909
    enum spa_bt_profile profile;
910
    struct rfcomm *rfcomm;
911
    struct spa_bt_device *d;
912
-   struct spa_bt_transport *t = NULL;
913
    spa_autoclose int fd = -1;
914
 
915
    if (!dbus_message_has_signature(m, "oha{sv}")) {
916
@@ -2226,34 +2298,37 @@
917
    spa_list_append(&backend->rfcomm_list, &rfcomm->link);
918
 
919
    if (profile == SPA_BT_PROFILE_HSP_HS || profile == SPA_BT_PROFILE_HSP_AG) {
920
-       t = _transport_create(rfcomm);
921
-       if (t == NULL) {
922
-           spa_log_warn(backend->log, "can't create transport: %m");
923
+       if (rfcomm_new_transport(rfcomm) < 0)
924
            goto fail_need_memory;
925
-       }
926
-       rfcomm->transport = t;
927
+
928
+       rfcomm->transport->codec = HFP_AUDIO_CODEC_CVSD;
929
        rfcomm->has_volume = rfcomm_volume_enabled(rfcomm);
930
 
931
        if (profile == SPA_BT_PROFILE_HSP_AG) {
932
            rfcomm->hs_state = hsp_hs_init1;
933
        }
934
 
935
-       spa_bt_device_connect_profile(t->device, profile);
936
+       spa_bt_device_connect_profile(rfcomm->device, profile);
937
 
938
-       spa_log_debug(backend->log, "Transport %s available for profile %s", t->path, handler);
939
+       spa_log_debug(backend->log, "Transport %s available for profile %s",
940
+               rfcomm->transport->path, handler);
941
    } else if (profile == SPA_BT_PROFILE_HFP_AG) {
942
        /* Start SLC connection */
943
        unsigned int hf_features = SPA_BT_HFP_HF_FEATURE_NONE;
944
+       bool has_msbc = device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_MSBC);
945
+       bool has_lc3 = device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_LC3_SWB);
946
 
947
-       /* Decide if we want to signal that the HF supports mSBC negotiation
948
+       /* Decide if we want to signal that the HF supports mSBC/LC3 negotiation
949
           This should be done when the bluetooth adapter supports the necessary transport mode */
950
-       if (device_supports_required_mSBC_transport_modes(backend, rfcomm->device)) {
951
+       if (has_msbc || has_lc3) {
952
            /* set the feature bit that indicates HF supports codec negotiation */
953
            hf_features |= SPA_BT_HFP_HF_FEATURE_CODEC_NEGOTIATION;
954
-           rfcomm->msbc_supported_by_hfp = true;
955
+           rfcomm->msbc_supported_by_hfp = has_msbc;
956
+           rfcomm->lc3_supported_by_hfp = has_lc3;
957
            rfcomm->codec_negotiation_supported = false;
958
        } else {
959
            rfcomm->msbc_supported_by_hfp = false;
960
+           rfcomm->lc3_supported_by_hfp = false;
961
            rfcomm->codec_negotiation_supported = false;
962
        }
963
 
964
@@ -2586,7 +2661,7 @@
965
    struct rfcomm *rfcomm;
966
 
967
    spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
968
-       if (rfcomm->slc_configured &&
969
+       if (rfcomm->profile == SPA_BT_PROFILE_HFP_HF && rfcomm->slc_configured &&
970
            ((indicator == CIND_CALL || indicator == CIND_CALLSETUP || indicator == CIND_CALLHELD) ||
971
            (rfcomm->cind_call_notify && (rfcomm->cind_enabled_indicators & (1 << indicator)))))
972
            rfcomm_send_reply(rfcomm, "+CIEV: %d,%d", indicator, value);
973
@@ -2615,7 +2690,7 @@
974
    spa_loop_utils_update_timer(backend->loop_utils, backend->ring_timer, &ts, NULL, false);
975
 
976
    spa_list_for_each(rfcomm, &backend->rfcomm_list, link) {
977
-       if (rfcomm->slc_configured) {
978
+       if (rfcomm->profile == SPA_BT_PROFILE_HFP_HF  && rfcomm->slc_configured) {
979
            rfcomm_send_reply(rfcomm, "RING");
980
            if (rfcomm->clip_notify && number)
981
                rfcomm_send_reply(rfcomm, "+CLIP: \"%s\",%u", number, type);
982
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/backend-ofono.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/backend-ofono.c Changed
18
 
1
@@ -20,14 +20,14 @@
2
 #include <spa/utils/type.h>
3
 #include <spa/utils/result.h>
4
 #include <spa/param/audio/raw.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "defs.h"
8
-#include "dbus-helpers.h"
9
 
10
 #define INITIAL_INTERVAL_NSEC  (500 * SPA_NSEC_PER_MSEC)
11
 #define ACTION_INTERVAL_NSEC   (3000 * SPA_NSEC_PER_MSEC)
12
 
13
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.ofono");
14
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.ofono");
15
 #undef SPA_LOG_TOPIC_DEFAULT
16
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
17
 
18
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bap-codec-caps.h Changed
18
 
1
@@ -53,7 +53,7 @@
2
 
3
 #define LC3_MAX_CHANNELS 28
4
 
5
-#define BAP_CHANNEL_NOT_ALLOWED    0x00000000
6
+#define BAP_CHANNEL_MONO   0x00000000 /* mono */
7
 #define BAP_CHANNEL_FL     0x00000001 /* front left */
8
 #define BAP_CHANNEL_FR     0x00000002 /* front right */
9
 #define BAP_CHANNEL_FC     0x00000004 /* front center */
10
@@ -126,6 +126,7 @@
11
    uint32_t locations;
12
    uint16_t supported_context;
13
    uint16_t context;
14
+   uint32_t channel_allocation;
15
 };
16
 
17
 struct bap_codec_qos {
18
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c Changed
859
 
1
@@ -24,15 +24,11 @@
2
 #define MAX_PACS   64
3
 
4
 static struct spa_log *log;
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs.lc3");
6
-#undef SPA_LOG_TOPIC_DEFAULT
7
-#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
 
9
 struct impl {
10
    lc3_encoder_t encLC3_MAX_CHANNELS;
11
    lc3_decoder_t decLC3_MAX_CHANNELS;
12
 
13
-   int mtu;
14
    int samplerate;
15
    int channels;
16
    int frame_dus;
17
@@ -46,6 +42,21 @@
18
    size_t size;
19
    int index;
20
    uint32_t locations;
21
+   uint32_t channel_allocation;
22
+   bool sink;
23
+   bool duplex;
24
+};
25
+
26
+struct bap_qos {
27
+   char *name;
28
+   uint8_t rate;
29
+   uint8_t frame_duration;
30
+   bool framing;
31
+   uint16_t framelen;
32
+   uint8_t retransmission;
33
+   uint16_t latency;
34
+   uint32_t delay;
35
+   unsigned int priority;
36
 };
37
 
38
 typedef struct {
39
@@ -54,12 +65,16 @@
40
    uint32_t channels;
41
    uint16_t framelen;
42
    uint8_t n_blks;
43
+   bool sink;
44
+   bool duplex;
45
+   unsigned int priority;
46
 } bap_lc3_t;
47
 
48
 static const struct {
49
    uint32_t bit;
50
    enum spa_audio_channel channel;
51
 } channel_bits = {
52
+   { BAP_CHANNEL_MONO, SPA_AUDIO_CHANNEL_MONO },
53
    { BAP_CHANNEL_FL,   SPA_AUDIO_CHANNEL_FL },
54
    { BAP_CHANNEL_FR,   SPA_AUDIO_CHANNEL_FR },
55
    { BAP_CHANNEL_FC,   SPA_AUDIO_CHANNEL_FC },
56
@@ -90,6 +105,113 @@
57
    { BAP_CHANNEL_RS,   SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */
58
 };
59
 
60
+#define BAP_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \
61
+   ((struct bap_qos){ .name = (name_), .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \
62
+            .framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_),  \
63
+            .delay = (delay_), .priority = (priority_) })
64
+
65
+static const struct bap_qos bap_qos_configs = {
66
+   /* Priority: low-latency > high-reliability, 7.5ms > 10ms,
67
+    * bigger frequency and sdu better */
68
+
69
+   /* BAP v1.0.1 Table 5.2; low-latency */
70
+   BAP_QOS("8_1_1",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_7_5, false,  26, 2,  8, 40000, 30), /* 8_1_1 */
71
+   BAP_QOS("8_2_1",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_10,  false,  30, 2, 10, 40000, 20), /* 8_2_1 */
72
+   BAP_QOS("16_1_1",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false,  30, 2,  8, 40000, 31), /* 16_1_1 */
73
+   BAP_QOS("16_2_1",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10,  false,  40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */
74
+   BAP_QOS("24_1_1",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false,  45, 2,  8, 40000, 32), /* 24_1_1 */
75
+   BAP_QOS("24_2_1",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10,  false,  60, 2, 10, 40000, 22), /* 24_2_1 */
76
+   BAP_QOS("32_1_1",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false,  60, 2,  8, 40000, 33), /* 32_1_1 */
77
+   BAP_QOS("32_2_1",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10,  false,  80, 2, 10, 40000, 23), /* 32_2_1 */
78
+   BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5,  true,  97, 5, 24, 40000, 34), /* 441_1_1 */
79
+   BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10,   true, 130, 5, 31, 40000, 24), /* 441_2_1 */
80
+   BAP_QOS("48_1_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  75, 5, 15, 40000, 35), /* 48_1_1 */
81
+   BAP_QOS("48_2_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 100, 5, 20, 40000, 25), /* 48_2_1 */
82
+   BAP_QOS("48_3_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  90, 5, 15, 40000, 36), /* 48_3_1 */
83
+   BAP_QOS("48_4_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 120, 5, 20, 40000, 26), /* 48_4_1 */
84
+   BAP_QOS("48_5_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 5, 15, 40000, 37), /* 48_5_1 */
85
+   BAP_QOS("48_6_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 155, 5, 20, 40000, 27), /* 48_6_1 */
86
+
87
+   /* BAP v1.0.1 Table 5.2; high-reliability */
88
+   BAP_QOS("8_1_2",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_7_5, false,  26, 13,  75, 40000, 10), /* 8_1_2 */
89
+   BAP_QOS("8_2_2",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_10,  false,  30, 13,  95, 40000,  0), /* 8_2_2 */
90
+   BAP_QOS("16_1_2",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false,  30, 13,  75, 40000, 11), /* 16_1_2 */
91
+   BAP_QOS("16_2_2",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10,  false,  40, 13,  95, 40000,  1), /* 16_2_2 */
92
+   BAP_QOS("24_1_2",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false,  45, 13,  75, 40000, 12), /* 24_1_2 */
93
+   BAP_QOS("24_2_2",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10,  false,  60, 13,  95, 40000,  2), /* 24_2_2 */
94
+   BAP_QOS("32_1_2",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false,  60, 13,  75, 40000, 13), /* 32_1_2 */
95
+   BAP_QOS("32_2_2",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10,  false,  80, 13,  95, 40000,  3), /* 32_2_2 */
96
+   BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5,  true,  97, 13,  80, 40000, 14), /* 441_1_2 */
97
+   BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10,   true, 130, 13,  85, 40000,  4), /* 441_2_2 */
98
+   BAP_QOS("48_1_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  75, 13,  75, 40000, 15), /* 48_1_2 */
99
+   BAP_QOS("48_2_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 100, 13,  95, 40000,  5), /* 48_2_2 */
100
+   BAP_QOS("48_3_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  90, 13,  75, 40000, 16), /* 48_3_2 */
101
+   BAP_QOS("48_4_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 120, 13, 100, 40000,  6), /* 48_4_2 */
102
+   BAP_QOS("48_5_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13,  75, 40000, 17), /* 48_5_2 */
103
+   BAP_QOS("48_6_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 155, 13, 100, 40000,  7), /* 48_6_2 */
104
+};
105
+
106
+static const struct bap_qos bap_bcast_qos_configs = {
107
+   /* Priority: low-latency > high-reliability, 7.5ms > 10ms,
108
+    * bigger frequency and sdu better */
109
+
110
+   /* BAP v1.0.1 Table 6.4; low-latency */
111
+   BAP_QOS("8_1_1",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_7_5, false,  26, 2,  8, 40000, 30), /* 8_1_1 */
112
+   BAP_QOS("8_2_1",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_10,  false,  30, 2, 10, 40000, 20), /* 8_2_1 */
113
+   BAP_QOS("16_1_1",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false,  30, 2,  8, 40000, 31), /* 16_1_1 */
114
+   BAP_QOS("16_2_1",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10,  false,  40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */
115
+   BAP_QOS("24_1_1",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false,  45, 2,  8, 40000, 32), /* 24_1_1 */
116
+   BAP_QOS("24_2_1",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10,  false,  60, 2, 10, 40000, 22), /* 24_2_1 */
117
+   BAP_QOS("32_1_1",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false,  60, 2,  8, 40000, 33), /* 32_1_1 */
118
+   BAP_QOS("32_2_1",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10,  false,  80, 2, 10, 40000, 23), /* 32_2_1 */
119
+   BAP_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true,   97, 4, 24, 40000, 34), /* 441_1_1 */
120
+   BAP_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10,  true,  130, 4, 31, 40000, 24), /* 441_2_1 */
121
+   BAP_QOS("48_1_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  75, 4, 15, 40000, 35), /* 48_1_1 */
122
+   BAP_QOS("48_2_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 100, 4, 20, 40000, 25), /* 48_2_1 */
123
+   BAP_QOS("48_3_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  90, 4, 15, 40000, 36), /* 48_3_1 */
124
+   BAP_QOS("48_4_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 120, 4, 20, 40000, 26), /* 48_4_1 */
125
+   BAP_QOS("48_5_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37), /* 48_5_1 */
126
+   BAP_QOS("48_6_1",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 155, 4, 20, 40000, 27), /* 48_6_1 */
127
+
128
+   /* BAP v1.0.1 Table 6.4; high-reliability */
129
+   BAP_QOS("8_1_2",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_7_5, false,  26, 4,  45, 40000, 10), /* 8_1_2 */
130
+   BAP_QOS("8_2_2",   LC3_CONFIG_FREQ_8KHZ,  LC3_CONFIG_DURATION_10,  false,  30, 4,  60, 40000,  0), /* 8_2_2 */
131
+   BAP_QOS("16_1_2",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false,  30, 4,  45, 40000, 11), /* 16_1_2 */
132
+   BAP_QOS("16_2_2",  LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10,  false,  40, 4,  60, 40000,  1), /* 16_2_2 */
133
+   BAP_QOS("24_1_2",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false,  45, 4,  45, 40000, 12), /* 24_1_2 */
134
+   BAP_QOS("24_2_2",  LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10,  false,  60, 4,  60, 40000,  2), /* 24_2_2 */
135
+   BAP_QOS("32_1_2",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false,  60, 4,  45, 40000, 13), /* 32_1_2 */
136
+   BAP_QOS("32_2_2",  LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10,  false,  80, 4,  60, 40000,  3), /* 32_2_2 */
137
+   BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true,   97, 4,  54, 40000, 14), /* 441_1_2 */
138
+   BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10,  true,  130, 4,  60, 40000,  4), /* 441_2_2 */
139
+   BAP_QOS("48_1_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  75, 4,  50, 40000, 15), /* 48_1_2 */
140
+   BAP_QOS("48_2_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 100, 4,  65, 40000,  5), /* 48_2_2 */
141
+   BAP_QOS("48_3_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false,  90, 4,  50, 40000, 16), /* 48_3_2 */
142
+   BAP_QOS("48_4_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 120, 4,  65, 40000,  6), /* 48_4_2 */
143
+   BAP_QOS("48_5_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4,  50, 40000, 17), /* 48_5_2 */
144
+   BAP_QOS("48_6_2",  LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10,  false, 155, 4,  65, 40000,  7), /* 48_6_2 */
145
+};
146
+
147
+static unsigned int get_rate_mask(uint8_t rate) {
148
+   switch (rate) {
149
+   case LC3_CONFIG_FREQ_8KHZ: return LC3_FREQ_8KHZ;
150
+   case LC3_CONFIG_FREQ_16KHZ: return LC3_FREQ_16KHZ;
151
+   case LC3_CONFIG_FREQ_24KHZ: return LC3_FREQ_24KHZ;
152
+   case LC3_CONFIG_FREQ_32KHZ: return LC3_FREQ_32KHZ;
153
+   case LC3_CONFIG_FREQ_44KHZ: return LC3_FREQ_44KHZ;
154
+   case LC3_CONFIG_FREQ_48KHZ: return LC3_FREQ_48KHZ;
155
+   }
156
+   return 0;
157
+}
158
+
159
+static unsigned int get_duration_mask(uint8_t rate) {
160
+   switch (rate) {
161
+   case LC3_CONFIG_DURATION_7_5: return LC3_DUR_7_5;
162
+   case LC3_CONFIG_DURATION_10: return LC3_DUR_10;
163
+   }
164
+   return 0;
165
+}
166
+
167
 static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len)
168
 {
169
    struct ltv *ltv = (struct ltv *)dest;
170
@@ -123,7 +245,8 @@
171
    uint16_t framelen2 = {htobs(LC3_MIN_FRAME_BYTES), htobs(LC3_MAX_FRAME_BYTES)};
172
 
173
    data += write_ltv_uint16(data, LC3_TYPE_FREQ,
174
-                            htobs(LC3_FREQ_48KHZ | LC3_FREQ_24KHZ | LC3_FREQ_16KHZ | LC3_FREQ_8KHZ));
175
+                            htobs(LC3_FREQ_48KHZ | LC3_FREQ_32KHZ | \
176
+                    LC3_FREQ_24KHZ | LC3_FREQ_16KHZ | LC3_FREQ_8KHZ));
177
    data += write_ltv_uint8(data, LC3_TYPE_DUR, LC3_DUR_ANY);
178
    data += write_ltv_uint8(data, LC3_TYPE_CHAN, LC3_CHAN_1 | LC3_CHAN_2);
179
    data += write_ltv(data, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen));
180
@@ -160,7 +283,7 @@
181
        struct spa_debug_context *debug_ctx)
182
 {
183
    /*
184
-    * BlueZ capabilites for the same codec may contain multiple
185
+    * BlueZ capabilities for the same codec may contain multiple
186
     * PACs separated by zero-length LTV (see BlueZ b907befc2d80)
187
     */
188
    int pac = 0;
189
@@ -190,10 +313,12 @@
190
    return pac + 1;
191
 }
192
 
193
-static uint8_t get_num_channels(uint32_t channels)
194
+static uint8_t get_channel_count(uint32_t channels)
195
 {
196
    uint8_t num;
197
 
198
+   channels &= BAP_CHANNEL_ALL;
199
+
200
    if (channels == 0)
201
        return 1;  /* MONO */
202
 
203
@@ -204,27 +329,84 @@
204
    return num;
205
 }
206
 
207
-static int select_channels(uint8_t channels, uint32_t locations, uint32_t *mapping, unsigned int max_channels)
208
+static bool supports_channel_count(uint8_t mask, uint8_t count)
209
 {
210
-   unsigned int i, num = 0;
211
+   if (count == 0 || count > 8)
212
+       return false;
213
+   return mask & (1u << (count - 1));
214
+}
215
 
216
-   if ((channels & LC3_CHAN_2) && max_channels >= 2)
217
-       num = 2;
218
-   else if ((channels & LC3_CHAN_1) && max_channels >= 1)
219
-       num = 1;
220
-   else
221
+static const struct bap_qos *select_bap_qos(unsigned int rate_mask, unsigned int duration_mask, uint16_t framelen_min, uint16_t framelen_max)
222
+{
223
+   const struct bap_qos *best = NULL;
224
+   unsigned int best_priority = 0;
225
+
226
+   SPA_FOR_EACH_ELEMENT_VAR(bap_qos_configs, c) {
227
+       if (c->priority < best_priority)
228
+           continue;
229
+       if (!(get_rate_mask(c->rate) & rate_mask))
230
+           continue;
231
+       if (!(get_duration_mask(c->frame_duration) & duration_mask))
232
+           continue;
233
+       if (c->framing)
234
+           continue;  /* XXX: framing not supported */
235
+       if (c->framelen < framelen_min || c->framelen > framelen_max)
236
+           continue;
237
+
238
+       best = c;
239
+       best_priority = c->priority;
240
+   }
241
+
242
+   return best;
243
+}
244
+
245
+static int select_channels(uint8_t channel_counts, uint32_t locations, uint32_t channel_allocation,
246
+       uint32_t *allocation)
247
+{
248
+   unsigned int i, num;
249
+
250
+   locations &= BAP_CHANNEL_ALL;
251
+
252
+   if (!channel_counts)
253
        return -1;
254
 
255
    if (!locations) {
256
-       *mapping = 0;  /* mono (omit Audio_Channel_Allocation) */
257
+       *allocation = 0;  /* mono (omit Audio_Channel_Allocation) */
258
+       return 0;
259
+   }
260
+
261
+   if (channel_allocation) {
262
+       channel_allocation &= locations;
263
+
264
+       /* sanity check channel allocation */
265
+       while (!supports_channel_count(channel_counts, get_channel_count(channel_allocation))) {
266
+           for (i = 32; i > 0; --i) {
267
+               uint32_t mask = (1u << (i-1));
268
+               if (channel_allocation & mask) {
269
+                   channel_allocation &= ~mask;
270
+                   break;
271
+               }
272
+           }
273
+           if (i == 0)
274
+               break;
275
+       }
276
+
277
+       *allocation = channel_allocation;
278
        return 0;
279
    }
280
 
281
    /* XXX: select some channels, but upper level should tell us what */
282
-   *mapping = 0;
283
+   if ((channel_counts & LC3_CHAN_2) && get_channel_count(locations) >= 2)
284
+       num = 2;
285
+   else if ((channel_counts & LC3_CHAN_1) && get_channel_count(locations) >= 1)
286
+       num = 1;
287
+   else
288
+       return -1;
289
+
290
+   *allocation = 0;
291
    for (i = 0; i < SPA_N_ELEMENTS(channel_bits); ++i) {
292
        if (locations & channel_bitsi.bit) {
293
-           *mapping |= channel_bitsi.bit;
294
+           *allocation |= channel_bitsi.bit;
295
            --num;
296
            if (num == 0)
297
                break;
298
@@ -240,13 +422,19 @@
299
    size_t data_size = pac->size;
300
    uint16_t framelen_min = 0, framelen_max = 0;
301
    int max_frames = -1;
302
-   uint8_t channels = 0;
303
+   uint8_t channel_counts = LC3_CHAN_1; /* Default: 1 channel (BAP v1.0.1 Sec 4.3.1) */
304
+   uint8_t max_channels = 0;
305
+   uint8_t duration_mask = 0;
306
+   uint16_t rate_mask = 0;
307
+   const struct bap_qos *bap_qos = NULL;
308
+   unsigned int i;
309
 
310
    if (!data_size)
311
        return false;
312
    memset(conf, 0, sizeof(*conf));
313
 
314
-   conf->frame_duration = 0xFF;
315
+   conf->sink = pac->sink;
316
+   conf->duplex = pac->duplex;
317
 
318
    /* XXX: we always use one frame block */
319
    conf->n_blks = 1;
320
@@ -262,41 +450,15 @@
321
        switch (ltv->type) {
322
        case LC3_TYPE_FREQ:
323
            spa_return_val_if_fail(ltv->len == 3, false);
324
-           {
325
-               uint16_t rate = ltv->value0 + (ltv->value1 << 8);
326
-               if (rate & LC3_FREQ_48KHZ)
327
-                   conf->rate = LC3_CONFIG_FREQ_48KHZ;
328
-               else if (rate & LC3_FREQ_24KHZ)
329
-                   conf->rate = LC3_CONFIG_FREQ_24KHZ;
330
-               else if (rate & LC3_FREQ_16KHZ)
331
-                   conf->rate = LC3_CONFIG_FREQ_16KHZ;
332
-               else if (rate & LC3_FREQ_8KHZ)
333
-                   conf->rate = LC3_CONFIG_FREQ_8KHZ;
334
-               else {
335
-                   spa_debugc(debug_ctx, "unsupported rate: 0x%04x", rate);
336
-                   return false;
337
-               }
338
-           }
339
+           rate_mask = ltv->value0 + (ltv->value1 << 8);
340
            break;
341
        case LC3_TYPE_DUR:
342
            spa_return_val_if_fail(ltv->len == 2, false);
343
-           {
344
-               uint8_t duration = ltv->value0;
345
-               if (duration & LC3_DUR_10)
346
-                   conf->frame_duration = LC3_CONFIG_DURATION_10;
347
-               else if (duration & LC3_DUR_7_5)
348
-                   conf->frame_duration = LC3_CONFIG_DURATION_7_5;
349
-               else {
350
-                   spa_debugc(debug_ctx, "unsupported duration: 0x%02x", duration);
351
-                   return false;
352
-               }
353
-           }
354
+           duration_mask = ltv->value0;
355
            break;
356
        case LC3_TYPE_CHAN:
357
            spa_return_val_if_fail(ltv->len == 2, false);
358
-           {
359
-               channels = ltv->value0;
360
-           }
361
+           channel_counts = ltv->value0;
362
            break;
363
        case LC3_TYPE_FRAMELEN:
364
            spa_return_val_if_fail(ltv->len == 5, false);
365
@@ -315,60 +477,64 @@
366
        data += ltv->len + 1;
367
    }
368
 
369
-   if (select_channels(channels, pac->locations, &conf->channels, max_frames) < 0) {
370
+   for (i = 0; i < 8; ++i)
371
+       if (channel_counts & (1u << i))
372
+           max_channels = i + 1;
373
+
374
+   /* Default: 1 frame per channel (BAP v1.0.1 Sec 4.3.1) */
375
+   if (max_frames < 0)
376
+       max_frames = max_channels;
377
+
378
+   /*
379
+    * Workaround:
380
+    * Creative Zen Hybrid Pro sets Supported_Max_Codec_Frames_Per_SDU == 1
381
+    * but channels == 0x3, and the 2-channel audio stream works.
382
+    */
383
+   if (max_frames < max_channels) {
384
+       spa_debugc(debug_ctx, "workaround: fixing bad Supported_Max_Codec_Frames_Per_SDU: %u->%u",
385
+               max_frames, max_channels);
386
+       max_frames = max_channels;
387
+   }
388
+
389
+   if (select_channels(channel_counts, pac->locations, pac->channel_allocation, &conf->channels) < 0) {
390
        spa_debugc(debug_ctx, "invalid channel configuration: 0x%02x %u",
391
-               channels, max_frames);
392
+               channel_counts, max_frames);
393
        return false;
394
    }
395
 
396
-   /* Default: 1 per channel (BAP v1.0.1 Sec 4.3.1) */
397
-   if (max_frames < 0)
398
-       max_frames = get_num_channels(conf->channels);
399
-   if (max_frames < get_num_channels(conf->channels)) {
400
+   if (max_frames < get_channel_count(conf->channels)) {
401
        spa_debugc(debug_ctx, "invalid max frames per SDU: %u", max_frames);
402
        return false;
403
    }
404
 
405
-   if (framelen_min < LC3_MIN_FRAME_BYTES || framelen_max > LC3_MAX_FRAME_BYTES) {
406
-       spa_debugc(debug_ctx, "invalid framelen: %u %u", framelen_min, framelen_max);
407
-       return false;
408
-   }
409
-   if (conf->frame_duration == 0xFF || !conf->rate) {
410
-       spa_debugc(debug_ctx, "no frame duration or rate");
411
-       return false;
412
+   /*
413
+    * Select supported rate + frame length combination.
414
+    *
415
+    * Frame length is not limited by ISO MTU, as kernel will fragment
416
+    * and reassemble SDUs as needed.
417
+    */
418
+   if (pac->sink && pac->duplex) {
419
+       /* 16KHz input is mandatory in BAP v1.0.1 Table 3.5, so prefer
420
+        * it for now for input rate in duplex configuration.
421
+        *
422
+        * Devices may list other values but not certain they will work properly.
423
+        */
424
+       bap_qos = select_bap_qos(rate_mask & LC3_FREQ_16KHZ, duration_mask, framelen_min, framelen_max);
425
    }
426
+   if (!bap_qos)
427
+       bap_qos = select_bap_qos(rate_mask, duration_mask, framelen_min, framelen_max);
428
 
429
-   /* BAP v1.0.1 Table 5.2; high-reliability */
430
-   switch (conf->rate) {
431
-   case LC3_CONFIG_FREQ_48KHZ:
432
-       if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
433
-           conf->framelen = 117;   /* 48_5_2 */
434
-       else
435
-           conf->framelen = 120;   /* 48_4_2 */
436
-       break;
437
-   case LC3_CONFIG_FREQ_24KHZ:
438
-       if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
439
-           conf->framelen = 45;    /* 24_1_2 */
440
-       else
441
-           conf->framelen = 60;    /* 24_2_2 */
442
-       break;
443
-   case LC3_CONFIG_FREQ_16KHZ:
444
-       if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
445
-           conf->framelen = 30;    /* 16_1_2 */
446
-       else
447
-           conf->framelen = 40;    /* 16_2_2 */
448
-       break;
449
-   case LC3_CONFIG_FREQ_8KHZ:
450
-       if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
451
-           conf->framelen = 26;    /* 8_1_2 */
452
-       else
453
-           conf->framelen = 30;    /* 8_2_2 */
454
-       break;
455
-   default:
456
-       spa_debugc(debug_ctx, "invalid rate");
457
+   if (!bap_qos) {
458
+       spa_debugc(debug_ctx, "no compatible configuration found, rate:0x%08x, duration:0x%08x frame:%u-%u",
459
+               rate_mask, duration_mask, framelen_min, framelen_max);
460
        return false;
461
    }
462
 
463
+   conf->rate = bap_qos->rate;
464
+   conf->frame_duration = bap_qos->frame_duration;
465
+   conf->framelen = bap_qos->framelen;
466
+   conf->priority = bap_qos->priority;
467
+
468
    return true;
469
 }
470
 
471
@@ -451,11 +617,11 @@
472
 
473
    PREFER_BOOL(conf->channels & LC3_CHAN_2);
474
    PREFER_BOOL(conf->channels & LC3_CHAN_1);
475
-   PREFER_BOOL(conf->rate & (LC3_CONFIG_FREQ_48KHZ | LC3_CONFIG_FREQ_24KHZ | LC3_CONFIG_FREQ_16KHZ | LC3_CONFIG_FREQ_8KHZ));
476
-   PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_48KHZ);
477
-   PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_24KHZ);
478
-   PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_16KHZ);
479
-   PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_8KHZ);
480
+
481
+   if (conf->sink && conf->duplex)
482
+       PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_16KHZ);
483
+
484
+   PREFER_EXPR(conf->priority);
485
 
486
    return 0;
487
 
488
@@ -487,6 +653,8 @@
489
    bap_lc3_t conf;
490
    uint8_t *data = config;
491
    uint32_t locations = 0;
492
+   uint32_t channel_allocation = 0;
493
+   bool sink = false, duplex = false;
494
    struct spa_debug_log_ctx debug_ctx = SPA_LOG_DEBUG_INIT(log, SPA_LOG_LEVEL_TRACE);
495
    int i;
496
 
497
@@ -494,12 +662,21 @@
498
        return -EINVAL;
499
 
500
    if (settings) {
501
-       for (i = 0; i < (int)settings->n_items; ++i)
502
+       for (i = 0; i < (int)settings->n_items; ++i) {
503
            if (spa_streq(settings->itemsi.key, "bluez5.bap.locations"))
504
                sscanf(settings->itemsi.value, "%"PRIu32, &locations);
505
+           if (spa_streq(settings->itemsi.key, "bluez5.bap.channel-allocation"))
506
+               sscanf(settings->itemsi.value, "%"PRIu32, &channel_allocation);
507
+       }
508
 
509
        if (spa_atob(spa_dict_lookup(settings, "bluez5.bap.debug")))
510
            debug_ctx = SPA_LOG_DEBUG_INIT(log, SPA_LOG_LEVEL_DEBUG);
511
+
512
+       /* Is remote endpoint sink or source */
513
+       sink = spa_atob(spa_dict_lookup(settings, "bluez5.bap.sink"));
514
+
515
+       /* Is remote endpoint duplex */
516
+       duplex = spa_atob(spa_dict_lookup(settings, "bluez5.bap.duplex"));
517
    }
518
 
519
    /* Select best conf from those possible */
520
@@ -512,8 +689,12 @@
521
        return -EINVAL;
522
    }
523
 
524
-   for (i = 0; i < npacs; ++i)
525
+   for (i = 0; i < npacs; ++i) {
526
        pacsi.locations = locations;
527
+       pacsi.channel_allocation = channel_allocation;
528
+       pacsi.sink = sink;
529
+       pacsi.duplex = duplex;
530
+   }
531
 
532
    qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
533
 
534
@@ -550,7 +731,7 @@
535
 
536
 static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
537
 {
538
-   uint8_t n_channels = get_num_channels(channels);
539
+   uint8_t n_channels = get_channel_count(channels);
540
    uint8_t n_positions = 0;
541
 
542
    spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS);
543
@@ -600,22 +781,27 @@
544
    spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_None, 0);
545
    choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f1);
546
    i = 0;
547
-   if (conf.rate & LC3_CONFIG_FREQ_48KHZ) {
548
+   if (conf.rate == LC3_CONFIG_FREQ_48KHZ) {
549
        if (i++ == 0)
550
            spa_pod_builder_int(b, 48000);
551
        spa_pod_builder_int(b, 48000);
552
    }
553
-   if (conf.rate & LC3_CONFIG_FREQ_24KHZ) {
554
+   if (conf.rate == LC3_CONFIG_FREQ_32KHZ) {
555
+       if (i++ == 0)
556
+           spa_pod_builder_int(b, 32000);
557
+       spa_pod_builder_int(b, 32000);
558
+   }
559
+   if (conf.rate == LC3_CONFIG_FREQ_24KHZ) {
560
        if (i++ == 0)
561
            spa_pod_builder_int(b, 24000);
562
        spa_pod_builder_int(b, 24000);
563
    }
564
-   if (conf.rate & LC3_CONFIG_FREQ_16KHZ) {
565
+   if (conf.rate == LC3_CONFIG_FREQ_16KHZ) {
566
        if (i++ == 0)
567
            spa_pod_builder_int(b, 16000);
568
        spa_pod_builder_int(b, 16000);
569
    }
570
-   if (conf.rate & LC3_CONFIG_FREQ_8KHZ) {
571
+   if (conf.rate == LC3_CONFIG_FREQ_8KHZ) {
572
        if (i++ == 0)
573
            spa_pod_builder_int(b, 8000);
574
        spa_pod_builder_int(b, 8000);
575
@@ -662,6 +848,9 @@
576
    case LC3_CONFIG_FREQ_48KHZ:
577
        info->info.raw.rate = 48000U;
578
        break;
579
+   case LC3_CONFIG_FREQ_32KHZ:
580
+       info->info.raw.rate = 32000U;
581
+       break;
582
    case LC3_CONFIG_FREQ_24KHZ:
583
        info->info.raw.rate = 24000U;
584
        break;
585
@@ -696,6 +885,7 @@
586
        const struct bap_endpoint_qos *endpoint_qos,
587
        struct bap_codec_qos *qos)
588
 {
589
+   const struct bap_qos *bap_qos;
590
    bap_lc3_t conf;
591
 
592
    spa_zero(*qos);
593
@@ -703,6 +893,14 @@
594
    if (!parse_conf(&conf, config, config_size))
595
        return -EINVAL;
596
 
597
+   bap_qos = select_bap_qos(get_rate_mask(conf.rate), get_duration_mask(conf.frame_duration),
598
+           conf.framelen, conf.framelen);
599
+   if (!bap_qos) {
600
+       /* shouldn't happen: select_config should pick existing one */
601
+       spa_log_error(log, "no QoS settings found");
602
+       return -EINVAL;
603
+   }
604
+
605
    qos->framing = false;
606
    if (endpoint_qos->phy & 0x2)
607
        qos->phy = 0x2;
608
@@ -710,42 +908,27 @@
609
        qos->phy = 0x1;
610
    else
611
        qos->phy = 0x2;
612
-   qos->sdu = conf.framelen * conf.n_blks * get_num_channels(conf.channels);
613
-   qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
614
-   qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_RELIABILITY;
615
 
616
-   /* Default values from BAP v1.0.1 Table 5.2; high-reliability */
617
-   qos->delay = 40000U;
618
-   qos->retransmission = 13;
619
+   qos->sdu = conf.framelen * conf.n_blks * get_channel_count(conf.channels);
620
+   qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
621
+   qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_BALANCED;
622
 
623
-   switch (conf.rate) {
624
-   case LC3_CONFIG_FREQ_8KHZ:
625
-   case LC3_CONFIG_FREQ_16KHZ:
626
-   case LC3_CONFIG_FREQ_24KHZ:
627
-   case LC3_CONFIG_FREQ_32KHZ:
628
-       /* F_1_2, F_2_2 */
629
-       qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 95);
630
-       break;
631
-   case LC3_CONFIG_FREQ_48KHZ:
632
-       /* 48_5_2, 48_4_2 */
633
-       qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 100);
634
-       break;
635
-   default:
636
-       qos->latency = 100;
637
-       break;
638
-   }
639
+   qos->delay = bap_qos->delay;
640
+   qos->latency = bap_qos->latency;
641
+   qos->retransmission = bap_qos->retransmission;
642
 
643
    /* Clamp to ASE values (if known) */
644
-   if (endpoint_qos->latency >= 0x0005 && endpoint_qos->latency <= 0x0FA0)
645
-       /* Values outside the range are RFU */
646
-       qos->latency = endpoint_qos->latency;
647
-   if (endpoint_qos->retransmission)
648
-       qos->retransmission = endpoint_qos->retransmission;
649
    if (endpoint_qos->delay_min)
650
        qos->delay = SPA_MAX(qos->delay, endpoint_qos->delay_min);
651
    if (endpoint_qos->delay_max)
652
        qos->delay = SPA_MIN(qos->delay, endpoint_qos->delay_max);
653
 
654
+   /*
655
+    * We ignore endpoint suggested latency and RTN. On current devices
656
+    * these do not appear to be very useful numbers, so it's better
657
+    * to just pick one from the table in the spec.
658
+    */
659
+
660
    return 0;
661
 }
662
 
663
@@ -772,11 +955,11 @@
664
        goto error;
665
 
666
    if (!parse_conf(&conf, config, config_len)) {
667
+       spa_log_error(log, "invalid LC3 config");
668
        res = -ENOTSUP;
669
        goto error;
670
    }
671
 
672
-   this->mtu = mtu;
673
    this->samplerate = config_info.info.raw.rate;
674
    this->channels = config_info.info.raw.channels;
675
    this->framelen = conf.framelen;
676
@@ -793,12 +976,17 @@
677
        goto error;
678
    }
679
 
680
-   this->samples = lc3_frame_samples(this->frame_dus, this->samplerate);
681
-   if (this->samples < 0) {
682
+   spa_log_info(log, "LC3 rate:%d frame_duration:%d channels:%d framelen:%d nblks:%d",
683
+           this->samplerate, this->frame_dus, this->channels, this->framelen, conf.n_blks);
684
+
685
+   res = lc3_frame_samples(this->frame_dus, this->samplerate);
686
+   if (res < 0) {
687
+       spa_log_error(log, "invalid LC3 frame samples");
688
        res = -EINVAL;
689
        goto error;
690
    }
691
-   this->codesize = this->samples * this->channels * conf.n_blks * sizeof(int32_t);
692
+   this->samples = res;
693
+   this->codesize = (size_t)this->samples * this->channels * conf.n_blks * sizeof(int32_t);
694
 
695
    if (!(flags & MEDIA_CODEC_FLAG_SINK)) {
696
        for (ich = 0; ich < this->channels; ich++) {
697
@@ -858,6 +1046,13 @@
698
    return this->codesize;
699
 }
700
 
701
+static uint64_t codec_get_interval(void *data)
702
+{
703
+   struct impl *this = data;
704
+
705
+   return (uint64_t)this->frame_dus * 1000;
706
+}
707
+
708
 static int codec_abr_process (void *data, size_t unsent)
709
 {
710
    return -ENOTSUP;
711
@@ -875,18 +1070,16 @@
712
        size_t *dst_out, int *need_flush)
713
 {
714
    struct impl *this = data;
715
-   int frame_bytes;
716
    int ich, res;
717
    int size, processed;
718
 
719
-   frame_bytes = lc3_frame_bytes(this->frame_dus, this->samplerate);
720
    processed = 0;
721
    size = 0;
722
 
723
    if (src_size < (size_t)this->codesize)
724
-       goto done;
725
-   if (dst_size < (size_t)frame_bytes)
726
-       goto done;
727
+       return -EINVAL;
728
+   if (dst_size < (size_t)this->framelen * this->channels)
729
+       return -EINVAL;
730
 
731
    for (ich = 0; ich < this->channels; ich++) {
732
        uint8_t *in = (uint8_t *)src + (ich * 4);
733
@@ -900,8 +1093,6 @@
734
 
735
    processed += this->codesize;
736
 
737
-done:
738
-   spa_assert(size <= this->mtu);
739
    *need_flush = NEED_FLUSH_ALL;
740
 
741
    return processed;
742
@@ -921,13 +1112,10 @@
743
    struct impl *this = data;
744
    int ich, res;
745
    int consumed;
746
-   int samples;
747
 
748
-   spa_return_val_if_fail((size_t)(this->framelen * this->channels) == src_size, -EINVAL);
749
    consumed = 0;
750
 
751
-   samples = lc3_frame_samples(this->frame_dus, this->samplerate);
752
-   if (samples == -1)
753
+   if (src_size < (size_t)this->framelen * this->channels)
754
        return -EINVAL;
755
    if (dst_size < this->codesize)
756
        return -EINVAL;
757
@@ -959,7 +1147,84 @@
758
 static void codec_set_log(struct spa_log *global_log)
759
 {
760
    log = global_log;
761
-   spa_log_topic_init(log, &log_topic);
762
+   spa_log_topic_init(log, &codec_plugin_log_topic);
763
+}
764
+
765
+static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
766
+               uint8_t *caps_size, struct spa_dict *settings,
767
+               struct bap_codec_qos *qos)
768
+{
769
+   int index = 0x0;
770
+   bool preset_found = false;
771
+   const char *preset = NULL;
772
+   int channel_allocation = 0;
773
+   uint8_t *data = caps;
774
+   *caps_size = 0;
775
+   int i;
776
+
777
+   if (settings) {
778
+       for (i = 0; i < (int)settings->n_items; ++i) {
779
+           if (spa_streq(settings->itemsi.key, "channel_allocation"))
780
+               sscanf(settings->itemsi.value, "%"PRIu32, &channel_allocation);
781
+           if (spa_streq(settings->itemsi.key, "preset"))
782
+               preset = spa_dict_lookup(settings, "preset");
783
+       }
784
+   }
785
+
786
+   if (preset == NULL)
787
+       return -EINVAL;
788
+
789
+   SPA_FOR_EACH_ELEMENT_VAR(bap_bcast_qos_configs, c) {
790
+       if (spa_streq(c->name, preset)) {
791
+           preset_found = true;
792
+           break;
793
+       }
794
+       index++;
795
+   }
796
+
797
+   if (!preset_found)
798
+       return -EINVAL;
799
+
800
+   switch (bap_bcast_qos_configsindex.rate) {
801
+   case LC3_CONFIG_FREQ_48KHZ:
802
+       data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_48KHZ);
803
+       break;
804
+   case LC3_CONFIG_FREQ_32KHZ:
805
+       data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_32KHZ);
806
+       break;
807
+   case LC3_CONFIG_FREQ_24KHZ:
808
+       data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_24KHZ);
809
+       break;
810
+   case LC3_CONFIG_FREQ_16KHZ:
811
+       data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_16KHZ);
812
+       break;
813
+   case LC3_CONFIG_FREQ_8KHZ:
814
+       data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_8KHZ);
815
+       break;
816
+   default:
817
+       return -EINVAL;
818
+   }
819
+   *caps_size += 3;
820
+
821
+   data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(bap_bcast_qos_configsindex.framelen));
822
+   *caps_size += 4;
823
+   data += write_ltv_uint8(data, LC3_TYPE_DUR, bap_bcast_qos_configsindex.frame_duration);
824
+   *caps_size += 3;
825
+   data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(channel_allocation));
826
+   *caps_size += 6;
827
+
828
+   if(bap_bcast_qos_configsindex.framing)
829
+       qos->framing = 1;
830
+   else
831
+       qos->framing = 0;
832
+   qos->sdu = bap_bcast_qos_configsindex.framelen * get_channel_count(channel_allocation);
833
+   qos->retransmission = bap_bcast_qos_configsindex.retransmission;
834
+   qos->latency = bap_bcast_qos_configsindex.latency;
835
+   qos->delay = bap_bcast_qos_configsindex.delay;
836
+   qos->phy = 2;
837
+   qos->interval = (bap_bcast_qos_configsindex.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
838
+
839
+   return true;
840
 }
841
 
842
 const struct media_codec bap_codec_lc3 = {
843
@@ -977,6 +1242,7 @@
844
    .init = codec_init,
845
    .deinit = codec_deinit,
846
    .get_block_size = codec_get_block_size,
847
+   .get_interval = codec_get_interval,
848
    .abr_process = codec_abr_process,
849
    .start_encode = codec_start_encode,
850
    .encode = codec_encode,
851
@@ -985,6 +1251,7 @@
852
    .reduce_bitpool = codec_reduce_bitpool,
853
    .increase_bitpool = codec_increase_bitpool,
854
    .set_log = codec_set_log,
855
+   .get_bis_config = codec_get_bis_config
856
 };
857
 
858
 MEDIA_CODEC_EXPORT_DEF(
859
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/bluez-hardware.conf -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bluez-hardware.conf Changed
13
 
1
@@ -40,9 +40,11 @@
2
     { name = "Motorola DC800", no-features =  sbc-xq  },  # #pipewire-1590
3
     { name = "Motorola S305", no-features =  sbc-xq  },  # #pipewire-1590
4
     { name = "PMK True Wireless Earbuds" no-features =  sbc-xq  }, # Primark earbud
5
+    { name = "Rockbox Brick", no-features =  hw-volume  }, # #pipewire-3786
6
     { name = "Soundcore Life P2-L", no-features =  msbc-alt1, msbc-alt1-rtl  },
7
     { name = "Soundcore Motion B", no-features =  hw-volume  },
8
     { name = "SoundCore mini", no-features =  hw-volume  },  # #pipewire-1686
9
+    { name = "SoundCore mini2", no-features =  hw-volume  },  # #pipewire-2927
10
     { name = "SoundCore 2", no-features =  sbc-xq  },  # #pipewire-2291
11
     { name = "Tribit MAXSound Plus", no-features =  hw-volume  },  # #pipewire-1592
12
     { name = "Urbanista Stockholm Plus", no-features =  msbc-alt1, msbc-alt1-rtl  },
13
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
959
 
1
@@ -32,16 +32,16 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/utils/string.h>
4
 #include <spa/utils/json.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "config.h"
8
 #include "codec-loader.h"
9
-#include "dbus-helpers.h"
10
 #include "player.h"
11
 #include "iso-io.h"
12
 #include "bap-codec-caps.h"
13
 #include "defs.h"
14
 
15
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5");
16
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5");
17
 #undef SPA_LOG_TOPIC_DEFAULT
18
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
19
 
20
@@ -120,6 +120,8 @@
21
    unsigned int connection_info_supported:1;
22
    unsigned int dummy_avrcp_player:1;
23
 
24
+   struct spa_list bcast_source_config_list;
25
+
26
    struct spa_bt_quirks *quirks;
27
 
28
 #define MAX_SETTINGS 128
29
@@ -146,6 +148,40 @@
30
    bool acceptor;
31
 };
32
 
33
+#define METADATA_MAX_LEN   255
34
+#define CC_MAX_LEN 255
35
+
36
+/*
37
+ * This structure stores metadata as defined
38
+ * in Assigned Numbers chapter 6.12.6 Metadata
39
+ * LTV structures. Length contains the size of
40
+ * type and value.
41
+ */
42
+struct spa_bt_metadata {
43
+   struct spa_list link;
44
+   int length;
45
+   int type;
46
+   uint8_t valueMETADATA_MAX_LEN - 1;
47
+};
48
+
49
+struct spa_bt_bis {
50
+   struct spa_list link;
51
+   char qos_preset255;
52
+   int channel_allocation;
53
+   struct spa_list metadata_list;
54
+};
55
+
56
+#define BROADCAST_CODE_LEN 16
57
+
58
+struct spa_bt_big {
59
+   struct spa_list link;
60
+   char broadcast_codeBROADCAST_CODE_LEN;
61
+   bool encryption;
62
+   int presentation_delay;
63
+   struct spa_list bis_list;
64
+   int big_id;
65
+};
66
+
67
 /*
68
  * Codec switching tries various codec/remote endpoint combinations
69
  * in order, until an acceptable one is found. This triggers BlueZ
70
@@ -408,11 +444,11 @@
71
 
72
    if (direction == SPA_BT_MEDIA_SOURCE)
73
        endpoint = codec->bap ? BAP_SOURCE_ENDPOINT : A2DP_SOURCE_ENDPOINT;
74
-   else if (direction == SPA_BT_MEDIA_SINK) 
75
+   else if (direction == SPA_BT_MEDIA_SINK)
76
        endpoint = codec->bap ? BAP_SINK_ENDPOINT : A2DP_SINK_ENDPOINT;
77
-   else if (direction == SPA_BT_MEDIA_SOURCE_BROADCAST) 
78
+   else if (direction == SPA_BT_MEDIA_SOURCE_BROADCAST)
79
        endpoint = BAP_BROADCAST_SOURCE_ENDPOINT;
80
-   else if (direction == SPA_BT_MEDIA_SINK_BROADCAST) 
81
+   else if (direction == SPA_BT_MEDIA_SINK_BROADCAST)
82
        endpoint = BAP_BROADCAST_SINK_ENDPOINT;
83
 
84
    *object_path = spa_aprintf("%s/%s", endpoint,
85
@@ -457,6 +493,8 @@
86
        const char *codec_ep_name =
87
            codec->endpoint_name ? codec->endpoint_name : codec->name;
88
 
89
+       if (!preferred && !codec->fill_caps)
90
+           continue;
91
        if (!spa_streq(ep_name, codec_ep_name))
92
            continue;
93
        if ((*sink && !codec->decode) || (!*sink && !codec->encode))
94
@@ -775,8 +813,6 @@
95
                qos->preferred_delay_min = value;
96
            else if (spa_streq(key, "PreferredMaximumDelay"))
97
                qos->preferred_delay_max = value;
98
-           else if (spa_streq(key, "Locations") || spa_streq(key, "Location"))
99
-               qos->locations = value;
100
        }
101
 
102
        dbus_message_iter_next(&dict_iter);
103
@@ -843,7 +879,7 @@
104
 
105
            dbus_message_iter_recurse(&it1, &it2);
106
            parse_endpoint_qos(monitor, &it2, qos);
107
-       } else if (spa_streq(key, "Locations")) {
108
+       } else if (spa_streq(key, "Locations") || spa_streq(key, "Location")) {
109
            dbus_uint32_t value;
110
 
111
            if (type != DBUS_TYPE_UINT32)
112
@@ -852,6 +888,15 @@
113
            dbus_message_iter_get_basic(&it1, &value);
114
            spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
115
            qos->locations = value;
116
+       } else if (spa_streq(key, "ChannelAllocation")) {
117
+           dbus_uint32_t value;
118
+
119
+           if (type != DBUS_TYPE_UINT32)
120
+               goto bad_property;
121
+
122
+           dbus_message_iter_get_basic(&it1, &value);
123
+           spa_log_debug(monitor->log, "ep qos: %s=%d", key, (int)value);
124
+           qos->channel_allocation = value;
125
        }
126
 
127
        dbus_message_iter_next(&dict_iter);
128
@@ -873,16 +918,17 @@
129
    int res;
130
    const struct media_codec *codec;
131
    struct spa_bt_remote_endpoint *ep;
132
-   bool sink;
133
+   bool sink, duplex;
134
    const char *err_msg = "Unknown error";
135
    struct spa_dict settings;
136
-   struct spa_dict_item setting_itemsSPA_N_ELEMENTS(monitor->global_setting_items) + 2;
137
+   struct spa_dict_item setting_itemsSPA_N_ELEMENTS(monitor->global_setting_items) + 5;
138
    int i;
139
 
140
    const char *endpoint_path = NULL;
141
    uint8_t capsA2DP_MAX_CAPS_SIZE;
142
    uint8_t configA2DP_MAX_CAPS_SIZE;
143
    char locations64 = {0};
144
+   char channel_allocation64 = {0};
145
    int caps_size = 0;
146
    int conf_size;
147
    DBusMessageIter dict;
148
@@ -919,23 +965,31 @@
149
        goto error_invalid;
150
    if (endpoint_qos.locations)
151
        spa_scnprintf(locations, sizeof(locations), "%"PRIu32, endpoint_qos.locations);
152
+   if (endpoint_qos.channel_allocation)
153
+       spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, endpoint_qos.channel_allocation);
154
 
155
    ep = remote_endpoint_find(monitor, endpoint_path);
156
-   if (!ep) {
157
+   if (!ep || !ep->device) {
158
        spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", endpoint_path);
159
        goto error_invalid;
160
    }
161
 
162
+   duplex = SPA_FLAG_IS_SET(ep->device->profiles, SPA_BT_PROFILE_BAP_DUPLEX);
163
+
164
    /* Call of SelectProperties means that local device acts as an initiator
165
-    * and therefor remote endpoint is an acceptor
166
+    * and therefore remote endpoint is an acceptor
167
     */
168
    ep->acceptor = true;
169
 
170
    for (i = 0; i < (int)monitor->global_settings.n_items; ++i)
171
        setting_itemsi = monitor->global_settings.itemsi;
172
    setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.locations", locations);
173
+   setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.channel-allocation", channel_allocation);
174
+   setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.sink", sink ? "true" : "false");
175
+   setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.duplex", duplex ? "true" : "false");
176
    setting_itemsi++ = SPA_DICT_ITEM_INIT("bluez5.bap.debug", "true");
177
    settings = SPA_DICT_INIT(setting_items, i);
178
+   spa_assert((size_t)i <= SPA_N_ELEMENTS(setting_items));
179
 
180
    conf_size = codec->select_config(codec, 0, caps, caps_size, &monitor->default_audio_info, &settings, config);
181
    if (conf_size < 0) {
182
@@ -962,6 +1016,7 @@
183
        struct bap_codec_qos qos;
184
        DBusMessageIter entry, variant, qos_dict;
185
        const char *entry_key = "QoS";
186
+       uint8_t cig = 0xff;
187
 
188
        spa_zero(qos);
189
 
190
@@ -972,10 +1027,18 @@
191
            goto error_invalid;
192
        }
193
 
194
+       if (ep->device->settings) {
195
+           const char *str = spa_dict_lookup(ep->device->settings, "bluez5.bap.cig");
196
+           uint32_t value;
197
+
198
+           if (spa_atou32(str, &value, 0))
199
+               cig = value;
200
+       }
201
+
202
        spa_log_debug(monitor->log, "select qos: interval:%d framing:%d phy:%d sdu:%d "
203
-               "rtn:%d latency:%d delay:%d target_latency:%d",
204
+               "rtn:%d latency:%d delay:%d target_latency:%d cig:%u",
205
                qos.interval, qos.framing, qos.phy, qos.sdu, qos.retransmission,
206
-               qos.latency, (int)qos.delay, qos.target_latency);
207
+               qos.latency, (int)qos.delay, qos.target_latency, cig);
208
 
209
        dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
210
        dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &entry_key);
211
@@ -997,6 +1060,9 @@
212
        append_basic_variant_dict_entry(&qos_dict, "PresentationDelay", DBUS_TYPE_UINT32, "u", &qos.delay);
213
        append_basic_variant_dict_entry(&qos_dict, "TargetLatency", DBUS_TYPE_BYTE, "y", &qos.target_latency);
214
 
215
+       if (cig < 0xf0)
216
+           append_basic_variant_dict_entry(&qos_dict, "CIG", DBUS_TYPE_BYTE, "y", &cig);
217
+
218
        dbus_message_iter_close_container(&variant, &qos_dict);
219
        dbus_message_iter_close_container(&entry, &variant);
220
        dbus_message_iter_close_container(&dict, &entry);
221
@@ -1350,8 +1416,35 @@
222
    free(adapter);
223
 }
224
 
225
+static void metadata_entry_free(struct spa_bt_metadata *metadata_entry)
226
+{
227
+   spa_list_remove(&metadata_entry->link);
228
+   free(metadata_entry);
229
+}
230
+
231
+static void bis_entry_free(struct spa_bt_bis *bis_entry)
232
+{
233
+   struct spa_bt_metadata *m;
234
+
235
+   spa_list_consume(m, &bis_entry->metadata_list, link)
236
+       metadata_entry_free(m);
237
+   spa_list_remove(&bis_entry->link);
238
+   free(bis_entry);
239
+}
240
+
241
+static void big_entry_free(struct spa_bt_big *big_entry)
242
+{
243
+   struct spa_bt_bis *b;
244
+
245
+   spa_list_consume(b, &big_entry->bis_list, link)
246
+       bis_entry_free(b);
247
+   spa_list_remove(&big_entry->link);
248
+   free(big_entry);
249
+}
250
+
251
 static uint32_t adapter_connectable_profiles(struct spa_bt_adapter *adapter)
252
 {
253
+   struct spa_bt_monitor *monitor = adapter->monitor;
254
    const uint32_t profiles = adapter->profiles;
255
    uint32_t mask = 0;
256
 
257
@@ -1380,6 +1473,9 @@
258
    if (profiles & SPA_BT_PROFILE_HFP_HF)
259
        mask |= SPA_BT_PROFILE_HFP_AG;
260
 
261
+   if (monitor->backend_selection == BACKEND_NONE)
262
+       mask &= ~SPA_BT_PROFILE_HEADSET_AUDIO;
263
+
264
    return mask;
265
 }
266
 
267
@@ -1447,6 +1543,7 @@
268
 {
269
    battery_remove(device);
270
    spa_bt_device_release_transports(device);
271
+   device->preferred_codec = NULL;
272
 }
273
 
274
 static void device_free(struct spa_bt_device *device)
275
@@ -1943,7 +2040,8 @@
276
            direction_connected = true;
277
    }
278
 
279
-   all_connected = (device->profiles & connected_profiles) == device->profiles;
280
+   all_connected = ((device->profiles & connected_profiles & connectable_profiles)
281
+               == (device->profiles & connectable_profiles));
282
 
283
    spa_list_for_each(set, &device->set_membership_list, link)
284
        spa_bt_for_each_set_member(s, set)
285
@@ -1957,7 +2055,7 @@
286
    if (connected_profiles == 0 && spa_list_is_empty(&device->codec_switch_list)) {
287
        device_stop_timer(device);
288
        device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
289
-   } else if (force || ((direction_connected || all_connected) && set_connected)) {
290
+   } else if (force || ((direction_connected || all_connected) && set_connected && connected_profiles)) {
291
        device_stop_timer(device);
292
        device_connected(monitor, device, BT_DEVICE_CONNECTED);
293
    } else {
294
@@ -2350,17 +2448,11 @@
295
 
296
                profile = spa_bt_profile_from_uuid(uuid);
297
 
298
-               /* Only add A2DP/BAP profiles if HSP/HFP backed is none.
299
-                * This allows BT device to connect instantly instead of waiting for
300
-                * profile timeout, because all available profiles are connected.
301
-                */
302
-               if (monitor->backend_selection != BACKEND_NONE || (monitor->backend_selection == BACKEND_NONE &&
303
-                       profile & (SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_MEDIA_SOURCE))) {
304
-                   if (profile && (device->profiles & profile) == 0) {
305
-                       spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
306
-                       device->profiles |= profile;
307
-                   }
308
+               if (profile && (device->profiles & profile) == 0) {
309
+                   spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
310
+                   device->profiles |= profile;
311
                }
312
+
313
                dbus_message_iter_next(&iter);
314
            }
315
 
316
@@ -2521,29 +2613,30 @@
317
    return NULL;
318
 }
319
 
320
-static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor, const char *object_path)
321
-{  
322
+static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor, const char *adapter_path,
323
+       const char *transport_path, const char *address)
324
+{
325
    struct spa_bt_device *d;
326
    struct spa_bt_adapter *adapter;
327
 
328
-   adapter = adapter_find(monitor, object_path);
329
+   adapter = adapter_find(monitor, adapter_path);
330
    if (adapter == NULL) {
331
-       spa_log_warn(monitor->log, "unknown adapter %s", object_path);
332
+       spa_log_warn(monitor->log, "unknown adapter %s", adapter_path);
333
        return NULL;
334
    }
335
 
336
-   d = device_create(monitor, object_path);
337
+   d = device_create(monitor, transport_path);
338
    if (d == NULL) {
339
        spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
340
-               object_path);
341
+               transport_path);
342
        return NULL;
343
    }
344
 
345
    d->adapter = adapter;
346
    d->adapter_path = strdup(adapter->path);
347
-   d->alias = strdup(adapter->alias);
348
-   d->name = strdup(adapter->name);
349
-   d->address = strdup("00:00:00:00:00:00");
350
+   d->address = spa_aprintf("%s.%d", address, d->id);
351
+   d->alias = strdup(d->address);
352
+   d->name = strdup(d->address);
353
    d->reconnect_state = BT_DEVICE_RECONNECT_STOP;
354
 
355
    device_update_hw_volume_profiles(d);
356
@@ -2581,28 +2674,19 @@
357
            if (spa_streq(key, "UUID")) {
358
                free(remote_endpoint->uuid);
359
                remote_endpoint->uuid = strdup(value);
360
+
361
+               if(spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK))
362
+                   /* Set remote endpoint as an acceptor for a broadcast sink.
363
+                    * So the transport is an initiator.
364
+                    */
365
+                   remote_endpoint->acceptor = true;
366
            }
367
            else if (spa_streq(key, "Device")) {
368
                struct spa_bt_device *device;
369
 
370
                device = spa_bt_device_find(monitor, value);
371
                if (device == NULL) {
372
-                   /*
373
-                   * If a broadcast sink endpoint is detected (over DBus) a new device
374
-                   * will be created.  This device will be our simulated remote device. 
375
-                   * This is done because BlueZ sets the adapter as the device
376
-                   * that is connected to for a broadcast sink endpoint/transport.
377
-                   */
378
-                   if (spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) {
379
-                       device = create_bcast_device(monitor, value);
380
-                       if (device == NULL)
381
-                           goto next;
382
-
383
-                       remote_endpoint->acceptor = true;
384
-                       device_set_connected(device, 1);
385
-                   } else {
386
-                       goto next;
387
-                   }
388
+                   goto next;
389
                }
390
 
391
                spa_log_debug(monitor->log, "remote_endpoint %p: device -> %p", remote_endpoint, device);
392
@@ -2667,6 +2751,19 @@
393
 next:
394
        dbus_message_iter_next(props_iter);
395
    }
396
+
397
+   /* BAP profile UUIDs do not appear in device UUID list.
398
+    * Instead, we detect these capabilities based on available
399
+    * endpoints (i.e. PACs).
400
+    */
401
+   if (remote_endpoint->uuid && remote_endpoint->device) {
402
+       enum spa_bt_profile profile;
403
+
404
+       profile = spa_bt_profile_from_uuid(remote_endpoint->uuid);
405
+       if (profile & SPA_BT_PROFILE_BAP_AUDIO)
406
+           spa_bt_device_add_profile(remote_endpoint->device, profile);
407
+   }
408
+
409
    return 0;
410
 }
411
 
412
@@ -3075,7 +3172,7 @@
413
 
414
    if (t_volume->hw_volume != t_volume->new_hw_volume) {
415
        t_volume->hw_volume = t_volume->new_hw_volume;
416
-       t_volume->volume = spa_bt_volume_hw_to_linear(t_volume->hw_volume,
417
+       t_volume->volume = (float)spa_bt_volume_hw_to_linear(t_volume->hw_volume,
418
                    t_volume->hw_volume_max);
419
        spa_log_debug(monitor->log, "transport %p: volume changed %d(%f) ",
420
            transport, t_volume->new_hw_volume, t_volume->volume);
421
@@ -3115,10 +3212,7 @@
422
 int spa_bt_transport_ensure_sco_io(struct spa_bt_transport *t, struct spa_loop *data_loop)
423
 {
424
    if (t->sco_io == NULL) {
425
-       t->sco_io = spa_bt_sco_io_create(data_loop,
426
-                        t->fd,
427
-                        t->read_mtu,
428
-                        t->write_mtu);
429
+       t->sco_io = spa_bt_sco_io_create(t, data_loop, t->monitor->log);
430
        if (t->sco_io == NULL)
431
            return -ENOMEM;
432
    }
433
@@ -3142,20 +3236,18 @@
434
    /* Fallback values when device does not provide information */
435
 
436
    if (t->media_codec == NULL)
437
-       return 30 * SPA_NSEC_PER_MSEC;
438
+       return 20 * SPA_NSEC_PER_MSEC;
439
 
440
    switch (t->media_codec->id) {
441
    case SPA_BLUETOOTH_AUDIO_CODEC_SBC:
442
    case SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ:
443
-       return 200 * SPA_NSEC_PER_MSEC;
444
    case SPA_BLUETOOTH_AUDIO_CODEC_MPEG:
445
    case SPA_BLUETOOTH_AUDIO_CODEC_AAC:
446
-       return 200 * SPA_NSEC_PER_MSEC;
447
    case SPA_BLUETOOTH_AUDIO_CODEC_APTX:
448
    case SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD:
449
-       return 150 * SPA_NSEC_PER_MSEC;
450
    case SPA_BLUETOOTH_AUDIO_CODEC_LDAC:
451
-       return 175 * SPA_NSEC_PER_MSEC;
452
+       return 125 * SPA_NSEC_PER_MSEC;
453
+   case SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD:
454
    case SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL:
455
    case SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX:
456
    case SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM:
457
@@ -3165,7 +3257,7 @@
458
    default:
459
        break;
460
    };
461
-   return 150 * SPA_NSEC_PER_MSEC;
462
+   return 125 * SPA_NSEC_PER_MSEC;
463
 }
464
 
465
 static int transport_update_props(struct spa_bt_transport *transport,
466
@@ -3206,7 +3298,45 @@
467
                    spa_bt_transport_set_state(transport, state);
468
            }
469
            else if (spa_streq(key, "Device")) {
470
+               char *pos;
471
                struct spa_bt_device *device = spa_bt_device_find(monitor, value);
472
+               if ((device == NULL) &&
473
+                   (transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK)) {
474
+                   /*
475
+                   * If a transport with profile broadcast source is detected (over DBus)
476
+                   * and no device is found for it, a new device will be created.
477
+                   * This device will be our simulated remote device.
478
+                   * This is done because BlueZ sets the adapter as the device
479
+                   * that is connected to a broadcast sink endpoint/transport.
480
+                   */
481
+                   device = spa_bt_device_find(monitor, transport->path);
482
+                   if (device == NULL) {
483
+                       device = create_bcast_device(monitor, value, transport->path, "00:00:00:00:00:00");
484
+                       if (device == NULL) {
485
+                           spa_log_warn(monitor->log, "could not find device %s", value);
486
+                       } else
487
+                           device_set_connected(device, 1);
488
+                   }
489
+               } if ((device != NULL) &&
490
+                   (transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) {
491
+                   /*
492
+                    * For each transport that has a broadcast source profile,
493
+                    * we need to create a new node for each BIS.
494
+                    * example of transport path = /org/bluez/hci0/dev_2D_9D_93_F9_D7_5E/bis1/fd0
495
+                    * Create new devices only for a case of a big with multiple BISes,
496
+                    * for this case will have the scanned device to the transport
497
+                    * "/fd0" and create new devices for the other transports from this device
498
+                    * that appear only in case of multiple BISes per BIG.
499
+                    */
500
+                   pos = strstr(transport->path, "/fd0");
501
+                   if (pos == NULL) {
502
+                       device = create_bcast_device(monitor, device->adapter_path, transport->path, device->address);
503
+                       if (device == NULL) {
504
+                           spa_log_warn(monitor->log, "could not find device created");
505
+                       } else
506
+                           device_set_connected(device, 1);
507
+                   }
508
+               }
509
                if (transport->device != device) {
510
                    if (transport->device != NULL)
511
                        spa_list_remove(&transport->device_link);
512
@@ -3316,7 +3446,6 @@
513
            transport->bap_bis = qos.bis;
514
            transport->delay_us = qos.qos.delay;
515
            transport->latency_us = (unsigned int)qos.qos.latency * 1000;
516
-           transport->bap_interval = qos.qos.interval;
517
 
518
            spa_bt_transport_emit_delay_changed(transport);
519
        }
520
@@ -3562,7 +3691,7 @@
521
            spa_log_error(monitor->log, "transport %p: transport_create_iso_io failed",
522
                    transport);
523
        /* For broadcast the initiator moves the transport state to SPA_BT_TRANSPORT_STATE_ACTIVE */
524
-       /* TODO: handeling multiple BIGs support */
525
+       /* TODO: handling multiple BIGs support */
526
        if ((transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) ||
527
            (transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE))    {
528
            spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
529
@@ -3734,7 +3863,7 @@
530
    }
531
 
532
    /* For LE Audio, multiple transport stream (CIS) can be linked together (CIG).
533
-    * If they are part of the same device they re-use the same fd, and call to
534
+    * If they are part of the same device they reuse the same fd, and call to
535
     * release should be done for the last one only.
536
     */
537
    spa_list_for_each(t_linked, &transport->bap_transport_linked, bap_transport_linked) {
538
@@ -4834,7 +4963,7 @@
539
                                    codec_id, caps, caps_size);
540
                        }
541
                }
542
-               
543
+
544
                if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK_BROADCAST)) {
545
                    caps_size = codec->fill_caps(codec, MEDIA_CODEC_FLAG_SINK, caps);
546
                    if (caps_size < 0)
547
@@ -5035,7 +5164,7 @@
548
            continue;
549
        if (endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK) ||
550
                endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE) ||
551
-               endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE_BROADCAST) || 
552
+               endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SOURCE_BROADCAST) ||
553
                endpoint_should_be_registered(monitor, codec, SPA_BT_MEDIA_SINK_BROADCAST))
554
            return true;
555
    }
556
@@ -5154,6 +5283,132 @@
557
                backend ? backend->name : "none");
558
 }
559
 
560
+static void configure_bis(struct spa_bt_monitor *monitor,
561
+               const struct media_codec *codec,
562
+               DBusConnection *conn,
563
+               const char *object_path,
564
+               const char *interface_name,
565
+               struct spa_bt_big *big,
566
+               struct spa_bt_bis *bis,
567
+               const char *local_endpoint)
568
+{
569
+   DBusMessageIter iter, entry, variant, qos_dict;
570
+   spa_autoptr(DBusMessage) msg = NULL;
571
+   DBusMessageIter dict;
572
+   int bis_id = 0xFF;
573
+   uint8_t caps CC_MAX_LEN;
574
+   uint8_t metadata METADATA_MAX_LEN;
575
+   uint8_t caps_size, metadata_size = 0;
576
+   struct bap_codec_qos qos;
577
+   int presentation_delay;
578
+   struct spa_bt_metadata *metadata_entry;
579
+   struct spa_dict settings;
580
+   struct spa_dict_item setting_items2;
581
+   char channel_allocation64 = {0};
582
+
583
+   int mse = 0;
584
+   int options = 0;
585
+   int skip = 0;
586
+   int sync_cte_type = 0;
587
+   int sync_factor = 1;
588
+   int sync_timeout = 2000;
589
+   int timeout = 2000;
590
+
591
+   /* Configure each BIS from a BIG */
592
+   spa_list_for_each(metadata_entry, &bis->metadata_list, link) {
593
+       if ((metadata_size + metadata_entry->length + 1) > METADATA_MAX_LEN) {
594
+           spa_log_warn(monitor->log, "Metadata configured for the BIS exceeds the maximum metadata size");
595
+           return;
596
+       }
597
+
598
+       metadatametadata_size = (uint8_t)metadata_entry->length;
599
+       metadata_size++;
600
+       metadatametadata_size = (uint8_t)metadata_entry->type;
601
+       metadata_size++;
602
+       memcpy(&metadatametadata_size, metadata_entry->value, metadata_entry->length - 1);
603
+       metadata_size += metadata_entry->length - 1;
604
+   }
605
+
606
+   spa_log_debug(monitor->log, "bis->channel_allocation %d", bis->channel_allocation);
607
+   if (bis->channel_allocation)
608
+       spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, bis->channel_allocation);
609
+   setting_items0 = SPA_DICT_ITEM_INIT("channel_allocation", channel_allocation);
610
+   setting_items1 = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
611
+   settings = SPA_DICT_INIT(setting_items, 2);
612
+
613
+   codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
614
+
615
+   msg = dbus_message_new_method_call(BLUEZ_SERVICE,
616
+               object_path,
617
+               interface_name,
618
+               "SetConfiguration");
619
+
620
+   dbus_message_iter_init_append(msg, &iter);
621
+   dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &local_endpoint);
622
+   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
623
+   append_basic_array_variant_dict_entry(&dict, "Capabilities", "ay", "y", DBUS_TYPE_BYTE, caps, caps_size);
624
+
625
+   append_basic_array_variant_dict_entry(&dict, "Metadata", "ay", "y", DBUS_TYPE_BYTE, metadata, metadata_size);
626
+
627
+   dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
628
+   dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &(const char *) { "QoS" });
629
+   dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &variant);
630
+
631
+   dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
632
+       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
633
+       DBUS_TYPE_STRING_AS_STRING
634
+       DBUS_TYPE_VARIANT_AS_STRING
635
+       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
636
+       &qos_dict);
637
+
638
+   append_basic_variant_dict_entry(&qos_dict, "BIG", DBUS_TYPE_BYTE, "y", &big->big_id);
639
+   append_basic_variant_dict_entry(&qos_dict, "BIS", DBUS_TYPE_BYTE, "y", &bis_id);
640
+   append_basic_variant_dict_entry(&qos_dict, "SyncFactor", DBUS_TYPE_BYTE, "y", &sync_factor);
641
+   append_basic_variant_dict_entry(&qos_dict, "Options", DBUS_TYPE_BYTE, "y", &options);
642
+   append_basic_variant_dict_entry(&qos_dict, "Skip", DBUS_TYPE_UINT16, "q", &skip);
643
+   append_basic_variant_dict_entry(&qos_dict, "SyncTimeout", DBUS_TYPE_UINT16, "q", &sync_timeout);
644
+   append_basic_variant_dict_entry(&qos_dict, "SyncCteType", DBUS_TYPE_BYTE, "y", &sync_cte_type);
645
+   append_basic_variant_dict_entry(&qos_dict, "MSE", DBUS_TYPE_BYTE, "y", &mse);
646
+   append_basic_variant_dict_entry(&qos_dict, "Timeout", DBUS_TYPE_UINT16, "q", &timeout);
647
+   append_basic_array_variant_dict_entry(&qos_dict, "BCode", "ay", "y", DBUS_TYPE_BYTE, big->broadcast_code, BROADCAST_CODE_LEN);
648
+   append_basic_variant_dict_entry(&qos_dict, "Encryption", DBUS_TYPE_BYTE, "y", &big->encryption);
649
+   append_basic_variant_dict_entry(&qos_dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval);
650
+   append_basic_variant_dict_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, "y", &qos.framing);
651
+   append_basic_variant_dict_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, "y", &qos.phy);
652
+   append_basic_variant_dict_entry(&qos_dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu);
653
+   append_basic_variant_dict_entry(&qos_dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission);
654
+   append_basic_variant_dict_entry(&qos_dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency);
655
+   append_basic_variant_dict_entry(&qos_dict, "PresentationDelay", DBUS_TYPE_UINT32, "u", &presentation_delay);
656
+
657
+   dbus_message_iter_close_container(&variant, &qos_dict);
658
+   dbus_message_iter_close_container(&entry, &variant);
659
+   dbus_message_iter_close_container(&dict, &entry);
660
+
661
+   dbus_message_iter_close_container(&iter, &dict);
662
+   dbus_message_set_no_reply(msg, TRUE);
663
+   if (!dbus_connection_send(conn, msg, NULL)) {
664
+       spa_log_error(monitor->log, "sending SetConfiguration failed");
665
+   }
666
+}
667
+
668
+static void configure_bcast_source(struct spa_bt_monitor *monitor,
669
+               const struct media_codec *codec,
670
+               DBusConnection *conn,
671
+               const char *object_path,
672
+               const char *interface_name,
673
+               const char *local_endpoint)
674
+{
675
+   struct spa_bt_big *big;
676
+   struct spa_bt_bis *bis;
677
+   /* Configure each BIS from a BIG */
678
+   spa_list_for_each(big, &monitor->bcast_source_config_list, link) {
679
+       spa_list_for_each(bis, &big->bis_list, link) {
680
+           configure_bis(monitor, codec, conn, object_path, interface_name,
681
+               big, bis, local_endpoint);
682
+       }
683
+   }
684
+}
685
+
686
 static void interface_added(struct spa_bt_monitor *monitor,
687
                DBusConnection *conn,
688
                const char *object_path,
689
@@ -5241,6 +5496,35 @@
690
        d = ep->device;
691
        if (d)
692
            spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
693
+
694
+       if (spa_streq(ep->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) {
695
+           int ret, i;
696
+           bool codec_found = false;
697
+           spa_autofree char *local_endpoint = NULL;
698
+           /* get local endpoint */
699
+
700
+           for (i = 0; monitor->media_codecs; i++) {
701
+               if (!monitor->media_codecsi->bap)
702
+                   continue;
703
+               if (!is_media_codec_enabled(monitor, monitor->media_codecsi))
704
+                   continue;
705
+               if (monitor->media_codecsi->codec_id == ep->codec){
706
+                   ret = media_codec_to_endpoint(monitor->media_codecsi, SPA_BT_MEDIA_SOURCE_BROADCAST, &local_endpoint);
707
+                   if (ret == 0) {
708
+                       codec_found = true;
709
+                       break;
710
+                   }
711
+               }
712
+           }
713
+
714
+           if (!codec_found) {
715
+               spa_log_warn(monitor->log, "endpoint codec not found");
716
+               return;
717
+           }
718
+
719
+           if (local_endpoint != NULL)
720
+               configure_bcast_source(monitor, monitor->media_codecsi, conn, object_path, interface_name, local_endpoint);
721
+       }
722
    }
723
 }
724
 
725
@@ -5307,6 +5591,37 @@
726
                if (d)
727
                    spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
728
            }
729
+       } else if (spa_streq(interface_name, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
730
+           struct spa_bt_transport *transport;
731
+           transport = spa_bt_transport_find(monitor, object_path);
732
+           if (transport != NULL) {
733
+               if (transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) {
734
+                   struct spa_bt_device *d = transport->device;
735
+                   if (d != NULL){
736
+                       device_free(d);
737
+                   }       
738
+               } else if (transport->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE) {
739
+                   /*
740
+                    * For each transport that has a broadcast source profile,
741
+                    * we need to create a new node for each BIS.
742
+                    * example of transport path = /org/bluez/hci0/dev_2D_9D_93_F9_D7_5E/bis1/fd0
743
+                    * Create new devices only for a case of a big with multiple BISes,
744
+                    * for this case will have the scanned device to the transport
745
+                    * "/fd0" and create new devices for the other transports from this device
746
+                    * that appear only in case of multiple BISes per BIG.
747
+                    * 
748
+                    * Here we delete the created devices.
749
+                    */
750
+                   char *pos = strstr(transport->path, "/fd0");
751
+                   if (pos == NULL) {
752
+                       struct spa_bt_device *d = transport->device;
753
+                       if (d != NULL){
754
+                           device_free(d);
755
+                       }
756
+                   }
757
+               }
758
+               spa_bt_transport_free(transport);
759
+           }
760
        }
761
 
762
        dbus_message_iter_next(&it);
763
@@ -5328,6 +5643,11 @@
764
        return;
765
    }
766
 
767
+   if (dbus_message_is_error(r, DBUS_ERROR_NAME_HAS_NO_OWNER)) {
768
+       spa_log_warn(monitor->log, "BlueZ system service is not available");
769
+       return;
770
+   }
771
+
772
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
773
        spa_log_error(monitor->log, "GetManagedObjects() failed: %s",
774
                dbus_message_get_error_name(r));
775
@@ -5545,7 +5865,12 @@
776
            transport = spa_bt_transport_find(monitor, path);
777
            if (transport == NULL) {
778
                spa_log_warn(monitor->log,
779
-                       "Properties changed in unknown transport %s", path);
780
+                       "Properties changed in unknown transport '%s'. "
781
+                       "Multiple sound server instances (PipeWire/Pulseaudio/bluez-alsa) are "
782
+                       "probably trying to use Bluetooth audio at the same time, which can "
783
+                       "cause problems. The system configuration likely should be fixed "
784
+                       "to have only one sound server that manages Bluetooth audio.",
785
+                       path);
786
                goto finish;
787
            }
788
 
789
@@ -5676,6 +6001,7 @@
790
    struct spa_bt_device *d;
791
    struct spa_bt_remote_endpoint *ep;
792
    struct spa_bt_transport *t;
793
+   struct spa_bt_big *b;
794
    const struct spa_dict_item *it;
795
    size_t i;
796
 
797
@@ -5704,6 +6030,8 @@
798
        device_free(d);
799
    spa_list_consume(a, &monitor->adapter_list, link)
800
        adapter_free(a);
801
+   spa_list_consume(b, &monitor->bcast_source_config_list, link)
802
+       big_entry_free(b);
803
 
804
    for (i = 0; i < SPA_N_ELEMENTS(monitor->backends); ++i) {
805
        spa_bt_backend_free(monitor->backendsi);
806
@@ -5806,6 +6134,138 @@
807
    return res;
808
 }
809
 
810
+static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const struct spa_dict *info)
811
+{
812
+   const char *str;
813
+   char key256;
814
+   char bis_key256;
815
+   char qos_key256;
816
+   char bcodeBROADCAST_CODE_LEN + 3;
817
+   int cursor;
818
+   int big_id = 0;
819
+   struct spa_json it4, it_array4;
820
+   struct spa_list big_list = SPA_LIST_INIT(&big_list);
821
+   struct spa_error_location loc;
822
+   struct spa_bt_big *big;
823
+
824
+   /* Search for bluez5.bcast_source.config */
825
+   if (!(info && (str = spa_dict_lookup(info, "bluez5.bcast_source.config"))))
826
+       return;
827
+
828
+   spa_json_init(&it0, str, strlen(str));
829
+
830
+   /* Verify is an array of BIGS */
831
+   if (spa_json_enter_array(&it0, &it_array0) <= 0)
832
+       goto parse_failed;
833
+
834
+   /* Iterate on all BIG objects */
835
+   while (spa_json_enter_object(&it_array0, &it1) > 0) {
836
+       struct spa_bt_big *big_entry = calloc(1, sizeof(struct spa_bt_big));
837
+
838
+       if (!big_entry)
839
+           goto errno_failed;
840
+
841
+       big_entry->big_id = big_id++;
842
+       spa_list_init(&big_entry->bis_list);
843
+       spa_list_append(&big_list, &big_entry->link);
844
+
845
+       /* Iterate on all BIG values */
846
+       while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
847
+           if (spa_streq(key, "broadcast_code")) {
848
+               if (spa_json_get_string(&it1, bcode, sizeof(bcode)) <= 0)
849
+                       goto parse_failed;
850
+               if (strlen(bcode) > BROADCAST_CODE_LEN)
851
+                   goto parse_failed;
852
+               memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
853
+               spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
854
+           } else if (spa_streq(key, "encryption")) {
855
+               if (spa_json_get_bool(&it1, &big_entry->encryption) <= 0)
856
+                   goto parse_failed;
857
+               spa_log_debug(monitor->log, "big_entry->encryption %d", big_entry->encryption);
858
+           } else if (spa_streq(key, "bis")) {
859
+               if (spa_json_enter_array(&it1, &it_array1) <= 0)
860
+                   goto parse_failed;
861
+               while (spa_json_enter_object(&it_array1, &it2) > 0) {
862
+                   /* Iterate on all BIS values */
863
+                   struct spa_bt_bis *bis_entry = calloc(1, sizeof(struct spa_bt_bis));
864
+
865
+                   if (!bis_entry)
866
+                       goto errno_failed;
867
+
868
+                   spa_list_init(&bis_entry->metadata_list);
869
+                   spa_list_append(&big_entry->bis_list, &bis_entry->link);
870
+
871
+                   while (spa_json_get_string(&it2, bis_key, sizeof(bis_key)) > 0) {
872
+                       if (spa_streq(bis_key, "qos_preset")) {
873
+                           if (spa_json_get_string(&it2, bis_entry->qos_preset, sizeof(bis_entry->qos_preset)) <= 0)
874
+                               goto parse_failed;
875
+                           spa_log_debug(monitor->log, "bis_entry->qos_preset %s", bis_entry->qos_preset);
876
+                       } else if (spa_streq(bis_key, "audio_channel_allocation")) {
877
+                           if (spa_json_get_int(&it2, &bis_entry->channel_allocation) <= 0)
878
+                               goto parse_failed;
879
+                           spa_log_debug(monitor->log, "bis_entry->channel_allocation %d", bis_entry->channel_allocation);
880
+                       } else if (spa_streq(bis_key, "metadata")) {
881
+                           if (spa_json_enter_array(&it2, &it_array2) <= 0)
882
+                               goto parse_failed;
883
+                           while (spa_json_enter_object(&it_array2, &it3) > 0) {
884
+                               struct spa_bt_metadata *metadata_entry = calloc(1, sizeof(struct spa_bt_metadata));
885
+
886
+                               if (!metadata_entry)
887
+                                   goto errno_failed;
888
+
889
+                               spa_list_append(&bis_entry->metadata_list, &metadata_entry->link);
890
+
891
+                               while (spa_json_get_string(&it3, qos_key, sizeof(qos_key)) > 0) {
892
+                                   if (spa_streq(qos_key, "type")) {
893
+                                       if (spa_json_get_int(&it3, &metadata_entry->type) <= 0)
894
+                                           goto parse_failed;
895
+                                       spa_log_debug(monitor->log, "metadata_entry->type %d", metadata_entry->type);
896
+                                   } else if (spa_streq(qos_key, "value")) {
897
+                                       if (spa_json_enter_array(&it3, &it_array3) <= 0)
898
+                                           goto parse_failed;
899
+                                       for (cursor = 0; cursor < METADATA_MAX_LEN - 1; cursor++) {
900
+                                           int temp_val = 0;
901
+                                           if (spa_json_get_int(&it_array3, &temp_val) <= 0)
902
+                                               break;
903
+                                           metadata_entry->valuecursor = (uint8_t)temp_val;
904
+                                           spa_log_debug(monitor->log, "metadata_entry->value%d %d", cursor, metadata_entry->valuecursor);
905
+                                       }
906
+                                       /* length is size of value plus 1 octet for type */
907
+                                       metadata_entry->length = cursor + 1;
908
+                                       spa_log_debug(monitor->log, "metadata_entry->length %d", metadata_entry->length);
909
+                                       spa_log_debug(monitor->log, "metadata_entry->value_size %d", cursor);
910
+                                   }
911
+                               }
912
+                           }
913
+                       }
914
+                   }
915
+               }
916
+           }
917
+       }
918
+   }
919
+
920
+   spa_list_insert_list(&monitor->bcast_source_config_list, &big_list);
921
+   return;
922
+
923
+errno_failed:
924
+   spa_log_warn(monitor->log, "failed in bluez5.bcast_source.config: %m");
925
+   goto cleanup;
926
+
927
+parse_failed:
928
+   str = spa_dict_lookup(info, "bluez5.bcast_source.config");
929
+   if (spa_json_get_error(&it0, str, &loc)) {
930
+       spa_debug_log_error_location(monitor->log, SPA_LOG_LEVEL_WARN,
931
+           &loc, "malformed bluez5.bcast_source.config: %s", loc.reason);
932
+   } else {
933
+       spa_log_warn(monitor->log, "malformed bluez5.bcast_source.config");
934
+   }
935
+   goto cleanup;
936
+
937
+cleanup:
938
+   spa_list_consume(big, &big_list, link)
939
+       big_entry_free(big);
940
+}
941
+
942
 static int parse_codec_array(struct spa_bt_monitor *this, const struct spa_dict *info)
943
 {
944
    const struct media_codec * const * const media_codecs = this->media_codecs;
945
@@ -5989,11 +6449,13 @@
946
    spa_list_init(&this->device_list);
947
    spa_list_init(&this->remote_endpoint_list);
948
    spa_list_init(&this->transport_list);
949
+   spa_list_init(&this->bcast_source_config_list);
950
 
951
    if ((res = parse_codec_array(this, info)) < 0)
952
        goto fail;
953
 
954
    parse_roles(this, info);
955
+   parse_broadcast_source_config(this, info);
956
 
957
    this->default_audio_info.rate = A2DP_CODEC_DEFAULT_RATE;
958
    this->default_audio_info.channels = A2DP_CODEC_DEFAULT_CHANNELS;
959
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/bluez5-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bluez5-device.c Changed
1026
 
1
@@ -34,16 +34,18 @@
2
 #include "defs.h"
3
 #include "media-codecs.h"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.device");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.device");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
-#define MAX_DEVICES    64
11
+#define MAX_NODES      (2*SPA_AUDIO_MAX_CHANNELS)
12
 
13
 #define DEVICE_ID_SOURCE   0
14
 #define DEVICE_ID_SINK     1
15
-#define DEVICE_ID_SOURCE_SET   2
16
-#define DEVICE_ID_SINK_SET 3
17
+#define DEVICE_ID_SOURCE_SET   (MAX_NODES + 0)
18
+#define DEVICE_ID_SINK_SET (MAX_NODES + 1)
19
+
20
+#define SINK_ID_FLAG       0x1
21
 #define DYNAMIC_NODE_ID_FLAG   0x1000
22
 
23
 static struct spa_i18n *_i18n;
24
@@ -57,7 +59,7 @@
25
    DEVICE_PROFILE_A2DP = 2,
26
    DEVICE_PROFILE_HSP_HFP = 3,
27
    DEVICE_PROFILE_BAP = 4,
28
-   DEVICE_PROFILE_LAST = DEVICE_PROFILE_BAP,
29
+   DEVICE_PROFILE_LAST,
30
 };
31
 
32
 struct props {
33
@@ -104,6 +106,7 @@
34
    struct impl *impl;
35
    struct spa_bt_transport *transport;
36
    struct spa_hook listener;
37
+   uint32_t id;
38
 };
39
 
40
 struct device_set {
41
@@ -149,16 +152,13 @@
42
    const struct media_codec **supported_codecs;
43
    size_t supported_codec_count;
44
 
45
-   struct dynamic_node dyn_media_source;
46
-   struct dynamic_node dyn_media_sink;
47
-   struct dynamic_node dyn_sco_source;
48
-   struct dynamic_node dyn_sco_sink;
49
+   struct dynamic_node dyn_nodesMAX_NODES + 2;
50
 
51
 #define MAX_SETTINGS 32
52
    struct spa_dict_item setting_itemsMAX_SETTINGS;
53
    struct spa_dict setting_dict;
54
 
55
-   struct node nodes4;
56
+   struct node nodesMAX_NODES + 2;
57
 };
58
 
59
 static void init_node(struct impl *this, struct node *node, uint32_t id)
60
@@ -247,6 +247,8 @@
61
        return HFP_AUDIO_CODEC_CVSD;
62
    case SPA_BLUETOOTH_AUDIO_CODEC_MSBC:
63
        return HFP_AUDIO_CODEC_MSBC;
64
+   case SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB:
65
+       return HFP_AUDIO_CODEC_LC3_SWB;
66
    default:
67
        return 0;
68
    }
69
@@ -257,6 +259,8 @@
70
    switch (codec) {
71
    case HFP_AUDIO_CODEC_MSBC:
72
        return SPA_BLUETOOTH_AUDIO_CODEC_MSBC;
73
+   case HFP_AUDIO_CODEC_LC3_SWB:
74
+       return SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB;
75
    case HFP_AUDIO_CODEC_CVSD:
76
        return SPA_BLUETOOTH_AUDIO_CODEC_CVSD;
77
    }
78
@@ -268,6 +272,8 @@
79
    switch (codec) {
80
    case HFP_AUDIO_CODEC_MSBC:
81
        return "mSBC";
82
+   case HFP_AUDIO_CODEC_LC3_SWB:
83
+       return "LC3-SWB";
84
    case HFP_AUDIO_CODEC_CVSD:
85
        return "CVSD";
86
    }
87
@@ -279,6 +285,8 @@
88
    switch (codec) {
89
    case HFP_AUDIO_CODEC_MSBC:
90
        return "msbc";
91
+   case HFP_AUDIO_CODEC_LC3_SWB:
92
+       return "lc3_swb";
93
    case HFP_AUDIO_CODEC_CVSD:
94
        return "cvsd";
95
    }
96
@@ -356,7 +364,7 @@
97
     */
98
    if (node->a2dp_duplex && node->transport && codec && codec->info &&
99
            spa_atob(spa_dict_lookup(codec->info, "duplex.boost")) &&
100
-           node->id == DEVICE_ID_SOURCE &&
101
+           !(node->id & SINK_ID_FLAG) &&
102
            !node->transport->volumesSPA_BT_VOLUME_ID_RX.active)
103
        return 10.0f;   /* 20 dB boost */
104
 
105
@@ -573,7 +581,7 @@
106
 
107
    for (i = 0; i < node->n_channels; ++i) {
108
        /* Session manager will override this, so put in some safe number */
109
-       node->volumesi = node->soft_volumesi = 0.064;
110
+       node->volumesi = node->soft_volumesi = 0.064f;
111
    }
112
 
113
    /* Produce member info json */
114
@@ -581,13 +589,13 @@
115
    spa_strbuf_append(&json, "");
116
    for (i = 0; i < n_members; ++i) {
117
        struct spa_bt_transport *t = membersi.transport;
118
+       uint32_t member_id = membersi.id;
119
        char object_path512;
120
        unsigned int j;
121
-       int member_id = (id == DEVICE_ID_SINK_SET) ? DEVICE_ID_SINK : DEVICE_ID_SOURCE;
122
 
123
        if (i > 0)
124
            spa_strbuf_append(&json, ",");
125
-       spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%d",
126
+       spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%"PRIu32,
127
                this->device_set.path, t->device->address, member_id);
128
        spa_strbuf_append(&json, "{\"object.path\":\"%s\",\"channels\":", object_path);
129
        for (j = 0; j < t->n_channels; ++j) {
130
@@ -633,9 +641,15 @@
131
    uint32_t n_items = 0;
132
    char transport32, str_id32, object_path512;
133
    bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG);
134
+   bool in_device_set = false;
135
 
136
    spa_log_debug(this->log, "%p: node, transport:%p id:%08x factory:%s", this, t, id, factory_name);
137
 
138
+   if (id & SINK_ID_FLAG)
139
+       in_device_set = this->device_set.path && (this->device_set.sinks > 1);
140
+   else
141
+       in_device_set = this->device_set.path && (this->device_set.sources > 1);
142
+
143
    snprintf(transport, sizeof(transport), "pointer:%p", t);
144
    items0 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_TRANSPORT, transport);
145
    items1 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PROFILE, spa_bt_profile_name(t->profile));
146
@@ -643,7 +657,7 @@
147
    items3 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address);
148
    items4 = SPA_DICT_ITEM_INIT("device.routes", "1");
149
    n_items = 5;
150
-   if (!is_dyn_node && !this->device_set.path) {
151
+   if (!is_dyn_node && !in_device_set) {
152
        snprintf(str_id, sizeof(str_id), "%d", id);
153
        itemsn_items = SPA_DICT_ITEM_INIT("card.profile.device", str_id);
154
        n_items++;
155
@@ -656,7 +670,7 @@
156
        itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.a2dp-duplex", "true");
157
        n_items++;
158
    }
159
-   if (this->device_set.path) {
160
+   if (in_device_set) {
161
        itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.set", this->device_set.path);
162
        n_items++;
163
        itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.internal", "true");
164
@@ -676,6 +690,8 @@
165
    info.props = &SPA_DICT_INIT(items, n_items);
166
 
167
    SPA_FLAG_CLEAR(id, DYNAMIC_NODE_ID_FLAG);
168
+   spa_assert(id < SPA_N_ELEMENTS(this->nodes));
169
+
170
    spa_device_emit_object_info(&this->hooks, id, &info);
171
 
172
    if (this->device_set.path) {
173
@@ -704,10 +720,8 @@
174
 
175
        if (prev_channels > 0) {
176
            size_t i;
177
-           /*
178
-            * Spread mono volume to all channels, if we had switched HFP -> A2DP.
179
-            * XXX: we should also use different route for hfp and a2dp
180
-            */
181
+
182
+           /* Spread mono volume to all channels, if we had switched HFP -> A2DP. */
183
            for (i = prev_channels; i < this->nodesid.n_channels; ++i)
184
                this->nodesid.volumesi = this->nodesid.volumesi % prev_channels;
185
        }
186
@@ -725,28 +739,51 @@
187
    }
188
 }
189
 
190
-static struct spa_bt_transport *find_device_transport(struct spa_bt_device *device, int profile,
191
-       enum spa_bluetooth_audio_codec codec)
192
+static void init_dummy_input_node(struct impl *this, uint32_t id)
193
+{
194
+   uint32_t prev_channels = this->nodesid.n_channels;
195
+
196
+   /* Don't emit a device node, only initialize volume etc. for the route */
197
+
198
+   spa_log_debug(this->log, "%p: node, id:%08x", this, id);
199
+
200
+   this->nodesid.impl = this;
201
+   this->nodesid.active = true;
202
+   this->nodesid.offload_acquired = false;
203
+   this->nodesid.a2dp_duplex = false;
204
+   this->nodesid.n_channels = 1;
205
+   this->nodesid.channels0 = SPA_AUDIO_CHANNEL_MONO;
206
+
207
+   if (prev_channels > 0) {
208
+       size_t i;
209
+
210
+       /* Spread mono volume to all channels */
211
+       for (i = prev_channels; i < this->nodesid.n_channels; ++i)
212
+           this->nodesid.volumesi = this->nodesid.volumesi % prev_channels;
213
+   }
214
+}
215
+
216
+static bool transport_enabled(struct spa_bt_transport *t, int profile)
217
+{
218
+   return (t->profile & t->device->connected_profiles) &&
219
+       (t->profile & profile) == t->profile;
220
+}
221
+
222
+static struct spa_bt_transport *find_device_transport(struct spa_bt_device *device, int profile)
223
 {
224
    struct spa_bt_transport *t;
225
 
226
    spa_list_for_each(t, &device->transport_list, device_link) {
227
-       bool codec_ok = codec == 0 ||
228
-           (t->media_codec != NULL && t->media_codec->id == codec) ||
229
-           get_hfp_codec_id(t->codec) == codec;
230
-
231
-       if ((t->profile & device->connected_profiles) &&
232
-               (t->profile & profile) == t->profile &&
233
-               codec_ok)
234
+       if (transport_enabled(t, profile))
235
            return t;
236
    }
237
 
238
    return NULL;
239
 }
240
 
241
-static struct spa_bt_transport *find_transport(struct impl *this, int profile, enum spa_bluetooth_audio_codec codec)
242
+static struct spa_bt_transport *find_transport(struct impl *this, int profile)
243
 {
244
-   return find_device_transport(this->bt_dev, profile, codec);
245
+   return find_device_transport(this->bt_dev, profile);
246
 }
247
 
248
 static void dynamic_node_transport_destroy(void *data)
249
@@ -835,9 +872,13 @@
250
    .volume_changed = dynamic_node_volume_changed,
251
 };
252
 
253
-static void emit_dynamic_node(struct dynamic_node *this, struct impl *impl,
254
+static void emit_dynamic_node(struct impl *impl,
255
    struct spa_bt_transport *t, uint32_t id, const char *factory_name, bool a2dp_duplex)
256
 {
257
+   struct dynamic_node *this = &impl->dyn_nodesid;
258
+
259
+   spa_assert(id < SPA_N_ELEMENTS(impl->dyn_nodes));
260
+
261
    spa_log_debug(impl->log, "%p: dynamic node, transport: %p->%p id: %08x->%08x",
262
            this, this->transport, t, this->id, id);
263
 
264
@@ -881,16 +922,22 @@
265
    struct device_set *set = &impl->device_set;
266
    unsigned int i;
267
 
268
-   for (i = 0; i < SPA_N_ELEMENTS(set->sink); ++i) {
269
+   for (i = 0; i < SPA_N_ELEMENTS(set->sink); ++i)
270
        if (set->sinki.transport)
271
            spa_hook_remove(&set->sinki.listener);
272
+
273
+   for (i = 0; i < SPA_N_ELEMENTS(set->source); ++i)
274
        if (set->sourcei.transport)
275
            spa_hook_remove(&set->sourcei.listener);
276
-   }
277
 
278
    free(set->path);
279
    spa_zero(*set);
280
+
281
    set->impl = impl;
282
+   for (i = 0; i < SPA_N_ELEMENTS(set->sink); ++i)
283
+       set->sinki.impl = impl;
284
+   for (i = 0; i < SPA_N_ELEMENTS(set->source); ++i)
285
+       set->sourcei.impl = impl;
286
 }
287
 
288
 static void device_set_transport_destroy(void *data)
289
@@ -911,36 +958,71 @@
290
    struct spa_bt_device *device = this->bt_dev;
291
    struct device_set *dset = &this->device_set;
292
    struct spa_bt_set_membership *set;
293
+   struct spa_bt_set_membership tmp_set = {
294
+       .device = device,
295
+       .rank = 0,
296
+       .leader = true,
297
+       .path = device->path,
298
+       .others = SPA_LIST_INIT(&tmp_set.others),
299
+   };
300
+   struct spa_list tmp_set_list = SPA_LIST_INIT(&tmp_set_list);
301
+   struct spa_list *membership_list = &device->set_membership_list;
302
 
303
-   device_set_clear(this);
304
-
305
-   if (!is_bap_client(this))
306
-       return;
307
+   /*
308
+    * If no device set, use a dummy one, so that we can handle also those devices
309
+    * here (they may have multiple transports regardless).
310
+    */
311
+   if (spa_list_is_empty(membership_list)) {
312
+       spa_list_append(&tmp_set_list, &tmp_set.link);
313
+       membership_list = &tmp_set_list;
314
+   }
315
 
316
-   spa_list_for_each(set, &device->set_membership_list, link) {
317
+   spa_list_for_each(set, membership_list, link) {
318
        struct spa_bt_set_membership *s;
319
        int num_devices = 0;
320
 
321
+       device_set_clear(this);
322
+
323
        spa_bt_for_each_set_member(s, set) {
324
            struct spa_bt_transport *t;
325
            bool active = false;
326
+           uint32_t source_id = DEVICE_ID_SOURCE;
327
+           uint32_t sink_id = DEVICE_ID_SINK;
328
 
329
            if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX))
330
                continue;
331
 
332
-           t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SOURCE, 0);
333
-           if (t && t->bap_initiator) {
334
+           spa_list_for_each(t, &s->device->transport_list, device_link) {
335
+               if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_SOURCE))
336
+                   continue;
337
+               if (!transport_enabled(t, SPA_BT_PROFILE_BAP_SOURCE))
338
+                   continue;
339
+               if (dset->sources >= SPA_N_ELEMENTS(dset->source))
340
+                   break;
341
+
342
                active = true;
343
+               dset->sourcedset->sources.impl = this;
344
                dset->sourcedset->sources.transport = t;
345
+               dset->sourcedset->sources.id = source_id;
346
+               source_id += 2;
347
                spa_bt_transport_add_listener(t, &dset->sourcedset->sources.listener,
348
                        &device_set_transport_events, &dset->sourcedset->sources);
349
                ++dset->sources;
350
            }
351
 
352
-           t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SINK, this->props.codec);
353
-           if (t && t->bap_initiator) {
354
+           spa_list_for_each(t, &s->device->transport_list, device_link) {
355
+               if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_SINK))
356
+                   continue;
357
+               if (!transport_enabled(t, SPA_BT_PROFILE_BAP_SINK))
358
+                   continue;
359
+               if (dset->sinks >= SPA_N_ELEMENTS(dset->sink))
360
+                   break;
361
+
362
                active = true;
363
+               dset->sinkdset->sinks.impl = this;
364
                dset->sinkdset->sinks.transport = t;
365
+               dset->sinkdset->sinks.id = sink_id;
366
+               sink_id += 2;
367
                spa_bt_transport_add_listener(t, &dset->sinkdset->sinks.listener,
368
                        &device_set_transport_events, &dset->sinkdset->sinks);
369
                ++dset->sinks;
370
@@ -950,17 +1032,22 @@
371
                ++num_devices;
372
        }
373
 
374
-       if (num_devices <= 1 || (dset->sinks <= 1 && dset->sources <= 1)) {
375
-           device_set_clear(this);
376
-           continue;
377
-       }
378
-
379
        spa_log_debug(this->log, "%p: %s belongs to set %s leader:%d", this,
380
                device->path, set->path, set->leader);
381
 
382
-       dset->path = strdup(set->path);
383
-       dset->leader = set->leader;
384
-       break;
385
+       if (is_bap_client(this)) {
386
+           dset->path = strdup(set->path);
387
+           dset->leader = set->leader;
388
+       } else {
389
+           /* XXX: device set nodes for BAP server not supported,
390
+            * XXX: it'll appear as multiple streams
391
+            */
392
+           dset->path = NULL;
393
+           dset->leader = false;
394
+       }
395
+
396
+       if (num_devices > 1)
397
+           break;
398
    }
399
 }
400
 
401
@@ -968,6 +1055,8 @@
402
 {
403
    struct spa_bt_transport *t;
404
 
405
+   this->props.codec = 0;
406
+
407
    device_set_update(this);
408
 
409
    switch (this->profile) {
410
@@ -975,51 +1064,40 @@
411
        break;
412
    case DEVICE_PROFILE_AG:
413
        if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) {
414
-           t = find_transport(this, SPA_BT_PROFILE_HFP_AG, 0);
415
+           t = find_transport(this, SPA_BT_PROFILE_HFP_AG);
416
            if (!t)
417
-               t = find_transport(this, SPA_BT_PROFILE_HSP_AG, 0);
418
+               t = find_transport(this, SPA_BT_PROFILE_HSP_AG);
419
            if (t) {
420
-               if (t->profile == SPA_BT_PROFILE_HSP_AG)
421
-                   this->props.codec = 0;
422
-               else
423
-                   this->props.codec = get_hfp_codec_id(t->codec);
424
-               emit_dynamic_node(&this->dyn_sco_source, this, t,
425
-                       0, SPA_NAME_API_BLUEZ5_SCO_SOURCE, false);
426
-               emit_dynamic_node(&this->dyn_sco_sink, this, t,
427
-                       1, SPA_NAME_API_BLUEZ5_SCO_SINK, false);
428
+               this->props.codec = get_hfp_codec_id(t->codec);
429
+               emit_dynamic_node(this, t, 0, SPA_NAME_API_BLUEZ5_SCO_SOURCE, false);
430
+               emit_dynamic_node(this, t, 1, SPA_NAME_API_BLUEZ5_SCO_SINK, false);
431
            }
432
        }
433
        if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_A2DP_SOURCE)) {
434
-           t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE, 0);
435
+           t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE);
436
            if (t) {
437
                this->props.codec = t->media_codec->id;
438
-               emit_dynamic_node(&this->dyn_media_source, this, t,
439
-                       2, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false);
440
+               emit_dynamic_node(this, t, 2, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false);
441
 
442
-               if (t->media_codec->duplex_codec) {
443
-                   emit_dynamic_node(&this->dyn_media_sink, this, t,
444
-                       3, SPA_NAME_API_BLUEZ5_A2DP_SINK, true);
445
-               }
446
+               if (t->media_codec->duplex_codec)
447
+                   emit_dynamic_node(this, t, 3, SPA_NAME_API_BLUEZ5_A2DP_SINK, true);
448
            }
449
        }
450
        break;
451
    case DEVICE_PROFILE_A2DP:
452
        if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) {
453
-           t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE, 0);
454
+           t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE);
455
            if (t) {
456
                this->props.codec = t->media_codec->id;
457
-               emit_dynamic_node(&this->dyn_media_source, this, t,
458
-                   DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false);
459
+               emit_dynamic_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false);
460
 
461
-               if (t->media_codec->duplex_codec) {
462
-                   emit_node(this, t,
463
-                       DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_A2DP_SINK, true);
464
-               }
465
+               if (t->media_codec->duplex_codec)
466
+                   emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_A2DP_SINK, true);
467
            }
468
        }
469
 
470
        if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) {
471
-           t = find_transport(this, SPA_BT_PROFILE_A2DP_SINK, this->props.codec);
472
+           t = find_transport(this, SPA_BT_PROFILE_A2DP_SINK);
473
            if (t) {
474
                this->props.codec = t->media_codec->id;
475
                emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_A2DP_SINK, false);
476
@@ -1030,40 +1108,61 @@
477
                }
478
            }
479
        }
480
+
481
+       /* Setup route for HFP input, for tracking its volume even though there is
482
+        * no node emitted yet. */
483
+       if ((this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) &&
484
+               !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) &&
485
+               !this->nodesDEVICE_ID_SOURCE.active)
486
+           init_dummy_input_node(this, DEVICE_ID_SOURCE);
487
+
488
+       if (!this->props.codec)
489
+           this->props.codec = SPA_BLUETOOTH_AUDIO_CODEC_SBC;
490
        break;
491
-   case DEVICE_PROFILE_BAP:
492
-       if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) {
493
-           t = find_transport(this, SPA_BT_PROFILE_BAP_SOURCE, 0);
494
-           if (t) {
495
-               this->props.codec = t->media_codec->id;
496
-               if (t->bap_initiator)
497
-                   emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
498
-               else
499
-                   emit_dynamic_node(&this->dyn_media_source, this, t,
500
-                       DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
501
-           }
502
+   case DEVICE_PROFILE_BAP: {
503
+       struct device_set *set = &this->device_set;
504
+       unsigned int i;
505
+
506
+       for (i = 0; i < set->sources; ++i) {
507
+           struct spa_bt_transport *t = set->sourcei.transport;
508
+           uint32_t id = set->sourcei.id;
509
+
510
+           if (id >= MAX_NODES)
511
+               continue;
512
+           if (t->device != this->bt_dev)
513
+               continue;
514
 
515
-           if (this->device_set.leader && this->device_set.sources > 0)
516
-               emit_device_set_node(this, DEVICE_ID_SOURCE_SET);
517
+           this->props.codec = t->media_codec->id;
518
+           if (t->bap_initiator)
519
+               emit_node(this, t, id, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
520
+           else
521
+               emit_dynamic_node(this, t, id, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
522
        }
523
 
524
-       if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SINK)) {
525
-           t = find_transport(this, SPA_BT_PROFILE_BAP_SINK, this->props.codec);
526
-           if (t) {
527
-               this->props.codec = t->media_codec->id;
528
-               if (t->bap_initiator)
529
-                   emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
530
-               else
531
-                   emit_dynamic_node(&this->dyn_media_sink, this, t,
532
-                       DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
533
-           }
534
+       if (set->path && set->leader && set->sources > 1)
535
+           emit_device_set_node(this, DEVICE_ID_SOURCE_SET);
536
 
537
-           if (this->device_set.leader && this->device_set.sinks > 0)
538
-               emit_device_set_node(this, DEVICE_ID_SINK_SET);
539
+       for (i = 0; i < set->sinks; ++i) {
540
+           struct spa_bt_transport *t = set->sinki.transport;
541
+           uint32_t id = set->sinki.id;
542
+
543
+           if (id >= MAX_NODES)
544
+               continue;
545
+           if (t->device != this->bt_dev)
546
+               continue;
547
+
548
+           this->props.codec = t->media_codec->id;
549
+           if (t->bap_initiator)
550
+               emit_node(this, t, id, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
551
+           else
552
+               emit_dynamic_node(this, t, id, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
553
        }
554
 
555
+       if (set->path && set->leader && set->sinks > 1)
556
+           emit_device_set_node(this, DEVICE_ID_SINK_SET);
557
+
558
        if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_BROADCAST_SINK)) {
559
-           t = find_transport(this, SPA_BT_PROFILE_BAP_BROADCAST_SINK, this->props.codec);
560
+           t = find_transport(this, SPA_BT_PROFILE_BAP_BROADCAST_SINK);
561
            if (t) {
562
                this->props.codec = t->media_codec->id;
563
                emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
564
@@ -1074,35 +1173,36 @@
565
        }
566
 
567
        if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) {
568
-           t = find_transport(this, SPA_BT_PROFILE_BAP_BROADCAST_SOURCE, this->props.codec);
569
+           t = find_transport(this, SPA_BT_PROFILE_BAP_BROADCAST_SOURCE);
570
            if (t) {
571
                this->props.codec = t->media_codec->id;
572
-               emit_dynamic_node(&this->dyn_media_source, this, t,
573
-                   DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
574
+               emit_dynamic_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
575
            }
576
        }
577
+
578
+       if (!this->props.codec)
579
+           this->props.codec = SPA_BLUETOOTH_AUDIO_CODEC_LC3;
580
        break;
581
+   };
582
    case DEVICE_PROFILE_HSP_HFP:
583
        if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) {
584
-           t = find_transport(this, SPA_BT_PROFILE_HFP_HF, this->props.codec);
585
+           t = find_transport(this, SPA_BT_PROFILE_HFP_HF);
586
            if (!t)
587
-               t = find_transport(this, SPA_BT_PROFILE_HSP_HS, 0);
588
+               t = find_transport(this, SPA_BT_PROFILE_HSP_HS);
589
            if (t) {
590
-               if (t->profile == SPA_BT_PROFILE_HSP_HS)
591
-                   this->props.codec = 0;
592
-               else
593
-                   this->props.codec = get_hfp_codec_id(t->codec);
594
+               this->props.codec = get_hfp_codec_id(t->codec);
595
                emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_SCO_SOURCE, false);
596
                emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_SCO_SINK, false);
597
            }
598
        }
599
 
600
-       if (spa_bt_device_supports_hfp_codec(this->bt_dev, get_hfp_codec(this->props.codec)) != 1)
601
-           this->props.codec = 0;
602
+       if (!this->props.codec)
603
+           this->props.codec = SPA_BLUETOOTH_AUDIO_CODEC_CVSD;
604
        break;
605
    default:
606
        return -EINVAL;
607
    }
608
+
609
    return 0;
610
 }
611
 
612
@@ -1129,10 +1229,8 @@
613
 {
614
    spa_log_debug(this->log, "%p: remove nodes", this);
615
 
616
-   remove_dynamic_node (&this->dyn_media_source);
617
-   remove_dynamic_node (&this->dyn_media_sink);
618
-   remove_dynamic_node (&this->dyn_sco_source);
619
-   remove_dynamic_node (&this->dyn_sco_sink);
620
+   for (uint32_t i = 0; i < SPA_N_ELEMENTS(this->dyn_nodes); i++)
621
+       remove_dynamic_node (&this->dyn_nodesi);
622
 
623
    for (uint32_t i = 0; i < SPA_N_ELEMENTS(this->nodes); i++) {
624
        struct node * node = &this->nodesi;
625
@@ -1176,7 +1274,6 @@
626
 
627
    this->profile = profile;
628
    this->prev_bt_connected_profiles = this->bt_dev->connected_profiles;
629
-   this->props.codec = codec;
630
 
631
    /*
632
     * A2DP/BAP: ensure there's a transport with the selected codec (0 means any).
633
@@ -1205,7 +1302,7 @@
634
        } else {
635
            return 0;
636
        }
637
-   } else if (profile == DEVICE_PROFILE_HSP_HFP && get_hfp_codec(codec) && !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_HFP_AG)) {
638
+   } else if (profile == DEVICE_PROFILE_HSP_HFP && get_hfp_codec(codec)) {
639
        int ret;
640
 
641
        this->switching_codec = true;
642
@@ -1220,7 +1317,6 @@
643
    }
644
 
645
    this->switching_codec = false;
646
-   this->props.codec = 0;
647
    emit_nodes(this);
648
 
649
    this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
650
@@ -1242,19 +1338,8 @@
651
 
652
    this->switching_codec = false;
653
 
654
-   if (status < 0) {
655
-       /* Failed to switch: return to a fallback profile */
656
-       spa_log_error(this->log, "failed to switch codec (%d), setting fallback profile", status);
657
-       if (this->profile == DEVICE_PROFILE_A2DP && this->props.codec != 0) {
658
-           this->props.codec = 0;
659
-       } else if (this->profile == DEVICE_PROFILE_BAP && this->props.codec != 0) {
660
-           this->props.codec = 0;
661
-       } else if (this->profile == DEVICE_PROFILE_HSP_HFP && this->props.codec != 0) {
662
-           this->props.codec = 0;
663
-       } else {
664
-           this->profile = DEVICE_PROFILE_OFF;
665
-       }
666
-   }
667
+   if (status < 0)
668
+       spa_log_error(this->log, "failed to switch codec (%d)", status);
669
 
670
    emit_remove_nodes(this);
671
    emit_nodes(this);
672
@@ -1311,8 +1396,6 @@
673
                  nodes_changed);
674
        break;
675
    case DEVICE_PROFILE_HSP_HFP:
676
-       if (spa_bt_device_supports_hfp_codec(this->bt_dev, get_hfp_codec(this->props.codec)) != 1)
677
-           this->props.codec = 0;
678
        nodes_changed = (connected_change & SPA_BT_PROFILE_HEADSET_HEAD_UNIT);
679
        spa_log_debug(this->log, "profiles changed: HSP/HFP nodes changed: %d",
680
                  nodes_changed);
681
@@ -1340,8 +1423,6 @@
682
 
683
    if (this->profile != DEVICE_PROFILE_BAP)
684
        return;
685
-   if (!is_bap_client(this))
686
-       return;
687
 
688
    spa_log_debug(this->log, "%p: device set changed", this);
689
 
690
@@ -1370,12 +1451,34 @@
691
    }
692
 }
693
 
694
+static void device_switch_profile(void *userdata)
695
+{
696
+   struct impl *this = userdata;
697
+   uint32_t profile;
698
+
699
+   switch(this->profile) {
700
+   case DEVICE_PROFILE_OFF:
701
+       profile = DEVICE_PROFILE_HSP_HFP;
702
+       break;
703
+   case DEVICE_PROFILE_HSP_HFP:
704
+       profile = DEVICE_PROFILE_OFF;
705
+       break;
706
+   default:
707
+       return;
708
+   }
709
+
710
+   spa_log_debug(this->log, "%p: device switch profile %d -> %d", this, this->profile, profile);
711
+
712
+   set_profile(this, profile, 0, false);
713
+}
714
+
715
 static const struct spa_bt_device_events bt_dev_events = {
716
    SPA_VERSION_BT_DEVICE_EVENTS,
717
    .connected = device_connected,
718
    .codec_switched = codec_switched,
719
    .profiles_changed = profiles_changed,
720
    .device_set_changed = device_set_changed,
721
+   .switch_profile = device_switch_profile,
722
 };
723
 
724
 static int impl_add_listener(void *object,
725
@@ -1413,7 +1516,8 @@
726
    return 0;
727
 }
728
 
729
-static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum spa_bluetooth_audio_codec codec)
730
+static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum spa_bluetooth_audio_codec codec,
731
+   bool hfp_input_for_a2dp)
732
 {
733
    struct spa_bt_device *device = this->bt_dev;
734
    uint32_t mask;
735
@@ -1428,6 +1532,8 @@
736
        media_codec = get_supported_media_codec(this, codec, NULL, device->connected_profiles);
737
        if (media_codec && media_codec->duplex_codec)
738
            have_input = true;
739
+       if (hfp_input_for_a2dp && this->nodesDEVICE_ID_SOURCE.active)
740
+           have_input = true;
741
        break;
742
    case DEVICE_PROFILE_BAP:
743
        if (device->connected_profiles & SPA_BT_PROFILE_BAP_SINK)
744
@@ -1453,15 +1559,9 @@
745
 
746
 static uint32_t get_profile_from_index(struct impl *this, uint32_t index, uint32_t *next, enum spa_bluetooth_audio_codec *codec)
747
 {
748
-   /*
749
-    * XXX: The codecs should probably become a separate param, and not have
750
-    * XXX: separate profiles for each one.
751
-    */
752
-
753
-   *codec = 0;
754
-   *next = index + 1;
755
-
756
-   if (index <= DEVICE_PROFILE_LAST) {
757
+   if (index < DEVICE_PROFILE_LAST) {
758
+       *codec = 0;
759
+       *next = index + 1;
760
        return index;
761
    } else if (index != SPA_ID_INVALID) {
762
        const struct spa_type_info *info;
763
@@ -1490,16 +1590,16 @@
764
 
765
 static uint32_t get_index_from_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_audio_codec codec)
766
 {
767
-   if (profile == DEVICE_PROFILE_OFF || profile == DEVICE_PROFILE_AG)
768
+   switch (profile) {
769
+   case DEVICE_PROFILE_OFF:
770
+   case DEVICE_PROFILE_AG:
771
        return profile;
772
 
773
-   if ((profile == DEVICE_PROFILE_A2DP) || (profile == DEVICE_PROFILE_BAP))
774
-       return codec + DEVICE_PROFILE_LAST;
775
-
776
-   if (profile == DEVICE_PROFILE_HSP_HFP) {
777
-       if (codec == 0 || (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HFP_AG))
778
-           return profile;
779
-
780
+   case DEVICE_PROFILE_A2DP:
781
+   case DEVICE_PROFILE_BAP:
782
+   case DEVICE_PROFILE_HSP_HFP:
783
+       if (!codec)
784
+           return SPA_ID_INVALID;
785
        return codec + DEVICE_PROFILE_LAST;
786
    }
787
 
788
@@ -1515,7 +1615,7 @@
789
        if (!(this->bt_dev->connected_profiles & i))
790
            continue;
791
 
792
-       t = find_transport(this, i, 0);
793
+       t = find_transport(this, i);
794
        if (t) {
795
            this->profile = (i & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY) ?
796
                DEVICE_PROFILE_AG : DEVICE_PROFILE_HSP_HFP;
797
@@ -1557,7 +1657,7 @@
798
        if (!(this->bt_dev->connected_profiles & i))
799
            continue;
800
 
801
-       t = find_transport(this, i, 0);
802
+       t = find_transport(this, i);
803
        if (t) {
804
            if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE)
805
                this->profile =  DEVICE_PROFILE_AG;
806
@@ -1679,8 +1779,8 @@
807
    case DEVICE_PROFILE_BAP:
808
    {
809
        uint32_t profile = device->connected_profiles &
810
-             (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE 
811
-               | SPA_BT_PROFILE_BAP_BROADCAST_SOURCE 
812
+             (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE
813
+               | SPA_BT_PROFILE_BAP_BROADCAST_SOURCE
814
                | SPA_BT_PROFILE_BAP_BROADCAST_SINK);
815
        size_t idx;
816
        const struct media_codec *media_codec;
817
@@ -1692,10 +1792,10 @@
818
        if (profile == 0)
819
            return NULL;
820
 
821
-       if ((profile & (SPA_BT_PROFILE_BAP_SINK)) || 
822
+       if ((profile & (SPA_BT_PROFILE_BAP_SINK)) ||
823
            (profile & (SPA_BT_PROFILE_BAP_BROADCAST_SINK)))
824
            n_sink++;
825
-       if ((profile & (SPA_BT_PROFILE_BAP_SOURCE)) || 
826
+       if ((profile & (SPA_BT_PROFILE_BAP_SOURCE)) ||
827
            (profile & (SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)))
828
            n_source++;
829
 
830
@@ -1764,31 +1864,40 @@
831
        /* make this device profile visible only if there is a head unit */
832
        uint32_t profile = device->connected_profiles &
833
              SPA_BT_PROFILE_HEADSET_HEAD_UNIT;
834
-       if (profile == 0) {
835
+       unsigned int hfp_codec = get_hfp_codec(codec);
836
+       unsigned int idx;
837
+
838
+       if (profile == 0)
839
            return NULL;
840
-       }
841
+
842
+       /* HFP will only enlist codec profiles */
843
+       if (codec == 0)
844
+           return NULL;
845
+       if (codec != SPA_BLUETOOTH_AUDIO_CODEC_CVSD &&
846
+               spa_bt_device_supports_hfp_codec(this->bt_dev, hfp_codec) != 1)
847
+           return NULL;
848
+
849
        name = spa_bt_profile_name(profile);
850
        n_source++;
851
        n_sink++;
852
-       if (codec) {
853
-           bool codec_ok = !(profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
854
-           unsigned int hfp_codec = get_hfp_codec(codec);
855
-           if (spa_bt_device_supports_hfp_codec(this->bt_dev, hfp_codec) != 1)
856
-               codec_ok = false;
857
-           if (!codec_ok) {
858
-               errno = EINVAL;
859
-               return NULL;
860
-           }
861
-           name_and_codec = spa_aprintf("%s-%s", name, get_hfp_codec_name(hfp_codec));
862
+
863
+       name_and_codec = spa_aprintf("%s-%s", name, get_hfp_codec_name(hfp_codec));
864
+
865
+       /*
866
+        * Give base name to highest priority profile, so that best codec can be
867
+        * selected at command line with out knowing which codecs are actually
868
+        * supported
869
+        */
870
+       for (idx = HFP_AUDIO_CODEC_LC3_SWB; idx > 0; --idx)
871
+           if (spa_bt_device_supports_hfp_codec(this->bt_dev, idx) == 1)
872
+               break;
873
+       if (hfp_codec < idx)
874
            name = name_and_codec;
875
-           desc_and_codec = spa_aprintf(_("Headset Head Unit (HSP/HFP, codec %s)"),
876
-                       get_hfp_codec_description(hfp_codec));
877
-           desc = desc_and_codec;
878
-           priority = 1 + hfp_codec;  /* prefer msbc over cvsd */
879
-       } else {
880
-           desc = _("Headset Head Unit (HSP/HFP)");
881
-           priority = 1;
882
-       }
883
+
884
+       desc_and_codec = spa_aprintf(_("Headset Head Unit (HSP/HFP, codec %s)"),
885
+               get_hfp_codec_description(hfp_codec));
886
+       desc = desc_and_codec;
887
+       priority = 1 + hfp_codec;  /* prefer lc3_swb > msbc > cvsd */
888
        break;
889
    }
890
    default:
891
@@ -1931,7 +2040,15 @@
892
        direction = SPA_DIRECTION_INPUT;
893
        snprintf(name, sizeof(name), "%s-input", name_prefix);
894
        enum_dev = DEVICE_ID_SOURCE;
895
-       if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP)
896
+
897
+       if ((this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) &&
898
+               !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) &&
899
+               !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_BAP_AUDIO) &&
900
+               (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT))
901
+           description = hfp_description;
902
+
903
+       if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP ||
904
+               profile == DEVICE_PROFILE_HSP_HFP)
905
            dev = enum_dev;
906
        else if (profile != SPA_ID_INVALID)
907
            enum_dev = SPA_ID_INVALID;
908
@@ -1946,16 +2063,6 @@
909
            enum_dev = SPA_ID_INVALID;
910
        break;
911
    case 2:
912
-       direction = SPA_DIRECTION_INPUT;
913
-       snprintf(name, sizeof(name), "%s-hf-input", name_prefix);
914
-       description = hfp_description;
915
-       enum_dev = DEVICE_ID_SOURCE;
916
-       if (profile == DEVICE_PROFILE_HSP_HFP)
917
-           dev = enum_dev;
918
-       else if (profile != SPA_ID_INVALID)
919
-           enum_dev = SPA_ID_INVALID;
920
-       break;
921
-   case 3:
922
        direction = SPA_DIRECTION_OUTPUT;
923
        snprintf(name, sizeof(name), "%s-hf-output", name_prefix);
924
        description = hfp_description;
925
@@ -1965,7 +2072,7 @@
926
        else if (profile != SPA_ID_INVALID)
927
            enum_dev = SPA_ID_INVALID;
928
        break;
929
-   case 4:
930
+   case 3:
931
        if (!this->device_set.leader) {
932
            errno = EINVAL;
933
            return NULL;
934
@@ -1978,7 +2085,7 @@
935
        else if (profile != SPA_ID_INVALID)
936
            enum_dev = SPA_ID_INVALID;
937
        break;
938
-   case 5:
939
+   case 4:
940
        if (!this->device_set.leader) {
941
            errno = EINVAL;
942
            return NULL;
943
@@ -2031,12 +2138,12 @@
944
 
945
        if (j == DEVICE_PROFILE_A2DP && !(port == 0 || port == 1))
946
            continue;
947
-       if (j == DEVICE_PROFILE_BAP && !(port == 0 || port == 1 || port == 4 || port == 5))
948
+       if (j == DEVICE_PROFILE_BAP && !(port == 0 || port == 1 || port == 3 || port == 4))
949
            continue;
950
-       if (j == DEVICE_PROFILE_HSP_HFP && !(port == 2 || port == 3))
951
+       if (j == DEVICE_PROFILE_HSP_HFP && !(port == 0 || port == 2))
952
            continue;
953
 
954
-       profile_mask = profile_direction_mask(this, j, codec);
955
+       profile_mask = profile_direction_mask(this, j, codec, false);
956
        if (!(profile_mask & (1 << direction)))
957
            continue;
958
 
959
@@ -2058,7 +2165,7 @@
960
        struct node *node = &this->nodesdev;
961
        struct spa_bt_transport_volume *t_volume;
962
 
963
-       mask = profile_direction_mask(this, this->profile, this->props.codec);
964
+       mask = profile_direction_mask(this, this->profile, this->props.codec, true);
965
        if (!(mask & (1 << direction)))
966
            return NULL;
967
 
968
@@ -2090,7 +2197,7 @@
969
                node->n_channels, node->channels);
970
 
971
        if ((this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) &&
972
-           dev == DEVICE_ID_SINK) {
973
+               (dev & SINK_ID_FLAG)) {
974
            spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0);
975
            spa_pod_builder_long(b, node->latency_offset);
976
        }
977
@@ -2142,7 +2249,7 @@
978
 #define FOR_EACH_MEDIA_CODEC(j, codec) \
979
        for (j = -1; iterate_supported_media_codecs(this, &j, &codec);)
980
 #define FOR_EACH_HFP_CODEC(j) \
981
-       for (j = HFP_AUDIO_CODEC_MSBC; j >= HFP_AUDIO_CODEC_CVSD; --j) \
982
+       for (j = HFP_AUDIO_CODEC_LC3_SWB; j >= HFP_AUDIO_CODEC_CVSD; --j) \
983
            if (spa_bt_device_supports_hfp_codec(this->bt_dev, j) == 1)
984
 
985
    spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_PropInfo, id);
986
@@ -2272,7 +2379,7 @@
987
    case SPA_PARAM_EnumRoute:
988
    {
989
        switch (result.index) {
990
-       case 0: case 1: case 2: case 3: case 4: case 5:
991
+       case 0: case 1: case 2: case 3: case 4:
992
            param = build_route(this, &b, id, result.index, SPA_ID_INVALID);
993
            if (param == NULL)
994
                goto next;
995
@@ -2625,6 +2732,9 @@
996
            } else if (codec_id == SPA_BLUETOOTH_AUDIO_CODEC_MSBC &&
997
                    spa_bt_device_supports_hfp_codec(this->bt_dev, HFP_AUDIO_CODEC_MSBC) == 1) {
998
                return set_profile(this, this->profile, codec_id, true);
999
+           } else if (codec_id == SPA_BLUETOOTH_AUDIO_CODEC_LC3_SWB &&
1000
+                   spa_bt_device_supports_hfp_codec(this->bt_dev, HFP_AUDIO_CODEC_LC3_SWB) == 1) {
1001
+               return set_profile(this, this->profile, codec_id, true);
1002
            }
1003
        }
1004
        return -EINVAL;
1005
@@ -2718,6 +2828,7 @@
1006
 {
1007
    struct impl *this;
1008
    const char *str;
1009
+   unsigned int i;
1010
 
1011
    spa_return_val_if_fail(factory != NULL, -EINVAL);
1012
    spa_return_val_if_fail(handle != NULL, -EINVAL);
1013
@@ -2764,10 +2875,8 @@
1014
 
1015
    reset_props(&this->props);
1016
 
1017
-   init_node(this, &this->nodes0, 0);
1018
-   init_node(this, &this->nodes1, 1);
1019
-   init_node(this, &this->nodes2, 2);
1020
-   init_node(this, &this->nodes3, 3);
1021
+   for (i = 0; i < SPA_N_ELEMENTS(this->nodes); ++i)
1022
+       init_node(this, &this->nodesi, i);
1023
 
1024
    this->info = SPA_DEVICE_INFO_INIT();
1025
    this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS |
1026
pipewire-1.2.0.tar.gz/spa/plugins/bluez5/bt-latency.h Added
177
 
1
@@ -0,0 +1,175 @@
2
+/* Spa Bluez5 ISO I/O */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Pauli Virtanen */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef SPA_BLUEZ5_BT_LATENCY_H
7
+#define SPA_BLUEZ5_BT_LATENCY_H
8
+
9
+#include <sys/socket.h>
10
+#include <linux/net_tstamp.h>
11
+#include <linux/errqueue.h>
12
+#include <linux/sockios.h>
13
+
14
+#include <spa/utils/defs.h>
15
+#include <spa/support/log.h>
16
+
17
+#include "rate-control.h"
18
+
19
+/* New kernel API */
20
+#ifndef BT_SCM_ERROR
21
+#define BT_SCM_ERROR 0x04
22
+#endif
23
+#ifndef BT_POLL_ERRQUEUE
24
+#define BT_POLL_ERRQUEUE 21
25
+#endif
26
+
27
+/**
28
+ * Bluetooth latency tracking.
29
+ */
30
+struct spa_bt_latency
31
+{
32
+   uint64_t value;
33
+   struct spa_bt_ptp ptp;
34
+   bool valid;
35
+   bool disabled;
36
+
37
+   struct {
38
+       int64_t send64;
39
+       uint32_t pos;
40
+       int64_t prev_tx;
41
+   } impl;
42
+};
43
+
44
+static inline void spa_bt_latency_init(struct spa_bt_latency *lat, int fd,
45
+       uint32_t period, struct spa_log *log)
46
+{
47
+   int so_timestamping = (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE |
48
+                   SOF_TIMESTAMPING_OPT_ID | SOF_TIMESTAMPING_OPT_TSONLY);
49
+   uint32_t flag;
50
+   int res;
51
+
52
+   spa_zero(*lat);
53
+
54
+   flag = 0;
55
+   res = setsockopt(fd, SOL_BLUETOOTH, BT_POLL_ERRQUEUE, &flag, sizeof(flag));
56
+   if (res < 0) {
57
+       spa_log_warn(log, "setsockopt(BT_POLL_ERRQUEUE) failed (kernel feature not enabled?): %d (%m)", errno);
58
+       lat->disabled = true;
59
+       return;
60
+   }
61
+
62
+   res = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping, sizeof(so_timestamping));
63
+   if (res < 0) {
64
+       spa_log_warn(log, "setsockopt(SO_TIMESTAMPING) failed (kernel feature not enabled?): %d (%m)", errno);
65
+       lat->disabled = true;
66
+       return;
67
+   }
68
+
69
+   /* Flush errqueue on start */
70
+   do {
71
+       res = recv(fd, NULL, 0, MSG_ERRQUEUE | MSG_DONTWAIT | MSG_TRUNC);
72
+   } while (res == 0);
73
+
74
+   spa_bt_ptp_init(&lat->ptp, period, period / 2);
75
+}
76
+
77
+static inline void spa_bt_latency_reset(struct spa_bt_latency *lat)
78
+{
79
+   lat->value = 0;
80
+   lat->valid = false;
81
+   spa_bt_ptp_init(&lat->ptp, lat->ptp.period, lat->ptp.period / 2);
82
+}
83
+
84
+static inline void spa_bt_latency_sent(struct spa_bt_latency *lat, uint64_t now)
85
+{
86
+   const unsigned int n = SPA_N_ELEMENTS(lat->impl.send);
87
+
88
+   if (lat->disabled)
89
+       return;
90
+
91
+   lat->impl.sendlat->impl.pos++ = now;
92
+   if (lat->impl.pos >= n)
93
+       lat->impl.pos = 0;
94
+}
95
+
96
+static inline int spa_bt_latency_recv_errqueue(struct spa_bt_latency *lat, int fd, struct spa_log *log)
97
+{
98
+   struct {
99
+       struct cmsghdr cm;
100
+       char control512;
101
+   } control;
102
+
103
+   if (lat->disabled)
104
+       return -EOPNOTSUPP;
105
+
106
+   do {
107
+       struct iovec data = {
108
+           .iov_base = NULL,
109
+           .iov_len = 0
110
+       };
111
+       struct msghdr msg = {
112
+           .msg_iov = &data,
113
+           .msg_iovlen = 1,
114
+           .msg_control = &control,
115
+           .msg_controllen = sizeof(control),
116
+       };
117
+       struct cmsghdr *cmsg;
118
+       struct scm_timestamping *tss = NULL;
119
+       struct sock_extended_err *serr = NULL;
120
+       int res;
121
+
122
+       res = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
123
+       if (res < 0) {
124
+           if (errno == EAGAIN || errno == EWOULDBLOCK)
125
+               break;
126
+           return -errno;
127
+       }
128
+
129
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
130
+           if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING)
131
+               tss = (void *)CMSG_DATA(cmsg);
132
+           else if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_SCM_ERROR)
133
+               serr = (void *)CMSG_DATA(cmsg);
134
+           else
135
+               continue;
136
+       }
137
+
138
+       if (!tss || !serr || serr->ee_errno != ENOMSG || serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING)
139
+           return -EINVAL;
140
+       if (serr->ee_info != SCM_TSTAMP_SND)
141
+           continue;
142
+
143
+       struct timespec *ts = &tss->ts0;
144
+       int64_t tx_time = SPA_TIMESPEC_TO_NSEC(ts);
145
+       uint32_t tx_pos = serr->ee_data % SPA_N_ELEMENTS(lat->impl.send);
146
+
147
+       lat->value = tx_time - lat->impl.sendtx_pos;
148
+
149
+       if (lat->impl.prev_tx && tx_time > lat->impl.prev_tx)
150
+           spa_bt_ptp_update(&lat->ptp, lat->value, tx_time - lat->impl.prev_tx);
151
+
152
+       lat->impl.prev_tx = tx_time;
153
+
154
+       spa_log_trace(log, "fd:%d latency%d nsec:%"PRIu64" range:%d..%d ms",
155
+               fd, tx_pos, lat->value,
156
+               (int)(spa_bt_ptp_valid(&lat->ptp) ? lat->ptp.min / SPA_NSEC_PER_MSEC : -1),
157
+               (int)(spa_bt_ptp_valid(&lat->ptp) ? lat->ptp.max / SPA_NSEC_PER_MSEC : -1));
158
+   } while (true);
159
+
160
+   lat->valid = spa_bt_ptp_valid(&lat->ptp);
161
+
162
+   return 0;
163
+}
164
+
165
+static inline void spa_bt_latency_flush(struct spa_bt_latency *lat, int fd, struct spa_log *log)
166
+{
167
+   int so_timestamping = 0;
168
+
169
+   /* Disable timestamping and flush errqueue */
170
+   setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping, sizeof(so_timestamping));
171
+   spa_bt_latency_recv_errqueue(lat, fd, log);
172
+
173
+   lat->disabled = true;
174
+}
175
+
176
+#endif
177
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/codec-loader.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/codec-loader.c Changed
34
 
1
@@ -15,7 +15,7 @@
2
 #define MAX_CODECS 0x3E
3
 #define MAX_HANDLES    MAX_CODECS
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.codecs");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
@@ -36,6 +36,7 @@
11
        SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD,
12
        SPA_BLUETOOTH_AUDIO_CODEC_APTX,
13
        SPA_BLUETOOTH_AUDIO_CODEC_AAC,
14
+       SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
15
        SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR,
16
        SPA_BLUETOOTH_AUDIO_CODEC_MPEG,
17
        SPA_BLUETOOTH_AUDIO_CODEC_SBC,
18
@@ -49,6 +50,7 @@
19
        SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71,
20
        SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX,
21
        SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO,
22
+       SPA_BLUETOOTH_AUDIO_CODEC_AAC_ELD,
23
    };
24
    size_t i;
25
    for (i = 0; i < SPA_N_ELEMENTS(order); ++i)
26
@@ -169,6 +171,7 @@
27
        MEDIA_CODEC_FACTORY_LIB("sbc"),
28
        MEDIA_CODEC_FACTORY_LIB("lc3plus"),
29
        MEDIA_CODEC_FACTORY_LIB("opus"),
30
+       MEDIA_CODEC_FACTORY_LIB("opus-g"),
31
        MEDIA_CODEC_FACTORY_LIB("lc3")
32
 #undef MEDIA_CODEC_FACTORY_LIB
33
    };
34
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/decode-buffer.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/decode-buffer.h Changed
84
 
1
@@ -55,21 +55,6 @@
2
    SPA_CLAMP((spike)*3/2, (packet_size), (max_buf) - 2*(packet_size))
3
 
4
 
5
-/** Windowed min/max */
6
-struct spa_bt_ptp
7
-{
8
-   union {
9
-       int32_t min;
10
-       int32_t mins4;
11
-   };
12
-   union {
13
-       int32_t max;
14
-       int32_t maxs4;
15
-   };
16
-   uint32_t pos;
17
-   uint32_t period;
18
-};
19
-
20
 struct spa_bt_decode_buffer
21
 {
22
    struct spa_log *log;
23
@@ -102,40 +87,6 @@
24
    uint8_t buffering:1;
25
 };
26
 
27
-static void spa_bt_ptp_init(struct spa_bt_ptp *p, int32_t period)
28
-{
29
-   size_t i;
30
-
31
-   spa_zero(*p);
32
-   for (i = 0; i < SPA_N_ELEMENTS(p->mins); ++i) {
33
-       p->minsi = INT32_MAX;
34
-       p->maxsi = INT32_MIN;
35
-   }
36
-   p->period = period;
37
-}
38
-
39
-static void spa_bt_ptp_update(struct spa_bt_ptp *p, int32_t value, uint32_t duration)
40
-{
41
-   const size_t n = SPA_N_ELEMENTS(p->mins);
42
-   size_t i;
43
-
44
-   for (i = 0; i < n; ++i) {
45
-       p->minsi = SPA_MIN(p->minsi, value);
46
-       p->maxsi = SPA_MAX(p->maxsi, value);
47
-   }
48
-
49
-   p->pos += duration;
50
-   if (p->pos >= p->period / (n - 1)) {
51
-       p->pos = 0;
52
-       for (i = 1; i < SPA_N_ELEMENTS(p->mins); ++i) {
53
-           p->minsi-1 = p->minsi;
54
-           p->maxsi-1 = p->maxsi;
55
-       }
56
-       p->minsn-1 = INT32_MAX;
57
-       p->maxsn-1 = INT32_MIN;
58
-   }
59
-}
60
-
61
 static int spa_bt_decode_buffer_init(struct spa_bt_decode_buffer *this, struct spa_log *log,
62
        uint32_t frame_size, uint32_t rate, uint32_t quantum_limit, uint32_t reserve)
63
 {
64
@@ -153,8 +104,8 @@
65
 
66
    spa_bt_rate_control_init(&this->ctl, 0);
67
 
68
-   spa_bt_ptp_init(&this->spike, (uint64_t)this->rate * BUFFERING_LONG_MSEC / 1000);
69
-   spa_bt_ptp_init(&this->packet_size, (uint64_t)this->rate * BUFFERING_SHORT_MSEC / 1000);
70
+   spa_bt_ptp_init(&this->spike, (uint64_t)this->rate * BUFFERING_LONG_MSEC / 1000, 0);
71
+   spa_bt_ptp_init(&this->packet_size, (uint64_t)this->rate * BUFFERING_SHORT_MSEC / 1000, 0);
72
 
73
    if ((this->buffer_decoded = malloc(this->buffer_size)) == NULL) {
74
        this->buffer_size = 0;
75
@@ -302,7 +253,7 @@
76
        level = SPA_MAX(level, -max_level);
77
        this->prev_consumed = SPA_MIN(this->prev_consumed, avg_period);
78
 
79
-       spa_bt_ptp_update(&this->spike, this->ctl.avg - level, this->prev_consumed);
80
+       spa_bt_ptp_update(&this->spike, (int32_t)(this->ctl.avg - level), this->prev_consumed);
81
 
82
        /* Update target level */
83
        if (this->target)
84
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/defs.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/defs.h Changed
72
 
1
@@ -141,8 +141,9 @@
2
 #define BUS_TYPE_USB       1
3
 #define BUS_TYPE_OTHER     255
4
 
5
-#define HFP_AUDIO_CODEC_CVSD    0x01
6
-#define HFP_AUDIO_CODEC_MSBC    0x02
7
+#define HFP_AUDIO_CODEC_CVSD   0x01
8
+#define HFP_AUDIO_CODEC_MSBC   0x02
9
+#define HFP_AUDIO_CODEC_LC3_SWB    0x03
10
 
11
 #define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint"
12
 #define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink"
13
@@ -160,9 +161,11 @@
14
 
15
 /* HFP uses SBC encoding with precisely defined parameters. Hence, the size
16
  * of the input (number of PCM samples) and output is known up front. */
17
-#define MSBC_DECODED_SIZE       240
18
-#define MSBC_ENCODED_SIZE       60  /* 2 bytes header + 57 mSBC payload + 1 byte padding */
19
-#define MSBC_PAYLOAD_SIZE       57
20
+#define MSBC_DECODED_SIZE          240
21
+#define MSBC_PAYLOAD_SIZE          57  /* 1 byte padding follows payload */
22
+#define LC3_SWB_DECODED_SIZE       960 /* 32 kHz mono S24_32 @ 7.5 ms */
23
+#define LC3_SWB_PAYLOAD_SIZE       58
24
+#define HFP_CODEC_PACKET_SIZE      60  /* 2 bytes header + payload */
25
 
26
 enum spa_bt_media_direction {
27
    SPA_BT_MEDIA_SOURCE,
28
@@ -190,6 +193,8 @@
29
    SPA_BT_PROFILE_HEADSET_HEAD_UNIT = (SPA_BT_PROFILE_HSP_HS | SPA_BT_PROFILE_HFP_HF),
30
    SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY = (SPA_BT_PROFILE_HSP_AG | SPA_BT_PROFILE_HFP_AG),
31
    SPA_BT_PROFILE_HEADSET_AUDIO =  (SPA_BT_PROFILE_HEADSET_HEAD_UNIT | SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY),
32
+   SPA_BT_PROFILE_BAP_AUDIO =  (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_BROADCAST_SINK |
33
+           SPA_BT_PROFILE_BAP_SOURCE | SPA_BT_PROFILE_BAP_BROADCAST_SOURCE),
34
 
35
    SPA_BT_PROFILE_MEDIA_SINK =     (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_BAP_SINK |
36
                                        SPA_BT_PROFILE_BAP_BROADCAST_SINK),
37
@@ -462,6 +467,9 @@
38
    /** Device set configuration changed */
39
    void (*device_set_changed) (void *data);
40
 
41
+   /** Switch profile between OFF and HSP_HFP */
42
+   void (*switch_profile) (void *data);
43
+
44
    /** Device freed */
45
    void (*destroy) (void *data);
46
 };
47
@@ -554,6 +562,7 @@
48
 #define spa_bt_device_emit_codec_switched(d,...)   spa_bt_device_emit(d, codec_switched, 0, __VA_ARGS__)
49
 #define spa_bt_device_emit_profiles_changed(d,...) spa_bt_device_emit(d, profiles_changed, 0, __VA_ARGS__)
50
 #define spa_bt_device_emit_device_set_changed(d)   spa_bt_device_emit(d, device_set_changed, 0)
51
+#define spa_bt_device_emit_switch_profile(d)       spa_bt_device_emit(d, switch_profile, 0)
52
 #define spa_bt_device_emit_destroy(d)          spa_bt_device_emit(d, destroy, 0)
53
 #define spa_bt_device_add_listener(d,listener,events,data)           \
54
    spa_hook_list_append(&(d)->listener_list, listener, events, data)
55
@@ -562,7 +571,7 @@
56
 
57
 struct spa_bt_sco_io;
58
 
59
-struct spa_bt_sco_io *spa_bt_sco_io_create(struct spa_loop *data_loop, int fd, uint16_t read_mtu, uint16_t write_mtu);
60
+struct spa_bt_sco_io *spa_bt_sco_io_create(struct spa_bt_transport *transport, struct spa_loop *data_loop, struct spa_log *log);
61
 void spa_bt_sco_io_destroy(struct spa_bt_sco_io *io);
62
 void spa_bt_sco_io_set_source_cb(struct spa_bt_sco_io *io, int (*source_cb)(void *userdata, uint8_t *data, int size), void *userdata);
63
 void spa_bt_sco_io_set_sink_cb(struct spa_bt_sco_io *io, int (*sink_cb)(void *userdata), void *userdata);
64
@@ -650,7 +659,6 @@
65
    uint8_t bap_cis;
66
    uint8_t bap_big;
67
    uint8_t bap_bis;
68
-   uint32_t bap_interval;
69
 
70
    struct spa_bt_iso_io *iso_io;
71
    struct spa_bt_sco_io *sco_io;
72
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/iso-io.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/iso-io.c Changed
319
 
1
@@ -7,7 +7,6 @@
2
 #include <stdio.h>
3
 #include <errno.h>
4
 #include <limits.h>
5
-#include <sys/socket.h>
6
 
7
 #include <spa/support/loop.h>
8
 #include <spa/support/log.h>
9
@@ -22,16 +21,20 @@
10
 #include "media-codecs.h"
11
 #include "defs.h"
12
 
13
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.iso");
14
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.iso");
15
 #undef SPA_LOG_TOPIC_DEFAULT
16
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
17
 
18
+#include "bt-latency.h"
19
+
20
 #define IDLE_TIME  (500 * SPA_NSEC_PER_MSEC)
21
 #define EMPTY_BUF_SIZE 65536
22
 
23
+#define LATENCY_PERIOD     (200 * SPA_NSEC_PER_MSEC)
24
+#define MAX_PACKET_QUEUE   3
25
+
26
 struct group {
27
    struct spa_log *log;
28
-   struct spa_log_topic log_topic;
29
    struct spa_loop *data_loop;
30
    struct spa_system *data_system;
31
    struct spa_source source;
32
@@ -40,7 +43,6 @@
33
    uint8_t id;
34
    uint64_t next;
35
    uint64_t duration;
36
-   uint32_t paused;
37
    bool started;
38
 };
39
 
40
@@ -56,6 +58,8 @@
41
 
42
    const struct media_codec *codec;
43
    uint32_t block_size;
44
+
45
+   struct spa_bt_latency tx_latency;
46
 };
47
 
48
 struct modify_info
49
@@ -132,17 +136,34 @@
50
            group->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
51
 }
52
 
53
-static int set_timers(struct group *group)
54
+static uint64_t get_time_ns(struct spa_system *system, clockid_t clockid)
55
 {
56
    struct timespec now;
57
 
58
-   spa_system_clock_gettime(group->data_system, CLOCK_MONOTONIC, &now);
59
-   group->next = SPA_ROUND_UP(SPA_TIMESPEC_TO_NSEC(&now) + group->duration,
60
+   spa_system_clock_gettime(system, clockid, &now);
61
+   return SPA_TIMESPEC_TO_NSEC(&now);
62
+}
63
+
64
+static int set_timers(struct group *group)
65
+{
66
+   if (group->duration == 0)
67
+       return -EINVAL;
68
+
69
+   group->next = SPA_ROUND_UP(get_time_ns(group->data_system, CLOCK_MONOTONIC) + group->duration,
70
            group->duration);
71
 
72
    return set_timeout(group, group->next);
73
 }
74
 
75
+static void drop_rx(int fd)
76
+{
77
+   ssize_t res;
78
+
79
+   do {
80
+       res = recv(fd, NULL, 0, MSG_TRUNC | MSG_DONTWAIT);
81
+   } while (res >= 0);
82
+}
83
+
84
 static void group_on_timeout(struct spa_source *source)
85
 {
86
    struct group *group = source->data;
87
@@ -159,13 +180,16 @@
88
        return;
89
    }
90
 
91
-   /*
92
-    * If a stream failed, pause output of all streams for a while to avoid
93
-    * desynchronization.
94
-    */
95
    spa_list_for_each(stream, &group->streams, link) {
96
-       if (!stream->sink)
97
+       if (!stream->sink) {
98
+           if (!stream->pull) {
99
+               /* Source not running: drop any incoming data */
100
+               drop_rx(stream->fd);
101
+           }
102
            continue;
103
+       }
104
+
105
+       spa_bt_latency_recv_errqueue(&stream->tx_latency, stream->fd, group->log);
106
 
107
        if (stream->this.need_resync) {
108
            resync = true;
109
@@ -176,18 +200,16 @@
110
            group->started = true;
111
    }
112
 
113
-   if (group->paused) {
114
-       --group->paused;
115
-       spa_log_debug(group->log, "%p: ISO group:%u paused:%u", group, group->id, group->paused);
116
-   }
117
-
118
    /* Produce output */
119
    spa_list_for_each(stream, &group->streams, link) {
120
-       int res;
121
+       int res = 0;
122
+       uint64_t now;
123
+       int32_t min_latency = INT32_MAX, max_latency = INT32_MIN;
124
+       struct stream *other;
125
 
126
        if (!stream->sink)
127
            continue;
128
-       if (group->paused || !group->started) {
129
+       if (!group->started) {
130
            stream->this.resync = true;
131
            stream->this.size = 0;
132
            continue;
133
@@ -201,21 +223,50 @@
134
            }
135
        }
136
 
137
+       spa_list_for_each(other, &group->streams, link) {
138
+           if (!other->sink || stream == other || !other->tx_latency.valid)
139
+               continue;
140
+           min_latency = SPA_MIN(min_latency, other->tx_latency.ptp.min);
141
+           max_latency = SPA_MAX(max_latency, other->tx_latency.ptp.max);
142
+       }
143
+
144
+       if (stream->tx_latency.valid && min_latency <= max_latency &&
145
+               stream->tx_latency.ptp.min > min_latency + (int64_t)group->duration/2 &&
146
+               stream->tx_latency.ptp.max > max_latency + (int64_t)group->duration/2) {
147
+           spa_log_debug(group->log, "%p: ISO group:%u latency skip align fd:%d", group, group->id, stream->fd);
148
+           spa_bt_latency_reset(&stream->tx_latency);
149
+           goto stream_done;
150
+       }
151
+
152
+       /* TODO: this should use rate match */
153
+       if (stream->tx_latency.valid &&
154
+               stream->tx_latency.ptp.min > MAX_PACKET_QUEUE * (int64_t)group->duration) {
155
+           spa_log_debug(group->log, "%p: ISO group:%u latency skip fd:%d", group, group->id, stream->fd);
156
+           spa_bt_latency_reset(&stream->tx_latency);
157
+           goto stream_done;
158
+       }
159
+
160
+       now = get_time_ns(group->data_system, CLOCK_REALTIME);
161
        res = send(stream->fd, stream->this.buf, stream->this.size, MSG_DONTWAIT | MSG_NOSIGNAL);
162
        if (res < 0) {
163
            res = -errno;
164
            fail = true;
165
+       } else {
166
+           spa_bt_latency_sent(&stream->tx_latency, now);
167
        }
168
 
169
-       spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u idle:%d res:%d",
170
+   stream_done:
171
+       spa_log_trace(group->log, "%p: ISO group:%u sent fd:%d size:%u ts:%u idle:%d res:%d latency:%d..%d us",
172
                group, group->id, stream->fd, (unsigned)stream->this.size,
173
-               (unsigned)stream->this.timestamp, stream->idle, res);
174
+               (unsigned)stream->this.timestamp, stream->idle, res,
175
+               stream->tx_latency.valid ? stream->tx_latency.ptp.min/1000 : -1,
176
+               stream->tx_latency.valid ? stream->tx_latency.ptp.max/1000 : -1);
177
 
178
        stream->this.size = 0;
179
    }
180
 
181
    if (fail)
182
-       group->paused = 1u + IDLE_TIME / group->duration;
183
+       spa_log_debug(group->log, "%p: ISO group:%d send failure", group, group->id);
184
 
185
    /* Pull data for the next interval */
186
    group->next += exp * group->duration;
187
@@ -245,11 +296,6 @@
188
    struct group *group;
189
    uint8_t id;
190
 
191
-   if (t->bap_interval <= 5000) {
192
-       errno = EINVAL;
193
-       return NULL;
194
-   }
195
-
196
    if (t->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) {
197
        id = t->bap_cig;
198
    } else if (t->profile & (SPA_BT_PROFILE_BAP_BROADCAST_SINK | SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) {
199
@@ -269,7 +315,7 @@
200
    group->log = log;
201
    group->data_loop = data_loop;
202
    group->data_system = data_system;
203
-   group->duration = t->bap_interval * SPA_NSEC_PER_USEC;
204
+   group->duration = 0;
205
 
206
    spa_list_init(&group->streams);
207
 
208
@@ -326,20 +372,22 @@
209
    struct spa_audio_info format = { 0 };
210
    int res;
211
    bool sink;
212
-   if((t->profile == SPA_BT_PROFILE_BAP_SINK) || 
213
-       (t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK)) {
214
+
215
+   if (t->profile == SPA_BT_PROFILE_BAP_SINK ||
216
+           t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) {
217
        sink = true;
218
    } else {
219
        sink = false;
220
    }
221
 
222
-
223
-   if (!t->media_codec->bap) {
224
+   if (!t->media_codec->bap || !t->media_codec->get_interval) {
225
        res = -EINVAL;
226
        goto fail;
227
    }
228
 
229
    if (sink) {
230
+       uint64_t interval;
231
+
232
        res = t->media_codec->validate_config(t->media_codec, 0, t->configuration, t->configuration_len, &format);
233
        if (res < 0)
234
            goto fail;
235
@@ -356,6 +404,20 @@
236
            res = -EINVAL;
237
            goto fail;
238
        }
239
+
240
+       interval = t->media_codec->get_interval(codec_data);
241
+       if (interval <= 5000) {
242
+           res = -EINVAL;
243
+           goto fail;
244
+       }
245
+
246
+       if (group->duration == 0) {
247
+           group->duration = interval;
248
+       } else if (interval != group->duration) {
249
+           /* SDU_Interval in ISO group must be same for each direction */
250
+           res = -EINVAL;
251
+           goto fail;
252
+       }
253
    }
254
 
255
    stream = calloc(1, sizeof(struct stream));
256
@@ -365,13 +427,15 @@
257
    stream->fd = t->fd;
258
    stream->sink = sink;
259
    stream->group = group;
260
-   stream->this.duration = group->duration;
261
+   stream->this.duration = sink ? group->duration : 0;
262
 
263
    stream->codec = t->media_codec;
264
    stream->this.codec_data = codec_data;
265
    stream->this.format = format;
266
    stream->block_size = block_size;
267
 
268
+   spa_bt_latency_init(&stream->tx_latency, stream->fd, LATENCY_PERIOD, group->log);
269
+
270
    if (sink)
271
        stream_silence(stream);
272
 
273
@@ -426,6 +490,8 @@
274
 
275
    stream_unlink(stream);
276
 
277
+   spa_bt_latency_flush(&stream->tx_latency, stream->fd, stream->group->log);
278
+
279
    if (spa_list_is_empty(&stream->group->streams))
280
        group_destroy(stream->group);
281
 
282
@@ -440,9 +506,12 @@
283
 {
284
    struct stream *stream;
285
 
286
-   spa_list_for_each(stream, &group->streams, link)
287
+   spa_list_for_each(stream, &group->streams, link) {
288
+       if (!stream->sink)
289
+           continue;
290
        if (stream->pull)
291
            return true;
292
+   }
293
 
294
    return false;
295
 }
296
@@ -453,6 +522,9 @@
297
    struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
298
    bool was_enabled, enabled;
299
 
300
+   if (!stream->sink)
301
+       return;
302
+
303
    was_enabled = group_is_enabled(stream->group);
304
 
305
    stream->pull = pull;
306
@@ -475,3 +547,12 @@
307
        return;
308
    }
309
 }
310
+
311
+/** Must be called from data thread */
312
+int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *this)
313
+{
314
+   struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
315
+   struct group *group = stream->group;
316
+
317
+   return spa_bt_latency_recv_errqueue(&stream->tx_latency, stream->fd, group->log);
318
+}
319
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/iso-io.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/iso-io.h Changed
8
 
1
@@ -44,5 +44,6 @@
2
 struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, struct spa_bt_transport *t);
3
 void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io);
4
 void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
5
+int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *io);
6
 
7
 #endif
8
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/media-codecs.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/media-codecs.c Changed
29
 
1
@@ -61,6 +61,18 @@
2
    return res;
3
 }
4
 
5
+int media_codec_get_config(const struct media_codec_config configs, size_t n,
6
+       uint32_t conf)
7
+{
8
+   size_t i;
9
+
10
+   for (i = 0; i < n; ++i)
11
+       if (configsi.config == conf)
12
+           return configsi.value;
13
+
14
+   return -EINVAL;
15
+}
16
+
17
 bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_id,
18
               const void *caps, size_t caps_size,
19
               const struct media_codec_audio_info *info,
20
@@ -175,6 +187,8 @@
21
    return 1;
22
 }
23
 
24
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
25
+
26
 static const struct spa_dict_item handle_info_items = {
27
         { SPA_KEY_FACTORY_DESCRIPTION, "Bluetooth codec plugin" },
28
 };
29
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/media-codecs.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/media-codecs.h Changed
119
 
1
@@ -26,7 +26,7 @@
2
 
3
 #define SPA_TYPE_INTERFACE_Bluez5CodecMedia    SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private"
4
 
5
-#define SPA_VERSION_BLUEZ5_CODEC_MEDIA     7
6
+#define SPA_VERSION_BLUEZ5_CODEC_MEDIA     9
7
 
8
 struct spa_bluez5_codec_a2dp {
9
    struct spa_interface iface;
10
@@ -39,10 +39,14 @@
11
 #define MEDIA_CODEC_EXPORT_DEF(basename,...)   \
12
    const char *codec_plugin_factory_name = MEDIA_CODEC_FACTORY_NAME(basename); \
13
    static const struct media_codec * const codec_plugin_media_codec_list = { __VA_ARGS__, NULL };  \
14
-   const struct media_codec * const * const codec_plugin_media_codecs = codec_plugin_media_codec_list;
15
+   const struct media_codec * const * const codec_plugin_media_codecs = codec_plugin_media_codec_list; \
16
+   SPA_LOG_TOPIC_DEFINE(codec_plugin_log_topic, "spa.bluez5.codecs." basename);
17
 
18
 extern const struct media_codec * const * const codec_plugin_media_codecs;
19
 extern const char *codec_plugin_factory_name;
20
+extern struct spa_log_topic codec_plugin_log_topic;
21
+#undef SPA_LOG_TOPIC_DEFAULT
22
+#define SPA_LOG_TOPIC_DEFAULT &codec_plugin_log_topic
23
 #endif
24
 
25
 #define MEDIA_CODEC_FLAG_SINK      (1 << 0)
26
@@ -77,7 +81,9 @@
27
 
28
    const struct media_codec *duplex_codec; /**< Codec for non-standard A2DP duplex channel */
29
 
30
-   struct spa_log *log;
31
+   int (*get_bis_config)(const struct media_codec *codec, uint8_t *caps,
32
+               uint8_t *caps_size, struct spa_dict *settings,
33
+               struct bap_codec_qos *qos);
34
 
35
    /** If fill_caps is NULL, no endpoint is registered (for sharing with another codec). */
36
    int (*fill_caps) (const struct media_codec *codec, uint32_t flags,
37
@@ -119,19 +125,71 @@
38
 
39
    int (*update_props) (void *data, void *props);
40
 
41
+   /** Number of bytes needed for encoding */
42
    int (*get_block_size) (void *data);
43
 
44
+   /**
45
+    * Duration of the next packet in nanoseconds.
46
+    *
47
+    * For BAP this shall be constant and equal to the SDU interval.
48
+    *
49
+    * \param data Codec data from init()
50
+    * \return Duration in nanoseconds.
51
+    */
52
+   uint64_t (*get_interval) (void *data);
53
+
54
    int (*abr_process) (void *data, size_t unsent);
55
 
56
+   /**
57
+    * Start encoding new packet.
58
+    *
59
+    * \param data Codec data from init()
60
+    * \param timestamp Packet time stamp (in samples played)
61
+    * \return Size of packet header written to dst in bytes, or < 0 for error
62
+    */
63
    int (*start_encode) (void *data,
64
        void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp);
65
+
66
+   /**
67
+    * Consume data from input buffer, encode to output buffer.
68
+    *
69
+    * \param data Codec data from init()
70
+    * \param src Source data. NULL if encoding packet fragment.
71
+    * \param dst Output buffer position. The memory region passed to the
72
+    *    previous start_encode() is still valid, and this position is inside
73
+    *    that region. The caller does not modify the contents of the buffer.
74
+    * \param dst_size Remaining buffer space after dst
75
+    * \param dst_out Bytes written to dst
76
+    * \param need_flush
77
+    *    - NEED_FLUSH_NO: don't flush this packet,
78
+    *    - NEED_FLUSH_ALL: flush this packet,
79
+    *    - NEED_FLUSH_FRAGMENT: flush packet fragment. The next start_encode()
80
+    *      and encode() are expected to produce more fragments or the final
81
+    *      fragment with NEED_FLUSH_ALL, without consuming source data.
82
+    *      The fragment start_encode() is called with the same output buffer
83
+    *      as previous. The fragment encode() will be called with NULL src.
84
+    *      No new source data will be fed in before NEED_FLUSH_ALL.
85
+    * \return Number of bytes consumed from src, or < 0 for error
86
+    */
87
    int (*encode) (void *data,
88
        const void *src, size_t src_size,
89
        void *dst, size_t dst_size,
90
        size_t *dst_out, int *need_flush);
91
 
92
+   /**
93
+    * Start decoding received packet.
94
+    *
95
+    * \return Number of bytes consumed from source data, or < 0 for error
96
+    */
97
    int (*start_decode) (void *data,
98
        const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp);
99
+
100
+   /**
101
+    * Decode received packet data.
102
+    *
103
+    * \param dst_out Number of bytes output to dst
104
+    * \return Number of bytes consumed from src, or < 0 for error
105
+    */
106
    int (*decode) (void *data,
107
        const void *src, size_t src_size,
108
        void *dst, size_t dst_size,
109
@@ -152,6 +210,9 @@
110
 int media_codec_select_config(const struct media_codec_config configs, size_t n,
111
    uint32_t cap, int preferred_value);
112
 
113
+int media_codec_get_config(const struct media_codec_config configs, size_t n,
114
+   uint32_t conf);
115
+
116
 bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_id,
117
    const void *caps, size_t caps_size, const struct media_codec_audio_info *info,
118
    const struct spa_dict *global_settings);
119
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/media-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/media-sink.c Changed
364
 
1
@@ -41,10 +41,12 @@
2
 #include "rate-control.h"
3
 #include "iso-io.h"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.media");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.sink.media");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
+#include "bt-latency.h"
11
+
12
 #define DEFAULT_CLOCK_NAME "clock.system.monotonic"
13
 
14
 struct props {
15
@@ -53,10 +55,11 @@
16
 };
17
 
18
 #define FILL_FRAMES 4
19
-#define MIN_BUFFERS 2
20
+#define MIN_BUFFERS 3
21
 #define MAX_BUFFERS 32
22
 #define BUFFER_SIZE    (8192*8)
23
 #define RATE_CTL_DIFF_MAX 0.005
24
+#define LATENCY_PERIOD     (200 * SPA_NSEC_PER_MSEC)
25
 
26
 /* Wait for two cycles before trying to sync ISO. On start/driver reassign,
27
  * first cycle may have strange number of samples. */
28
@@ -108,6 +111,7 @@
29
    struct spa_log *log;
30
    struct spa_loop *data_loop;
31
    struct spa_system *data_system;
32
+   struct spa_loop_utils *loop_utils;
33
 
34
    struct spa_hook_list hooks;
35
    struct spa_callbacks callbacks;
36
@@ -158,6 +162,9 @@
37
    uint64_t prev_flush_time;
38
    uint64_t next_flush_time;
39
 
40
+   uint64_t packet_delay_ns;
41
+   struct spa_source *update_delay_event;
42
+
43
    const struct media_codec *codec;
44
    bool codec_props_changed;
45
    void *codec_props;
46
@@ -371,12 +378,31 @@
47
    struct port *port = &this->port;
48
    int64_t delay;
49
 
50
+   /* in main loop */
51
+
52
    if (this->transport == NULL)
53
        return;
54
 
55
-   delay = spa_bt_transport_get_delay_nsec(this->transport);
56
+   /*
57
+    * We start flushing data immediately, so the delay is:
58
+    *
59
+    * (packet delay) + (codec internal delay) + (transport delay) + (latency offset)
60
+    *
61
+    * and doesn't depend on the quantum. The codec internal delay is neglected.
62
+    * Kernel knows the latency due to socket/controller queue, but doesn't
63
+    * tell us, so not included but hopefully in < 20 ms range.
64
+    */
65
+
66
+   delay = __atomic_load_n(&this->packet_delay_ns, __ATOMIC_RELAXED);
67
+   delay += spa_bt_transport_get_delay_nsec(this->transport);
68
    delay += SPA_CLAMP(this->props.latency_offset, -delay, INT64_MAX / 2);
69
+   delay = SPA_MAX(delay, 0);
70
+
71
    port->latency.min_ns = port->latency.max_ns = delay;
72
+   port->latency.min_rate = port->latency.max_rate = 0;
73
+   port->latency.min_quantum = port->latency.max_quantum = 0.0f;
74
+
75
+   spa_log_info(this->log, "%p: total latency:%d ms", this, (int)(delay / SPA_NSEC_PER_MSEC));
76
 
77
    if (emit_latency) {
78
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
79
@@ -385,6 +411,29 @@
80
    }
81
 }
82
 
83
+static void update_delay_event(void *data, uint64_t count)
84
+{
85
+   struct impl *this = data;
86
+
87
+   /* in main loop */
88
+   set_latency(this, true);
89
+}
90
+
91
+static void update_packet_delay(struct impl *this, uint64_t delay)
92
+{
93
+   uint64_t old_delay = this->packet_delay_ns;
94
+
95
+   /* in data thread */
96
+
97
+   delay = SPA_MAX(delay, old_delay);
98
+   if (delay == old_delay)
99
+       return;
100
+
101
+   __atomic_store_n(&this->packet_delay_ns, delay, __ATOMIC_RELAXED);
102
+   if (this->update_delay_event)
103
+       spa_loop_utils_signal_event(this->loop_utils, this->update_delay_event);
104
+}
105
+
106
 static int apply_props(struct impl *this, const struct spa_pod *param)
107
 {
108
    struct props new_props = this->props;
109
@@ -489,9 +538,9 @@
110
 
111
    /* Account for resampling delay */
112
    resampling = (port->current_format.info.raw.rate != this->process_rate) || this->following;
113
-   if (port->rate_match && this->clock && resampling) {
114
+   if (port->rate_match && this->position && resampling) {
115
        t -= (uint64_t)port->rate_match->delay * SPA_NSEC_PER_SEC
116
-           / this->clock->rate.denom;
117
+           / this->position->clock.rate.denom;
118
        t += SPA_NSEC_PER_SEC / port->current_format.info.raw.rate;
119
    }
120
 
121
@@ -814,6 +863,8 @@
122
            this->iso_pending = false;
123
 
124
            reset_buffer(this);
125
+
126
+           update_packet_delay(this, iso_io->duration * 3/2);
127
        }
128
        return 0;
129
    }
130
@@ -888,7 +939,7 @@
131
             */
132
 #if 1
133
            this->next_flush_time += SPA_MIN(packet_time,
134
-                   duration_ns * (port->n_buffers - 1));
135
+                   duration_ns * (SPA_MAX(port->n_buffers, 2u) - 2));
136
 #endif
137
        } else {
138
            if (this->next_flush_time == 0)
139
@@ -896,6 +947,8 @@
140
            this->next_flush_time += packet_time;
141
        }
142
 
143
+       update_packet_delay(this, packet_time);
144
+
145
        if (this->need_flush == NEED_FLUSH_FRAGMENT) {
146
            reset_buffer(this);
147
            this->fragment = true;
148
@@ -992,7 +1045,7 @@
149
    max_err = iso_io->duration;
150
 
151
    if (iso_io->resync && err >= 0) {
152
-       unsigned int req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
153
+       unsigned int req = (unsigned int)(err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC);
154
 
155
        if (req > 0) {
156
            spa_bt_rate_control_init(&port->ratectl, 0);
157
@@ -1000,7 +1053,7 @@
158
        }
159
        spa_log_debug(this->log, "%p: ISO sync skip frames:%u", this, req);
160
    } else if (iso_io->resync && -err >= 0) {
161
-       unsigned int req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
162
+       unsigned int req = (unsigned int)(-err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC);
163
        static const uint8_t empty8192 = {0};
164
 
165
        if (req > 0) {
166
@@ -1035,9 +1088,18 @@
167
 {
168
    struct impl *this = source->data;
169
 
170
+   if (source->rmask & SPA_IO_ERR) {
171
+       /* TX timestamp info? */
172
+       if (this->transport && this->transport->iso_io)
173
+           if (spa_bt_iso_io_recv_errqueue(this->transport->iso_io) == 0)
174
+               return;
175
+
176
+       /* Otherwise: actual error */
177
+   }
178
+
179
    spa_log_trace(this->log, "%p: flush event", this);
180
 
181
-   if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) {
182
+   if (source->rmask & (SPA_IO_HUP | SPA_IO_ERR)) {
183
        spa_log_warn(this->log, "%p: error %d", this, source->rmask);
184
        if (this->flush_source.loop)
185
            spa_loop_remove_source(this->data_loop, &this->flush_source);
186
@@ -1083,7 +1145,7 @@
187
    uint32_t rate;
188
    struct spa_io_buffers *io = port->io;
189
    uint64_t prev_time, now_time;
190
-   int res;
191
+   int status, res;
192
 
193
    if (this->started) {
194
        if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) {
195
@@ -1110,31 +1172,24 @@
196
 
197
    setup_matching(this);
198
 
199
-   this->next_time = now_time + duration * SPA_NSEC_PER_SEC / rate * port->ratectl.corr;
200
+   this->next_time = (uint64_t)(now_time + duration * SPA_NSEC_PER_SEC / rate * port->ratectl.corr);
201
 
202
    if (SPA_LIKELY(this->clock)) {
203
-       int64_t delay_nsec = 0;
204
-
205
        this->clock->nsec = now_time;
206
        this->clock->rate = this->clock->target_rate;
207
        this->clock->position += this->clock->duration;
208
        this->clock->duration = duration;
209
        this->clock->rate_diff = 1 / port->ratectl.corr;
210
        this->clock->next_nsec = this->next_time;
211
-
212
-       if (this->transport)
213
-           delay_nsec = spa_bt_transport_get_delay_nsec(this->transport);
214
-
215
-       /* Negative delay doesn't work properly, so disallow it */
216
-       delay_nsec += SPA_CLAMP(this->props.latency_offset, -delay_nsec, INT64_MAX / 2);
217
-
218
-       this->clock->delay = (delay_nsec * this->clock->rate.denom) / SPA_NSEC_PER_SEC;
219
+       this->clock->delay = 0;
220
    }
221
 
222
+   status = this->transport_started ? SPA_STATUS_NEED_DATA : SPA_STATUS_HAVE_DATA;
223
 
224
-   spa_log_trace(this->log, "%p: %d", this, io->status);
225
-   io->status = SPA_STATUS_NEED_DATA;
226
-   spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
227
+   spa_log_trace(this->log, "%p: %d -> %d", this, io->status, status);
228
+   io->status = status;
229
+   io->buffer_id = SPA_ID_INVALID;
230
+   spa_node_call_ready(&this->callbacks, status);
231
 
232
    set_timeout(this, this->next_time);
233
 }
234
@@ -1184,8 +1239,11 @@
235
                &port->current_format,
236
                this->codec_props,
237
                this->transport->write_mtu);
238
-       if (this->codec_data == NULL)
239
+       if (this->codec_data == NULL) {
240
+           spa_log_error(this->log, "%p: codec %s initialization failed", this,
241
+                   this->codec->description);
242
            return -EIO;
243
+       }
244
    } else {
245
        this->own_codec_data = false;
246
        this->codec_data = this->transport->iso_io->codec_data;
247
@@ -1231,6 +1289,8 @@
248
 
249
    spa_bt_rate_control_init(&port->ratectl, 0);
250
 
251
+   this->update_delay_event = spa_loop_utils_add_event(this->loop_utils, update_delay_event, this);
252
+
253
    if (!this->transport->iso_io) {
254
        this->flush_timer_source.data = this;
255
        this->flush_timer_source.fd = this->flush_timerfd;
256
@@ -1279,6 +1339,8 @@
257
        return res;
258
    }
259
 
260
+   this->packet_delay_ns = 0;
261
+
262
    this->source.data = this;
263
    this->source.fd = this->timerfd;
264
    this->source.func = media_on_timeout;
265
@@ -1307,6 +1369,12 @@
266
    if (this->source.loop)
267
        spa_loop_remove_source(this->data_loop, &this->source);
268
    set_timeout(this, 0);
269
+
270
+   if (this->update_delay_event) {
271
+       spa_loop_utils_destroy_source(this->loop_utils, this->update_delay_event);
272
+       this->update_delay_event = NULL;
273
+   }
274
+
275
    return 0;
276
 }
277
 
278
@@ -1323,7 +1391,6 @@
279
 
280
    if (this->flush_source.loop)
281
        spa_loop_remove_source(this->data_loop, &this->flush_source);
282
-
283
    if (this->flush_timer_source.loop)
284
        spa_loop_remove_source(this->data_loop, &this->flush_timer_source);
285
    enable_flush_timer(this, false);
286
@@ -1331,6 +1398,9 @@
287
    if (this->transport->iso_io)
288
        spa_bt_iso_io_set_cb(this->transport->iso_io, NULL, NULL);
289
 
290
+   /* Drop queued data */
291
+   drop_frames(this, UINT32_MAX);
292
+
293
    return 0;
294
 }
295
 
296
@@ -1409,12 +1479,12 @@
297
    char *node_group = NULL;
298
 
299
    if (this->transport && (this->transport->profile & SPA_BT_PROFILE_BAP_SINK)) {
300
-       spa_scnprintf(node_group_buf, sizeof(node_group_buf), "bluez-iso-%s-cig-%d",
301
+       spa_scnprintf(node_group_buf, sizeof(node_group_buf), "\"bluez-iso-%s-cig-%d\"",
302
                this->transport->device->adapter->address,
303
                this->transport->bap_cig);
304
        node_group = node_group_buf;
305
    } else if (this->transport && (this->transport->profile & SPA_BT_PROFILE_BAP_BROADCAST_SINK)) {
306
-       spa_scnprintf(node_group_buf, sizeof(node_group_buf), "bluez-iso-%s-big-%d",
307
+       spa_scnprintf(node_group_buf, sizeof(node_group_buf), "\"bluez-iso-%s-big-%d\"",
308
                this->transport->device->adapter->address,
309
                this->transport->bap_big);
310
        node_group = node_group_buf;
311
@@ -1708,6 +1778,8 @@
312
        port->have_format = true;
313
    }
314
 
315
+   set_latency(this, false);
316
+
317
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
318
    if (port->have_format) {
319
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
320
@@ -1847,8 +1919,13 @@
321
        return SPA_STATUS_HAVE_DATA;
322
    }
323
 
324
-   if (!this->started || !this->transport_started)
325
-       return SPA_STATUS_OK;
326
+   if (!this->started || !this->transport_started) {
327
+       if (io->status != SPA_STATUS_HAVE_DATA) {
328
+           io->status = SPA_STATUS_HAVE_DATA;
329
+           io->buffer_id = SPA_ID_INVALID;
330
+       }
331
+       return SPA_STATUS_HAVE_DATA;
332
+   }
333
 
334
    if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) {
335
        struct buffer *b = &port->buffersio->buffer_id;
336
@@ -2058,6 +2135,7 @@
337
    this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
338
    this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
339
    this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem);
340
+   this->loop_utils = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils);
341
 
342
    spa_log_topic_init(this->log, &log_topic);
343
 
344
@@ -2069,6 +2147,10 @@
345
        spa_log_error(this->log, "a data system is needed");
346
        return -EINVAL;
347
    }
348
+   if (this->loop_utils == NULL) {
349
+       spa_log_error(this->log, "loop utils are needed");
350
+       return -EINVAL;
351
+   }
352
 
353
    this->node.iface = SPA_INTERFACE_INIT(
354
            SPA_TYPE_INTERFACE_Node,
355
@@ -2105,8 +2187,6 @@
356
    port->info.n_params = N_PORT_PARAMS;
357
 
358
    port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
359
-   port->latency.min_quantum = 1.0f;
360
-   port->latency.max_quantum = 1.0f;
361
 
362
    spa_list_init(&port->ready);
363
 
364
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/media-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/media-source.c Changed
145
 
1
@@ -35,8 +35,9 @@
2
 #include "defs.h"
3
 #include "rtp.h"
4
 #include "media-codecs.h"
5
+#include "iso-io.h"
6
 
7
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.source.media");
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.source.media");
9
 #undef SPA_LOG_TOPIC_DEFAULT
10
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
11
 
12
@@ -147,6 +148,8 @@
13
    uint8_t buffer_read4096;
14
    struct timespec now;
15
    uint64_t sample_count;
16
+
17
+   uint32_t errqueue_count;
18
 };
19
 
20
 #define CHECK_PORT(this,d,p)    ((d) == SPA_DIRECTION_OUTPUT && (p) == 0)
21
@@ -454,6 +457,24 @@
22
    return dst_size - avail;
23
 }
24
 
25
+static void handle_errqueue(struct impl *this)
26
+{
27
+   int res;
28
+
29
+   /* iso-io/media-sink use these for TX latency.
30
+    * Someone else should be reading them, so drop
31
+    * only after yielding.
32
+    */
33
+   if (this->errqueue_count < 4) {
34
+       this->errqueue_count++;
35
+       return;
36
+   }
37
+
38
+   this->errqueue_count = 0;
39
+   res = recv(this->fd, NULL, 0, MSG_ERRQUEUE | MSG_TRUNC);
40
+   spa_log_trace(this->log, "%p: ignoring errqueue data (%d)", this, res);
41
+}
42
+
43
 static void media_on_ready_read(struct spa_source *source)
44
 {
45
    struct impl *this = source->data;
46
@@ -466,6 +487,11 @@
47
 
48
    /* make sure the source is an input */
49
    if ((source->rmask & SPA_IO_IN) == 0) {
50
+       if (source->rmask & SPA_IO_ERR) {
51
+           handle_errqueue(this);
52
+           return;
53
+       }
54
+
55
        spa_log_error(this->log, "source is not an input, rmask=%d", source->rmask);
56
        goto stop;
57
    }
58
@@ -474,6 +500,8 @@
59
        goto stop;
60
    }
61
 
62
+   this->errqueue_count = 0;
63
+
64
    spa_log_trace(this->log, "socket poll");
65
 
66
    /* read */
67
@@ -526,6 +554,8 @@
68
 stop:
69
    if (this->source.loop)
70
        spa_loop_remove_source(this->data_loop, &this->source);
71
+   if (this->transport && this->transport->iso_io)
72
+       spa_bt_iso_io_set_cb(this->transport->iso_io, NULL, NULL);
73
 }
74
 
75
 static int setup_matching(struct impl *this)
76
@@ -590,7 +620,7 @@
77
 
78
    setup_matching(this);
79
 
80
-   this->next_time = now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate;
81
+   this->next_time = (uint64_t)(now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate);
82
 
83
    if (SPA_LIKELY(this->clock)) {
84
        this->clock->nsec = now_time;
85
@@ -612,6 +642,21 @@
86
    set_timeout(this, this->next_time);
87
 }
88
 
89
+static void media_iso_pull(struct spa_bt_iso_io *iso_io)
90
+{
91
+   /* TODO: eventually use iso-io here, currently this is used just to indicate to
92
+    * iso-io whether this source is running or not. */
93
+}
94
+
95
+static int do_start_iso_io(struct spa_loop *loop, bool async, uint32_t seq,
96
+       const void *data, size_t size, void *user_data)
97
+{
98
+   struct impl *this = user_data;
99
+
100
+   spa_bt_iso_io_set_cb(this->transport->iso_io, media_iso_pull, this);
101
+   return 0;
102
+}
103
+
104
 static int transport_start(struct impl *this)
105
 {
106
    int res, val;
107
@@ -669,6 +714,9 @@
108
                port->current_format.info.raw.rate * 80 / 1000);
109
    }
110
 
111
+   this->sample_count = 0;
112
+   this->errqueue_count = 0;
113
+
114
    this->source.data = this;
115
 
116
    this->source.fd = this->fd;
117
@@ -679,7 +727,8 @@
118
        spa_log_error(this->log, "%p: failed to add poll source: %s", this,
119
                spa_strerror(res));
120
 
121
-   this->sample_count = 0;
122
+   if (this->transport->iso_io)
123
+       spa_loop_invoke(this->data_loop, do_start_iso_io, 0, NULL, 0, true, this);
124
 
125
    this->transport_started = true;
126
 
127
@@ -737,6 +786,8 @@
128
 
129
    if (this->timer_source.loop)
130
        spa_loop_remove_source(this->data_loop, &this->timer_source);
131
+   if (this->transport && this->transport->iso_io)
132
+       spa_bt_iso_io_set_cb(this->transport->iso_io, NULL, NULL);
133
    set_timeout(this, 0);
134
 
135
    return 0;
136
@@ -757,6 +808,8 @@
137
 
138
    if (this->source.loop)
139
        spa_loop_remove_source(this->data_loop, &this->source);
140
+   if (this->transport->iso_io)
141
+       spa_bt_iso_io_set_cb(this->transport->iso_io, NULL, NULL);
142
 
143
    return 0;
144
 }
145
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/meson.build Changed
27
 
1
@@ -67,6 +67,10 @@
2
   bluez5_sources += 'backend-hsphfpd.c'
3
 endif
4
 
5
+if get_option('bluez5-codec-lc3').allowed() and lc3_dep.found()
6
+  bluez5_deps += lc3_dep
7
+endif
8
+
9
 # The library uses GObject, and cannot be unloaded
10
 bluez5_link_args =  '-Wl,-z', '-Wl,nodelete' 
11
 
12
@@ -149,6 +153,14 @@
13
     dependencies :  spa_dep, opus_dep, mathlib ,
14
     install : true,
15
     install_dir : spa_plugindir / 'bluez5')
16
+
17
+  bluez_codec_opus_g = shared_library('spa-codec-bluez5-opus-g',
18
+     'a2dp-codec-opus-g.c', 'media-codecs.c' ,
19
+    include_directories :  configinc ,
20
+    c_args : opus_args,
21
+    dependencies :  spa_dep, opus_dep, mathlib ,
22
+    install : true,
23
+    install_dir : spa_plugindir / 'bluez5')
24
 endif
25
 
26
 if get_option('bluez5-codec-lc3').allowed() and lc3_dep.found()
27
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/midi-enum.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/midi-enum.c Changed
10
 
1
@@ -29,7 +29,7 @@
2
 #define MIDI_OBJECT_PATH   "/midi"
3
 #define MIDI_PROFILE_PATH  MIDI_OBJECT_PATH "/profile"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.midi");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.midi");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/midi-node.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/midi-node.c Changed
33
 
1
@@ -41,7 +41,7 @@
2
 
3
 #include "bluez5-interface-gen.h"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.midi.node");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.midi.node");
7
 #undef SPA_LOG_TOPIC_DEFAULT
8
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
@@ -598,7 +598,7 @@
11
                -SPA_CLAMP(err_nsec, -20*SPA_NSEC_PER_MSEC, 20*SPA_NSEC_PER_MSEC)
12
                * this->rate / SPA_NSEC_PER_SEC);
13
        tcorr = SPA_MIN(device_elapsed, SPA_NSEC_PER_SEC) * (corr - 1);
14
-       sync->device_time += tcorr;
15
+       sync->device_time += (uint64_t)tcorr;
16
 
17
        /* reset if too much off */
18
        if (err_nsec < -50 * SPA_NSEC_PER_MSEC ||
19
@@ -2011,11 +2011,13 @@
20
            SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"),
21
            SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "in"),
22
            SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, "in"),
23
+           SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, "group.0"),
24
        };
25
        static const struct spa_dict_item out_port_items = {
26
            SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"),
27
            SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "out"),
28
            SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, "out"),
29
+           SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, "group.0"),
30
        };
31
        static const struct spa_dict in_port_props = SPA_DICT_INIT_ARRAY(in_port_items);
32
        static const struct spa_dict out_port_props = SPA_DICT_INIT_ARRAY(out_port_items);
33
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/midi-server.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/midi-server.c Changed
31
 
1
@@ -15,8 +15,9 @@
2
 #include "bluez5-interface-gen.h"
3
 #include "dbus-monitor.h"
4
 
5
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.midi.server");
6
 #undef SPA_LOG_TOPIC_DEFAULT
7
-#define SPA_LOG_TOPIC_DEFAULT (&impl->log_topic)
8
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
9
 
10
 #define MIDI_SERVER_PATH   "/midiserver%u"
11
 #define MIDI_SERVICE_PATH  "/midiserver%u/service"
12
@@ -29,7 +30,6 @@
13
 {
14
    struct spa_bt_midi_server this;
15
 
16
-   struct spa_log_topic log_topic;
17
    struct spa_log *log;
18
 
19
    const struct spa_bt_midi_server_cb *cb;
20
@@ -517,9 +517,8 @@
21
    impl->user_data = user_data;
22
    impl->cb = cb;
23
    impl->log = log;
24
-   impl->log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.midi.server");
25
    impl->conn = conn;
26
-   spa_log_topic_init(impl->log, &impl->log_topic);
27
+   spa_log_topic_init(impl->log, &log_topic);
28
 
29
    if ((res = export_objects(impl)) < 0)
30
        goto fail;
31
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/modemmanager.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/modemmanager.c Changed
39
 
1
@@ -4,10 +4,10 @@
2
 
3
 #include <errno.h>
4
 #include <spa/utils/string.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include <ModemManager.h>
8
 
9
-#include "dbus-helpers.h"
10
 #include "modemmanager.h"
11
 
12
 #define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
13
@@ -570,7 +570,7 @@
14
        const char *path;
15
        struct call *call_object;
16
        const char *mm_call_interface = MM_DBUS_INTERFACE_CALL;
17
-       spa_autoptr(DBusMessage) m = NULL;
18
+       spa_autoptr(DBusMessage) m2 = NULL;
19
 
20
        if (!spa_streq(this->modem.path, dbus_message_get_path(m)))
21
            goto finish;
22
@@ -590,12 +590,12 @@
23
        call_object->path = strdup(path);
24
        spa_list_append(&this->call_list, &call_object->link);
25
 
26
-       m = dbus_message_new_method_call(MM_DBUS_SERVICE, path, DBUS_INTERFACE_PROPERTIES, "GetAll");
27
-       if (m == NULL)
28
+       m2 = dbus_message_new_method_call(MM_DBUS_SERVICE, path, DBUS_INTERFACE_PROPERTIES, "GetAll");
29
+       if (m2 == NULL)
30
            goto finish;
31
-       dbus_message_append_args(m, DBUS_TYPE_STRING, &mm_call_interface, DBUS_TYPE_INVALID);
32
+       dbus_message_append_args(m2, DBUS_TYPE_STRING, &mm_call_interface, DBUS_TYPE_INVALID);
33
 
34
-       call_object->pending = send_with_reply(this->conn, m, mm_get_call_properties_reply, call_object);
35
+       call_object->pending = send_with_reply(this->conn, m2, mm_get_call_properties_reply, call_object);
36
        if (!call_object->pending) {
37
            spa_log_error(this->log, "dbus call failure");
38
            goto finish;
39
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/player.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/player.c Changed
21
 
1
@@ -7,9 +7,9 @@
2
 #include <dbus/dbus.h>
3
 
4
 #include <spa/utils/string.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "defs.h"
8
-#include "dbus-helpers.h"
9
 #include "player.h"
10
 
11
 #define PLAYER_OBJECT_PATH_BASE    "/media_player"
12
@@ -50,7 +50,7 @@
13
    " </interface>"                         \
14
    "</node>"
15
 
16
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.player");
17
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.player");
18
 #undef SPA_LOG_TOPIC_DEFAULT
19
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
20
 
21
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/plugin.c Changed
18
 
1
@@ -6,6 +6,7 @@
2
 #include <stdio.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_bluez5_dbus_factory;
8
 extern const struct spa_handle_factory spa_bluez5_device_factory;
9
@@ -18,6 +19,8 @@
10
 extern const struct spa_handle_factory spa_bluez5_midi_enum_factory;
11
 extern const struct spa_handle_factory spa_bluez5_midi_node_factory;
12
 
13
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
14
+
15
 SPA_EXPORT
16
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
17
 {
18
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/quirks.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/quirks.c Changed
42
 
1
@@ -36,10 +36,11 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include "defs.h"
8
 
9
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.quirks");
10
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.quirks");
11
 #undef SPA_LOG_TOPIC_DEFAULT
12
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
13
 
14
@@ -159,6 +160,7 @@
15
    struct spa_json data = SPA_JSON_INIT(str, len);
16
    struct spa_json rules;
17
    char key1024;
18
+   struct spa_error_location loc;
19
 
20
    if (spa_json_enter_object(&data, &rules) <= 0)
21
        spa_json_init(&rules, str, len);
22
@@ -182,6 +184,10 @@
23
        else if (spa_streq(key, "bluez5.features.device") && !this->device_rules)
24
            this->device_rules = strndup(value, sz);
25
    }
26
+
27
+   if (spa_json_get_error(&rules, str, &loc))
28
+       spa_debug_log_error_location(this->log, SPA_LOG_LEVEL_ERROR, &loc,
29
+               "spa.bluez5 quirks syntax error: %s", loc.reason);
30
 }
31
 
32
 static int load_conf(struct spa_bt_quirks *this, const char *path)
33
@@ -211,7 +217,7 @@
34
    const char *str;
35
 
36
    if (!info) {
37
-       errno = -EINVAL;
38
+       errno = EINVAL;
39
        return NULL;
40
    }
41
 
42
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/rate-control.h -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/rate-control.h Changed
85
 
1
@@ -7,6 +7,67 @@
2
 
3
 #include <spa/utils/defs.h>
4
 
5
+/** Windowed min/max */
6
+struct spa_bt_ptp
7
+{
8
+   union {
9
+       int32_t min;
10
+       int32_t mins4;
11
+   };
12
+   union {
13
+       int32_t max;
14
+       int32_t maxs4;
15
+   };
16
+   uint32_t pos;
17
+   uint32_t left;
18
+   uint32_t period;
19
+};
20
+
21
+static inline void spa_bt_ptp_init(struct spa_bt_ptp *p, int32_t period, uint32_t min_duration)
22
+{
23
+   size_t i;
24
+
25
+   spa_zero(*p);
26
+   for (i = 0; i < SPA_N_ELEMENTS(p->mins); ++i) {
27
+       p->minsi = INT32_MAX;
28
+       p->maxsi = INT32_MIN;
29
+   }
30
+   p->left = min_duration;
31
+   p->period = period;
32
+}
33
+
34
+static inline void spa_bt_ptp_update(struct spa_bt_ptp *p, int32_t value, uint32_t duration)
35
+{
36
+   const size_t n = SPA_N_ELEMENTS(p->mins);
37
+   size_t i;
38
+
39
+   for (i = 0; i < n; ++i) {
40
+       p->minsi = SPA_MIN(p->minsi, value);
41
+       p->maxsi = SPA_MAX(p->maxsi, value);
42
+   }
43
+
44
+   p->pos += duration;
45
+   if (p->pos >= p->period / (n - 1)) {
46
+       p->pos = 0;
47
+       for (i = 1; i < SPA_N_ELEMENTS(p->mins); ++i) {
48
+           p->minsi-1 = p->minsi;
49
+           p->maxsi-1 = p->maxsi;
50
+       }
51
+       p->minsn-1 = INT32_MAX;
52
+       p->maxsn-1 = INT32_MIN;
53
+   }
54
+
55
+   if (p->left < duration)
56
+       p->left = 0;
57
+   else
58
+       p->left -= duration;
59
+}
60
+
61
+static inline bool spa_bt_ptp_valid(struct spa_bt_ptp *p)
62
+{
63
+   return p->left == 0;
64
+}
65
+
66
 /**
67
  * Rate controller.
68
  *
69
@@ -98,13 +159,13 @@
70
    double corr;
71
 };
72
 
73
-static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level)
74
+static inline void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level)
75
 {
76
    this->avg = level;
77
    this->corr = 1.0;
78
 }
79
 
80
-static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level,
81
+static inline double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level,
82
        double target, double duration, double period, double rate_diff_max)
83
 {
84
    /*
85
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/sco-io.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/sco-io.c Changed
81
 
1
@@ -30,6 +30,10 @@
2
 
3
 #include "defs.h"
4
 
5
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.sco-io");
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+
9
 
10
 /* We'll use the read rx data size to find the correct packet size for writing,
11
  * since kernel might not report it as the socket MTU, see
12
@@ -54,6 +58,7 @@
13
    uint16_t read_mtu;
14
    uint16_t write_mtu;
15
 
16
+   struct spa_log *log;
17
    struct spa_loop *data_loop;
18
    struct spa_source source;
19
 
20
@@ -103,6 +108,9 @@
21
            goto stop;
22
        }
23
 
24
+       if (res != (int)io->read_size)
25
+           spa_log_trace(io->log, "%p: packet size:%d", io, res);
26
+
27
        io->read_size = res;
28
 
29
        if (io->source_cb) {
30
@@ -185,23 +193,42 @@
31
 }
32
 
33
 
34
-struct spa_bt_sco_io *spa_bt_sco_io_create(struct spa_loop *data_loop,
35
-                                           int fd,
36
-                                           uint16_t read_mtu,
37
-                                           uint16_t write_mtu)
38
+struct spa_bt_sco_io *spa_bt_sco_io_create(struct spa_bt_transport *transport, struct spa_loop *data_loop, struct spa_log *log)
39
 {
40
    struct spa_bt_sco_io *io;
41
 
42
+   spa_log_topic_init(log, &log_topic);
43
+
44
    io = calloc(1, sizeof(struct spa_bt_sco_io));
45
    if (io == NULL)
46
        return io;
47
 
48
-   io->fd = fd;
49
-   io->read_mtu = read_mtu;
50
-   io->write_mtu = write_mtu;
51
+   io->fd = transport->fd;
52
+   io->read_mtu = transport->read_mtu;
53
+   io->write_mtu = transport->write_mtu;
54
    io->data_loop = data_loop;
55
+   io->log = log;
56
+
57
+   if (transport->device->adapter->bus_type == BUS_TYPE_USB) {
58
+       /* For USB we need to wait for RX to know it. Using wrong size doesn't
59
+        * work anyway, and may result to errors printed to dmesg if too big.
60
+        */
61
+       io->read_size = 0;
62
+   } else {
63
+       /* Set some sensible initial packet size */
64
+       switch (transport->codec) {
65
+       case HFP_AUDIO_CODEC_CVSD:
66
+           io->read_size = 48;  /* 3ms S16_LE 8000 Hz */
67
+           break;
68
+       case HFP_AUDIO_CODEC_MSBC:
69
+       case HFP_AUDIO_CODEC_LC3_SWB:
70
+       default:
71
+           io->read_size = HFP_CODEC_PACKET_SIZE;
72
+           break;
73
+       }
74
+   }
75
 
76
-   io->read_size = 0;
77
+   spa_log_debug(io->log, "%p: initial packet size:%d", io, io->read_size);
78
 
79
    /* Add the ready callback */
80
    io->source.data = io;
81
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/sco-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/sco-sink.c Changed
512
 
1
@@ -33,18 +33,26 @@
2
 
3
 #include "defs.h"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.sco");
6
+#ifdef HAVE_LC3
7
+#include <lc3.h>
8
+#endif
9
+
10
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.sink.sco");
11
 #undef SPA_LOG_TOPIC_DEFAULT
12
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
13
 
14
 #define DEFAULT_CLOCK_NAME "clock.system.monotonic"
15
 
16
 struct props {
17
+   int64_t latency_offset;
18
    char clock_name64;
19
 };
20
 
21
 #define MAX_BUFFERS 32
22
 
23
+#define ALT1_PACKET_SIZE   24
24
+#define ALT6_PACKET_SIZE   60
25
+
26
 struct buffer {
27
    uint32_t id;
28
    unsigned int outstanding:1;
29
@@ -121,6 +129,8 @@
30
    unsigned int following:1;
31
    unsigned int flush_pending:1;
32
 
33
+   unsigned int is_internal:1;
34
+
35
    /* Sources */
36
    struct spa_source source;
37
    struct spa_source flush_timer_source;
38
@@ -134,16 +144,26 @@
39
    uint64_t current_time;
40
    uint64_t next_time;
41
    uint64_t process_time;
42
+
43
    uint64_t prev_flush_time;
44
    uint64_t next_flush_time;
45
 
46
-   /* mSBC */
47
-   sbc_t msbc;
48
+   /* Codecs */
49
    uint8_t *buffer;
50
    uint8_t *buffer_head;
51
    uint8_t *buffer_next;
52
    int buffer_size;
53
-   int msbc_seq;
54
+   int h2_seq;
55
+
56
+   /* mSBC */
57
+   sbc_t msbc;
58
+
59
+   /* LC3 */
60
+#ifdef HAVE_LC3
61
+   lc3_encoder_t lc3;
62
+#else
63
+   void *lc3;
64
+#endif
65
 };
66
 
67
 #define CHECK_PORT(this,d,p)   ((d) == SPA_DIRECTION_INPUT && (p) == 0)
68
@@ -152,6 +172,7 @@
69
 
70
 static void reset_props(struct props *props)
71
 {
72
+   props->latency_offset = 0;
73
    strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
74
 }
75
 
76
@@ -180,6 +201,13 @@
77
    case SPA_PARAM_PropInfo:
78
    {
79
        switch (result.index) {
80
+       case 0:
81
+           param = spa_pod_builder_add_object(&b,
82
+               SPA_TYPE_OBJECT_PropInfo, id,
83
+               SPA_PROP_INFO_id,   SPA_POD_Id(SPA_PROP_latencyOffsetNsec),
84
+               SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"),
85
+               SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, INT64_MIN, INT64_MAX));
86
+           break;
87
        default:
88
            return 0;
89
        }
90
@@ -187,10 +215,13 @@
91
    }
92
    case SPA_PARAM_Props:
93
    {
94
+       struct props *p = &this->props;
95
+
96
        switch (result.index) {
97
        case 0:
98
            param = spa_pod_builder_add_object(&b,
99
-               SPA_TYPE_OBJECT_Props, id);
100
+               SPA_TYPE_OBJECT_Props, id,
101
+               SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset));
102
            break;
103
        default:
104
            return 0;
105
@@ -284,6 +315,53 @@
106
 
107
 static void emit_node_info(struct impl *this, bool full);
108
 
109
+static void emit_port_info(struct impl *this, struct port *port, bool full);
110
+
111
+static void set_latency(struct impl *this, bool emit_latency)
112
+{
113
+   struct port *port = &this->port;
114
+   int64_t delay;
115
+
116
+   if (this->transport == NULL)
117
+       return;
118
+
119
+   /*
120
+    * We start flushing data immediately, so the delay is:
121
+    *
122
+    * (transport delay) + (packet delay) + (codec internal delay) + (latency offset)
123
+    *
124
+    * and doesn't depend on the quantum. The codec internal delay is neglected.
125
+    * Kernel knows the latency due to socket/controller queue, but doesn't
126
+    * tell us, so not included but hopefully in < 20 ms range.
127
+    */
128
+
129
+   switch (this->transport->codec) {
130
+   case HFP_AUDIO_CODEC_MSBC:
131
+   case HFP_AUDIO_CODEC_LC3_SWB:
132
+       delay = 7500 * SPA_NSEC_PER_USEC;
133
+       break;
134
+   default:
135
+       delay = this->transport->write_mtu / (2 * 8000);
136
+       break;
137
+   }
138
+
139
+   delay += spa_bt_transport_get_delay_nsec(this->transport);
140
+   delay += SPA_CLAMP(this->props.latency_offset, -delay, INT64_MAX / 2);
141
+   delay = SPA_MAX(delay, 0);
142
+
143
+   spa_log_info(this->log, "%p: total latency:%d ms", this, (int)(delay / SPA_NSEC_PER_MSEC));
144
+
145
+   port->latency.min_ns = port->latency.max_ns = delay;
146
+   port->latency.min_rate = port->latency.max_rate = 0;
147
+   port->latency.min_quantum = port->latency.max_quantum = 0.0f;
148
+
149
+   if (emit_latency) {
150
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
151
+       port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL;
152
+       emit_port_info(this, port, false);
153
+   }
154
+}
155
+
156
 static int apply_props(struct impl *this, const struct spa_pod *param)
157
 {
158
    struct props new_props = this->props;
159
@@ -293,11 +371,16 @@
160
        reset_props(&new_props);
161
    } else {
162
        spa_pod_parse_object(param,
163
-               SPA_TYPE_OBJECT_Props, NULL);
164
+               SPA_TYPE_OBJECT_Props, NULL,
165
+               SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&new_props.latency_offset));
166
    }
167
 
168
    changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0);
169
    this->props = new_props;
170
+
171
+   if (changed)
172
+       set_latency(this, true);
173
+
174
    return changed;
175
 }
176
 
177
@@ -362,6 +445,28 @@
178
    return bytes / port->frame_size;
179
 }
180
 
181
+static int lc3_encode_frame(struct impl *this, const void *src, size_t src_size, void *dst, size_t dst_size,
182
+       ssize_t *dst_out)
183
+{
184
+#ifdef HAVE_LC3
185
+   int res;
186
+
187
+   if (src_size < LC3_SWB_DECODED_SIZE)
188
+       return -EINVAL;
189
+   if (dst_size < LC3_SWB_PAYLOAD_SIZE)
190
+       return -EINVAL;
191
+
192
+   res = lc3_encode(this->lc3, LC3_PCM_FORMAT_S24, src, 1, LC3_SWB_PAYLOAD_SIZE, dst);
193
+   if (res != 0)
194
+       return -EINVAL;
195
+
196
+   *dst_out = LC3_SWB_PAYLOAD_SIZE;
197
+   return LC3_SWB_DECODED_SIZE;
198
+#else
199
+   return -EOPNOTSUPP;
200
+#endif
201
+}
202
+
203
 static int flush_data(struct impl *this)
204
 {
205
    struct port *port = &this->port;
206
@@ -373,12 +478,9 @@
207
    if (this->transport == NULL || this->transport->sco_io == NULL || !this->flush_timer_source.loop)
208
        return -EIO;
209
 
210
-   const uint32_t min_in_size =
211
-       (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
212
-       MSBC_DECODED_SIZE : this->transport->write_mtu;
213
-   uint8_t * const packet =
214
-       (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
215
-       this->buffer_head : port->write_buffer;
216
+   const uint32_t min_in_size = (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ? MSBC_DECODED_SIZE :
217
+       (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB) ? LC3_SWB_DECODED_SIZE :
218
+       this->transport->write_mtu;
219
    const uint32_t packet_samples = min_in_size / port->frame_size;
220
    const uint64_t packet_time = (uint64_t)packet_samples * SPA_NSEC_PER_SEC
221
        / port->current_format.info.raw.rate;
222
@@ -433,30 +535,40 @@
223
        return 0;
224
    }
225
 
226
-   if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
227
+   if (this->transport->codec == HFP_AUDIO_CODEC_MSBC ||
228
+           this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB) {
229
        ssize_t out_encoded;
230
-
231
        /* Encode */
232
-       if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) {
233
+       if (this->buffer_next + HFP_CODEC_PACKET_SIZE > this->buffer + this->buffer_size) {
234
            /* Buffer overrun; shouldn't usually happen. Drop data and reset. */
235
            this->buffer_head = this->buffer_next = this->buffer;
236
-           spa_log_warn(this->log, "sco-sink: mSBC buffer overrun, dropping data");
237
+           spa_log_warn(this->log, "sco-sink: mSBC/LC3 buffer overrun, dropping data");
238
        }
239
+
240
+       /* H2 sync header */
241
        this->buffer_next0 = 0x01;
242
-       this->buffer_next1 = sntablethis->msbc_seq % 4;
243
+       this->buffer_next1 = sntablethis->h2_seq % 4;
244
        this->buffer_next59 = 0x00;
245
-       this->msbc_seq = (this->msbc_seq + 1) % 4;
246
-       processed = sbc_encode(&this->msbc, port->write_buffer, port->write_buffer_size,
247
-               this->buffer_next + 2, MSBC_ENCODED_SIZE - 3, &out_encoded);
248
+       this->h2_seq = (this->h2_seq + 1) % 4;
249
+
250
+       if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
251
+           processed = sbc_encode(&this->msbc, port->write_buffer, port->write_buffer_size,
252
+                   this->buffer_next + 2, HFP_CODEC_PACKET_SIZE - 3, &out_encoded);
253
+           out_encoded += 1; /* pad */
254
+       } else {
255
+           processed = lc3_encode_frame(this, port->write_buffer, port->write_buffer_size,
256
+                   this->buffer_next + 2, HFP_CODEC_PACKET_SIZE - 2, &out_encoded);
257
+       }
258
+
259
        if (processed < 0) {
260
-           spa_log_warn(this->log, "sbc_encode failed: %d", processed);
261
+           spa_log_warn(this->log, "encode failed: %d", processed);
262
            return -EINVAL;
263
        }
264
-       this->buffer_next += out_encoded + 3;
265
+       this->buffer_next += out_encoded + 2;
266
        port->write_buffer_size = 0;
267
 
268
        /* Write */
269
-       written = spa_bt_sco_io_write(this->transport->sco_io, packet,
270
+       written = spa_bt_sco_io_write(this->transport->sco_io, this->buffer_head,
271
                this->buffer_next - this->buffer_head);
272
        if (written < 0) {
273
            spa_log_warn(this->log, "failed to write data: %d (%s)",
274
@@ -468,9 +580,9 @@
275
 
276
        if (this->buffer_head == this->buffer_next)
277
            this->buffer_head = this->buffer_next = this->buffer;
278
-       else if (this->buffer_next + MSBC_ENCODED_SIZE > this->buffer + this->buffer_size) {
279
+       else if (this->buffer_next + HFP_CODEC_PACKET_SIZE > this->buffer + this->buffer_size) {
280
            /* Written bytes is not necessarily commensurate
281
-            * with MSBC_ENCODED_SIZE. If this occurs, copy data.
282
+            * with HFP_CODEC_PACKET_SIZE. If this occurs, copy data.
283
             */
284
            int size = this->buffer_next - this->buffer_head;
285
            spa_memmove(this->buffer, this->buffer_head, size);
286
@@ -478,7 +590,7 @@
287
            this->buffer_head = this->buffer;
288
        }
289
    } else {
290
-       written = spa_bt_sco_io_write(this->transport->sco_io, packet,
291
+       written = spa_bt_sco_io_write(this->transport->sco_io, port->write_buffer,
292
                port->write_buffer_size);
293
        if (written < 0) {
294
            spa_log_warn(this->log, "sco-sink: write failure: %d (%s)",
295
@@ -586,7 +698,7 @@
296
    uint32_t rate;
297
    struct spa_io_buffers *io = port->io;
298
    uint64_t prev_time, now_time;
299
-   int res;
300
+   int status, res;
301
 
302
    if (this->started) {
303
        if ((res = spa_system_timerfd_read(this->data_system, this->timerfd, &exp)) < 0) {
304
@@ -622,9 +734,12 @@
305
        this->clock->delay = 0;
306
    }
307
 
308
-   spa_log_trace(this->log, "%p: %d", this, io->status);
309
-   io->status = SPA_STATUS_NEED_DATA;
310
-   spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
311
+   status = this->transport_started ? SPA_STATUS_NEED_DATA : SPA_STATUS_HAVE_DATA;
312
+
313
+   spa_log_trace(this->log, "%p: %d -> %d", this, io->status, status);
314
+   io->status = status;
315
+   io->buffer_id = SPA_ID_INVALID;
316
+   spa_node_call_ready(&this->callbacks, status);
317
 
318
    set_timeout(this, this->next_time);
319
 }
320
@@ -660,7 +775,7 @@
321
 
322
    spa_log_debug(this->log, "%p: start transport", this);
323
 
324
-   /* Init mSBC if needed */
325
+   /* Init mSBC/LC3 if needed */
326
    if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
327
        res = sbc_init_msbc(&this->msbc, 0);
328
        if (res < 0)
329
@@ -674,7 +789,26 @@
330
         * commensurate, we may end up doing memmoves, but nothing worse
331
         * is going to happen.
332
         */
333
-       this->buffer_size = lcm(24, lcm(60, lcm(this->transport->write_mtu, 2 * MSBC_ENCODED_SIZE)));
334
+       this->buffer_size = lcm(ALT1_PACKET_SIZE, lcm(ALT6_PACKET_SIZE, lcm(this->transport->write_mtu, 2 * HFP_CODEC_PACKET_SIZE)));
335
+   } else if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB) {
336
+#ifdef HAVE_LC3
337
+       this->lc3 = lc3_setup_encoder(7500, 32000, 0,
338
+               calloc(1, lc3_encoder_size(7500, 32000)));
339
+       if (!this->lc3)
340
+           return -EINVAL;
341
+
342
+       spa_assert(lc3_frame_samples(7500, 32000) * this->port.frame_size == LC3_SWB_DECODED_SIZE);
343
+
344
+       this->buffer_size = lcm(ALT1_PACKET_SIZE, lcm(ALT6_PACKET_SIZE, lcm(this->transport->write_mtu, 2 * HFP_CODEC_PACKET_SIZE)));
345
+#else
346
+       res = -EOPNOTSUPP;
347
+       goto fail;
348
+#endif
349
+   } else {
350
+       this->buffer_size = 0;
351
+   }
352
+
353
+   if (this->buffer_size) {
354
        this->buffer = calloc(this->buffer_size, sizeof(uint8_t));
355
        this->buffer_head = this->buffer_next = this->buffer;
356
        if (this->buffer == NULL) {
357
@@ -685,6 +819,9 @@
358
 
359
    spa_return_val_if_fail(this->transport->write_mtu <= sizeof(this->port.write_buffer), -EINVAL);
360
 
361
+   spa_log_info(this->log, "%p: using codec %d, delay:%"PRIi64" ms", this, this->transport->codec,
362
+           (int64_t)(spa_bt_transport_get_delay_nsec(this->transport) / SPA_NSEC_PER_MSEC));
363
+
364
    /* start socket i/o */
365
    if ((res = spa_bt_transport_ensure_sco_io(this->transport, this->data_loop)) < 0)
366
        goto fail;
367
@@ -708,6 +845,8 @@
368
    free(this->buffer);
369
    this->buffer = NULL;
370
    sbc_finish(&this->msbc);
371
+   free(this->lc3);
372
+   this->lc3 = NULL;
373
    return res;
374
 }
375
 
376
@@ -802,7 +941,7 @@
377
        spa_loop_remove_source(this->data_loop, &this->flush_timer_source);
378
    enable_flush_timer(this, false);
379
 
380
-   /* Drop buffered data in the ready queue. Ideally there shouldn't be any. */
381
+   /* Drop queued data */
382
    drop_port_output(this);
383
 
384
    return 0;
385
@@ -824,6 +963,8 @@
386
    }
387
 
388
    sbc_finish(&this->msbc);
389
+   free(this->lc3);
390
+   this->lc3 = NULL;
391
 }
392
 
393
 static int do_stop(struct impl *this)
394
@@ -884,9 +1025,9 @@
395
 
396
 static void emit_node_info(struct impl *this, bool full)
397
 {
398
-   static const struct spa_dict_item hu_node_info_items = {
399
+   const struct spa_dict_item hu_node_info_items = {
400
        { SPA_KEY_DEVICE_API, "bluez5" },
401
-       { SPA_KEY_MEDIA_CLASS, "Audio/Sink" },
402
+       { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Sink/Internal" : "Audio/Sink" },
403
        { SPA_KEY_NODE_DRIVER, "true" },
404
    };
405
 
406
@@ -1018,13 +1159,21 @@
407
 
408
        /* set the info structure */
409
        struct spa_audio_info_raw info = { 0, };
410
-       info.format = SPA_AUDIO_FORMAT_S16_LE;
411
+
412
+       if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
413
+           info.format = SPA_AUDIO_FORMAT_S24_32_LE;
414
+       else
415
+           info.format = SPA_AUDIO_FORMAT_S16_LE;
416
        info.channels = 1;
417
        info.position0 = SPA_AUDIO_CHANNEL_MONO;
418
 
419
        /* CVSD format has a rate of 8kHz
420
-        * MSBC format has a rate of 16kHz */
421
-       if (this->transport->codec == HFP_AUDIO_CODEC_MSBC)
422
+        * MSBC format has a rate of 16kHz
423
+        * LC3-SWB format has a rate of 32kHz
424
+        */
425
+       if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
426
+           info.rate = 32000;
427
+       else if (this->transport->codec == HFP_AUDIO_CODEC_MSBC)
428
            info.rate = 16000;
429
        else
430
            info.rate = 8000;
431
@@ -1140,6 +1289,9 @@
432
    } else {
433
        struct spa_audio_info info = { 0 };
434
 
435
+       if (!this->transport)
436
+           return -EIO;
437
+
438
        if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
439
            return err;
440
 
441
@@ -1150,16 +1302,31 @@
442
        if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
443
            return -EINVAL;
444
 
445
-       if (info.info.raw.format != SPA_AUDIO_FORMAT_S16_LE ||
446
-           info.info.raw.rate == 0 ||
447
+       if (info.info.raw.rate == 0 ||
448
            info.info.raw.channels != 1)
449
            return -EINVAL;
450
 
451
-       port->frame_size = info.info.raw.channels * 2;
452
+       switch (info.info.raw.format) {
453
+       case SPA_AUDIO_FORMAT_S16_LE:
454
+           if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
455
+               return -EINVAL;
456
+           port->frame_size = info.info.raw.channels * 2;
457
+           break;
458
+       case SPA_AUDIO_FORMAT_S24_32_LE:
459
+           if (this->transport->codec != HFP_AUDIO_CODEC_LC3_SWB)
460
+               return -EINVAL;
461
+           port->frame_size = info.info.raw.channels * 4;
462
+           break;
463
+       default:
464
+           return -EINVAL;
465
+       }
466
+
467
        port->current_format = info;
468
        port->have_format = true;
469
    }
470
 
471
+   set_latency(this, false);
472
+
473
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
474
    if (port->have_format) {
475
        port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE;
476
@@ -1296,8 +1463,13 @@
477
        return SPA_STATUS_HAVE_DATA;
478
    }
479
 
480
-   if (!this->started || !this->transport_started)
481
-       return SPA_STATUS_OK;
482
+   if (!this->started || !this->transport_started) {
483
+       if (io->status != SPA_STATUS_HAVE_DATA) {
484
+           io->status = SPA_STATUS_HAVE_DATA;
485
+           io->buffer_id = SPA_ID_INVALID;
486
+       }
487
+       return SPA_STATUS_HAVE_DATA;
488
+   }
489
 
490
    if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) {
491
        struct buffer *b = &port->buffersio->buffer_id;
492
@@ -1525,6 +1697,9 @@
493
    if (info && (str = spa_dict_lookup(info, "clock.quantum-limit")))
494
        spa_atou32(str, &this->quantum_limit, 0);
495
 
496
+   if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL)
497
+       this->is_internal = spa_atob(str);
498
+
499
    if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)))
500
        sscanf(str, "pointer:%p", &this->transport);
501
 
502
@@ -1532,6 +1707,9 @@
503
        spa_log_error(this->log, "a transport is needed");
504
        return -EINVAL;
505
    }
506
+
507
+   set_latency(this, false);
508
+
509
    spa_bt_transport_add_listener(this->transport,
510
            &this->transport_listener, &transport_events, this);
511
 
512
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/sco-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/sco-source.c Changed
412
 
1
@@ -35,7 +35,11 @@
2
 
3
 #include "defs.h"
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.source.sco");
6
+#ifdef HAVE_LC3
7
+#include <lc3.h>
8
+#endif
9
+
10
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.source.sco");
11
 #undef SPA_LOG_TOPIC_DEFAULT
12
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
13
 
14
@@ -120,6 +124,8 @@
15
    unsigned int resampling:1;
16
    unsigned int io_error:1;
17
 
18
+   unsigned int is_internal:1;
19
+
20
    struct spa_source timer_source;
21
    int timerfd;
22
 
23
@@ -129,14 +135,23 @@
24
    uint64_t current_time;
25
    uint64_t next_time;
26
 
27
+   /* Codecs */
28
+   bool h2_seq_initialized;
29
+   uint8_t h2_seq;
30
+
31
+   /* mSBC/LC3 frame parsing */
32
+   uint8_t recv_bufferHFP_CODEC_PACKET_SIZE;
33
+   uint8_t recv_buffer_pos;
34
+
35
    /* mSBC */
36
    sbc_t msbc;
37
-   bool msbc_seq_initialized;
38
-   uint8_t msbc_seq;
39
 
40
-   /* mSBC frame parsing */
41
-   uint8_t msbc_bufferMSBC_ENCODED_SIZE;
42
-   uint8_t msbc_buffer_pos;
43
+   /* LC3 */
44
+#ifdef HAVE_LC3
45
+   lc3_decoder_t lc3;
46
+#else
47
+   void *lc3;
48
+#endif
49
 
50
    struct timespec now;
51
 };
52
@@ -343,51 +358,53 @@
53
    }
54
 }
55
 
56
-/* Append data to msbc buffer, syncing buffer start to frame headers */
57
-static void msbc_buffer_append_byte(struct impl *this, uint8_t byte)
58
+/* Append data to recv buffer, syncing buffer start to headers */
59
+static void recv_buffer_append_byte(struct impl *this, uint8_t byte)
60
 {
61
-        /* Parse mSBC frame header */
62
-        if (this->msbc_buffer_pos == 0) {
63
+        /* Parse H2 sync header */
64
+        if (this->recv_buffer_pos == 0) {
65
                 if (byte != 0x01) {
66
-                        this->msbc_buffer_pos = 0;
67
+                        this->recv_buffer_pos = 0;
68
                         return;
69
                 }
70
-        }
71
-        else if (this->msbc_buffer_pos == 1) {
72
+        } else if (this->recv_buffer_pos == 1) {
73
                 if (!((byte & 0x0F) == 0x08 &&
74
                       ((byte >> 4) & 1) == ((byte >> 5) & 1) &&
75
                       ((byte >> 6) & 1) == ((byte >> 7) & 1))) {
76
-                        this->msbc_buffer_pos = 0;
77
+                        this->recv_buffer_pos = 0;
78
                         return;
79
                 }
80
-        }
81
-        else if (this->msbc_buffer_pos == 2) {
82
-                /* .. and beginning of MSBC frame: SYNCWORD + 2 nul bytes */
83
-                if (byte != 0xAD) {
84
-                        this->msbc_buffer_pos = 0;
85
-                        return;
86
-                }
87
-        }
88
-        else if (this->msbc_buffer_pos == 3) {
89
-                if (byte != 0x00) {
90
-                        this->msbc_buffer_pos = 0;
91
-                        return;
92
-                }
93
-        }
94
-        else if (this->msbc_buffer_pos == 4) {
95
-                if (byte != 0x00) {
96
-                        this->msbc_buffer_pos = 0;
97
-                        return;
98
+        } else if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
99
+       /* Beginning of MSBC frame: SYNCWORD + 2 nul bytes */
100
+       if (this->recv_buffer_pos == 2) {
101
+           if (byte != 0xAD) {
102
+               this->recv_buffer_pos = 0;
103
+               return;
104
+           }
105
+       }
106
+       else if (this->recv_buffer_pos == 3) {
107
+           if (byte != 0x00) {
108
+               this->recv_buffer_pos = 0;
109
+               return;
110
+           }
111
+       }
112
+       else if (this->recv_buffer_pos == 4) {
113
+           if (byte != 0x00) {
114
+               this->recv_buffer_pos = 0;
115
+               return;
116
+           }
117
                 }
118
-        }
119
-        else if (this->msbc_buffer_pos >= MSBC_ENCODED_SIZE) {
120
-                /* Packet completed. Reset. */
121
-                this->msbc_buffer_pos = 0;
122
-                msbc_buffer_append_byte(this, byte);
123
-                return;
124
-        }
125
-        this->msbc_bufferthis->msbc_buffer_pos = byte;
126
-        ++this->msbc_buffer_pos;
127
+   }
128
+
129
+   if (this->recv_buffer_pos >= HFP_CODEC_PACKET_SIZE) {
130
+       /* Packet completed. Reset. */
131
+       this->recv_buffer_pos = 0;
132
+       recv_buffer_append_byte(this, byte);
133
+       return;
134
+   }
135
+
136
+        this->recv_bufferthis->recv_buffer_pos = byte;
137
+        ++this->recv_buffer_pos;
138
 }
139
 
140
 /* Helper function for debugging */
141
@@ -419,14 +436,38 @@
142
    return true;
143
 }
144
 
145
-static uint32_t preprocess_and_decode_msbc_data(void *userdata, uint8_t *read_data, int size_read)
146
+static int lc3_decode_frame(struct impl *this, const void *src, size_t src_size, void *dst,
147
+       size_t dst_size, size_t *dst_out)
148
+{
149
+#ifdef HAVE_LC3
150
+   int res;
151
+
152
+   if (src_size != LC3_SWB_PAYLOAD_SIZE)
153
+       return -EINVAL;
154
+   if (dst_size < LC3_SWB_DECODED_SIZE)
155
+       return -EINVAL;
156
+
157
+   res = lc3_decode(this->lc3, src, src_size, LC3_PCM_FORMAT_S24, dst, 1);
158
+   if (res != 0)
159
+       return -EINVAL;
160
+
161
+   *dst_out = LC3_SWB_DECODED_SIZE;
162
+   return LC3_SWB_DECODED_SIZE;
163
+#else
164
+   return -EOPNOTSUPP;
165
+#endif
166
+}
167
+
168
+static uint32_t preprocess_and_decode_codec_data(void *userdata, uint8_t *read_data, int size_read)
169
 {
170
    struct impl *this = userdata;
171
    struct port *port = &this->port;
172
    uint32_t decoded = 0;
173
    int i;
174
+   uint32_t decoded_size = (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ? MSBC_DECODED_SIZE :
175
+       LC3_SWB_DECODED_SIZE;
176
 
177
-   spa_log_trace(this->log, "handling mSBC data");
178
+   spa_log_trace(this->log, "handling mSBC/LC3 data");
179
 
180
    /*
181
     * Check if the packet contains only zeros - if so ignore the packet.
182
@@ -443,44 +484,49 @@
183
        int seq, processed;
184
        size_t written;
185
 
186
-       msbc_buffer_append_byte(this, read_datai);
187
+       recv_buffer_append_byte(this, read_datai);
188
 
189
-       if (this->msbc_buffer_pos != MSBC_ENCODED_SIZE)
190
+       if (this->recv_buffer_pos != HFP_CODEC_PACKET_SIZE)
191
            continue;
192
 
193
        /*
194
-        * Handle found mSBC packet
195
+        * Handle found mSBC/LC3 packet
196
         */
197
 
198
        buf = spa_bt_decode_buffer_get_write(&port->buffer, &avail);
199
 
200
        /* Check sequence number */
201
-       seq = ((this->msbc_buffer1 >> 4) & 1) |
202
-           ((this->msbc_buffer1 >> 6) & 2);
203
-
204
-       spa_log_trace(this->log, "mSBC packet seq=%u", seq);
205
-       if (!this->msbc_seq_initialized) {
206
-           this->msbc_seq_initialized = true;
207
-           this->msbc_seq = seq;
208
-       } else if (seq != this->msbc_seq) {
209
+       seq = ((this->recv_buffer1 >> 4) & 1) |
210
+           ((this->recv_buffer1 >> 6) & 2);
211
+
212
+       spa_log_trace(this->log, "mSBC/LC3 packet seq=%u", seq);
213
+       if (!this->h2_seq_initialized) {
214
+           this->h2_seq_initialized = true;
215
+           this->h2_seq = seq;
216
+       } else if (seq != this->h2_seq) {
217
            /* TODO: PLC (too late to insert data now) */
218
            spa_log_info(this->log,
219
-                   "missing mSBC packet: %u != %u", seq, this->msbc_seq);
220
-           this->msbc_seq = seq;
221
+                   "missing mSBC/LC3 packet: %u != %u", seq, this->h2_seq);
222
+           this->h2_seq = seq;
223
        }
224
 
225
-       this->msbc_seq = (this->msbc_seq + 1) % 4;
226
+       this->h2_seq = (this->h2_seq + 1) % 4;
227
 
228
-       if (avail < MSBC_DECODED_SIZE)
229
-           spa_log_warn(this->log, "Output buffer full, dropping msbc data");
230
+       if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
231
+           if (avail < decoded_size)
232
+               spa_log_warn(this->log, "Output buffer full, dropping msbc data");
233
 
234
-       /* decode frame */
235
-       processed = sbc_decode(
236
-           &this->msbc, this->msbc_buffer + 2, MSBC_ENCODED_SIZE - 3,
237
+           /* decode frame */
238
+           processed = sbc_decode(
239
+               &this->msbc, this->recv_buffer + 2, HFP_CODEC_PACKET_SIZE - 3,
240
+                       buf, avail, &written);
241
+       } else {
242
+           processed = lc3_decode_frame(this, this->recv_buffer + 2, HFP_CODEC_PACKET_SIZE - 2,
243
                    buf, avail, &written);
244
+       }
245
 
246
        if (processed < 0) {
247
-           spa_log_warn(this->log, "sbc_decode failed: %d", processed);
248
+           spa_log_warn(this->log, "decode failed: %d", processed);
249
            /* TODO: manage errors */
250
            continue;
251
        }
252
@@ -518,8 +564,9 @@
253
    hexdump_to_log(this, read_data, size_read);
254
 #endif
255
 
256
-   if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
257
-       decoded = preprocess_and_decode_msbc_data(userdata, read_data, size_read);
258
+   if (this->transport->codec == HFP_AUDIO_CODEC_MSBC ||
259
+           this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB) {
260
+       decoded = preprocess_and_decode_codec_data(userdata, read_data, size_read);
261
    } else {
262
        uint32_t avail;
263
        uint8_t *packet;
264
@@ -620,7 +667,7 @@
265
 
266
    setup_matching(this);
267
 
268
-   this->next_time = now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate;
269
+   this->next_time = (uint64_t)(now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate);
270
 
271
    if (SPA_LIKELY(this->clock)) {
272
        this->clock->nsec = now_time;
273
@@ -685,7 +732,7 @@
274
    spa_bt_decode_buffer_set_max_latency(&port->buffer,
275
            port->current_format.info.raw.rate * 40 / 1000);
276
 
277
-   /* Init mSBC if needed */
278
+   /* Init mSBC/LC3 if needed */
279
    if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
280
        res = sbc_init_msbc(&this->msbc, 0);
281
        if (res < 0)
282
@@ -693,9 +740,24 @@
283
 
284
        /* Libsbc expects audio samples by default in host endianness, mSBC requires little endian */
285
        this->msbc.endian = SBC_LE;
286
-       this->msbc_seq_initialized = false;
287
+       this->h2_seq_initialized = false;
288
+
289
+       this->recv_buffer_pos = 0;
290
+   } else if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB) {
291
+#ifdef HAVE_LC3
292
+       this->lc3 = lc3_setup_decoder(7500, 32000, 0,
293
+               calloc(1, lc3_decoder_size(7500, 32000)));
294
+       if (!this->lc3)
295
+           return -EINVAL;
296
 
297
-       this->msbc_buffer_pos = 0;
298
+       spa_assert(lc3_frame_samples(7500, 32000) * port->frame_size == LC3_SWB_DECODED_SIZE);
299
+
300
+       this->h2_seq_initialized = false;
301
+       this->recv_buffer_pos = 0;
302
+#else
303
+       res = -EINVAL;
304
+       goto fail;
305
+#endif
306
    }
307
 
308
    this->io_error = false;
309
@@ -712,6 +774,8 @@
310
 
311
 fail:
312
    sbc_finish(&this->msbc);
313
+   free(this->lc3);
314
+   this->lc3 = NULL;
315
    return res;
316
 }
317
 
318
@@ -804,6 +868,8 @@
319
    spa_bt_decode_buffer_clear(&port->buffer);
320
 
321
    sbc_finish(&this->msbc);
322
+   free(this->lc3);
323
+   this->lc3 = NULL;
324
 }
325
 
326
 static int do_stop(struct impl *this)
327
@@ -865,9 +931,9 @@
328
 
329
 static void emit_node_info(struct impl *this, bool full)
330
 {
331
-   static const struct spa_dict_item hu_node_info_items = {
332
+   const struct spa_dict_item hu_node_info_items = {
333
        { SPA_KEY_DEVICE_API, "bluez5" },
334
-       { SPA_KEY_MEDIA_CLASS, "Audio/Source" },
335
+       { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Source/Internal" : "Audio/Source" },
336
        { SPA_KEY_NODE_DRIVER, "true" },
337
    };
338
    const struct spa_dict_item ag_node_info_items = {
339
@@ -997,13 +1063,20 @@
340
 
341
        /* set the info structure */
342
        struct spa_audio_info_raw info = { 0, };
343
-       info.format = SPA_AUDIO_FORMAT_S16_LE;
344
+       if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
345
+           info.format = SPA_AUDIO_FORMAT_S24_32_LE;
346
+       else
347
+           info.format = SPA_AUDIO_FORMAT_S16_LE;
348
        info.channels = 1;
349
        info.position0 = SPA_AUDIO_CHANNEL_MONO;
350
 
351
         /* CVSD format has a rate of 8kHz
352
-         * MSBC format has a rate of 16kHz */
353
-       if (this->transport->codec == HFP_AUDIO_CODEC_MSBC)
354
+         * MSBC format has a rate of 16kHz
355
+         * LC3-SWB format has a rate of 32kHz
356
+         */
357
+       if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
358
+           info.rate = 32000;
359
+       else if (this->transport->codec == HFP_AUDIO_CODEC_MSBC)
360
            info.rate = 16000;
361
        else
362
            info.rate = 8000;
363
@@ -1119,6 +1192,9 @@
364
    } else {
365
        struct spa_audio_info info = { 0 };
366
 
367
+       if (!this->transport)
368
+           return -EIO;
369
+
370
        if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
371
            return err;
372
 
373
@@ -1129,12 +1205,25 @@
374
        if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
375
            return -EINVAL;
376
 
377
-       if (info.info.raw.format != SPA_AUDIO_FORMAT_S16_LE ||
378
-           info.info.raw.rate == 0 ||
379
+       if (info.info.raw.rate == 0 ||
380
            info.info.raw.channels != 1)
381
            return -EINVAL;
382
 
383
-       port->frame_size = info.info.raw.channels * 2;
384
+       switch (info.info.raw.format) {
385
+       case SPA_AUDIO_FORMAT_S16_LE:
386
+           if (this->transport->codec == HFP_AUDIO_CODEC_LC3_SWB)
387
+               return -EINVAL;
388
+           port->frame_size = info.info.raw.channels * 2;
389
+           break;
390
+       case SPA_AUDIO_FORMAT_S24_32_LE:
391
+           if (this->transport->codec != HFP_AUDIO_CODEC_LC3_SWB)
392
+               return -EINVAL;
393
+           port->frame_size = info.info.raw.channels * 4;
394
+           break;
395
+       default:
396
+           return -EINVAL;
397
+       }
398
+
399
        port->current_format = info;
400
        port->have_format = true;
401
    }
402
@@ -1628,6 +1717,9 @@
403
    if (info && (str = spa_dict_lookup(info, "clock.quantum-limit")))
404
        spa_atou32(str, &this->quantum_limit, 0);
405
 
406
+   if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL)
407
+       this->is_internal = spa_atob(str);
408
+
409
    if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)))
410
        sscanf(str, "pointer:%p", &this->transport);
411
 
412
pipewire-1.0.1.tar.bz2/spa/plugins/bluez5/upower.c -> pipewire-1.2.0.tar.gz/spa/plugins/bluez5/upower.c Changed
11
 
1
@@ -4,8 +4,8 @@
2
 
3
 #include <errno.h>
4
 #include <spa/utils/string.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
-#include "dbus-helpers.h"
8
 #include "upower.h"
9
 
10
 #define UPOWER_SERVICE "org.freedesktop.UPower"
11
pipewire-1.0.1.tar.bz2/spa/plugins/control/mixer.c -> pipewire-1.2.0.tar.gz/spa/plugins/control/mixer.c Changed
372
 
1
@@ -21,7 +21,9 @@
2
 #include <spa/control/control.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#define NAME "control-mixer"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.control-mixer");
9
 
10
 #define MAX_BUFFERS     64
11
 #define MAX_PORTS       512
12
@@ -39,7 +41,7 @@
13
    uint32_t direction;
14
    uint32_t id;
15
 
16
-   struct spa_io_buffers *io;
17
+   struct spa_io_buffers *io2;
18
 
19
    uint64_t info_all;
20
    struct spa_port_info info;
21
@@ -58,6 +60,8 @@
22
    struct spa_handle handle;
23
    struct spa_node node;
24
 
25
+   uint32_t quantum_limit;
26
+
27
    struct spa_log *log;
28
 
29
    struct spa_loop *data_loop;
30
@@ -66,6 +70,8 @@
31
    struct spa_node_info info;
32
    struct spa_param_info params8;
33
 
34
+   struct spa_io_position *position;
35
+
36
    struct spa_hook_list hooks;
37
 
38
    uint32_t port_count;
39
@@ -83,13 +89,16 @@
40
 };
41
 
42
 #define PORT_VALID(p)                ((p) != NULL && (p)->valid)
43
+#define CHECK_ANY_IN(this,d,p)       ((d) == SPA_DIRECTION_INPUT && (p) == SPA_ID_INVALID)
44
 #define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && !PORT_VALID(this->in_ports(p)))
45
 #define CHECK_IN_PORT(this,d,p)      ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && PORT_VALID(this->in_ports(p)))
46
 #define CHECK_OUT_PORT(this,d,p)     ((d) == SPA_DIRECTION_OUTPUT && (p) == 0)
47
 #define CHECK_PORT(this,d,p)         (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p))
48
+#define CHECK_PORT_ANY(this,d,p)     (CHECK_ANY_IN(this,d,p) || CHECK_PORT(this,d,p))
49
 #define GET_IN_PORT(this,p)          (this->in_portsp)
50
 #define GET_OUT_PORT(this,p)         (&this->out_portsp)
51
 #define GET_PORT(this,d,p)           (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p))
52
+#define GET_PORT_ANY(this,d,p)       (CHECK_ANY_IN(this,d,p) ? NULL : GET_PORT(this,d,p))
53
 
54
 static int impl_node_enum_params(void *object, int seq,
55
                 uint32_t id, uint32_t start, uint32_t num,
56
@@ -106,7 +115,16 @@
57
 
58
 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
59
 {
60
-   return -ENOTSUP;
61
+   struct impl *this = object;
62
+
63
+   switch (id) {
64
+   case SPA_IO_Position:
65
+       this->position = data;
66
+       break;
67
+   default:
68
+       return -ENOTSUP;
69
+   }
70
+   return 0;
71
 }
72
 
73
 static int impl_node_send_command(void *object, const struct spa_command *command)
74
@@ -226,7 +244,7 @@
75
        this->last_port = port_id + 1;
76
    port->valid = true;
77
 
78
-   spa_log_debug(this->log, NAME " %p: add port %d %d", this, port_id, this->last_port);
79
+   spa_log_debug(this->log, "%p: add port %d %d", this, port_id, this->last_port);
80
    emit_port_info(this, port, true);
81
 
82
    return 0;
83
@@ -260,15 +278,14 @@
84
 
85
        this->last_port = i + 1;
86
    }
87
-   spa_log_debug(this->log, NAME " %p: remove port %d %d", this, port_id, this->last_port);
88
+   spa_log_debug(this->log, "%p: remove port %d %d", this, port_id, this->last_port);
89
 
90
    spa_node_emit_port_info(&this->hooks, direction, port_id, NULL);
91
 
92
    return 0;
93
 }
94
 
95
-static int port_enum_formats(void *object,
96
-                enum spa_direction direction, uint32_t port_id,
97
+static int port_enum_formats(void *object, struct port *port,
98
                 uint32_t index,
99
                 struct spa_pod **param,
100
                 struct spa_pod_builder *builder)
101
@@ -303,9 +320,9 @@
102
 
103
    spa_return_val_if_fail(this != NULL, -EINVAL);
104
    spa_return_val_if_fail(num != 0, -EINVAL);
105
-   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
106
+   spa_return_val_if_fail(CHECK_PORT_ANY(this, direction, port_id), -EINVAL);
107
 
108
-   port = GET_PORT(this, direction, port_id);
109
+   port = GET_PORT_ANY(this, direction, port_id);
110
 
111
    result.id = id;
112
    result.next = start;
113
@@ -316,19 +333,19 @@
114
 
115
    switch (id) {
116
    case SPA_PARAM_EnumFormat:
117
-       if ((res = port_enum_formats(this, direction, port_id, result.index, &param, &b)) <= 0)
118
+       if ((res = port_enum_formats(this, port, result.index, &param, &b)) <= 0)
119
            return res;
120
        break;
121
 
122
    case SPA_PARAM_Format:
123
-       if (!port->have_format)
124
+       if (port == NULL || !port->have_format)
125
            return -EIO;
126
-       if ((res = port_enum_formats(this, direction, port_id, result.index, &param, &b)) <= 0)
127
+       if ((res = port_enum_formats(this, port, result.index, &param, &b)) <= 0)
128
            return res;
129
        break;
130
 
131
    case SPA_PARAM_Buffers:
132
-       if (!port->have_format)
133
+       if (port == NULL || !port->have_format)
134
            return -EIO;
135
        if (result.index > 0)
136
            return 0;
137
@@ -337,7 +354,8 @@
138
            SPA_TYPE_OBJECT_ParamBuffers, id,
139
            SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS),
140
            SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
141
-           SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(4096, 512, INT32_MAX),
142
+           SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(this->quantum_limit,
143
+               this->quantum_limit, INT32_MAX),
144
            SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(1));
145
        break;
146
 
147
@@ -349,6 +367,12 @@
148
                SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_Buffers),
149
                SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
150
            break;
151
+       case 1:
152
+           param = spa_pod_builder_add_object(&b,
153
+               SPA_TYPE_OBJECT_ParamIO, id,
154
+               SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_AsyncBuffers),
155
+               SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_async_buffers)));
156
+           break;
157
        default:
158
            return 0;
159
        }
160
@@ -371,7 +395,7 @@
161
 static int clear_buffers(struct impl *this, struct port *port)
162
 {
163
    if (port->n_buffers > 0) {
164
-       spa_log_debug(this->log, NAME " %p: clear buffers %p", this, port);
165
+       spa_log_debug(this->log, "%p: clear buffers %p", this, port);
166
        port->n_buffers = 0;
167
        spa_list_init(&port->queue);
168
    }
169
@@ -385,7 +409,7 @@
170
 
171
    spa_list_append(&port->queue, &b->link);
172
    SPA_FLAG_SET(b->flags, BUFFER_FLAG_QUEUED);
173
-   spa_log_trace_fp(this->log, NAME " %p: queue buffer %d", this, b->id);
174
+   spa_log_trace_fp(this->log, "%p: queue buffer %d", this, b->id);
175
    return 0;
176
 }
177
 
178
@@ -399,7 +423,7 @@
179
    b = spa_list_first(&port->queue, struct buffer, link);
180
    spa_list_remove(&b->link);
181
    SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_QUEUED);
182
-   spa_log_trace_fp(this->log, NAME " %p: dequeue buffer %d", this, b->id);
183
+   spa_log_trace_fp(this->log, "%p: dequeue buffer %d", this, b->id);
184
    return b;
185
 }
186
 
187
@@ -438,7 +462,7 @@
188
        if (!port->have_format) {
189
            this->n_formats++;
190
            port->have_format = true;
191
-           spa_log_debug(this->log, NAME " %p: set format on port %d:%d",
192
+           spa_log_debug(this->log, "%p: set format on port %d:%d",
193
                    this, direction, port_id);
194
        }
195
    }
196
@@ -491,7 +515,7 @@
197
 
198
    port = GET_PORT(this, direction, port_id);
199
 
200
-   spa_log_debug(this->log, NAME " %p: use buffers %d on port %d:%d",
201
+   spa_log_debug(this->log, "%p: use buffers %d on port %d:%d",
202
            this, n_buffers, direction, port_id);
203
 
204
    spa_return_val_if_fail(!this->started || port->io == NULL, -EIO);
205
@@ -513,7 +537,7 @@
206
        b->id = i;
207
 
208
        if (d0.data == NULL) {
209
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %d", this, i);
210
+           spa_log_error(this->log, "%p: invalid memory on buffer %d", this, i);
211
            return -EINVAL;
212
        }
213
        if (direction == SPA_DIRECTION_OUTPUT)
214
@@ -527,13 +551,24 @@
215
 struct io_info {
216
    struct port *port;
217
    void *data;
218
+   size_t size;
219
 };
220
 
221
 static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
222
        const void *data, size_t size, void *user_data)
223
 {
224
    struct io_info *info = user_data;
225
-   info->port->io = info->data;
226
+   if (info->size >= sizeof(struct spa_io_async_buffers)) {
227
+       struct spa_io_async_buffers *ab = info->data;
228
+       info->port->io0 = &ab->buffers0;
229
+       info->port->io1 = &ab->buffers1;
230
+   } else if (info->size >= sizeof(struct spa_io_buffers)) {
231
+       info->port->io0 = info->data;
232
+       info->port->io1 = info->data;
233
+   } else {
234
+       info->port->io0 = NULL;
235
+       info->port->io1 = NULL;
236
+   }
237
    return 0;
238
 }
239
 
240
@@ -548,7 +583,7 @@
241
 
242
    spa_return_val_if_fail(this != NULL, -EINVAL);
243
 
244
-   spa_log_debug(this->log, NAME " %p: port %d:%d io %d %p/%zd", this,
245
+   spa_log_debug(this->log, "%p: port %d:%d io %d %p/%zd", this,
246
            direction, port_id, id, data, size);
247
 
248
    spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
249
@@ -556,9 +591,11 @@
250
    port = GET_PORT(this, direction, port_id);
251
    info.port = port;
252
    info.data = data;
253
+   info.size = size;
254
 
255
    switch (id) {
256
    case SPA_IO_Buffers:
257
+   case SPA_IO_AsyncBuffers:
258
        spa_loop_invoke(this->data_loop,
259
                                do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
260
        break;
261
@@ -627,14 +664,15 @@
262
    struct spa_pod_frame f;
263
         struct buffer *outb;
264
    struct spa_data *d;
265
+   uint32_t cycle = this->position->clock.cycle & 1;
266
 
267
    spa_return_val_if_fail(this != NULL, -EINVAL);
268
 
269
    outport = GET_OUT_PORT(this, 0);
270
-   if ((outio = outport->io) == NULL)
271
+   if ((outio = outport->iocycle) == NULL)
272
        return -EIO;
273
 
274
-   spa_log_trace_fp(this->log, NAME " %p: status %p %d %d",
275
+   spa_log_trace_fp(this->log, "%p: status %p %d %d",
276
            this, outio, outio->status, outio->buffer_id);
277
 
278
    if (outio->status == SPA_STATUS_HAVE_DATA)
279
@@ -649,7 +687,7 @@
280
    /* get output buffer */
281
    if ((outb = dequeue_buffer(this, outport)) == NULL) {
282
        if (outport->n_buffers > 0)
283
-                   spa_log_warn(this->log, NAME " %p: out of buffers (%d)",
284
+                   spa_log_warn(this->log, "%p: out of buffers (%d)",
285
                    this, outport->n_buffers);
286
                 return -EPIPE;
287
         }
288
@@ -664,33 +702,34 @@
289
        struct spa_io_buffers *inio = NULL;
290
        void *pod;
291
 
292
-       if (!inport->valid ||
293
-           (inio = inport->io) == NULL ||
294
-           inio->buffer_id >= inport->n_buffers ||
295
+       if (SPA_UNLIKELY(!PORT_VALID(inport) || (inio = inport->iocycle) == NULL)) {
296
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d io:%p/%p/%d",
297
+                   this, i, PORT_VALID(inport),
298
+                   inport->io0, inport->io1, cycle);
299
+           continue;
300
+       }
301
+       if (inio->buffer_id >= inport->n_buffers ||
302
            inio->status != SPA_STATUS_HAVE_DATA) {
303
-           spa_log_trace_fp(this->log, NAME " %p: skip input idx:%d valid:%d "
304
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d "
305
                    "io:%p status:%d buf_id:%d n_buffers:%d", this,
306
-               i, inport->valid, inio,
307
-               inio ? inio->status : -1,
308
-               inio ? inio->buffer_id : SPA_ID_INVALID,
309
-               inport->n_buffers);
310
+               i, inio, inio->status, inio->buffer_id, inport->n_buffers);
311
            continue;
312
        }
313
 
314
-       spa_log_trace_fp(this->log, NAME " %p: mix input %d %p->%p %d %d", this,
315
+       spa_log_trace_fp(this->log, "%p: mix input %d %p->%p %d %d", this,
316
                i, inio, outio, inio->status, inio->buffer_id);
317
 
318
        d = inport->buffersinio->buffer_id.buffer->datas;
319
 
320
        if ((pod = spa_pod_from_data(d->data, d->maxsize,
321
                d->chunk->offset, d->chunk->size)) == NULL) {
322
-           spa_log_trace_fp(this->log, NAME " %p: skip input idx:%d max:%u "
323
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d max:%u "
324
                    "offset:%u size:%u", this, i,
325
                    d->maxsize, d->chunk->offset, d->chunk->size);
326
            continue;
327
        }
328
        if (!spa_pod_is_sequence(pod)) {
329
-           spa_log_trace_fp(this->log, NAME " %p: skip input idx:%d", this, i);
330
+           spa_log_trace_fp(this->log, "%p: skip input idx:%d", this, i);
331
            continue;
332
        }
333
 
334
@@ -731,6 +770,12 @@
335
    }
336
    spa_pod_builder_pop(&builder, &f);
337
 
338
+   if (builder.state.offset > d->maxsize) {
339
+       spa_log_warn(this->log, "%p: control overflow %d > %d",
340
+               this, builder.state.offset, d->maxsize);
341
+       builder.state.offset = 0;
342
+   }
343
+
344
    d->chunk->offset = 0;
345
    d->chunk->size = builder.state.offset;
346
    d->chunk->stride = 1;
347
@@ -807,6 +852,7 @@
348
 {
349
    struct impl *this;
350
    struct port *port;
351
+   uint32_t i;
352
 
353
    spa_return_val_if_fail(factory != NULL, -EINVAL);
354
    spa_return_val_if_fail(handle != NULL, -EINVAL);
355
@@ -824,6 +870,16 @@
356
        return -EINVAL;
357
    }
358
 
359
+   this->quantum_limit = 8192;
360
+
361
+   for (i = 0; info && i < info->n_items; i++) {
362
+       const char *k = info->itemsi.key;
363
+       const char *s = info->itemsi.value;
364
+       if (spa_streq(k, "clock.quantum-limit")) {
365
+           spa_atou32(s, &this->quantum_limit, 0);
366
+       }
367
+   }
368
+
369
    spa_hook_list_init(&this->hooks);
370
 
371
    this->node.iface = SPA_INTERFACE_INIT(
372
pipewire-1.0.1.tar.bz2/spa/plugins/control/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/control/plugin.c Changed
14
 
1
@@ -5,9 +5,12 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_control_mixer_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/ffmpeg/ffmpeg-dec.c -> pipewire-1.2.0.tar.gz/spa/plugins/ffmpeg/ffmpeg-dec.c Changed
12
 
1
@@ -20,6 +20,10 @@
2
 
3
 #include "ffmpeg.h"
4
 
5
+#undef SPA_LOG_TOPIC_DEFAULT
6
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
7
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.ffmpeg.dec");
8
+
9
 #define IS_VALID_PORT(this,d,id)   ((id) == 0)
10
 #define GET_IN_PORT(this,p)        (&this->in_portsp)
11
 #define GET_OUT_PORT(this,p)       (&this->out_portsp)
12
pipewire-1.0.1.tar.bz2/spa/plugins/ffmpeg/ffmpeg-enc.c -> pipewire-1.2.0.tar.gz/spa/plugins/ffmpeg/ffmpeg-enc.c Changed
12
 
1
@@ -19,6 +19,10 @@
2
 
3
 #include "ffmpeg.h"
4
 
5
+#undef SPA_LOG_TOPIC_DEFAULT
6
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
7
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.ffmpeg.enc");
8
+
9
 #define IS_VALID_PORT(this,d,id)   ((id) == 0)
10
 #define GET_IN_PORT(this,p)        (&this->in_portsp)
11
 #define GET_OUT_PORT(this,p)       (&this->out_portsp)
12
pipewire-1.0.1.tar.bz2/spa/plugins/ffmpeg/ffmpeg.c -> pipewire-1.2.0.tar.gz/spa/plugins/ffmpeg/ffmpeg.c Changed
18
 
1
@@ -6,6 +6,7 @@
2
 #include <stdio.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 #include <spa/node/node.h>
7
 
8
 #include <libavcodec/avcodec.h>
9
@@ -104,6 +105,8 @@
10
 }
11
 #endif
12
 
13
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
14
+
15
 SPA_EXPORT
16
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
17
 {
18
pipewire-1.0.1.tar.bz2/spa/plugins/jack/jack-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/jack/jack-device.c Changed
21
 
1
@@ -28,7 +28,9 @@
2
 
3
 #include "jack-client.h"
4
 
5
-#define NAME  "jack-device"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.jack-device");
9
 
10
 #define MAX_DEVICES    64
11
 
12
@@ -108,7 +110,7 @@
13
 
14
    res = spa_jack_client_open(&this->client, "PipeWire", NULL);
15
    if (res < 0) {
16
-       spa_log_error(this->log, NAME" %p: can't open client: %s",
17
+       spa_log_error(this->log, "%p: can't open client: %s",
18
                this, spa_strerror(res));
19
        return res;
20
    }
21
pipewire-1.0.1.tar.bz2/spa/plugins/jack/jack-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/jack/jack-sink.c Changed
75
 
1
@@ -27,7 +27,9 @@
2
 
3
 #include "jack-client.h"
4
 
5
-#define NAME "jack-sink"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.jack-sink");
9
 
10
 #define MAX_PORTS 128
11
 #define MAX_BUFFERS 8
12
@@ -404,7 +406,7 @@
13
            NULL, JACK_DEFAULT_AUDIO_TYPE,
14
                         JackPortIsPhysical|JackPortIsInput);
15
    if (ports == NULL) {
16
-       spa_log_error(this->log, NAME" %p: can't enumerate ports", this);
17
+       spa_log_error(this->log, "%p: can't enumerate ports", this);
18
        res = -ENODEV;
19
        goto exit;
20
    }
21
@@ -421,7 +423,7 @@
22
                jack_port_type(p),
23
                JackPortIsOutput, 0);
24
        if (port->jack_port == NULL) {
25
-           spa_log_error(this->log, NAME" %p: jack_port_register() %d (%s) failed",
26
+           spa_log_error(this->log, "%p: jack_port_register() %d (%s) failed",
27
                    this, i, portsi);
28
            res = -EFAULT;
29
            goto exit_free;
30
@@ -452,7 +454,7 @@
31
    for (i = 0; portsi; i++) {
32
        struct port *port = GET_IN_PORT(this, i);
33
        if (jack_connect(client, jack_port_name(port->jack_port), portsi)) {
34
-           spa_log_warn(this->log, NAME" %p: Failed to connect %s to %s",
35
+           spa_log_warn(this->log, "%p: Failed to connect %s to %s",
36
                    this, jack_port_name(port->jack_port), portsi);
37
             }
38
    }
39
@@ -591,7 +593,7 @@
40
 static int clear_buffers(struct impl *this, struct port *port)
41
 {
42
    if (port->n_buffers > 0) {
43
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
44
+       spa_log_debug(this->log, "%p: clear buffers", this);
45
        port->n_buffers = 0;
46
        this->started = false;
47
    }
48
@@ -739,7 +741,7 @@
49
    uint32_t i;
50
    int res = 0;
51
 
52
-   spa_log_trace(this->log, NAME" %p: process %d", this, this->n_in_ports);
53
+   spa_log_trace(this->log, "%p: process %d", this, this->n_in_ports);
54
 
55
    for (i = 0; i < this->n_in_ports; i++) {
56
        struct port *port = GET_IN_PORT(this, i);
57
@@ -758,7 +760,7 @@
58
            continue;
59
        }
60
 
61
-       spa_log_trace(this->log, NAME" %p: port %d: buffer %d", this, i, io->buffer_id);
62
+       spa_log_trace(this->log, "%p: port %d: buffer %d", this, i, io->buffer_id);
63
        b = &port->buffersio->buffer_id;
64
        src = &b->outbuf->datas0;
65
 
66
@@ -840,7 +842,7 @@
67
        sscanf(str, "pointer:%p", &this->client);
68
 
69
    if (this->client == NULL) {
70
-       spa_log_error(this->log, NAME" %p: missing "SPA_KEY_API_JACK_CLIENT
71
+       spa_log_error(this->log, "%p: missing "SPA_KEY_API_JACK_CLIENT
72
                " property", this);
73
        return -EINVAL;
74
    }
75
pipewire-1.0.1.tar.bz2/spa/plugins/jack/jack-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/jack/jack-source.c Changed
84
 
1
@@ -27,7 +27,9 @@
2
 
3
 #include "jack-client.h"
4
 
5
-#define NAME "jack-source"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.jack-source");
9
 
10
 #define MAX_PORTS 128
11
 #define MAX_BUFFERS 8
12
@@ -209,7 +211,7 @@
13
    struct buffer *b = &port->buffersid;
14
 
15
    if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
16
-       spa_log_trace(this->log, NAME " %p: reuse buffer %d", this, id);
17
+       spa_log_trace(this->log, "%p: reuse buffer %d", this, id);
18
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
19
        spa_list_append(&port->empty, &b->link);
20
    }
21
@@ -404,7 +406,7 @@
22
            NULL, JACK_DEFAULT_AUDIO_TYPE,
23
                         JackPortIsPhysical|JackPortIsOutput);
24
    if (ports == NULL) {
25
-       spa_log_error(this->log, NAME" %p: can't enumerate ports", this);
26
+       spa_log_error(this->log, "%p: can't enumerate ports", this);
27
        res = -ENODEV;
28
        goto exit;
29
    }
30
@@ -421,7 +423,7 @@
31
                jack_port_type(p),
32
                JackPortIsInput, 0);
33
        if (port->jack_port == NULL) {
34
-           spa_log_error(this->log, NAME" %p: jack_port_register() %d (%s) failed",
35
+           spa_log_error(this->log, "%p: jack_port_register() %d (%s) failed",
36
                    this, i, portsi);
37
            res = -EFAULT;
38
            goto exit_free;
39
@@ -452,7 +454,7 @@
40
    for (i = 0; portsi; i++) {
41
        struct port *port = GET_OUT_PORT(this, i);
42
        if (jack_connect(client, portsi, jack_port_name(port->jack_port))) {
43
-           spa_log_warn(this->log, NAME" %p: Failed to connect %s to %s",
44
+           spa_log_warn(this->log, "%p: Failed to connect %s to %s",
45
                    this, jack_port_name(port->jack_port), portsi);
46
             }
47
    }
48
@@ -591,7 +593,7 @@
49
 static int clear_buffers(struct impl *this, struct port *port)
50
 {
51
    if (port->n_buffers > 0) {
52
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
53
+       spa_log_debug(this->log, "%p: clear buffers", this);
54
        port->n_buffers = 0;
55
        spa_list_init(&port->empty);
56
        this->started = false;
57
@@ -753,7 +755,7 @@
58
    uint32_t i;
59
    int res = 0;
60
 
61
-   spa_log_trace(this->log, NAME" %p: process %d", this, this->n_out_ports);
62
+   spa_log_trace(this->log, "%p: process %d", this, this->n_out_ports);
63
 
64
    for (i = 0; i < this->n_out_ports; i++) {
65
        struct port *port = GET_OUT_PORT(this, i);
66
@@ -772,7 +774,7 @@
67
        }
68
 
69
        if ((b = dequeue_buffer(this, port)) == NULL) {
70
-           spa_log_trace(this->log, NAME" %p: out of buffers", this);
71
+           spa_log_trace(this->log, "%p: out of buffers", this);
72
            io->status = -EPIPE;
73
            continue;
74
        }
75
@@ -865,7 +867,7 @@
76
        sscanf(str, "pointer:%p", &this->client);
77
 
78
    if (this->client == NULL) {
79
-       spa_log_error(this->log, NAME" %p: missing "SPA_KEY_API_JACK_CLIENT
80
+       spa_log_error(this->log, "%p: missing "SPA_KEY_API_JACK_CLIENT
81
                " property", this);
82
        return -EINVAL;
83
    }
84
pipewire-1.0.1.tar.bz2/spa/plugins/jack/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/jack/plugin.c Changed
16
 
1
@@ -5,11 +5,14 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_jack_device_factory;
8
 extern const struct spa_handle_factory spa_jack_source_factory;
9
 extern const struct spa_handle_factory spa_jack_sink_factory;
10
 
11
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
12
+
13
 SPA_EXPORT
14
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
15
 {
16
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/libcamera-device.cpp -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/libcamera-device.cpp Changed
86
 
1
@@ -7,11 +7,6 @@
2
 #include "config.h"
3
 
4
 #include <stddef.h>
5
-#include <sys/types.h>
6
-#include <sys/stat.h>
7
-#include <fcntl.h>
8
-
9
-#include <sys/ioctl.h>
10
 
11
 #include <spa/support/plugin.h>
12
 #include <spa/support/log.h>
13
@@ -61,12 +56,10 @@
14
 static const libcamera::Span<const int64_t> cameraDevice(
15
            const Camera *camera)
16
 {
17
-#ifdef HAVE_LIBCAMERA_SYSTEM_DEVICES
18
    const ControlList &props = camera->properties();
19
 
20
    if (auto devices = props.get(properties::SystemDevices))
21
        return devices.value();
22
-#endif
23
 
24
    return {};
25
 }
26
@@ -99,6 +92,26 @@
27
    return nullptr;
28
 }
29
 
30
+static const char *cameraRot(const Camera *camera)
31
+{
32
+   const ControlList &props = camera->properties();
33
+
34
+   if (auto rotation = props.get(properties::Rotation)) {
35
+       switch (rotation.value()) {
36
+       case 90:
37
+           return "90";
38
+       case 180:
39
+           return "180";
40
+       case 270:
41
+           return "270";
42
+       default:
43
+           return "0";
44
+       }
45
+   }
46
+
47
+   return nullptr;
48
+}
49
+
50
 static int emit_info(struct impl *impl, bool full)
51
 {
52
    struct spa_dict_item items10;
53
@@ -122,6 +135,8 @@
54
 
55
    if (auto location = cameraLoc(impl->camera.get()))
56
        ADD_ITEM(SPA_KEY_API_LIBCAMERA_LOCATION, location);
57
+   if (auto rotation = cameraRot(impl->camera.get()))
58
+       ADD_ITEM(SPA_KEY_API_LIBCAMERA_ROTATION, rotation);
59
 
60
    const auto model = cameraModel(impl->camera.get());
61
    ADD_ITEM(SPA_KEY_DEVICE_PRODUCT_NAME, model.c_str());
62
@@ -135,10 +150,12 @@
63
    if (!device_numbers.empty()) {
64
        spa_strbuf_init(&buf, devices_str, sizeof(devices_str));
65
 
66
-       /* created a space separated string of all the device numbers */
67
-       for (int64_t device_number : device_numbers)
68
+       /* encode device numbers into a json array */
69
+       spa_strbuf_append(&buf, " ");
70
+       for(int64_t device_number : device_numbers)
71
            spa_strbuf_append(&buf, "%" PRId64 " ", device_number);
72
 
73
+       spa_strbuf_append(&buf, "");
74
        ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, devices_str);
75
    }
76
 
77
@@ -217,7 +234,7 @@
78
 }
79
 
80
 static const struct spa_device_methods impl_device = {
81
-   SPA_VERSION_DEVICE_METHODS,
82
+   .version = SPA_VERSION_DEVICE_METHODS,
83
    .add_listener = impl_add_listener,
84
    .sync = impl_sync,
85
    .enum_params = impl_enum_params,
86
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/libcamera-manager.cpp -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/libcamera-manager.cpp Changed
40
 
1
@@ -112,9 +112,9 @@
2
 
3
    if (impl->n_devices >= MAX_DEVICES)
4
        return NULL;
5
-   id = get_free_id(impl);;
6
+   id = get_free_id(impl);
7
    device = &impl->devicesid;
8
-   device->id = get_free_id(impl);;
9
+   device->id = id;
10
    device->camera = std::move(camera);
11
    impl->n_devices++;
12
    return device;
13
@@ -151,7 +151,6 @@
14
    struct spa_dict_item items20;
15
    struct spa_dict dict;
16
    uint32_t n_items = 0;
17
-   char path256;
18
 
19
    info = SPA_DEVICE_OBJECT_INFO_INIT();
20
 
21
@@ -165,8 +164,7 @@
22
    ADD_ITEM(SPA_KEY_DEVICE_ENUM_API,"libcamera.manager");
23
    ADD_ITEM(SPA_KEY_DEVICE_API, "libcamera");
24
    ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Video/Device");
25
-   snprintf(path, sizeof(path), "%s", device->camera->id().c_str());
26
-   ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, path);
27
+   ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, device->camera->id().c_str());
28
 #undef ADD_ITEM
29
 
30
    dict = SPA_DICT_INIT(items, n_items);
31
@@ -352,7 +350,7 @@
32
 }
33
 
34
 static const struct spa_device_methods impl_device = {
35
-   SPA_VERSION_DEVICE_METHODS,
36
+   .version = SPA_VERSION_DEVICE_METHODS,
37
    .add_listener = impl_device_add_listener,
38
 };
39
 
40
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/libcamera-source.cpp -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/libcamera-source.cpp Changed
82
 
1
@@ -60,14 +60,6 @@
2
    void *ptr;
3
 };
4
 
5
-#define MAX_CONTROLS   64
6
-
7
-struct control {
8
-   uint32_t id;
9
-   uint32_t ctrl_id;
10
-   double value;
11
-};
12
-
13
 struct port {
14
    struct impl *impl;
15
 
16
@@ -78,16 +70,14 @@
17
 
18
    uint32_t memtype = 0;
19
 
20
-   struct control controlsMAX_CONTROLS;
21
-   uint32_t n_controls = 0;
22
-
23
    struct buffer buffersMAX_BUFFERS;
24
    uint32_t n_buffers = 0;
25
    struct spa_list queue;
26
    struct spa_ringbuffer ring = SPA_RINGBUFFER_INIT();
27
    uint32_t ring_idsMAX_BUFFERS;
28
 
29
-   static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_PARAMS;
30
+   static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS |
31
+       SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS;
32
    struct spa_port_info info = SPA_PORT_INFO_INIT();
33
    struct spa_io_buffers *io = nullptr;
34
    struct spa_io_sequence *control = nullptr;
35
@@ -413,15 +403,14 @@
36
    return 0;
37
 }
38
 
39
-static const struct spa_dict_item info_items = {
40
-   { SPA_KEY_DEVICE_API, "libcamera" },
41
-   { SPA_KEY_MEDIA_CLASS, "Video/Source" },
42
-   { SPA_KEY_MEDIA_ROLE, "Camera" },
43
-   { SPA_KEY_NODE_DRIVER, "true" },
44
-};
45
-
46
 static void emit_node_info(struct impl *impl, bool full)
47
 {
48
+   static const struct spa_dict_item info_items = {
49
+       { SPA_KEY_DEVICE_API, "libcamera" },
50
+       { SPA_KEY_MEDIA_CLASS, "Video/Source" },
51
+       { SPA_KEY_MEDIA_ROLE, "Camera" },
52
+       { SPA_KEY_NODE_DRIVER, "true" },
53
+   };
54
    uint64_t old = full ? impl->info.change_mask : 0;
55
    if (full)
56
        impl->info.change_mask = impl->info_all;
57
@@ -435,10 +424,15 @@
58
 
59
 static void emit_port_info(struct impl *impl, struct port *port, bool full)
60
 {
61
+   static const struct spa_dict_item info_items = {
62
+       { SPA_KEY_PORT_GROUP, "stream.0" },
63
+   };
64
    uint64_t old = full ? port->info.change_mask : 0;
65
    if (full)
66
        port->info.change_mask = port->info_all;
67
    if (port->info.change_mask) {
68
+       struct spa_dict dict = SPA_DICT_INIT_ARRAY(info_items);
69
+       port->info.props = &dict;
70
        spa_node_emit_port_info(&impl->hooks,
71
                SPA_DIRECTION_OUTPUT, 0, &port->info);
72
        port->info.change_mask = old;
73
@@ -905,7 +899,7 @@
74
 }
75
 
76
 static const struct spa_node_methods impl_node = {
77
-   SPA_VERSION_NODE_METHODS,
78
+   .version = SPA_VERSION_NODE_METHODS,
79
    .add_listener = impl_node_add_listener,
80
    .set_callbacks = impl_node_set_callbacks,
81
    .sync = impl_node_sync,
82
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/libcamera-utils.cpp -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp Changed
107
 
1
@@ -165,16 +165,16 @@
2
    /* RGB formats */
3
    MAKE_FMT(formats::RGB565, RGB16, video, raw),
4
    MAKE_FMT(formats::RGB565_BE, RGB16, video, raw),
5
-   MAKE_FMT(formats::RGB888, RGB, video, raw),
6
-   MAKE_FMT(formats::BGR888, BGR, video, raw),
7
-   MAKE_FMT(formats::XRGB8888, xRGB, video, raw),
8
-   MAKE_FMT(formats::XBGR8888, xBGR, video, raw),
9
-   MAKE_FMT(formats::RGBX8888, RGBx, video, raw),
10
-   MAKE_FMT(formats::BGRX8888, BGRx, video, raw),
11
-   MAKE_FMT(formats::ARGB8888, ARGB, video, raw),
12
-   MAKE_FMT(formats::ABGR8888, ABGR, video, raw),
13
-   MAKE_FMT(formats::RGBA8888, RGBA, video, raw),
14
-   MAKE_FMT(formats::BGRA8888, BGRA, video, raw),
15
+   MAKE_FMT(formats::RGB888, BGR, video, raw),
16
+   MAKE_FMT(formats::BGR888, RGB, video, raw),
17
+   MAKE_FMT(formats::XRGB8888, BGRx, video, raw),
18
+   MAKE_FMT(formats::XBGR8888, RGBx, video, raw),
19
+   MAKE_FMT(formats::RGBX8888, xBGR, video, raw),
20
+   MAKE_FMT(formats::BGRX8888, xRGB, video, raw),
21
+   MAKE_FMT(formats::ARGB8888, BGRA, video, raw),
22
+   MAKE_FMT(formats::ABGR8888, RGBA, video, raw),
23
+   MAKE_FMT(formats::RGBA8888, ABGR, video, raw),
24
+   MAKE_FMT(formats::BGRA8888, ARGB, video, raw),
25
 
26
    MAKE_FMT(formats::YUYV, YUY2, video, raw),
27
    MAKE_FMT(formats::YVYU, YVYU, video, raw),
28
@@ -716,25 +716,23 @@
29
 }
30
 
31
 static const struct {
32
-   Transform libcamera_transform;
33
-   uint32_t spa_transform_value;
34
-} transform_map = {
35
-   { Transform::Identity, SPA_META_TRANSFORMATION_None },
36
-   { Transform::Rot0, SPA_META_TRANSFORMATION_None },
37
-   { Transform::HFlip, SPA_META_TRANSFORMATION_Flipped },
38
-   { Transform::VFlip, SPA_META_TRANSFORMATION_Flipped180 },
39
-   { Transform::HVFlip, SPA_META_TRANSFORMATION_180 },
40
-   { Transform::Rot180, SPA_META_TRANSFORMATION_180 },
41
-   { Transform::Transpose, SPA_META_TRANSFORMATION_Flipped90 },
42
-   { Transform::Rot90, SPA_META_TRANSFORMATION_90 },
43
-   { Transform::Rot270, SPA_META_TRANSFORMATION_270 },
44
-   { Transform::Rot180Transpose, SPA_META_TRANSFORMATION_Flipped270 },
45
+   Orientation libcamera_orientation; /* clockwise rotation then horizontal mirroring */
46
+   uint32_t spa_transform_value; /* horizontal mirroring then counter-clockwise rotation */
47
+} orientation_map = {
48
+   { Orientation::Rotate0, SPA_META_TRANSFORMATION_None },
49
+   { Orientation::Rotate0Mirror, SPA_META_TRANSFORMATION_Flipped },
50
+   { Orientation::Rotate90, SPA_META_TRANSFORMATION_270 },
51
+   { Orientation::Rotate90Mirror, SPA_META_TRANSFORMATION_Flipped90 },
52
+   { Orientation::Rotate180, SPA_META_TRANSFORMATION_180 },
53
+   { Orientation::Rotate180Mirror, SPA_META_TRANSFORMATION_Flipped180 },
54
+   { Orientation::Rotate270, SPA_META_TRANSFORMATION_90 },
55
+   { Orientation::Rotate270Mirror, SPA_META_TRANSFORMATION_Flipped270 },
56
 };
57
 
58
-static uint32_t libcamera_transform_to_spa_transform_value(Transform transform)
59
+static uint32_t libcamera_orientation_to_spa_transform_value(Orientation orientation)
60
 {
61
-   for (const auto& t : transform_map) {
62
-       if (t.libcamera_transform == transform)
63
+   for (const auto& t : orientation_map) {
64
+       if (t.libcamera_orientation == orientation)
65
            return t.spa_transform_value;
66
    }
67
    return SPA_META_TRANSFORMATION_None;
68
@@ -765,7 +763,7 @@
69
        } else if (d0.type & (1u << SPA_DATA_MemPtr)) {
70
            port->memtype = SPA_DATA_MemPtr;
71
        } else {
72
-           spa_log_error(impl->log, "v4l2: can't use buffers of type %d", d0.type);
73
+           spa_log_error(impl->log, "can't use buffers of type %d", d0.type);
74
            return -EINVAL;
75
        }
76
    }
77
@@ -788,9 +786,9 @@
78
            buffersi, SPA_META_VideoTransform, sizeof(*b->videotransform));
79
        if (b->videotransform) {
80
            b->videotransform->transform =
81
-               libcamera_transform_to_spa_transform_value(impl->config->transform);
82
-           spa_log_debug(impl->log, "Setting videotransform for buffer %d to %u (from %s)",
83
-               i, b->videotransform->transform, transformToString(impl->config->transform));
84
+               libcamera_orientation_to_spa_transform_value(impl->config->orientation);
85
+           spa_log_debug(impl->log, "Setting videotransform for buffer %u to %u",
86
+               i, b->videotransform->transform);
87
 
88
        }
89
 
90
@@ -807,6 +805,7 @@
91
 
92
            if (port->memtype == SPA_DATA_DmaBuf ||
93
                port->memtype == SPA_DATA_MemFd) {
94
+               dj.flags |= SPA_DATA_FLAG_MAPPABLE;
95
                dj.fd = bufsi->planes()j.fd.get();
96
                spa_log_debug(impl->log, "Got fd = %ld for buffer: #%d", dj.fd, i);
97
                dj.data = NULL;
98
@@ -921,7 +920,7 @@
99
    int res;
100
 
101
    if (!port->current_format) {
102
-       spa_log_error(impl->log, "Exting %s with -EIO", __FUNCTION__);
103
+       spa_log_error(impl->log, "Exiting %s with -EIO", __FUNCTION__);
104
        return -EIO;
105
    }
106
 
107
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/libcamera.c -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/libcamera.c Changed
12
 
1
@@ -9,7 +9,9 @@
2
 
3
 #include "libcamera.h"
4
 
5
-struct spa_log_topic libcamera_log_topic = SPA_LOG_TOPIC(0, "spa.libcamera");
6
+SPA_LOG_TOPIC_DEFINE(libcamera_log_topic, "spa.libcamera");
7
+
8
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
9
 
10
 SPA_EXPORT
11
 int spa_handle_factory_enum(const struct spa_handle_factory **factory,
12
pipewire-1.0.1.tar.bz2/spa/plugins/libcamera/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/libcamera/meson.build Changed
9
 
1
@@ -8,6 +8,6 @@
2
 libcameralib = shared_library('spa-libcamera',
3
   libcamera_sources,
4
   include_directories :  configinc ,
5
-  dependencies :  spa_dep, libudev_dep, libcamera_dep, pthread_lib  ,
6
+  dependencies :  spa_dep, libcamera_dep, pthread_lib  ,
7
   install : true,
8
   install_dir : spa_plugindir / 'libcamera')
9
pipewire-1.0.1.tar.bz2/spa/plugins/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/meson.build Changed
14
 
1
@@ -45,10 +45,9 @@
2
 endif
3
 
4
 v4l2_header_found = cc.has_header('linux/videodev2.h', required: get_option('v4l2'))
5
-v4l2_supported = libudev_dep.found() and v4l2_header_found
6
 summary({'V4L2 kernel header': v4l2_header_found}, bool_yn: true, section: 'Backend')
7
-summary({'V4L2 enabled': v4l2_supported}, bool_yn: true, section: 'Backend')
8
-if v4l2_supported
9
+summary({'V4L2 enabled': v4l2_header_found}, bool_yn: true, section: 'Backend')
10
+if v4l2_header_found
11
   subdir('v4l2')
12
 endif
13
 if libcamera_dep.found()
14
pipewire-1.0.1.tar.bz2/spa/plugins/support/cpu.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/cpu.c Changed
11
 
1
@@ -22,7 +22,8 @@
2
 #include <spa/utils/names.h>
3
 #include <spa/utils/string.h>
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.cpu");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.cpu");
7
+
8
 #undef SPA_LOG_TOPIC_DEFAULT
9
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
10
 
11
pipewire-1.0.1.tar.bz2/spa/plugins/support/dbus.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/dbus.c Changed
20
 
1
@@ -20,7 +20,8 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/support/dbus.h>
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.dbus");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.dbus");
7
+
8
 #undef SPA_LOG_TOPIC_DEFAULT
9
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
10
 
11
@@ -557,6 +558,8 @@
12
    impl_enum_interface_info,
13
 };
14
 
15
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
16
+
17
 SPA_EXPORT
18
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
19
 {
20
pipewire-1.0.1.tar.bz2/spa/plugins/support/evl-plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/evl-plugin.c Changed
14
 
1
@@ -6,9 +6,12 @@
2
 #include <stdio.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_support_evl_system_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/support/evl-system.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/evl-system.c Changed
26
 
1
@@ -24,7 +24,9 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/utils/string.h>
4
 
5
-#define NAME "evl-system"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.evl-system");
9
 
10
 #define MAX_POLL   512
11
 
12
@@ -465,11 +467,11 @@
13
    impl->pid = getpid();
14
 
15
    if ((res = evl_init()) < 0) {
16
-       spa_log_error(impl->log, NAME " %p: init failed: %s", impl, spa_strerror(res));
17
+       spa_log_error(impl->log, "%p: init failed: %s", impl, spa_strerror(res));
18
        return res;
19
    }
20
 
21
-   spa_log_info(impl->log, NAME " %p: initialized", impl);
22
+   spa_log_info(impl->log, "%p: initialized", impl);
23
 
24
    return 0;
25
 }
26
pipewire-1.0.1.tar.bz2/spa/plugins/support/journal.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/journal.c Changed
136
 
1
@@ -21,9 +21,9 @@
2
 
3
 #include <systemd/sd-journal.h>
4
 
5
-#include "log-patterns.h"
6
-
7
-#define NAME "journal"
8
+#undef SPA_LOG_TOPIC_DEFAULT
9
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
10
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.journal");
11
 
12
 #define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_INFO
13
 
14
@@ -33,8 +33,6 @@
15
 
16
    /* if non-null, we'll additionally forward all logging to there */
17
    struct spa_log *chain_log;
18
-
19
-   struct spa_list patterns;
20
 };
21
 
22
 static SPA_PRINTF_FUNC(7,0) void
23
@@ -47,6 +45,7 @@
24
          const char *fmt,
25
          va_list args)
26
 {
27
+   static const char * const levels = { "-", "E", "W", "I", "D", "T", "*T*" };
28
    struct impl *impl = object;
29
    char line_buffer32;
30
    char file_bufferstrlen("CODE_FILE=") + strlen(file) + 1;
31
@@ -57,6 +56,7 @@
32
    if (impl->chain_log != NULL) {
33
        va_list args_copy;
34
        va_copy(args_copy, args);
35
+       impl->chain_log->level = impl->log.level;
36
        spa_log_logtv(impl->chain_log,
37
                  level, topic,
38
                  file, line, func, fmt, args_copy);
39
@@ -81,9 +81,26 @@
40
        break;
41
    }
42
 
43
-   if (topic)
44
+   if (spa_log_level_topic_enabled(&impl->log, topic, SPA_LOG_LEVEL_DEBUG)) {
45
+       const char *lev = levelsSPA_CLAMP(level, 0u, SPA_N_ELEMENTS(levels) - 1u);
46
+       const char *tp = topic ? topic->topic : "";
47
+
48
+       if (file && func) {
49
+           const char *f = strrchr(file, '/');
50
+
51
+           f = f ? f+1 : file;
52
+           sz = spa_scnprintf(message_buffer, sizeof(message_buffer),
53
+                   "%s %s%s%s:%d:%s: ",
54
+                   lev, tp, topic ? " " : "",
55
+                   f, line, func);
56
+       } else {
57
+           sz = spa_scnprintf(message_buffer, sizeof(message_buffer),
58
+                   "%s %s%s", lev, tp, topic ? ": " : "");
59
+       }
60
+   } else if (topic) {
61
        sz = spa_scnprintf(message_buffer, sizeof(message_buffer),
62
-                  "%s: ", topic->topic);
63
+               "%s: ", topic->topic);
64
+   }
65
 
66
    /* we'll be using the low-level journal API, which expects us to provide
67
     * the location explicitly. line and file are to be passed as preformatted
68
@@ -143,22 +160,12 @@
69
    va_end(args);
70
 }
71
 
72
-static void
73
-impl_log_topic_init(void *object, struct spa_log_topic *t)
74
-{
75
-   struct impl *impl = object;
76
-   enum spa_log_level level = impl->log.level;
77
-
78
-   support_log_topic_init(&impl->patterns, level, t);
79
-}
80
-
81
 static const struct spa_log_methods impl_log = {
82
    SPA_VERSION_LOG_METHODS,
83
    .log = impl_log_log,
84
    .logv = impl_log_logv,
85
    .logt = impl_log_logt,
86
    .logtv = impl_log_logtv,
87
-   .topic_init = impl_log_topic_init,
88
 };
89
 
90
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
91
@@ -180,12 +187,11 @@
92
 
93
 static int impl_clear(struct spa_handle *handle)
94
 {
95
-   struct impl *this;
96
+   struct impl SPA_UNUSED *this;
97
 
98
    spa_return_val_if_fail(handle != NULL, -EINVAL);
99
 
100
    this = (struct impl *) handle;
101
-   support_log_free_patterns(&this->patterns);
102
 
103
    return 0;
104
 }
105
@@ -251,13 +257,9 @@
106
            &impl_log, impl);
107
    impl->log.level = DEFAULT_LOG_LEVEL;
108
 
109
-   spa_list_init(&impl->patterns);
110
-
111
    if (info) {
112
        if ((str = spa_dict_lookup(info, SPA_KEY_LOG_LEVEL)) != NULL)
113
            impl->log.level = atoi(str);
114
-       if ((str = spa_dict_lookup(info, SPA_KEY_LOG_PATTERNS)) != NULL)
115
-           support_log_parse_patterns(&impl->patterns, str);
116
    }
117
 
118
    /* if our stderr goes to the journal, there's no point in logging both
119
@@ -268,7 +270,7 @@
120
    else
121
        impl->chain_log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
122
 
123
-   spa_log_debug(&impl->log, NAME " %p: initialized", impl);
124
+   spa_log_debug(&impl->log, "%p: initialized", impl);
125
 
126
    return 0;
127
 }
128
@@ -307,6 +309,7 @@
129
    .enum_interface_info = impl_enum_interface_info,
130
 };
131
 
132
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
133
 
134
 SPA_EXPORT
135
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
136
pipewire-1.0.1.tar.bz2/spa/plugins/support/logger.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/logger.c Changed
103
 
1
@@ -20,13 +20,13 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/ansi.h>
4
 
5
-#include "log-patterns.h"
6
-
7
 #if defined(__FreeBSD__) || defined(__MidnightBSD__)
8
 #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
9
 #endif
10
 
11
-#define NAME "logger"
12
+#undef SPA_LOG_TOPIC_DEFAULT
13
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
14
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.logger");
15
 
16
 #define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_INFO
17
 
18
@@ -48,8 +48,6 @@
19
    unsigned int colors:1;
20
    unsigned int timestamp:1;
21
    unsigned int line:1;
22
-
23
-   struct spa_list patterns;
24
 };
25
 
26
 static SPA_PRINTF_FUNC(7,0) void
27
@@ -94,8 +92,8 @@
28
    if (impl->timestamp) {
29
        struct timespec now;
30
        clock_gettime(CLOCK_MONOTONIC_RAW, &now);
31
-       spa_scnprintf(timestamp, sizeof(timestamp), "%05lu.%06lu",
32
-           (now.tv_sec & 0x1FFFFFFF) % 100000, now.tv_nsec / 1000);
33
+       spa_scnprintf(timestamp, sizeof(timestamp), "%05jd.%06jd",
34
+           (intmax_t) (now.tv_sec & 0x1FFFFFFF) % 100000, (intmax_t) now.tv_nsec / 1000);
35
    }
36
 
37
    if (topic && topic->topic)
38
@@ -221,22 +219,12 @@
39
         }
40
 }
41
 
42
-static void
43
-impl_log_topic_init(void *object, struct spa_log_topic *t)
44
-{
45
-   struct impl *impl = object;
46
-   enum spa_log_level level = impl->log.level;
47
-
48
-   support_log_topic_init(&impl->patterns, level, t);
49
-}
50
-
51
 static const struct spa_log_methods impl_log = {
52
    SPA_VERSION_LOG_METHODS,
53
    .log = impl_log_log,
54
    .logv = impl_log_logv,
55
    .logt = impl_log_logt,
56
    .logtv = impl_log_logtv,
57
-   .topic_init = impl_log_topic_init,
58
 };
59
 
60
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
61
@@ -264,8 +252,6 @@
62
 
63
    this = (struct impl *) handle;
64
 
65
-   support_log_free_patterns(&this->patterns);
66
-
67
    if (this->close_file && this->file != NULL)
68
        fclose(this->file);
69
 
70
@@ -313,7 +299,6 @@
71
 
72
    loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
73
    this->system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
74
-   spa_list_init(&this->patterns);
75
 
76
    if (loop != NULL && this->system != NULL) {
77
        this->source.func = on_trace_event;
78
@@ -358,8 +343,6 @@
79
                    this->close_file = true;
80
            }
81
        }
82
-       if ((str = spa_dict_lookup(info, SPA_KEY_LOG_PATTERNS)) != NULL)
83
-           support_log_parse_patterns(&this->patterns, str);
84
    }
85
    if (this->file == NULL) {
86
        this->file = stderr;
87
@@ -370,13 +353,13 @@
88
    if (linebuf)
89
        setlinebuf(this->file);
90
 
91
-   if (!isatty(fileno(this->file)) && !force_colors) {
92
+   if (this->colors && !force_colors && !isatty(fileno(this->file)) ) {
93
        this->colors = false;
94
    }
95
 
96
    spa_ringbuffer_init(&this->trace_rb);
97
 
98
-   spa_log_debug(&this->log, NAME " %p: initialized to %s linebuf:%u", this, dest, linebuf);
99
+   spa_log_debug(&this->log, "%p: initialized to %s linebuf:%u", this, dest, linebuf);
100
 
101
    return 0;
102
 }
103
pipewire-1.0.1.tar.bz2/spa/plugins/support/loop.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/loop.c Changed
531
 
1
@@ -9,12 +9,15 @@
2
 #include <stdlib.h>
3
 #include <stdio.h>
4
 #include <pthread.h>
5
+#include <threads.h>
6
 
7
 #include <spa/support/loop.h>
8
 #include <spa/support/system.h>
9
 #include <spa/support/log.h>
10
 #include <spa/support/plugin.h>
11
 #include <spa/utils/list.h>
12
+#include <spa/utils/cleanup.h>
13
+#include <spa/utils/atomic.h>
14
 #include <spa/utils/names.h>
15
 #include <spa/utils/ratelimit.h>
16
 #include <spa/utils/result.h>
17
@@ -22,14 +25,17 @@
18
 #include <spa/utils/ringbuffer.h>
19
 #include <spa/utils/string.h>
20
 
21
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.loop");
22
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.loop");
23
+
24
 #undef SPA_LOG_TOPIC_DEFAULT
25
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
26
 
27
+
28
 #define MAX_ALIGN  8
29
 #define ITEM_ALIGN 8
30
 #define DATAS_SIZE (4096*8)
31
 #define MAX_EP     32
32
+#define DEFAULT_RETRY  (1 * SPA_USEC_PER_SEC)
33
 
34
 /** \cond */
35
 
36
@@ -37,6 +43,7 @@
37
    size_t item_size;
38
    spa_invoke_func_t func;
39
    uint32_t seq;
40
+   uint32_t count;
41
    void *data;
42
    size_t size;
43
    bool block;
44
@@ -57,22 +64,34 @@
45
 
46
    struct spa_list source_list;
47
    struct spa_list destroy_list;
48
+   struct spa_list queue_list;
49
    struct spa_hook_list hooks_list;
50
+   int retry_timeout;
51
 
52
    int poll_fd;
53
    pthread_t thread;
54
    int enter_count;
55
 
56
    struct spa_source *wakeup;
57
+
58
+   tss_t queue_tss_id;
59
+   pthread_mutex_t queue_lock;
60
+   uint32_t count;
61
+   uint32_t flush_count;
62
+
63
+   unsigned int polling:1;
64
+};
65
+
66
+struct queue {
67
+   struct impl *impl;
68
+   struct spa_list link;
69
+
70
    int ack_fd;
71
    struct spa_ratelimit rate_limit;
72
 
73
    struct spa_ringbuffer buffer;
74
    uint8_t *buffer_data;
75
    uint8_t buffer_memDATAS_SIZE + MAX_ALIGN;
76
-
77
-   uint32_t flush_count;
78
-   unsigned int polling:1;
79
 };
80
 
81
 struct source_impl {
82
@@ -153,29 +172,46 @@
83
    return res;
84
 }
85
 
86
-static void flush_items(struct impl *impl)
87
+static inline int32_t item_compare(struct invoke_item *a, struct invoke_item *b)
88
+{
89
+   return (int32_t)(a->count - b->count);
90
+}
91
+
92
+static void flush_all_queues(struct impl *impl)
93
 {
94
-   uint32_t index, flush_count;
95
-   int32_t avail;
96
+   uint32_t flush_count;
97
    int res;
98
 
99
+   pthread_mutex_lock(&impl->queue_lock);
100
    flush_count = ++impl->flush_count;
101
-   avail = spa_ringbuffer_get_read_index(&impl->buffer, &index);
102
-   while (avail > 0) {
103
-       struct invoke_item *item;
104
-       bool block;
105
+   while (true) {
106
+       struct queue *cqueue, *queue;
107
+       struct invoke_item *citem, *item = NULL;
108
+       uint32_t cindex, index;
109
        spa_invoke_func_t func;
110
+       bool block;
111
 
112
-       item = SPA_PTROFF(impl->buffer_data, index & (DATAS_SIZE - 1), struct invoke_item);
113
-       block = item->block;
114
-       func = item->func;
115
+       spa_list_for_each(cqueue, &impl->queue_list, link) {
116
+           if (spa_ringbuffer_get_read_index(&cqueue->buffer, &cindex) <
117
+                   (int32_t)sizeof(struct invoke_item))
118
+               continue;
119
+           citem = SPA_PTROFF(cqueue->buffer_data, cindex & (DATAS_SIZE - 1), struct invoke_item);
120
+
121
+           if (item == NULL || item_compare(citem, item) < 0) {
122
+               item = citem;
123
+               queue = cqueue;
124
+               index = cindex;
125
+           }
126
+       }
127
+       if (item == NULL)
128
+           break;
129
 
130
-       spa_log_trace_fp(impl->log, "%p: flush item %p", impl, item);
131
+       spa_log_trace_fp(impl->log, "%p: flush item %p", queue, item);
132
        /* first we remove the function from the item so that recursive
133
         * calls don't call the callback again. We can't update the
134
         * read index before we call the function because then the item
135
         * might get overwritten. */
136
-       item->func = NULL;
137
+       func = spa_steal_ptr(item->func);
138
        if (func)
139
            item->res = func(&impl->loop, true, item->seq, item->data,
140
                item->size, item->user_data);
141
@@ -186,35 +222,20 @@
142
            break;
143
 
144
        index += item->item_size;
145
-       avail -= item->item_size;
146
-       spa_ringbuffer_read_update(&impl->buffer, index);
147
+       block = item->block;
148
+       spa_ringbuffer_read_update(&queue->buffer, index);
149
 
150
        if (block) {
151
-           if ((res = spa_system_eventfd_write(impl->system, impl->ack_fd, 1)) < 0)
152
+           if ((res = spa_system_eventfd_write(impl->system, queue->ack_fd, 1)) < 0)
153
                spa_log_warn(impl->log, "%p: failed to write event fd:%d: %s",
154
-                       impl, impl->ack_fd, spa_strerror(res));
155
+                       queue, queue->ack_fd, spa_strerror(res));
156
        }
157
    }
158
+   pthread_mutex_unlock(&impl->queue_lock);
159
 }
160
 
161
 static int
162
-loop_invoke_inthread(struct impl *impl,
163
-       spa_invoke_func_t func,
164
-       uint32_t seq,
165
-       const void *data,
166
-       size_t size,
167
-       bool block,
168
-       void *user_data)
169
-{
170
-   /* we should probably have a second ringbuffer for the in-thread pending
171
-    * callbacks. A recursive callback when flushing will insert itself
172
-    * before this one. */
173
-   flush_items(impl);
174
-   return func ? func(&impl->loop, true, seq, data, size, user_data) : 0;
175
-}
176
-
177
-static int
178
-loop_invoke(void *object,
179
+loop_queue_invoke(void *object,
180
        spa_invoke_func_t func,
181
        uint32_t seq,
182
        const void *data,
183
@@ -222,27 +243,25 @@
184
        bool block,
185
        void *user_data)
186
 {
187
-   struct impl *impl = object;
188
+   struct queue *queue = object;
189
+   struct impl *impl = queue->impl;
190
    struct invoke_item *item;
191
-   int res;
192
+   int res, suppressed;
193
    int32_t filled;
194
    uint32_t avail, idx, offset, l0;
195
+   size_t need;
196
+   uint64_t nsec;
197
+   bool in_thread;
198
 
199
-   /* the ringbuffer can only be written to from one thread, if we are
200
-    * in the same thread as the loop, don't write into the ringbuffer
201
-    * but try to emit the calback right away after flushing what we have */
202
-   if (impl->thread == 0 || pthread_equal(impl->thread, pthread_self()))
203
-       return loop_invoke_inthread(impl, func, seq, data, size, block, user_data);
204
+   in_thread = (impl->thread == 0 || pthread_equal(impl->thread, pthread_self()));
205
 
206
-   filled = spa_ringbuffer_get_write_index(&impl->buffer, &idx);
207
-   if (filled < 0 || filled > DATAS_SIZE) {
208
-       spa_log_warn(impl->log, "%p: queue xrun %d", impl, filled);
209
-       return -EPIPE;
210
-   }
211
-   avail = DATAS_SIZE - filled;
212
+retry:
213
+   filled = spa_ringbuffer_get_write_index(&queue->buffer, &idx);
214
+   spa_assert_se(filled >= 0 && filled <= DATAS_SIZE && "queue xrun");
215
+   avail = (uint32_t)(DATAS_SIZE - filled);
216
    if (avail < sizeof(struct invoke_item)) {
217
-       spa_log_warn(impl->log, "%p: queue full %d", impl, avail);
218
-       return -EPIPE;
219
+       need = sizeof(struct invoke_item);
220
+       goto xrun;
221
    }
222
    offset = idx & (DATAS_SIZE - 1);
223
 
224
@@ -250,16 +269,17 @@
225
     * invoke_item, see below */
226
    l0 = DATAS_SIZE - offset;
227
 
228
-   item = SPA_PTROFF(impl->buffer_data, offset, struct invoke_item);
229
+   item = SPA_PTROFF(queue->buffer_data, offset, struct invoke_item);
230
    item->func = func;
231
    item->seq = seq;
232
+   item->count = SPA_ATOMIC_INC(impl->count);
233
    item->size = size;
234
-   item->block = block;
235
+   item->block = in_thread ? false : block;
236
    item->user_data = user_data;
237
    item->res = 0;
238
    item->item_size = SPA_ROUND_UP_N(sizeof(struct invoke_item) + size, ITEM_ALIGN);
239
 
240
-   spa_log_trace_fp(impl->log, "%p: add item %p filled:%d", impl, item, filled);
241
+   spa_log_trace_fp(impl->log, "%p: add item %p filled:%d", queue, item, filled);
242
 
243
    if (l0 >= item->item_size) {
244
        /* item + size fit in current ringbuffer idx */
245
@@ -272,51 +292,130 @@
246
    } else {
247
        /* item does not fit, place the invoke_item at idx and start the
248
         * data at the start of the ringbuffer */
249
-       item->data = impl->buffer_data;
250
+       item->data = queue->buffer_data;
251
        item->item_size = SPA_ROUND_UP_N(l0 + size, ITEM_ALIGN);
252
    }
253
    if (avail < item->item_size) {
254
-       int suppressed;
255
-       uint64_t nsec = get_time_ns(impl->system);
256
-       if ((suppressed = spa_ratelimit_test(&impl->rate_limit, nsec)) >= 0) {
257
-           spa_log_warn(impl->log, "%p: queue full %d, need %zd (%d suppressed)",
258
-                   impl, avail, item->item_size, suppressed);
259
-       }
260
-       return -EPIPE;
261
+       need = item->item_size;
262
+       goto xrun;
263
    }
264
    if (data && size > 0)
265
        memcpy(item->data, data, size);
266
 
267
-   spa_ringbuffer_write_update(&impl->buffer, idx + item->item_size);
268
+   spa_ringbuffer_write_update(&queue->buffer, idx + item->item_size);
269
 
270
-   loop_signal_event(impl, impl->wakeup);
271
+   if (in_thread) {
272
+       flush_all_queues(impl);
273
+       res = item->res;
274
+   } else {
275
+       loop_signal_event(impl, impl->wakeup);
276
 
277
-   if (block) {
278
-       uint64_t count = 1;
279
+       if (block) {
280
+           uint64_t count = 1;
281
 
282
-       spa_loop_control_hook_before(&impl->hooks_list);
283
+           spa_loop_control_hook_before(&impl->hooks_list);
284
 
285
-       if ((res = spa_system_eventfd_read(impl->system, impl->ack_fd, &count)) < 0)
286
-           spa_log_warn(impl->log, "%p: failed to read event fd:%d: %s",
287
-                   impl, impl->ack_fd, spa_strerror(res));
288
+           if ((res = spa_system_eventfd_read(impl->system, queue->ack_fd, &count)) < 0)
289
+               spa_log_warn(impl->log, "%p: failed to read event fd:%d: %s",
290
+                       queue, queue->ack_fd, spa_strerror(res));
291
 
292
-       spa_loop_control_hook_after(&impl->hooks_list);
293
+           spa_loop_control_hook_after(&impl->hooks_list);
294
 
295
-       res = item->res;
296
-   }
297
-   else {
298
-       if (seq != SPA_ID_INVALID)
299
-           res = SPA_RESULT_RETURN_ASYNC(seq);
300
-       else
301
-           res = 0;
302
+           res = item->res;
303
+       }
304
+       else {
305
+           if (seq != SPA_ID_INVALID)
306
+               res = SPA_RESULT_RETURN_ASYNC(seq);
307
+           else
308
+               res = 0;
309
+       }
310
    }
311
    return res;
312
+
313
+xrun:
314
+   nsec = get_time_ns(impl->system);
315
+   if ((suppressed = spa_ratelimit_test(&queue->rate_limit, nsec)) >= 0) {
316
+       spa_log_warn(impl->log, "%p: queue full %d, need %zd (%d suppressed)",
317
+               queue, avail, need, suppressed);
318
+   }
319
+   if (impl->retry_timeout == 0)
320
+       return -EPIPE;
321
+   usleep(impl->retry_timeout);
322
+   goto retry;
323
 }
324
 
325
 static void wakeup_func(void *data, uint64_t count)
326
 {
327
    struct impl *impl = data;
328
-   flush_items(impl);
329
+   flush_all_queues(impl);
330
+}
331
+
332
+static void loop_queue_destroy(void *data)
333
+{
334
+   struct queue *queue = data;
335
+   struct impl *impl = queue->impl;
336
+
337
+   pthread_mutex_lock(&impl->queue_lock);
338
+   spa_list_remove(&queue->link);
339
+   pthread_mutex_unlock(&impl->queue_lock);
340
+
341
+   spa_system_close(impl->system, queue->ack_fd);
342
+   free(queue);
343
+}
344
+
345
+static struct queue *loop_create_queue(void *object, uint32_t flags)
346
+{
347
+   struct impl *impl = object;
348
+   struct queue *queue;
349
+   int res;
350
+
351
+   queue = calloc(1, sizeof(struct queue));
352
+   if (queue == NULL)
353
+       return NULL;
354
+
355
+   queue->impl = impl;
356
+
357
+   queue->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
358
+   queue->rate_limit.burst = 1;
359
+
360
+   queue->buffer_data = SPA_PTR_ALIGN(queue->buffer_mem, MAX_ALIGN, uint8_t);
361
+   spa_ringbuffer_init(&queue->buffer);
362
+
363
+   if ((res = spa_system_eventfd_create(impl->system,
364
+           SPA_FD_EVENT_SEMAPHORE | SPA_FD_CLOEXEC)) < 0) {
365
+       spa_log_error(impl->log, "%p: can't create ack event: %s",
366
+               impl, spa_strerror(res));
367
+       goto error;
368
+   }
369
+   queue->ack_fd = res;
370
+
371
+   pthread_mutex_lock(&impl->queue_lock);
372
+   spa_list_append(&impl->queue_list, &queue->link);
373
+   pthread_mutex_unlock(&impl->queue_lock);
374
+
375
+   spa_log_info(impl->log, "%p created queue %p", impl, queue);
376
+
377
+   return queue;
378
+
379
+error:
380
+   free(queue);
381
+   errno = -res;
382
+   return NULL;
383
+}
384
+
385
+static int loop_invoke(void *object, spa_invoke_func_t func, uint32_t seq,
386
+       const void *data, size_t size, bool block, void *user_data)
387
+{
388
+   struct impl *impl = object;
389
+   struct queue *local_queue = tss_get(impl->queue_tss_id);
390
+
391
+   if (local_queue == NULL) {
392
+       local_queue = loop_create_queue(impl, 0);
393
+       if (local_queue == NULL)
394
+           return -errno;
395
+       tss_set(impl->queue_tss_id, local_queue);
396
+   }
397
+   return loop_queue_invoke(local_queue, func, seq, data, size, block, user_data);
398
 }
399
 
400
 static int loop_get_fd(void *object)
401
@@ -366,7 +465,7 @@
402
 
403
    if (--impl->enter_count == 0) {
404
        impl->thread = 0;
405
-       flush_items(impl);
406
+       flush_all_queues(impl);
407
        impl->polling = false;
408
    }
409
 }
410
@@ -946,6 +1045,7 @@
411
 {
412
    struct impl *impl;
413
    struct source_impl *source;
414
+   struct queue *queue;
415
 
416
    spa_return_val_if_fail(handle != NULL, -EINVAL);
417
 
418
@@ -957,9 +1057,12 @@
419
 
420
    spa_list_consume(source, &impl->source_list, link)
421
        loop_destroy_source(impl, &source->source);
422
+   spa_list_consume(queue, &impl->queue_list, link)
423
+       loop_queue_destroy(queue);
424
 
425
-   spa_system_close(impl->system, impl->ack_fd);
426
    spa_system_close(impl->system, impl->poll_fd);
427
+   pthread_mutex_destroy(&impl->queue_lock);
428
+   tss_delete(impl->queue_tss_id);
429
 
430
    return 0;
431
 }
432
@@ -971,6 +1074,15 @@
433
    return sizeof(struct impl);
434
 }
435
 
436
+#define CHECK(expression,label)                        \
437
+do {                                   \
438
+   if ((errno = (expression)) != 0) {              \
439
+       res = -errno;                       \
440
+       spa_log_error(impl->log, #expression ": %s", strerror(errno));  \
441
+       goto label;                     \
442
+   }                               \
443
+} while(false);
444
+
445
 static int
446
 impl_init(const struct spa_handle_factory *factory,
447
      struct spa_handle *handle,
448
@@ -980,6 +1092,7 @@
449
 {
450
    struct impl *impl;
451
    const char *str;
452
+   pthread_mutexattr_t attr;
453
    int res;
454
 
455
    spa_return_val_if_fail(factory != NULL, -EINVAL);
456
@@ -1002,12 +1115,19 @@
457
            SPA_VERSION_LOOP_UTILS,
458
            &impl_loop_utils, impl);
459
 
460
+   impl->retry_timeout = DEFAULT_RETRY;
461
    if (info) {
462
        if ((str = spa_dict_lookup(info, "loop.cancel")) != NULL &&
463
            spa_atob(str))
464
            impl->control.iface.cb.funcs = &impl_loop_control_cancel;
465
+       if ((str = spa_dict_lookup(info, "loop.retry-timeout")) != NULL)
466
+           impl->retry_timeout = atoi(str);
467
    }
468
 
469
+   CHECK(pthread_mutexattr_init(&attr), error_exit);
470
+   CHECK(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), error_exit);
471
+   CHECK(pthread_mutex_init(&impl->queue_lock, &attr), error_exit);
472
+
473
    impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
474
    spa_log_topic_init(impl->log, &log_topic);
475
    impl->system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
476
@@ -1015,38 +1135,32 @@
477
    if (impl->system == NULL) {
478
        spa_log_error(impl->log, "%p: a System is needed", impl);
479
        res = -EINVAL;
480
-       goto error_exit;
481
+       goto error_exit_free_mutex;
482
    }
483
-   impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
484
-   impl->rate_limit.burst = 1;
485
-
486
    if ((res = spa_system_pollfd_create(impl->system, SPA_FD_CLOEXEC)) < 0) {
487
        spa_log_error(impl->log, "%p: can't create pollfd: %s",
488
                impl, spa_strerror(res));
489
-       goto error_exit;
490
+       goto error_exit_free_mutex;
491
    }
492
    impl->poll_fd = res;
493
 
494
    spa_list_init(&impl->source_list);
495
+   spa_list_init(&impl->queue_list);
496
    spa_list_init(&impl->destroy_list);
497
    spa_hook_list_init(&impl->hooks_list);
498
 
499
-   impl->buffer_data = SPA_PTR_ALIGN(impl->buffer_mem, MAX_ALIGN, uint8_t);
500
-   spa_ringbuffer_init(&impl->buffer);
501
-
502
    impl->wakeup = loop_add_event(impl, wakeup_func, impl);
503
    if (impl->wakeup == NULL) {
504
        res = -errno;
505
        spa_log_error(impl->log, "%p: can't create wakeup event: %m", impl);
506
        goto error_exit_free_poll;
507
    }
508
-   if ((res = spa_system_eventfd_create(impl->system,
509
-           SPA_FD_EVENT_SEMAPHORE | SPA_FD_CLOEXEC)) < 0) {
510
-       spa_log_error(impl->log, "%p: can't create ack event: %s",
511
-               impl, spa_strerror(res));
512
+
513
+   if (tss_create(&impl->queue_tss_id, (tss_dtor_t)loop_queue_destroy) != 0) {
514
+       res = -errno;
515
+       spa_log_error(impl->log, "%p: can't create tss: %m", impl);
516
        goto error_exit_free_wakeup;
517
    }
518
-   impl->ack_fd = res;
519
 
520
    spa_log_debug(impl->log, "%p: initialized", impl);
521
 
522
@@ -1056,6 +1170,8 @@
523
    loop_destroy_source(impl, impl->wakeup);
524
 error_exit_free_poll:
525
    spa_system_close(impl->system, impl->poll_fd);
526
+error_exit_free_mutex:
527
+   pthread_mutex_destroy(&impl->queue_lock);
528
 error_exit:
529
    return res;
530
 }
531
pipewire-1.0.1.tar.bz2/spa/plugins/support/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/support/meson.build Changed
17
 
1
@@ -1,7 +1,6 @@
2
 spa_support_sources = 
3
   'cpu.c',
4
   'logger.c',
5
-  'log-patterns.c',
6
   'loop.c',
7
   'node-driver.c',
8
   'null-audio-sink.c',
9
@@ -56,7 +55,6 @@
10
 if systemd_dep.found()
11
   spa_journal_sources = 
12
     'journal.c',
13
-    'log-patterns.c',
14
   
15
 
16
   spa_journal_lib = shared_library('spa-journal',
17
pipewire-1.0.1.tar.bz2/spa/plugins/support/node-driver.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/node-driver.c Changed
325
 
1
@@ -8,7 +8,7 @@
2
 #include <string.h>
3
 #include <stdio.h>
4
 #include <fcntl.h>
5
-#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
6
+#ifdef __linux__
7
 #include <linux/ethtool.h>
8
 #include <linux/sockios.h>
9
 #endif
10
@@ -27,12 +27,19 @@
11
 #include <spa/node/utils.h>
12
 #include <spa/param/param.h>
13
 
14
-#define NAME "driver"
15
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.driver");
16
+
17
+#undef SPA_LOG_TOPIC_DEFAULT
18
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
19
 
20
 #define DEFAULT_FREEWHEEL  false
21
 #define DEFAULT_FREEWHEEL_WAIT 10
22
 #define DEFAULT_CLOCK_PREFIX   "clock.system"
23
 #define DEFAULT_CLOCK_ID   CLOCK_MONOTONIC
24
+#define DEFAULT_RESYNC_MS  10
25
+
26
+#define CLOCK_OFFSET_NAVG  20
27
+#define CLOCK_OFFSET_MAX_ERR   (50 * SPA_NSEC_PER_USEC)
28
 
29
 #define CLOCKFD 3
30
 #define FD_TO_CLOCKID(fd)  ((~(clockid_t) (fd) << 3) | CLOCKFD)
31
@@ -46,6 +53,12 @@
32
    char clock_name64;
33
    clockid_t clock_id;
34
    uint32_t freewheel_wait;
35
+   float resync_ms;
36
+};
37
+
38
+struct clock_offset {
39
+   int64_t offset;
40
+   int64_t err;
41
 };
42
 
43
 struct impl {
44
@@ -81,6 +94,9 @@
45
    uint64_t base_time;
46
    struct spa_dll dll;
47
    double max_error;
48
+   double max_resync;
49
+
50
+   struct clock_offset nsec_offset;
51
 };
52
 
53
 static void reset_props(struct props *props)
54
@@ -89,6 +105,7 @@
55
    spa_zero(props->clock_name);
56
    props->clock_id = CLOCK_MONOTONIC;
57
    props->freewheel_wait = DEFAULT_FREEWHEEL_WAIT;
58
+   props->resync_ms = DEFAULT_RESYNC_MS;
59
 }
60
 
61
 static const struct clock_info {
62
@@ -186,6 +203,68 @@
63
    return 0;
64
 }
65
 
66
+static int64_t get_nsec_offset(struct impl *this, uint64_t *now)
67
+{
68
+   struct timespec ts1, ts2, ts3;
69
+   int64_t t1, t2, t3;
70
+
71
+   /* Offset between timer clock and monotonic */
72
+   if (this->timer_clockid == CLOCK_MONOTONIC)
73
+       return 0;
74
+
75
+   spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &ts1);
76
+   spa_system_clock_gettime(this->data_system, this->timer_clockid, &ts2);
77
+   spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &ts3);
78
+
79
+   t1 = SPA_TIMESPEC_TO_NSEC(&ts1);
80
+   t2 = SPA_TIMESPEC_TO_NSEC(&ts2);
81
+   t3 = SPA_TIMESPEC_TO_NSEC(&ts3);
82
+
83
+   if (now)
84
+       *now = t3;
85
+
86
+   return t1 + (t3 - t1) / 2 - t2;
87
+}
88
+
89
+static int64_t clock_offset_update(struct clock_offset *off, int64_t offset, struct spa_log *log)
90
+{
91
+   const int64_t max_resync = CLOCK_OFFSET_MAX_ERR;
92
+   const int64_t n = CLOCK_OFFSET_NAVG;
93
+   int64_t err;
94
+
95
+   /* Moving average smoothing, discarding outliers */
96
+   err = offset - off->offset;
97
+
98
+   if (SPA_ABS(err) > max_resync) {
99
+       /* Clock jump */
100
+       spa_log_info(log, "nsec err %"PRIi64" > max_resync %"PRIi64", resetting",
101
+               err, max_resync);
102
+       off->offset = offset;
103
+       off->err = 0;
104
+       err = 0;
105
+   } else if (SPA_ABS(err) / 2 <= off->err) {
106
+       off->offset += err / n;
107
+   }
108
+
109
+   off->err += (SPA_ABS(err) - off->err) / n;
110
+
111
+   spa_log_trace(log, "clock offset %"PRIi64" err:%"PRIi64" abs-err:%"PRIi64,
112
+           off->offset, err, off->err);
113
+
114
+   return off->offset;
115
+}
116
+
117
+static int64_t smooth_nsec_offset(struct impl *this, uint64_t *now)
118
+{
119
+   int64_t offset;
120
+
121
+   if (this->timer_clockid == CLOCK_MONOTONIC)
122
+       return 0;
123
+
124
+   offset = get_nsec_offset(this, now);
125
+   return clock_offset_update(&this->nsec_offset, offset, this->log);
126
+}
127
+
128
 static int reassign_follower(struct impl *this)
129
 {
130
    bool following;
131
@@ -199,7 +278,7 @@
132
 
133
    following = is_following(this);
134
    if (following != this->following) {
135
-       spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following);
136
+       spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following);
137
        this->following = following;
138
        spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
139
    }
140
@@ -239,7 +318,7 @@
141
 #if 0
142
    return ((__uint128_t)val * num) / denom;
143
 #else
144
-   return (double)val / denom * num;
145
+   return (uint64_t)((double)val / denom * num);
146
 #endif
147
 }
148
 
149
@@ -254,7 +333,7 @@
150
    if ((res = spa_system_timerfd_read(this->data_system,
151
                this->timer_source.fd, &expirations)) < 0) {
152
        if (res != -EAGAIN)
153
-           spa_log_error(this->log, NAME " %p: timerfd error: %s",
154
+           spa_log_error(this->log, "%p: timerfd error: %s",
155
                    this, spa_strerror(res));
156
        return;
157
    }
158
@@ -266,7 +345,7 @@
159
        rate = 48000;
160
    }
161
    if (this->props.freewheel)
162
-       nsec = gettime_nsec(this, this->props.clock_id);
163
+       nsec = gettime_nsec(this, this->timer_clockid);
164
    else
165
        nsec = this->next_time;
166
 
167
@@ -281,6 +360,7 @@
168
    if (this->last_time == 0) {
169
        spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
170
        this->max_error = rate * MAX_ERROR_MS / 1000;
171
+       this->max_resync = rate * this->props.resync_ms / 1000;
172
        position = current_position;
173
    } else if (SPA_LIKELY(this->clock)) {
174
        position = this->clock->position + this->clock->duration;
175
@@ -288,23 +368,29 @@
176
        position = current_position;
177
    }
178
 
179
-   /* check the elapsed time of the other clock against
180
-    * the graph clock elapsed time, feed this error into the
181
-    * dll and adjust the timeout of our MONOTONIC clock. */
182
-   err = (double)position - (double)current_position;
183
-   if (err > this->max_error)
184
-       err = this->max_error;
185
-   else if (err < -this->max_error)
186
-       err = -this->max_error;
187
-
188
    this->last_time = current_time;
189
 
190
    if (this->props.freewheel) {
191
        corr = 1.0;
192
        this->next_time = nsec + this->props.freewheel_wait * SPA_NSEC_PER_SEC;
193
    } else if (this->tracking) {
194
+       /* check the elapsed time of the other clock against
195
+        * the graph clock elapsed time, feed this error into the
196
+        * dll and adjust the timeout of our MONOTONIC clock. */
197
+       err = (double)position - (double)current_position;
198
+       if (fabs(err) > this->max_error) {
199
+           if (fabs(err) > this->max_resync) {
200
+               spa_log_warn(this->log, "err %f > max_resync %f, resetting",
201
+                       err, this->max_resync);
202
+               spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
203
+               position = current_position;
204
+               err = 0.0;
205
+           } else {
206
+               err = SPA_CLAMPD(err, -this->max_error, this->max_error);
207
+           }
208
+       }
209
        corr = spa_dll_update(&this->dll, err);
210
-       this->next_time = nsec + duration / corr * 1e9 / rate;
211
+       this->next_time = (uint64_t)(nsec + duration / corr * 1e9 / rate);
212
    } else {
213
        corr = 1.0;
214
        this->next_time = scale_u64(position + duration, SPA_NSEC_PER_SEC, rate);
215
@@ -319,13 +405,16 @@
216
    }
217
 
218
    if (SPA_LIKELY(this->clock)) {
219
-       this->clock->nsec = nsec;
220
+       uint64_t nsec_now = nsec;
221
+       int64_t nsec_offset = smooth_nsec_offset(this, &nsec_now);
222
+
223
+       this->clock->nsec = SPA_MIN(nsec + nsec_offset, nsec_now);
224
        this->clock->rate = this->clock->target_rate;
225
        this->clock->position = position;
226
        this->clock->duration = duration;
227
        this->clock->delay = 0;
228
        this->clock->rate_diff = corr;
229
-       this->clock->next_nsec = this->next_time;
230
+       this->clock->next_nsec = this->next_time + nsec_offset;
231
    }
232
 
233
    spa_node_call_ready(&this->callbacks,
234
@@ -499,7 +588,7 @@
235
    return sizeof(struct impl);
236
 }
237
 
238
-int get_phc_index(struct spa_system *s, const char *name) {
239
+static int get_phc_index(struct spa_system *s, const char *name) {
240
 #ifdef ETHTOOL_GET_TS_INFO
241
    struct ethtool_ts_info info = {0};
242
    struct ifreq ifr = {0};
243
@@ -508,21 +597,19 @@
244
    info.cmd = ETHTOOL_GET_TS_INFO;
245
    strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
246
    ifr.ifr_data = (char *) &info;
247
-   fd = socket(AF_INET, SOCK_DGRAM, 0);
248
 
249
-   if (fd < 0) {
250
-       return -1;
251
-   }
252
+   fd = socket(AF_INET, SOCK_DGRAM, 0);
253
+   if (fd < 0)
254
+       return -errno;
255
 
256
    err = spa_system_ioctl(s, fd, SIOCETHTOOL, &ifr);
257
    close(fd);
258
-   if (err < 0) {
259
-       return err;
260
-   }
261
+   if (err < 0)
262
+       return -errno;
263
 
264
    return info.phc_index;
265
 #else
266
-   return -1;
267
+   return -ENOTSUP;
268
 #endif
269
 }
270
 
271
@@ -597,30 +684,33 @@
272
            if (this->clock_fd >= 0) {
273
                close(this->clock_fd);
274
            }
275
-           this->clock_fd = open(s, O_RDWR);
276
+           this->clock_fd = open(s, O_RDONLY);
277
 
278
            if (this->clock_fd == -1) {
279
-               spa_log_warn(this->log, "failed to open clock device '%s'", s);
280
+               spa_log_warn(this->log, "failed to open clock device '%s': %m", s);
281
            } else {
282
                this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
283
            }
284
        } else if (spa_streq(k, "clock.interface") && this->clock_fd < 0) {
285
            int phc_index = get_phc_index(this->data_system, s);
286
            if (phc_index < 0) {
287
-               spa_log_warn(this->log, "failed to get phc device index for interface '%s'", s);
288
+               spa_log_warn(this->log, "failed to get phc device index for interface '%s': %s",
289
+                       s, spa_strerror(phc_index));
290
            } else {
291
                char dev19;
292
                spa_scnprintf(dev, sizeof(dev), "/dev/ptp%d", phc_index);
293
-               this->clock_fd = open(dev, O_RDWR);
294
-           }
295
-
296
-           if (this->clock_fd == -1) {
297
-               spa_log_warn(this->log, "failed to open clock device '%s'", s);
298
-           } else {
299
-               this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
300
+               this->clock_fd = open(dev, O_RDONLY);
301
+               if (this->clock_fd == -1) {
302
+                   spa_log_warn(this->log, "failed to open clock device '%s' "
303
+                           "for interface '%s': %m", dev, s);
304
+               } else {
305
+                   this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
306
+               }
307
            }
308
        } else if (spa_streq(k, "freewheel.wait")) {
309
            this->props.freewheel_wait = atoi(s);
310
+       } else if (spa_streq(k, "resync.ms")) {
311
+           this->props.resync_ms = (float)atof(s);
312
        }
313
    }
314
    if (this->props.clock_name0 == '\0') {
315
@@ -633,6 +723,9 @@
316
    this->timer_clockid = this->tracking ? CLOCK_MONOTONIC : this->props.clock_id;
317
    this->max_error = 128;
318
 
319
+   this->nsec_offset.offset = get_nsec_offset(this, NULL);
320
+   this->nsec_offset.err = 0;
321
+
322
    this->timer_source.func = on_timeout;
323
    this->timer_source.data = this;
324
    this->timer_source.fd = spa_system_timerfd_create(this->data_system,
325
pipewire-1.0.1.tar.bz2/spa/plugins/support/null-audio-sink.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/null-audio-sink.c Changed
103
 
1
@@ -29,7 +29,9 @@
2
 #include <spa/pod/filter.h>
3
 #include <spa/control/control.h>
4
 
5
-#define NAME "null-audio-sink"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.null-audio-sink");
9
 
10
 #define DEFAULT_CLOCK_NAME "clock.system.monotonic"
11
 
12
@@ -37,7 +39,6 @@
13
    uint32_t format;
14
    uint32_t channels;
15
    uint32_t rate;
16
-   uint32_t n_pos;
17
    uint32_t posSPA_AUDIO_MAX_CHANNELS;
18
    char clock_name64;
19
    unsigned int debug:1;
20
@@ -49,7 +50,6 @@
21
    props->format = 0;
22
    props->channels = 0;
23
    props->rate = 0;
24
-   props->n_pos = 0;
25
    strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name));
26
    props->debug = false;
27
    props->driver = true;
28
@@ -227,7 +227,7 @@
29
 
30
    following = is_following(this);
31
    if (following != this->following) {
32
-       spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following);
33
+       spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following);
34
        this->following = following;
35
        spa_loop_invoke(this->data_loop, do_set_timers, 0, NULL, 0, true, this);
36
    }
37
@@ -274,7 +274,7 @@
38
    if ((res = spa_system_timerfd_read(this->data_system,
39
                this->timer_source.fd, &expirations)) < 0) {
40
        if (res != -EAGAIN)
41
-           spa_log_error(this->log, NAME " %p: timerfd error: %s",
42
+           spa_log_error(this->log, "%p: timerfd error: %s",
43
                    this, spa_strerror(res));
44
        return;
45
    }
46
@@ -469,10 +469,10 @@
47
                SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(DEFAULT_CHANNELS, 1, INT32_MAX),
48
                0);
49
        }
50
-       if (this->props.n_pos != 0) {
51
+       if (this->props.channels != 0) {
52
            spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0);
53
            spa_pod_builder_array(builder, sizeof(uint32_t), SPA_TYPE_Id,
54
-                   this->props.n_pos, this->props.pos);
55
+                   this->props.channels, this->props.pos);
56
        }
57
        *param = spa_pod_builder_pop(builder, &f0);
58
        break;
59
@@ -573,7 +573,7 @@
60
 static int clear_buffers(struct impl *this, struct port *port)
61
 {
62
    if (port->n_buffers > 0) {
63
-       spa_log_info(this->log, NAME " %p: clear buffers", this);
64
+       spa_log_info(this->log, "%p: clear buffers", this);
65
        port->n_buffers = 0;
66
        this->started = false;
67
    }
68
@@ -728,7 +728,7 @@
69
        b->outbuf = buffersi;
70
 
71
        if (d0.data == NULL) {
72
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
73
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
74
                      buffersi);
75
            return -EINVAL;
76
        }
77
@@ -888,10 +888,10 @@
78
         if (spa_json_enter_array(&it0, &it1) <= 0)
79
                 spa_json_init(&it1, val, len);
80
 
81
-   this->props.n_pos = 0;
82
+   this->props.channels = 0;
83
    while (spa_json_get_string(&it1, v, sizeof(v)) > 0 &&
84
-       this->props.n_pos < SPA_AUDIO_MAX_CHANNELS) {
85
-       this->props.posthis->props.n_pos++ = channel_from_name(v);
86
+       this->props.channels < SPA_AUDIO_MAX_CHANNELS) {
87
+       this->props.posthis->props.channels++ = channel_from_name(v);
88
    }
89
 }
90
 
91
@@ -991,10 +991,7 @@
92
                    "%s", s);
93
        }
94
    }
95
-   if (this->props.n_pos > 0)
96
-       this->props.channels = this->props.n_pos;
97
-
98
-   spa_log_info(this->log, NAME " %p: initialized", this);
99
+   spa_log_info(this->log, "%p: initialized", this);
100
 
101
    return 0;
102
 }
103
pipewire-1.0.1.tar.bz2/spa/plugins/support/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/plugin.c Changed
18
 
1
@@ -6,6 +6,7 @@
2
 #include <stdio.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_support_logger_factory;
8
 extern const struct spa_handle_factory spa_support_system_factory;
9
@@ -14,6 +15,8 @@
10
 extern const struct spa_handle_factory spa_support_node_driver_factory;
11
 extern const struct spa_handle_factory spa_support_null_audio_sink_factory;
12
 
13
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
14
+
15
 SPA_EXPORT
16
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
17
 {
18
pipewire-1.0.1.tar.bz2/spa/plugins/support/system.c -> pipewire-1.2.0.tar.gz/spa/plugins/support/system.c Changed
31
 
1
@@ -21,7 +21,8 @@
2
 #include <spa/utils/names.h>
3
 #include <spa/utils/string.h>
4
 
5
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.system");
6
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.system");
7
+
8
 #undef SPA_LOG_TOPIC_DEFAULT
9
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
10
 
11
@@ -190,7 +191,7 @@
12
 static int impl_eventfd_create(void *object, int flags)
13
 {
14
    struct impl *impl = object;
15
-   int fl = 0, res;
16
+   int fl = 0, res, err;
17
    if (flags & SPA_FD_CLOEXEC)
18
        fl |= EFD_CLOEXEC;
19
    if (flags & SPA_FD_NONBLOCK)
20
@@ -198,8 +199,9 @@
21
    if (flags & SPA_FD_EVENT_SEMAPHORE)
22
        fl |= EFD_SEMAPHORE;
23
    res = eventfd(0, fl);
24
+   err = -errno; /* save errno in case it is overwritten before return */
25
    spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
26
-   return res < 0 ? -errno : res;
27
+   return res < 0 ? err : res;
28
 }
29
 
30
 static int impl_eventfd_write(void *object, int fd, uint64_t count)
31
pipewire-1.0.1.tar.bz2/spa/plugins/test/fakesink.c -> pipewire-1.2.0.tar.gz/spa/plugins/test/fakesink.c Changed
82
 
1
@@ -22,7 +22,9 @@
2
 #include <spa/pod/parser.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#define NAME "fakesink"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesink");
9
 
10
 struct props {
11
    bool live;
12
@@ -205,7 +207,7 @@
13
        if ((res = spa_system_timerfd_read(this->data_system,
14
                    this->timer_source.fd, &expirations)) < 0) {
15
            if (res != -EAGAIN)
16
-               spa_log_error(this->log, NAME " %p: timerfd error: %s",
17
+               spa_log_error(this->log, "%p: timerfd error: %s",
18
                        this, spa_strerror(res));
19
        }
20
    }
21
@@ -231,7 +233,7 @@
22
        spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
23
    }
24
    if (spa_list_is_empty(&port->ready)) {
25
-       spa_log_error(this->log, NAME " %p: no buffers", this);
26
+       spa_log_error(this->log, "%p: no buffers", this);
27
        return -EPIPE;
28
    }
29
 
30
@@ -240,7 +242,7 @@
31
 
32
    n_bytes = b->outbuf->datas0.maxsize;
33
 
34
-   spa_log_trace(this->log, NAME " %p: dequeue buffer %d", this, b->id);
35
+   spa_log_trace(this->log, "%p: dequeue buffer %d", this, b->id);
36
 
37
    render_buffer(this, b);
38
 
39
@@ -505,7 +507,7 @@
40
 static int clear_buffers(struct impl *this, struct port *port)
41
 {
42
    if (port->n_buffers > 0) {
43
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
44
+       spa_log_debug(this->log, "%p: clear buffers", this);
45
        port->n_buffers = 0;
46
        spa_list_init(&port->ready);
47
        this->started = false;
48
@@ -583,7 +585,7 @@
49
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
50
 
51
        if (d0.data == NULL) {
52
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
53
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
54
                      buffersi);
55
        }
56
    }
57
@@ -635,13 +637,13 @@
58
        struct buffer *b = &port->buffersio->buffer_id;
59
 
60
        if (!b->outstanding) {
61
-           spa_log_warn(this->log, NAME " %p: buffer %u in use", this,
62
+           spa_log_warn(this->log, "%p: buffer %u in use", this,
63
                     io->buffer_id);
64
            io->status = -EINVAL;
65
            return -EINVAL;
66
        }
67
 
68
-       spa_log_trace(this->log, NAME " %p: queue buffer %u", this, io->buffer_id);
69
+       spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id);
70
 
71
        spa_list_append(&port->ready, &b->link);
72
        b->outstanding = false;
73
@@ -818,7 +820,7 @@
74
 
75
 const struct spa_handle_factory spa_fakesink_factory = {
76
    SPA_VERSION_HANDLE_FACTORY,
77
-   NAME,
78
+   "fakesink",
79
    NULL,
80
    impl_get_size,
81
    impl_init,
82
pipewire-1.0.1.tar.bz2/spa/plugins/test/fakesrc.c -> pipewire-1.2.0.tar.gz/spa/plugins/test/fakesrc.c Changed
75
 
1
@@ -22,7 +22,9 @@
2
 #include <spa/pod/parser.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#define NAME "fakesrc"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesrc");
9
 
10
 struct props {
11
    bool live;
12
@@ -220,7 +222,7 @@
13
        if ((res = spa_system_timerfd_read(this->data_system,
14
                    this->timer_source.fd, &expirations)) < 0) {
15
            if (res != -EAGAIN)
16
-               spa_log_error(this->log, NAME " %p: timerfd error: %s",
17
+               spa_log_error(this->log, "%p: timerfd error: %s",
18
                        this, spa_strerror(res));
19
        }
20
    }
21
@@ -240,7 +242,7 @@
22
    if (spa_list_is_empty(&port->empty)) {
23
        set_timer(this, false);
24
        this->underrun = true;
25
-       spa_log_error(this->log, NAME " %p: out of buffers", this);
26
+       spa_log_error(this->log, "%p: out of buffers", this);
27
        return -EPIPE;
28
    }
29
    b = spa_list_first(&port->empty, struct buffer, link);
30
@@ -249,7 +251,7 @@
31
 
32
    n_bytes = b->outbuf->datas0.maxsize;
33
 
34
-   spa_log_trace(this->log, NAME " %p: dequeue buffer %d", this, b->id);
35
+   spa_log_trace(this->log, "%p: dequeue buffer %d", this, b->id);
36
 
37
    fill_buffer(this, b);
38
 
39
@@ -518,7 +520,7 @@
40
 static int clear_buffers(struct impl *this, struct port *port)
41
 {
42
    if (port->n_buffers > 0) {
43
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
44
+       spa_log_debug(this->log, "%p: clear buffers", this);
45
        port->n_buffers = 0;
46
        spa_list_init(&port->empty);
47
        this->started = false;
48
@@ -595,7 +597,7 @@
49
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
50
 
51
        if (d0.data == NULL) {
52
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
53
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
54
                      buffersi);
55
        }
56
        spa_list_append(&port->empty, &b->link);
57
@@ -633,7 +635,7 @@
58
    struct buffer *b = &port->buffersid;
59
    spa_return_if_fail(b->outstanding);
60
 
61
-   spa_log_trace(this->log, NAME " %p: reuse buffer %d", this, id);
62
+   spa_log_trace(this->log, "%p: reuse buffer %d", this, id);
63
 
64
    b->outstanding = false;
65
    spa_list_append(&port->empty, &b->link);
66
@@ -848,7 +850,7 @@
67
 
68
 const struct spa_handle_factory spa_fakesrc_factory = {
69
    SPA_VERSION_HANDLE_FACTORY,
70
-   NAME,
71
+   "fakesrc",
72
    NULL,
73
    impl_get_size,
74
    impl_init,
75
pipewire-1.0.1.tar.bz2/spa/plugins/test/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/test/plugin.c Changed
15
 
1
@@ -5,10 +5,13 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_fakesrc_factory;
8
 extern const struct spa_handle_factory spa_fakesink_factory;
9
 
10
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
11
+
12
 SPA_EXPORT
13
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
14
 {
15
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/meson.build Changed
20
 
1
@@ -1,10 +1,16 @@
2
 v4l2_sources = 'v4l2.c',
3
                 'v4l2-device.c',
4
-                'v4l2-udev.c',
5
                 'v4l2-source.c'
6
+v4l2_dependencies =  spa_dep, libinotify_dep 
7
+
8
+if libudev_dep.found()
9
+v4l2_sources +=  'v4l2-udev.c' 
10
+v4l2_dependencies +=  libudev_dep 
11
+endif
12
 
13
 v4l2lib = shared_library('spa-v4l2',
14
                           v4l2_sources,
15
-                          dependencies :  spa_dep, libudev_dep, libinotify_dep ,
16
+                          include_directories :  configinc ,
17
+                          dependencies : v4l2_dependencies,
18
                           install : true,
19
                           install_dir : spa_plugindir / 'v4l2')
20
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/v4l2-device.c -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/v4l2-device.c Changed
66
 
1
@@ -23,15 +23,17 @@
2
 
3
 #include "v4l2.h"
4
 
5
-#define NAME "v4l2-device"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.v4l2-device");
9
 
10
 static const char default_device = "/dev/video0";
11
 
12
 struct props {
13
    char device64;
14
    char devnum32;
15
-   char product_id6;
16
-   char vendor_id6;
17
+   char product_id7;
18
+   char vendor_id7;
19
    int device_fd;
20
 };
21
 
22
@@ -60,7 +62,8 @@
23
    uint32_t n_items = 0;
24
    struct spa_device_info info;
25
    struct spa_param_info params2;
26
-   char path128, version16, capabilities16, device_caps16;
27
+   char path128, version16, capabilities16, device_caps16, devices_str16;
28
+   struct spa_strbuf buf;
29
 
30
    if ((res = spa_v4l2_open(&this->dev, this->props.device)) < 0)
31
        return res;
32
@@ -79,7 +82,14 @@
33
    if (this->props.vendor_id0)
34
        ADD_ITEM(SPA_KEY_DEVICE_VENDOR_ID, this->props.vendor_id);
35
    ADD_ITEM(SPA_KEY_API_V4L2_PATH, (char *)this->props.device);
36
-   ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, (char *)this->props.devnum);
37
+
38
+   /* encode device number into a json array */
39
+   spa_strbuf_init(&buf, devices_str, sizeof(devices_str));
40
+   spa_strbuf_append(&buf, " ");
41
+   spa_strbuf_append(&buf, "%s", this->props.devnum);
42
+   spa_strbuf_append(&buf, " ");
43
+   ADD_ITEM(SPA_KEY_DEVICE_DEVIDS, devices_str);
44
+
45
    ADD_ITEM(SPA_KEY_API_V4L2_CAP_DRIVER, (char *)this->dev.cap.driver);
46
    ADD_ITEM(SPA_KEY_API_V4L2_CAP_CARD, (char *)this->dev.cap.card);
47
    ADD_ITEM(SPA_KEY_API_V4L2_CAP_BUS_INFO, (char *)this->dev.cap.bus_info);
48
@@ -234,13 +244,13 @@
49
    reset_props(&this->props);
50
 
51
    if (info && (str = spa_dict_lookup(info, SPA_KEY_API_V4L2_PATH)))
52
-       strncpy(this->props.device, str, 63);
53
+       strncpy(this->props.device, str, sizeof(this->props.device)-1);
54
    if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_DEVIDS)))
55
-       strncpy(this->props.devnum, str, 31);
56
+       strncpy(this->props.devnum, str, sizeof(this->props.devnum)-1);
57
    if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_PRODUCT_ID)))
58
-       strncpy(this->props.product_id, str, 5);
59
+       strncpy(this->props.product_id, str, sizeof(this->props.product_id)-1);
60
    if (info && (str = spa_dict_lookup(info, SPA_KEY_DEVICE_VENDOR_ID)))
61
-       strncpy(this->props.vendor_id, str, 5);
62
+       strncpy(this->props.vendor_id, str, sizeof(this->props.vendor_id)-1);
63
 
64
    return 0;
65
 }
66
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/v4l2-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/v4l2-source.c Changed
171
 
1
@@ -26,6 +26,7 @@
2
 #include <spa/param/latency-utils.h>
3
 #include <spa/pod/filter.h>
4
 #include <spa/control/control.h>
5
+#include <spa/debug/types.h>
6
 
7
 #include "v4l2.h"
8
 
9
@@ -54,6 +55,7 @@
10
    struct spa_list link;
11
    struct spa_buffer *outbuf;
12
    struct spa_meta_header *h;
13
+   struct spa_meta_videotransform *vt;
14
    struct v4l2_buffer v4l2_buffer;
15
    void *ptr;
16
 };
17
@@ -71,6 +73,7 @@
18
    struct impl *impl;
19
 
20
    bool alloc_buffers;
21
+   bool probed_expbuf;
22
    bool have_expbuf;
23
 
24
    bool next_fmtdesc;
25
@@ -81,7 +84,6 @@
26
 
27
    bool have_format;
28
    struct spa_video_info current_format;
29
-   struct spa_fraction rate;
30
 
31
    struct spa_v4l2_device dev;
32
 
33
@@ -121,6 +123,8 @@
34
    struct spa_log *log;
35
    struct spa_loop *data_loop;
36
 
37
+   enum spa_meta_videotransform_value transform;
38
+
39
    uint64_t info_all;
40
    struct spa_node_info info;
41
 #define NODE_PropInfo  0
42
@@ -149,15 +153,14 @@
43
 
44
 #include "v4l2-utils.c"
45
 
46
-static const struct spa_dict_item info_items = {
47
-   { SPA_KEY_DEVICE_API, "v4l2" },
48
-   { SPA_KEY_MEDIA_CLASS, "Video/Source" },
49
-   { SPA_KEY_MEDIA_ROLE, "Camera" },
50
-   { SPA_KEY_NODE_DRIVER, "true" },
51
-};
52
-
53
 static void emit_node_info(struct impl *this, bool full)
54
 {
55
+   static const struct spa_dict_item info_items = {
56
+       { SPA_KEY_DEVICE_API, "v4l2" },
57
+       { SPA_KEY_MEDIA_CLASS, "Video/Source" },
58
+       { SPA_KEY_MEDIA_ROLE, "Camera" },
59
+       { SPA_KEY_NODE_DRIVER, "true" },
60
+   };
61
    uint64_t old = full ? this->info.change_mask : 0;
62
    if (full)
63
        this->info.change_mask = this->info_all;
64
@@ -170,10 +173,14 @@
65
 
66
 static void emit_port_info(struct impl *this, struct port *port, bool full)
67
 {
68
+   static const struct spa_dict_item info_items = {
69
+       { SPA_KEY_PORT_GROUP, "stream.0" },
70
+   };
71
    uint64_t old = full ? port->info.change_mask : 0;
72
    if (full)
73
        port->info.change_mask = port->info_all;
74
    if (port->info.change_mask) {
75
+       port->info.props = &SPA_DICT_INIT_ARRAY(info_items);
76
        spa_node_emit_port_info(&this->hooks,
77
                SPA_DIRECTION_OUTPUT, 0, &port->info);
78
        port->info.change_mask = old;
79
@@ -580,6 +587,12 @@
80
                SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
81
                SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
82
            break;
83
+       case 1:
84
+           param = spa_pod_builder_add_object(&b,
85
+               SPA_TYPE_OBJECT_ParamMeta, id,
86
+               SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoTransform),
87
+               SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_videotransform)));
88
+           break;
89
        default:
90
            return 0;
91
        }
92
@@ -695,11 +708,19 @@
93
     done:
94
    this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
95
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
96
+   port->paramsPORT_Latency.flags ^= SPA_PARAM_INFO_SERIAL;
97
    if (port->have_format) {
98
+       uint64_t latency;
99
+       latency = port->info.rate.num * SPA_NSEC_PER_SEC / port->info.rate.denom;
100
+       this->latencySPA_DIRECTION_OUTPUT =
101
+           SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT,
102
+                   .min_ns = latency,
103
+                   .max_ns = latency);
104
        port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
105
        port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
106
        this->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READ);
107
    } else {
108
+       this->latencySPA_DIRECTION_OUTPUT = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
109
        port->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
110
        port->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
111
        this->paramsNODE_Format = SPA_PARAM_INFO(SPA_PARAM_Format, 0);
112
@@ -715,7 +736,7 @@
113
                    uint32_t id, uint32_t flags,
114
                    const struct spa_pod *param)
115
 {
116
-   int res;
117
+   int res = 0;
118
    struct impl *this = object;
119
    struct port *port;
120
 
121
@@ -957,8 +978,8 @@
122
      uint32_t n_support)
123
 {
124
    struct impl *this;
125
-   const char *str;
126
    struct port *port;
127
+   uint32_t i;
128
    int res;
129
 
130
    spa_return_val_if_fail(factory != NULL, -EINVAL);
131
@@ -1005,6 +1026,7 @@
132
    port->impl = this;
133
    spa_list_init(&port->queue);
134
    port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
135
+           SPA_PORT_CHANGE_MASK_PROPS |
136
            SPA_PORT_CHANGE_MASK_PARAMS;
137
    port->info = SPA_PORT_INFO_INIT();
138
    port->info.flags = SPA_PORT_FLAG_LIVE |
139
@@ -1020,19 +1042,23 @@
140
    port->info.params = port->params;
141
    port->info.n_params = N_PORT_PARAMS;
142
 
143
-   port->alloc_buffers = true;
144
-   port->have_expbuf = true;
145
+   port->probed_expbuf = false;
146
    port->have_query_ext_ctrl = true;
147
    port->dev.log = this->log;
148
    port->dev.fd = -1;
149
 
150
-   if (info && (str = spa_dict_lookup(info, SPA_KEY_API_V4L2_PATH))) {
151
-       strncpy(this->props.device, str, 63);
152
-       if ((res = spa_v4l2_open(&port->dev, this->props.device)) < 0)
153
-           return res;
154
-       spa_v4l2_close(&port->dev);
155
+   for (i = 0; info && i < info->n_items; i++) {
156
+       const char *k = info->itemsi.key;
157
+       const char *s = info->itemsi.value;
158
+       if (spa_streq(k, SPA_KEY_API_V4L2_PATH)) {
159
+           strncpy(this->props.device, s, 63);
160
+           if ((res = spa_v4l2_open(&port->dev, this->props.device)) < 0)
161
+               return res;
162
+           spa_v4l2_close(&port->dev);
163
+       } else if (spa_streq(k, "meta.videotransform.transform")) {
164
+           this->transform = spa_debug_type_find_type_short(spa_type_meta_videotransform_type, s);
165
+       }
166
    }
167
-
168
    return 0;
169
 }
170
 
171
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/v4l2-udev.c -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/v4l2-udev.c Changed
177
 
1
@@ -24,20 +24,20 @@
2
 #include <spa/monitor/device.h>
3
 #include <spa/monitor/utils.h>
4
 
5
-#define NAME "v4l2-udev"
6
+#include "v4l2.h"
7
 
8
 #define MAX_DEVICES    64
9
 
10
-#define ACTION_ADD 0
11
-#define ACTION_REMOVE  1
12
-#define ACTION_DISABLE 2
13
+enum action {
14
+   ACTION_CHANGE,
15
+   ACTION_REMOVE,
16
+};
17
 
18
 struct device {
19
    uint32_t id;
20
    struct udev_device *dev;
21
    int inotify_wd;
22
    unsigned int accessible:1;
23
-   unsigned int ignored:1;
24
    unsigned int emitted:1;
25
 };
26
 
27
@@ -358,48 +358,47 @@
28
    return device->accessible;
29
 }
30
 
31
-static void process_device(struct impl *this, uint32_t action, struct udev_device *dev)
32
+static void process_device(struct impl *this, enum action action, struct device *device)
33
 {
34
-   uint32_t id;
35
-   struct device *device;
36
-   bool emitted;
37
-
38
-   if ((id = get_device_id(this, dev)) == SPA_ID_INVALID)
39
-       return;
40
-
41
-   device = find_device(this, id);
42
-   if (device && device->ignored)
43
-       return;
44
-
45
    switch (action) {
46
-   case ACTION_ADD:
47
-       if (device == NULL)
48
-           device = add_device(this, id, dev);
49
-       if (device == NULL)
50
-           return;
51
-       if (!check_access(this, device))
52
-           return;
53
-       emit_object_info(this, device);
54
+   case ACTION_CHANGE:
55
+       check_access(this, device);
56
+       if (device->accessible && !device->emitted) {
57
+           emit_object_info(this, device);
58
+       } else if (!device->accessible && device->emitted) {
59
+           device->emitted = false;
60
+           spa_device_emit_object_info(&this->hooks, device->id, NULL);
61
+       }
62
        break;
63
+   case ACTION_REMOVE: {
64
+       bool emitted = device->emitted;
65
+       uint32_t id = device->id;
66
 
67
-   case ACTION_REMOVE:
68
-       if (device == NULL)
69
-           return;
70
-       emitted = device->emitted;
71
        remove_device(this, device);
72
-       if (emitted)
73
-           spa_device_emit_object_info(&this->hooks, id, NULL);
74
-       break;
75
 
76
-   case ACTION_DISABLE:
77
-       if (device == NULL)
78
-           return;
79
-       if (device->emitted) {
80
-           device->emitted = false;
81
+       if (emitted)
82
            spa_device_emit_object_info(&this->hooks, id, NULL);
83
-       }
84
        break;
85
    }
86
+   }
87
+}
88
+
89
+static void process_udev_device(struct impl *this, enum action action, struct udev_device *udev_device)
90
+{
91
+   struct device *device;
92
+   uint32_t id;
93
+
94
+   if ((id = get_device_id(this, udev_device)) == SPA_ID_INVALID)
95
+       return;
96
+
97
+   device = find_device(this, id);
98
+   if (action == ACTION_CHANGE && !device)
99
+       device = add_device(this, id, udev_device);
100
+
101
+   if (!device)
102
+       return;
103
+
104
+   process_device(this, action, device);
105
 }
106
 
107
 static int stop_inotify(struct impl *this)
108
@@ -431,8 +430,6 @@
109
        void *p, *e;
110
 
111
        len = read(source->fd, &buf, sizeof(buf));
112
-       if (len < 0 && errno != EAGAIN)
113
-           break;
114
        if (len <= 0)
115
            break;
116
 
117
@@ -441,25 +438,23 @@
118
        for (p = &buf; p < e;
119
            p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) {
120
            event = (const struct inotify_event *) p;
121
+           struct device *device = NULL;
122
 
123
-           if ((event->mask & IN_ATTRIB)) {
124
-               struct device *device = NULL;
125
-
126
-               for (size_t i = 0; i < this->n_devices; i++) {
127
-                   if (this->devicesi.inotify_wd == event->wd) {
128
-                       device = &this->devicesi;
129
-                       break;
130
-                   }
131
+           for (size_t i = 0; i < this->n_devices; i++) {
132
+               if (this->devicesi.inotify_wd == event->wd) {
133
+                   device = &this->devicesi;
134
+                   break;
135
                }
136
+           }
137
 
138
-               spa_assert(device);
139
+           if (!device)
140
+               continue;
141
 
142
-               bool access = check_access(this, device);
143
-               if (access && !device->emitted)
144
-                   process_device(this, ACTION_ADD, device->dev);
145
-               else if (!access && device->emitted)
146
-                   process_device(this, ACTION_DISABLE, device->dev);
147
-           }
148
+           if (event->mask & IN_ATTRIB)
149
+               process_device(this, ACTION_CHANGE, device);
150
+
151
+           if (event->mask & IN_IGNORED)
152
+               device->inotify_wd = -1;
153
        }
154
    }
155
 }
156
@@ -507,9 +502,9 @@
157
 
158
    if (spa_streq(action, "add") ||
159
        spa_streq(action, "change")) {
160
-       process_device(this, ACTION_ADD, dev);
161
+       process_udev_device(this, ACTION_CHANGE, dev);
162
    } else if (spa_streq(action, "remove")) {
163
-       process_device(this, ACTION_REMOVE, dev);
164
+       process_udev_device(this, ACTION_REMOVE, dev);
165
    }
166
    udev_device_unref(dev);
167
 }
168
@@ -579,7 +574,7 @@
169
        if (dev == NULL)
170
            continue;
171
 
172
-       process_device(this, ACTION_ADD, dev);
173
+       process_udev_device(this, ACTION_CHANGE, dev);
174
 
175
        udev_device_unref(dev);
176
    }
177
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/v4l2-utils.c -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/v4l2-utils.c Changed
383
 
1
@@ -367,11 +367,11 @@
2
    return NULL;
3
 }
4
 
5
-static uint32_t
6
+static int
7
 enum_filter_format(uint32_t media_type, int32_t media_subtype,
8
           const struct spa_pod *filter, uint32_t index)
9
 {
10
-   uint32_t video_format = 0;
11
+   uint32_t video_format = SPA_VIDEO_FORMAT_UNKNOWN;
12
 
13
    switch (media_type) {
14
    case SPA_MEDIA_TYPE_video:
15
@@ -383,11 +383,10 @@
16
            const uint32_t *values;
17
 
18
            if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_format)))
19
-               return SPA_VIDEO_FORMAT_UNKNOWN;
20
+               return -ENOENT;
21
 
22
            val = spa_pod_get_values(&p->value, &n_values, &choice);
23
-
24
-           if (val->type != SPA_TYPE_Id)
25
+           if (val->type != SPA_TYPE_Id || n_values == 0)
26
                return SPA_VIDEO_FORMAT_UNKNOWN;
27
 
28
            values = SPA_POD_BODY(val);
29
@@ -503,7 +502,7 @@
30
    int res, n_fractions;
31
    const struct format_info *info;
32
    struct spa_pod_choice *choice;
33
-   uint32_t filter_media_type, filter_media_subtype, video_format;
34
+   uint32_t filter_media_type, filter_media_subtype;
35
    struct spa_v4l2_device *dev = &port->dev;
36
    uint8_t buffer1024;
37
    struct spa_pod_builder b = { 0 };
38
@@ -545,16 +544,19 @@
39
        if (filter) {
40
            struct v4l2_format fmt;
41
 
42
-           video_format = enum_filter_format(filter_media_type,
43
+           res = enum_filter_format(filter_media_type,
44
                        filter_media_subtype,
45
                        filter, port->fmtdesc.index);
46
-
47
-           if (video_format == SPA_VIDEO_FORMAT_UNKNOWN)
48
+           if (res == -ENOENT)
49
+               goto do_enum_fmt;
50
+           if (res < 0)
51
+               goto exit;
52
+           if (res == SPA_VIDEO_FORMAT_UNKNOWN)
53
                goto enum_end;
54
 
55
            info = find_format_info_by_media_type(filter_media_type,
56
                                  filter_media_subtype,
57
-                                 video_format, 0);
58
+                                 res, 0);
59
            if (info == NULL)
60
                goto next_fmtdesc;
61
 
62
@@ -580,6 +582,7 @@
63
            }
64
 
65
        } else {
66
+do_enum_fmt:
67
            if ((res = xioctl(dev->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) {
68
                if (errno == EINVAL)
69
                    goto enum_end;
70
@@ -610,7 +613,7 @@
71
                goto do_frmsize;
72
 
73
            val = spa_pod_get_values(&p->value, &n_vals, &choice);
74
-           if (val->type != SPA_TYPE_Rectangle)
75
+           if (val->type != SPA_TYPE_Rectangle || n_vals == 0)
76
                goto enum_end;
77
 
78
            if (choice == SPA_CHOICE_None) {
79
@@ -648,7 +651,7 @@
80
                goto have_size;
81
 
82
            val = spa_pod_get_values(&p->value, &n_values, &choice);
83
-           if (val->type != SPA_TYPE_Rectangle)
84
+           if (val->type != SPA_TYPE_Rectangle || n_values == 0)
85
                goto have_size;
86
 
87
            values = SPA_POD_BODY_CONST(val);
88
@@ -768,8 +771,7 @@
89
                goto have_framerate;
90
 
91
            val = spa_pod_get_values(&p->value, &n_values, &choice);
92
-
93
-           if (val->type != SPA_TYPE_Fraction)
94
+           if (val->type != SPA_TYPE_Fraction || n_values == 0)
95
                goto enum_end;
96
 
97
            values = SPA_POD_BODY(val);
98
@@ -815,6 +817,7 @@
99
                         port->frmival.discrete.denominator,
100
                         port->frmival.discrete.numerator);
101
            port->frmival.index++;
102
+           n_fractions++;
103
        } else if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS ||
104
               port->frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
105
            if (n_fractions == 0)
106
@@ -828,20 +831,23 @@
107
 
108
            if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
109
                choice->body.type = SPA_CHOICE_Range;
110
+               n_fractions += 2;
111
            } else {
112
                choice->body.type = SPA_CHOICE_Step;
113
                spa_pod_builder_fraction(&b,
114
                             port->frmival.stepwise.step.denominator,
115
                             port->frmival.stepwise.step.numerator);
116
+               n_fractions += 3;
117
            }
118
 
119
            port->frmsize.index++;
120
            port->next_frmsize = true;
121
            break;
122
        }
123
-       n_fractions++;
124
    }
125
-   if (n_fractions <= 1)
126
+   if (n_fractions == 0)
127
+       goto next_frmsize;
128
+   if (n_fractions == 1)
129
        choice->body.type = SPA_CHOICE_None;
130
 
131
    spa_pod_builder_pop(&b, &f1);
132
@@ -859,6 +865,48 @@
133
    return res;
134
 }
135
 
136
+static int probe_expbuf(struct impl *this)
137
+{
138
+   struct port *port = &this->out_ports0;
139
+   struct spa_v4l2_device *dev = &port->dev;
140
+   struct v4l2_requestbuffers reqbuf;
141
+   struct v4l2_exportbuffer expbuf;
142
+
143
+   if (port->probed_expbuf)
144
+       return 0;
145
+   port->probed_expbuf = true;
146
+
147
+   spa_zero(reqbuf);
148
+   reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
149
+   reqbuf.memory = V4L2_MEMORY_MMAP;
150
+   reqbuf.count = 2;
151
+
152
+   if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
153
+       spa_log_error(this->log, "'%s' VIDIOC_REQBUFS: %m", this->props.device);
154
+       return -errno;
155
+   }
156
+
157
+   spa_zero(expbuf);
158
+   expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
159
+   expbuf.index = 0;
160
+   expbuf.flags = O_CLOEXEC | O_RDONLY;
161
+   if (xioctl(dev->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
162
+       spa_log_info(this->log, "'%s' EXPBUF not supported: %m", this->props.device);
163
+       port->have_expbuf = false;
164
+       port->alloc_buffers = false;
165
+   } else {
166
+       port->have_expbuf = true;
167
+       port->alloc_buffers = true;
168
+   }
169
+
170
+   reqbuf.count = 0;
171
+   if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
172
+       spa_log_error(this->log, "'%s' VIDIOC_REQBUFS: %m", this->props.device);
173
+       return -errno;
174
+   }
175
+   return 0;
176
+}
177
+
178
 static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, uint32_t flags)
179
 {
180
    struct port *port = &this->out_ports0;
181
@@ -903,8 +951,9 @@
182
    info = find_format_info_by_media_type(format->media_type,
183
                          format->media_subtype, video_format, 0);
184
    if (info == NULL || size == NULL || framerate == NULL) {
185
-       spa_log_error(this->log, "unknown media type %d %d %d", format->media_type,
186
-                 format->media_subtype, video_format);
187
+       spa_log_error(this->log, "%s: unknown media type %d %d %d",
188
+               this->props.device, format->media_type,
189
+               format->media_subtype, video_format);
190
        return -EINVAL;
191
    }
192
 
193
@@ -935,14 +984,15 @@
194
 
195
    /* some cheap USB cam's won't accept any change */
196
    if (xioctl(dev->fd, VIDIOC_S_PARM, &streamparm) < 0)
197
-       spa_log_warn(this->log, "VIDIOC_S_PARM: %m");
198
+       spa_log_warn(this->log, "%s: VIDIOC_S_PARM: %m", this->props.device);
199
 
200
    match = (reqfmt.fmt.pix.pixelformat == fmt.fmt.pix.pixelformat &&
201
            reqfmt.fmt.pix.width == fmt.fmt.pix.width &&
202
            reqfmt.fmt.pix.height == fmt.fmt.pix.height);
203
 
204
    if (!match && !SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) {
205
-       spa_log_error(this->log, "wanted %.4s %dx%d, got %.4s %dx%d",
206
+       spa_log_error(this->log, "%s: wanted %.4s %dx%d, got %.4s %dx%d",
207
+               this->props.device,
208
                (char *)&reqfmt.fmt.pix.pixelformat,
209
                reqfmt.fmt.pix.width, reqfmt.fmt.pix.height,
210
                (char *)&fmt.fmt.pix.pixelformat,
211
@@ -962,8 +1012,6 @@
212
    dev->have_format = true;
213
    size->width = fmt.fmt.pix.width;
214
    size->height = fmt.fmt.pix.height;
215
-   port->rate.denom = framerate->num = streamparm.parm.capture.timeperframe.denominator;
216
-   port->rate.num = framerate->denom = streamparm.parm.capture.timeperframe.numerator;
217
 
218
    port->fmt = fmt;
219
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE;
220
@@ -971,7 +1019,10 @@
221
        SPA_PORT_FLAG_LIVE |
222
        SPA_PORT_FLAG_PHYSICAL |
223
        SPA_PORT_FLAG_TERMINAL;
224
-   port->info.rate = SPA_FRACTION(port->rate.num, port->rate.denom);
225
+   port->info.rate.num = streamparm.parm.capture.timeperframe.numerator;
226
+   port->info.rate.denom = streamparm.parm.capture.timeperframe.denominator;
227
+
228
+   probe_expbuf(this);
229
 
230
    return match ? 0 : 1;
231
 }
232
@@ -1318,22 +1369,27 @@
233
    if (xioctl(dev->fd, VIDIOC_DQBUF, &buf) < 0)
234
        return -errno;
235
 
236
+   /* Drop the first frame in order to work around common firmware
237
+    * timestamp issues */
238
+   if (buf.sequence == 0)
239
+       return 0;
240
+
241
    pts = SPA_TIMEVAL_TO_NSEC(&buf.timestamp);
242
    spa_log_trace(this->log, "v4l2 %p: have output %d", this, buf.index);
243
 
244
    if (this->clock) {
245
        /* FIXME, we should follow the driver clock and target_ values.
246
         * for now we ignore and use our own. */
247
-       this->clock->target_rate = port->rate;
248
+       this->clock->target_rate = port->info.rate;
249
        this->clock->target_duration = 1;
250
 
251
        this->clock->nsec = pts;
252
-       this->clock->rate = port->rate;
253
+       this->clock->rate = port->info.rate;
254
        this->clock->position = buf.sequence;
255
        this->clock->duration = 1;
256
        this->clock->delay = 0;
257
        this->clock->rate_diff = 1.0;
258
-       this->clock->next_nsec = pts + 1000000000LL / port->rate.denom;
259
+       this->clock->next_nsec = pts + port->info.rate.num * SPA_NSEC_PER_SEC / port->info.rate.denom;
260
    }
261
 
262
    b = &port->buffersbuf.index;
263
@@ -1346,6 +1402,9 @@
264
        b->h->pts = pts;
265
        b->h->dts_offset = 0;
266
    }
267
+   if (b->vt) {
268
+       b->vt->transform = this->transform;
269
+   }
270
 
271
    d = b->outbuf->datas;
272
    d0.chunk->offset = 0;
273
@@ -1424,7 +1483,8 @@
274
        } else if (d0.type == SPA_DATA_DmaBuf) {
275
            port->memtype = V4L2_MEMORY_DMABUF;
276
        } else {
277
-           spa_log_error(this->log, "can't use buffers of type %d", d0.type);
278
+           spa_log_error(this->log, "%s: can't use buffers of type %d",
279
+                   this->props.device, d0.type);
280
            return -EINVAL;
281
        }
282
    }
283
@@ -1453,11 +1513,13 @@
284
        b->outbuf = buffersi;
285
        b->flags = BUFFER_FLAG_OUTSTANDING;
286
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
287
+       b->vt = spa_buffer_find_meta_data(buffersi, SPA_META_VideoTransform, sizeof(*b->vt));
288
 
289
-       spa_log_debug(this->log, "import buffer %p", buffersi);
290
+       spa_log_debug(this->log, "%s: import buffer %p", this->props.device, buffersi);
291
 
292
        if (buffersi->n_datas < 1) {
293
-           spa_log_error(this->log, "invalid memory on buffer %p", buffersi);
294
+           spa_log_error(this->log, "%s: invalid memory on buffer %p",
295
+                   this->props.device, buffersi);
296
            return -EINVAL;
297
        }
298
        d = buffersi->datas;
299
@@ -1492,8 +1554,8 @@
300
            b->v4l2_buffer.m.fd = d0.fd;
301
        }
302
        else {
303
-           spa_log_error(this->log, "invalid port memory %d",
304
-                   port->memtype);
305
+           spa_log_error(this->log, "%s: invalid port memory %d",
306
+                   this->props.device, port->memtype);
307
            return -EIO;
308
        }
309
 
310
@@ -1539,7 +1601,8 @@
311
        struct spa_data *d;
312
 
313
        if (buffersi->n_datas < 1) {
314
-           spa_log_error(this->log, "invalid buffer data");
315
+           spa_log_error(this->log, "%s: invalid buffer data",
316
+                   this->props.device);
317
            return -EINVAL;
318
        }
319
 
320
@@ -1548,6 +1611,7 @@
321
        b->outbuf = buffersi;
322
        b->flags = BUFFER_FLAG_OUTSTANDING;
323
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
324
+       b->vt = spa_buffer_find_meta_data(buffersi, SPA_META_VideoTransform, sizeof(*b->vt));
325
 
326
        spa_zero(b->v4l2_buffer);
327
        b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
328
@@ -1576,6 +1640,7 @@
329
 
330
        spa_log_debug(this->log, "data types %08x", d0.type);
331
 
332
+again:
333
        if (port->have_expbuf &&
334
            d0.type != SPA_ID_INVALID &&
335
            (d0.type & ((1u << SPA_DATA_DmaBuf)|(1u<<SPA_DATA_MemFd)))) {
336
@@ -1590,7 +1655,7 @@
337
                    spa_log_debug(this->log, "'%s' VIDIOC_EXPBUF not supported: %m",
338
                            this->props.device);
339
                    port->have_expbuf = false;
340
-                   goto fallback;
341
+                   goto again;
342
                }
343
                spa_log_error(this->log, "'%s' VIDIOC_EXPBUF: %m", this->props.device);
344
                return -errno;
345
@@ -1599,14 +1664,13 @@
346
                d0.type = SPA_DATA_DmaBuf;
347
            else
348
                d0.type = SPA_DATA_MemFd;
349
-           d0.flags = SPA_DATA_FLAG_READABLE;
350
+           d0.flags = SPA_DATA_FLAG_READABLE | SPA_DATA_FLAG_MAPPABLE;
351
            d0.fd = expbuf.fd;
352
            d0.data = NULL;
353
            SPA_FLAG_SET(b->flags, BUFFER_FLAG_ALLOCATED);
354
            spa_log_debug(this->log, "EXPBUF fd:%d", expbuf.fd);
355
            use_expbuf = true;
356
        } else if (d0.type & (1u << SPA_DATA_MemPtr)) {
357
-fallback:
358
            d0.type = SPA_DATA_MemPtr;
359
            d0.flags = SPA_DATA_FLAG_READABLE;
360
            d0.fd = -1;
361
@@ -1625,7 +1689,9 @@
362
            spa_log_debug(this->log, "mmap offset:%u data:%p", d0.mapoffset, b->ptr);
363
            use_expbuf = false;
364
        } else {
365
-           spa_log_error(this->log, "unsupported data type:%08x", d0.type);
366
+           spa_log_error(this->log, "%s: unsupported data type:%08x",
367
+                   this->props.device, d0.type);
368
+           port->alloc_buffers = false;
369
            return -ENOTSUP;
370
        }
371
        spa_v4l2_buffer_recycle(this, i);
372
@@ -1668,8 +1734,8 @@
373
        if ((res = read_init(this)) < 0)
374
            return res;
375
    } else {
376
-       spa_log_error(this->log, "invalid capabilities %08x",
377
-                   dev->cap.capabilities);
378
+       spa_log_error(this->log, "%s: invalid capabilities %08x",
379
+               this->props.device, dev->cap.capabilities);
380
        return -EIO;
381
    }
382
 
383
pipewire-1.0.1.tar.bz2/spa/plugins/v4l2/v4l2.c -> pipewire-1.2.0.tar.gz/spa/plugins/v4l2/v4l2.c Changed
33
 
1
@@ -7,13 +7,16 @@
2
 #include <spa/support/plugin.h>
3
 #include <spa/support/log.h>
4
 
5
+#include "config.h"
6
 #include "v4l2.h"
7
 
8
 extern const struct spa_handle_factory spa_v4l2_source_factory;
9
 extern const struct spa_handle_factory spa_v4l2_udev_factory;
10
 extern const struct spa_handle_factory spa_v4l2_device_factory;
11
 
12
-struct spa_log_topic v4l2_log_topic = SPA_LOG_TOPIC(0, "spa.v4l2");
13
+SPA_LOG_TOPIC_DEFINE(v4l2_log_topic, "spa.v4l2");
14
+
15
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
16
 
17
 SPA_EXPORT
18
 int spa_handle_factory_enum(const struct spa_handle_factory **factory,
19
@@ -27,8 +30,13 @@
20
        *factory = &spa_v4l2_source_factory;
21
        break;
22
    case 1:
23
+#ifdef HAVE_LIBUDEV
24
        *factory = &spa_v4l2_udev_factory;
25
        break;
26
+#else
27
+       (*index)++;
28
+       SPA_FALLTHROUGH;
29
+#endif
30
    case 2:
31
        *factory = &spa_v4l2_device_factory;
32
        break;
33
pipewire-1.0.1.tar.bz2/spa/plugins/videoconvert/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/videoconvert/plugin.c Changed
14
 
1
@@ -5,9 +5,12 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_videoadapter_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/videoconvert/videoadapter.c -> pipewire-1.2.0.tar.gz/spa/plugins/videoconvert/videoadapter.c Changed
1088
 
1
@@ -2,7 +2,9 @@
2
 /* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
3
 /* SPDX-License-Identifier: MIT */
4
 
5
+#include "spa/utils/dict.h"
6
 #include <spa/support/plugin.h>
7
+#include <spa/support/plugin-loader.h>
8
 #include <spa/support/log.h>
9
 #include <spa/support/cpu.h>
10
 
11
@@ -20,17 +22,18 @@
12
 #include <spa/param/param.h>
13
 #include <spa/param/video/format-utils.h>
14
 #include <spa/param/latency-utils.h>
15
+#include <spa/param/tag-utils.h>
16
 #include <spa/debug/format.h>
17
 #include <spa/debug/pod.h>
18
 #include <spa/debug/log.h>
19
 
20
 #undef SPA_LOG_TOPIC_DEFAULT
21
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
22
-static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.videoadapter");
23
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.videoadapter");
24
 
25
 #define DEFAULT_ALIGN  16
26
 
27
-#define MAX_PORTS  1
28
+#define MAX_PORTS  (1+1)
29
 
30
 /** \cond */
31
 
32
@@ -40,6 +43,7 @@
33
 
34
    struct spa_log *log;
35
    struct spa_cpu *cpu;
36
+   struct spa_plugin_loader *ploader;
37
 
38
    uint32_t max_align;
39
    enum spa_direction direction;
40
@@ -48,14 +52,16 @@
41
 
42
    struct spa_node *follower;
43
    struct spa_hook follower_listener;
44
-   uint32_t follower_flags;
45
+   uint64_t follower_flags;
46
    struct spa_video_info follower_current_format;
47
    struct spa_video_info default_format;
48
+   int in_set_param;
49
 
50
    struct spa_handle *hnd_convert;
51
    struct spa_node *convert;
52
    struct spa_hook convert_listener;
53
-   uint32_t convert_flags;
54
+   uint64_t convert_flags;
55
+   char *convertname;
56
 
57
    uint32_t n_buffers;
58
    struct spa_buffer **buffers;
59
@@ -74,10 +80,12 @@
60
 #define IDX_PortConfig     5
61
 #define IDX_Latency        6
62
 #define IDX_ProcessLatency 7
63
-#define N_NODE_PARAMS      8
64
+#define IDX_Tag            8
65
+#define N_NODE_PARAMS      9
66
    struct spa_param_info paramsN_NODE_PARAMS;
67
    uint32_t convert_params_flagsN_NODE_PARAMS;
68
    uint32_t follower_params_flagsN_NODE_PARAMS;
69
+   uint64_t follower_port_flags;
70
 
71
    struct spa_hook_list hooks;
72
    struct spa_callbacks callbacks;
73
@@ -85,10 +93,14 @@
74
    unsigned int add_listener:1;
75
    unsigned int have_format:1;
76
    unsigned int started:1;
77
-   unsigned int driver:1;
78
+   unsigned int ready:1;
79
    unsigned int async:1;
80
    unsigned int passthrough:1;
81
    unsigned int follower_removing:1;
82
+   unsigned int in_recalc;
83
+
84
+   unsigned int warned:1;
85
+   unsigned int driver:1;
86
 };
87
 
88
 /** \endcond */
89
@@ -120,13 +132,35 @@
90
    return 0;
91
 }
92
 
93
+static int convert_enum_port_config(struct impl *this,
94
+       int seq, uint32_t id, uint32_t start, uint32_t num,
95
+       const struct spa_pod *filter, struct spa_pod_builder *builder)
96
+{
97
+   struct spa_pod *f1, *f2 = NULL;
98
+   int res;
99
+
100
+   f1 = spa_pod_builder_add_object(builder,
101
+       SPA_TYPE_OBJECT_ParamPortConfig, id,
102
+           SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(this->direction));
103
+
104
+   if (filter) {
105
+       if ((res = spa_pod_filter(builder, &f2, f1, filter)) < 0)
106
+           return res;
107
+   }
108
+   else {
109
+       f2 = f1;
110
+   }
111
+   return spa_node_enum_params(this->convert, seq, id, start, num, f2);
112
+}
113
+
114
 static int impl_node_enum_params(void *object, int seq,
115
                 uint32_t id, uint32_t start, uint32_t num,
116
                 const struct spa_pod *filter)
117
 {
118
    struct impl *this = object;
119
    uint8_t buffer4096;
120
-   struct spa_pod_dynamic_builder b;
121
+   spa_auto(spa_pod_dynamic_builder) b = { 0 };
122
+   struct spa_pod_builder_state state;
123
    struct spa_result_node_params result;
124
    uint32_t count = 0;
125
    int res;
126
@@ -134,6 +168,9 @@
127
    spa_return_val_if_fail(this != NULL, -EINVAL);
128
    spa_return_val_if_fail(num != 0, -EINVAL);
129
 
130
+   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
131
+   spa_pod_builder_get_state(&b.b, &state);
132
+
133
    result.id = id;
134
    result.next = start;
135
 next:
136
@@ -141,15 +178,34 @@
137
 
138
    spa_log_debug(this->log, "%p: %d id:%u", this, seq, id);
139
 
140
-   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
141
+   spa_pod_builder_reset(&b.b, &state);
142
 
143
    switch (id) {
144
    case SPA_PARAM_EnumPortConfig:
145
-   case SPA_PARAM_PortConfig:
146
        if (this->convert == NULL)
147
            return 0;
148
-       res = spa_node_enum_params(this->convert, seq, id, start, num, filter);
149
-       return res;
150
+       return convert_enum_port_config(this, seq, id, start, num, filter, &b.b);
151
+   case SPA_PARAM_PortConfig:
152
+       if (this->passthrough) {
153
+           switch (result.index) {
154
+           case 0:
155
+               result.param = spa_pod_builder_add_object(&b.b,
156
+                   SPA_TYPE_OBJECT_ParamPortConfig, id,
157
+                   SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(this->direction),
158
+                   SPA_PARAM_PORT_CONFIG_mode,      SPA_POD_Id(
159
+                       SPA_PARAM_PORT_CONFIG_MODE_passthrough));
160
+               result.next++;
161
+               res = 1;
162
+               break;
163
+           default:
164
+               return 0;
165
+           }
166
+       } else {
167
+           if (this->convert == NULL)
168
+               return 0;
169
+           return convert_enum_port_config(this, seq, id, start, num, filter, &b.b);
170
+       }
171
+       break;
172
    case SPA_PARAM_PropInfo:
173
        res = follower_enum_params(this,
174
                id, IDX_PropInfo, &result, filter, &b.b);
175
@@ -165,6 +221,7 @@
176
    case SPA_PARAM_EnumFormat:
177
    case SPA_PARAM_Format:
178
    case SPA_PARAM_Latency:
179
+   case SPA_PARAM_Tag:
180
        res = spa_node_port_enum_params_sync(this->follower,
181
                this->direction, 0,
182
                id, &result.next, filter, &result.param, &b.b);
183
@@ -172,16 +229,12 @@
184
    default:
185
        return -ENOENT;
186
    }
187
-
188
-   if (res == 1) {
189
-       spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
190
-       count++;
191
-   }
192
-   spa_pod_dynamic_builder_clean(&b);
193
-
194
    if (res != 1)
195
        return res;
196
 
197
+   spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
198
+   count++;
199
+
200
    if (count != num)
201
        goto next;
202
 
203
@@ -191,6 +244,8 @@
204
 static int link_io(struct impl *this)
205
 {
206
    int res;
207
+   struct spa_io_rate_match *rate_match;
208
+   size_t rate_match_size;
209
 
210
    if (this->convert == NULL)
211
        return 0;
212
@@ -200,21 +255,32 @@
213
    spa_zero(this->io_rate_match);
214
    this->io_rate_match.rate = 1.0;
215
 
216
+   if (this->follower == this->target) {
217
+       rate_match = NULL;
218
+       rate_match_size = 0;
219
+   } else {
220
+       rate_match = &this->io_rate_match;
221
+       rate_match_size = sizeof(this->io_rate_match);
222
+   }
223
+
224
    if ((res = spa_node_port_set_io(this->follower,
225
            this->direction, 0,
226
            SPA_IO_RateMatch,
227
-           &this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
228
+           rate_match, rate_match_size)) < 0) {
229
        spa_log_debug(this->log, "%p: set RateMatch on follower disabled %d %s", this,
230
            res, spa_strerror(res));
231
    }
232
    else if ((res = spa_node_port_set_io(this->convert,
233
            SPA_DIRECTION_REVERSE(this->direction), 0,
234
            SPA_IO_RateMatch,
235
-           &this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
236
+           rate_match, rate_match_size)) < 0) {
237
        spa_log_warn(this->log, "%p: set RateMatch on convert failed %d %s", this,
238
            res, spa_strerror(res));
239
    }
240
 
241
+   if (this->follower == this->target)
242
+       return 0;
243
+
244
    this->io_buffers = SPA_IO_BUFFERS_INIT;
245
 
246
    if ((res = spa_node_port_set_io(this->follower,
247
@@ -280,7 +346,7 @@
248
 
249
    if (filter) {
250
        spa_log_error(this->log, "with this filter:");
251
-       spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 2, NULL, filter);
252
+       spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 2, NULL, filter);
253
    } else {
254
        spa_log_error(this->log, "there was no filter");
255
    }
256
@@ -298,7 +364,7 @@
257
            break;
258
        }
259
        spa_log_error(this->log, "unmatched %s %d:", debug, count);
260
-       spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 2, NULL, param);
261
+       spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 2, NULL, param);
262
        count++;
263
    }
264
    if (count == 0)
265
@@ -318,12 +384,9 @@
266
    uint32_t i, size, buffers, blocks, align, flags, stride = 0;
267
    uint32_t *aligns;
268
    struct spa_data *datas;
269
-   uint32_t follower_flags, conv_flags;
270
-
271
-   spa_log_debug(this->log, "%p: %d", this, this->n_buffers);
272
+   uint64_t follower_flags, conv_flags;
273
 
274
-   if (this->target == this->follower)
275
-       return 0;
276
+   spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers);
277
 
278
    if (this->n_buffers > 0)
279
        return 0;
280
@@ -382,6 +445,9 @@
281
            SPA_PARAM_BUFFERS_align,   SPA_POD_OPT_Int(&align))) < 0)
282
        return res;
283
 
284
+   if (this->async)
285
+       buffers = SPA_MAX(2u, buffers);
286
+
287
    spa_log_debug(this->log, "%p: buffers:%d, blocks:%d, size:%d, stride:%d align:%d %d:%d",
288
            this, buffers, blocks, size, stride, align, follower_alloc, conv_alloc);
289
 
290
@@ -420,8 +486,12 @@
291
 
292
 static int configure_format(struct impl *this, uint32_t flags, const struct spa_pod *format)
293
 {
294
+   uint8_t buffer4096;
295
    int res;
296
 
297
+   if (format == NULL && !this->have_format)
298
+       return 0;
299
+
300
    spa_log_debug(this->log, "%p: configure format:", this);
301
    if (format)
302
        spa_debug_log_format(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, format);
303
@@ -431,14 +501,13 @@
304
                       SPA_PARAM_Format, flags,
305
                       format)) < 0)
306
            return res;
307
+
308
    if (res > 0) {
309
-       uint8_t buffer4096;
310
-       struct spa_pod_builder b = { 0 };
311
+       struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
312
        uint32_t state = 0;
313
        struct spa_pod *fmt;
314
 
315
        /* format was changed to nearest compatible format */
316
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
317
 
318
        if ((res = spa_node_port_enum_params_sync(this->follower,
319
                    this->direction, 0,
320
@@ -449,7 +518,7 @@
321
        format = fmt;
322
    }
323
 
324
-   if (this->target != this->follower && this->convert) {
325
+   if (this->convert && this->target != this->follower) {
326
        if ((res = spa_node_port_set_param(this->convert,
327
                       SPA_DIRECTION_REVERSE(this->direction), 0,
328
                       SPA_PARAM_Format, flags,
329
@@ -460,10 +529,9 @@
330
    this->have_format = format != NULL;
331
    if (format == NULL) {
332
        this->n_buffers = 0;
333
-   } else {
334
+   } else if (this->target != this->follower) {
335
        res = negotiate_buffers(this);
336
    }
337
-
338
    return res;
339
 }
340
 
341
@@ -478,7 +546,7 @@
342
 
343
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
344
 
345
-   spa_log_debug(this->log, "%p: configure convert %p", this, this->target);
346
+   spa_log_debug(this->log, "%p: configure convert %p %d", this, this->target, mode);
347
 
348
    param = spa_pod_builder_add_object(&b,
349
        SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
350
@@ -488,17 +556,95 @@
351
    return spa_node_set_param(this->convert, SPA_PARAM_PortConfig, 0, param);
352
 }
353
 
354
-extern const struct spa_handle_factory spa_videoconvert_factory;
355
-
356
 static const struct spa_node_events follower_node_events;
357
 
358
+static int recalc_latency(struct impl *this, struct spa_node *src, enum spa_direction direction,
359
+       uint32_t port_id, struct spa_node *dst)
360
+{
361
+   struct spa_pod_builder b = { 0 };
362
+   uint8_t buffer1024;
363
+   struct spa_pod *param;
364
+   uint32_t index = 0;
365
+   struct spa_latency_info latency;
366
+   int res;
367
+
368
+   spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id);
369
+
370
+   if (this->target == this->follower)
371
+       return 0;
372
+
373
+   if (dst == NULL)
374
+       return 0;
375
+
376
+   while (true) {
377
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
378
+       if ((res = spa_node_port_enum_params_sync(src,
379
+                       direction, port_id, SPA_PARAM_Latency,
380
+                       &index, NULL, &param, &b)) != 1) {
381
+           param = NULL;
382
+           break;
383
+       }
384
+       if ((res = spa_latency_parse(param, &latency)) < 0)
385
+           return res;
386
+       if (latency.direction == direction)
387
+           break;
388
+   }
389
+   if ((res = spa_node_port_set_param(dst,
390
+                   SPA_DIRECTION_REVERSE(direction), 0,
391
+                   SPA_PARAM_Latency, 0, param)) < 0)
392
+       return res;
393
+
394
+   return 0;
395
+}
396
+
397
+static int recalc_tag(struct impl *this, struct spa_node *src, enum spa_direction direction,
398
+       uint32_t port_id, struct spa_node *dst)
399
+{
400
+   spa_auto(spa_pod_dynamic_builder) b = { 0 };
401
+   struct spa_pod_builder_state state;
402
+   uint8_t buffer2048;
403
+   struct spa_pod *param;
404
+   uint32_t index = 0;
405
+   struct spa_tag_info info;
406
+   int res;
407
+
408
+   spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id);
409
+
410
+   if (this->target == this->follower)
411
+       return 0;
412
+
413
+   if (dst == NULL)
414
+       return 0;
415
+
416
+   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 2048);
417
+   spa_pod_builder_get_state(&b.b, &state);
418
+
419
+   while (true) {
420
+       void *tag_state = NULL;
421
+       spa_pod_builder_reset(&b.b, &state);
422
+       if ((res = spa_node_port_enum_params_sync(src,
423
+                       direction, port_id, SPA_PARAM_Tag,
424
+                       &index, NULL, &param, &b.b)) != 1) {
425
+           param = NULL;
426
+           break;
427
+       }
428
+       if ((res = spa_tag_parse(param, &info, &tag_state)) < 0)
429
+           return res;
430
+       if (info.direction == direction)
431
+           break;
432
+   }
433
+   return spa_node_port_set_param(dst, SPA_DIRECTION_REVERSE(direction), 0,
434
+                   SPA_PARAM_Tag, 0, param);
435
+}
436
+
437
+
438
 static int reconfigure_mode(struct impl *this, bool passthrough,
439
                 enum spa_direction direction, struct spa_pod *format)
440
 {
441
    int res = 0;
442
    struct spa_hook l;
443
 
444
-   spa_log_info(this->log, "%p: passthrough mode %d", this, passthrough);
445
+   spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
446
 
447
    if (this->passthrough != passthrough) {
448
        if (passthrough) {
449
@@ -530,16 +676,19 @@
450
        } else {
451
            /* add converter ports */
452
            configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
453
-           link_io(this);
454
        }
455
+       link_io(this);
456
    }
457
-
458
    this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS;
459
-   this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE;
460
+   SPA_FLAG_CLEAR(this->info.flags, SPA_NODE_FLAG_NEED_CONFIGURE);
461
+   SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC,
462
+           this->async && this->follower == this->target);
463
    this->paramsIDX_Props.user++;
464
 
465
    emit_node_info(this, false);
466
 
467
+   spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
468
+
469
    return 0;
470
 }
471
 
472
@@ -562,9 +711,8 @@
473
        if ((res = spa_format_parse(param, &info.media_type, &info.media_subtype)) < 0)
474
            return res;
475
        if (info.media_type != SPA_MEDIA_TYPE_video ||
476
-           info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
477
-           return -EINVAL;
478
-
479
+           info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
480
+               return -EINVAL;
481
        if (spa_format_video_raw_parse(param, &info.info.raw) < 0)
482
            return -EINVAL;
483
 
484
@@ -599,8 +747,9 @@
485
                info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
486
                return -ENOTSUP;
487
 
488
-           if (spa_format_video_raw_parse(format, &info.info.raw) >= 0)
489
+           if (spa_format_video_raw_parse(format, &info.info.raw) >= 0) {
490
                this->default_format = info;
491
+           }
492
        }
493
 
494
        switch (mode) {
495
@@ -629,13 +778,16 @@
496
    }
497
 
498
    case SPA_PARAM_Props:
499
-       if (this->target != this->follower)
500
-           res = spa_node_set_param(this->target, id, flags, param);
501
-       res2 = spa_node_set_param(this->follower, id, flags, param);
502
+   {
503
+       int in_set_param = ++this->in_set_param;
504
+       res = spa_node_set_param(this->follower, id, flags, param);
505
+       if (this->target != this->follower && this->in_set_param == in_set_param)
506
+           res2 = spa_node_set_param(this->target, id, flags, param);
507
        if (res < 0 && res2 < 0)
508
            return res;
509
        res = 0;
510
        break;
511
+   }
512
    case SPA_PARAM_ProcessLatency:
513
        res = spa_node_set_param(this->follower, id, flags, param);
514
        break;
515
@@ -712,10 +864,12 @@
516
    struct spa_pod_builder b = { 0 };
517
    int res;
518
 
519
-   if (this->have_format)
520
+   if (this->target == this->follower)
521
        return 0;
522
 
523
-   if (this->target == this->follower)
524
+   spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format);
525
+
526
+   if (this->have_format)
527
        return 0;
528
 
529
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
530
@@ -739,18 +893,16 @@
531
            goto done;
532
        }
533
    }
534
-   if (this->convert) {
535
-       state = 0;
536
-       if ((res = spa_node_port_enum_params_sync(this->convert,
537
-                   SPA_DIRECTION_REVERSE(this->direction), 0,
538
-                   SPA_PARAM_EnumFormat, &state,
539
-                   format, &format, &b)) != 1) {
540
-           debug_params(this, this->convert,
541
-                   SPA_DIRECTION_REVERSE(this->direction), 0,
542
-                   SPA_PARAM_EnumFormat, format, "convert format", res);
543
-           res = -ENOTSUP;
544
-           goto done;
545
-       }
546
+   state = 0;
547
+   if (this->convert && (res = spa_node_port_enum_params_sync(this->convert,
548
+               SPA_DIRECTION_REVERSE(this->direction), 0,
549
+               SPA_PARAM_EnumFormat, &state,
550
+               format, &format, &b)) != 1) {
551
+       debug_params(this, this->convert,
552
+               SPA_DIRECTION_REVERSE(this->direction), 0,
553
+               SPA_PARAM_EnumFormat, format, "convert format", res);
554
+       res = -ENOTSUP;
555
+       goto done;
556
    }
557
    if (format == NULL) {
558
        res = -ENOTSUP;
559
@@ -787,18 +939,24 @@
560
 
561
    switch (SPA_NODE_COMMAND_ID(command)) {
562
    case SPA_NODE_COMMAND_Start:
563
-       if ((res = negotiate_format(this)) < 0)
564
-           return res;
565
-       if ((res = negotiate_buffers(this)) < 0)
566
-           return res;
567
+       spa_log_debug(this->log, "%p: starting %d", this, this->started);
568
+       if (this->target != this->follower) {
569
+           if (this->started)
570
+               return 0;
571
+           if ((res = negotiate_format(this)) < 0)
572
+               return res;
573
+       }
574
+       this->ready = true;
575
+       this->warned = false;
576
        break;
577
    case SPA_NODE_COMMAND_Suspend:
578
-       configure_format(this, 0, NULL);
579
-       SPA_FALLTHROUGH
580
+       spa_log_debug(this->log, "%p: suspending", this);
581
+       break;
582
    case SPA_NODE_COMMAND_Pause:
583
-       this->started = false;
584
+       spa_log_debug(this->log, "%p: pausing", this);
585
        break;
586
    case SPA_NODE_COMMAND_Flush:
587
+       spa_log_debug(this->log, "%p: flushing", this);
588
        this->io_buffers.status = SPA_STATUS_OK;
589
        break;
590
    default:
591
@@ -809,20 +967,41 @@
592
        spa_log_error(this->log, "%p: can't send command %d: %s",
593
                this, SPA_NODE_COMMAND_ID(command),
594
                spa_strerror(res));
595
-       return res;
596
    }
597
 
598
-   if (this->target != this->follower) {
599
+   if (res >= 0 && this->target != this->follower) {
600
        if ((res = spa_node_send_command(this->follower, command)) < 0) {
601
            spa_log_error(this->log, "%p: can't send command %d: %s",
602
                    this, SPA_NODE_COMMAND_ID(command),
603
                    spa_strerror(res));
604
-           return res;
605
        }
606
    }
607
    switch (SPA_NODE_COMMAND_ID(command)) {
608
    case SPA_NODE_COMMAND_Start:
609
-       this->started = true;
610
+       if (res < 0) {
611
+           spa_log_debug(this->log, "%p: start failed", this);
612
+           this->ready = false;
613
+           configure_format(this, 0, NULL);
614
+       } else {
615
+           this->started = true;
616
+           spa_log_debug(this->log, "%p: started", this);
617
+       }
618
+       break;
619
+   case SPA_NODE_COMMAND_Suspend:
620
+       configure_format(this, 0, NULL);
621
+       this->started = false;
622
+       this->warned = false;
623
+       this->ready = false;
624
+       spa_log_debug(this->log, "%p: suspended", this);
625
+       break;
626
+   case SPA_NODE_COMMAND_Pause:
627
+       this->started = false;
628
+       this->warned = false;
629
+       this->ready = false;
630
+       spa_log_debug(this->log, "%p: paused", this);
631
+       break;
632
+   case SPA_NODE_COMMAND_Flush:
633
+       spa_log_debug(this->log, "%p: flushed", this);
634
        break;
635
    }
636
    return res;
637
@@ -866,26 +1045,90 @@
638
                (this->paramsidx.flags & SPA_PARAM_INFO_SERIAL) |
639
                (info->paramsi.flags & SPA_PARAM_INFO_READWRITE);
640
 
641
-           if (!this->add_listener) {
642
-               this->paramsidx.user++;
643
-               spa_log_debug(this->log, "param %d changed", info->paramsi.id);
644
-           }
645
+           if (this->add_listener)
646
+               continue;
647
+
648
+           this->paramsidx.user++;
649
+           spa_log_debug(this->log, "param %d changed", info->paramsi.id);
650
        }
651
    }
652
    emit_node_info(this, false);
653
 }
654
 
655
+static void follower_convert_port_info(void *data,
656
+       enum spa_direction direction, uint32_t port_id,
657
+       const struct spa_port_info *info)
658
+{
659
+   struct impl *this = data;
660
+   uint32_t i;
661
+   int res;
662
+
663
+   spa_log_debug(this->log, "%p: convert port info %s %p %08"PRIx64, this,
664
+           this->direction == SPA_DIRECTION_INPUT ?
665
+               "Input" : "Output", info, info->change_mask);
666
+
667
+   if (this->convert && info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
668
+       for (i = 0; i < info->n_params; i++) {
669
+           uint32_t idx;
670
+
671
+           switch (info->paramsi.id) {
672
+           case SPA_PARAM_Latency:
673
+               idx = IDX_Latency;
674
+               break;
675
+           case SPA_PARAM_Tag:
676
+               idx = IDX_Tag;
677
+               break;
678
+           default:
679
+               continue;
680
+           }
681
+
682
+           if (!this->add_listener &&
683
+               this->convert_params_flagsidx == info->paramsi.flags)
684
+               continue;
685
+
686
+           this->convert_params_flagsidx = info->paramsi.flags;
687
+
688
+           if (this->add_listener)
689
+               continue;
690
+
691
+           if (idx == IDX_Latency) {
692
+               this->in_recalc++;
693
+               res = recalc_latency(this, this->convert, direction, port_id, this->follower);
694
+               this->in_recalc--;
695
+               spa_log_debug(this->log, "latency: %d (%s)", res,
696
+                       spa_strerror(res));
697
+           }
698
+           if (idx == IDX_Tag) {
699
+               this->in_recalc++;
700
+               res = recalc_tag(this, this->convert, direction, port_id, this->follower);
701
+               this->in_recalc--;
702
+               spa_log_debug(this->log, "tag: %d (%s)", res,
703
+                       spa_strerror(res));
704
+           }
705
+           spa_log_debug(this->log, "param %d changed", info->paramsi.id);
706
+       }
707
+   }
708
+}
709
+
710
 static void convert_port_info(void *data,
711
        enum spa_direction direction, uint32_t port_id,
712
        const struct spa_port_info *info)
713
 {
714
    struct impl *this = data;
715
+   struct spa_port_info pi;
716
 
717
    if (direction != this->direction) {
718
-       if (port_id == 0)
719
+       if (port_id == 0) {
720
+           /* handle the converter output port into the follower separately */
721
+           follower_convert_port_info(this, direction, port_id, info);
722
            return;
723
-       else
724
+       } else
725
+           /* the monitor ports are exposed */
726
            port_id--;
727
+   } else if (info) {
728
+       pi = *info;
729
+       pi.flags = this->follower_port_flags;
730
+       info = &pi;
731
    }
732
 
733
    spa_log_debug(this->log, "%p: port info %d:%d", this,
734
@@ -938,6 +1181,8 @@
735
        this->info.flags |= SPA_NODE_FLAG_OUT_PORT_CONFIG;
736
        this->info.max_output_ports = MAX_PORTS;
737
    }
738
+   SPA_FLAG_UPDATE(this->info.flags, SPA_NODE_FLAG_ASYNC,
739
+           this->async && this->follower == this->target);
740
 
741
    spa_log_debug(this->log, "%p: follower info %s", this,
742
            this->direction == SPA_DIRECTION_INPUT ?
743
@@ -974,50 +1219,17 @@
744
                (this->paramsidx.flags & SPA_PARAM_INFO_SERIAL) |
745
                (info->paramsi.flags & SPA_PARAM_INFO_READWRITE);
746
 
747
-           if (!this->add_listener) {
748
-               this->paramsidx.user++;
749
-               spa_log_debug(this->log, "param %d changed", info->paramsi.id);
750
-           }
751
+           if (this->add_listener)
752
+               continue;
753
+
754
+           this->paramsidx.user++;
755
+           spa_log_debug(this->log, "param %d changed", info->paramsi.id);
756
        }
757
    }
758
    emit_node_info(this, false);
759
 
760
    spa_zero(this->info.props);
761
    this->info.change_mask &= ~SPA_NODE_CHANGE_MASK_PROPS;
762
-
763
-}
764
-
765
-static int recalc_latency(struct impl *this, enum spa_direction direction, uint32_t port_id)
766
-{
767
-   struct spa_pod_builder b = { 0 };
768
-   uint8_t buffer1024;
769
-   struct spa_pod *param;
770
-   uint32_t index = 0;
771
-   struct spa_latency_info latency;
772
-   int res;
773
-
774
-   spa_log_debug(this->log, "%p: ", this);
775
-
776
-   if (this->target == this->follower)
777
-       return 0;
778
-
779
-   while (true) {
780
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
781
-       if ((res = spa_node_port_enum_params_sync(this->follower,
782
-                       direction, port_id, SPA_PARAM_Latency,
783
-                       &index, NULL, &param, &b)) != 1)
784
-           return res;
785
-       if ((res = spa_latency_parse(param, &latency)) < 0)
786
-           return res;
787
-       if (latency.direction == direction)
788
-           break;
789
-   }
790
-   if ((res = spa_node_port_set_param(this->target,
791
-                   SPA_DIRECTION_REVERSE(direction), 0,
792
-                   SPA_PARAM_Latency, 0, param)) < 0)
793
-       return res;
794
-
795
-   return 0;
796
 }
797
 
798
 static void follower_port_info(void *data,
799
@@ -1033,9 +1245,15 @@
800
          return;
801
    }
802
 
803
-   spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64, this,
804
+   this->follower_port_flags = info->flags &
805
+       (SPA_PORT_FLAG_LIVE |
806
+        SPA_PORT_FLAG_PHYSICAL |
807
+        SPA_PORT_FLAG_TERMINAL);
808
+
809
+   spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64" recalc:%u", this,
810
            this->direction == SPA_DIRECTION_INPUT ?
811
-               "Input" : "Output", info, info->change_mask);
812
+               "Input" : "Output", info, info->change_mask,
813
+               this->in_recalc);
814
 
815
    if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
816
        for (i = 0; i < info->n_params; i++) {
817
@@ -1051,29 +1269,43 @@
818
            case SPA_PARAM_Latency:
819
                idx = IDX_Latency;
820
                break;
821
+           case SPA_PARAM_Tag:
822
+               idx = IDX_Tag;
823
+               break;
824
            default:
825
                continue;
826
            }
827
+
828
            if (!this->add_listener &&
829
                this->follower_params_flagsidx == info->paramsi.flags)
830
                continue;
831
 
832
+           this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
833
            this->follower_params_flagsidx = info->paramsi.flags;
834
            this->paramsidx.flags =
835
                (this->paramsidx.flags & SPA_PARAM_INFO_SERIAL) |
836
                (info->paramsi.flags & SPA_PARAM_INFO_READWRITE);
837
 
838
-           if (idx == IDX_Latency) {
839
-               res = recalc_latency(this, direction, port_id);
840
+           if (this->add_listener)
841
+               continue;
842
+
843
+           if (idx == IDX_Latency && this->in_recalc == 0) {
844
+               res = recalc_latency(this, this->follower, direction, port_id, this->target);
845
                spa_log_debug(this->log, "latency: %d (%s)", res,
846
                        spa_strerror(res));
847
            }
848
-
849
-           this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
850
-           if (!this->add_listener) {
851
-               this->paramsidx.user++;
852
-               spa_log_debug(this->log, "param %d changed", info->paramsi.id);
853
+           if (idx == IDX_Tag && this->in_recalc == 0) {
854
+               res = recalc_tag(this, this->follower, direction, port_id, this->target);
855
+               spa_log_debug(this->log, "tag: %d (%s)", res,
856
+                       spa_strerror(res));
857
+           }
858
+           if (idx == IDX_EnumFormat) {
859
+               spa_log_debug(this->log, "new formats");
860
+               /* we will renegotiate when restarting */
861
            }
862
+
863
+           this->paramsidx.user++;
864
+           spa_log_debug(this->log, "param %d changed", info->paramsi.id);
865
        }
866
    }
867
    emit_node_info(this, false);
868
@@ -1124,6 +1356,11 @@
869
 
870
    spa_log_trace_fp(this->log, "%p: ready %d", this, status);
871
 
872
+   if (!this->ready) {
873
+       spa_log_info(this->log, "%p: ready stopped node", this);
874
+       return -EIO;
875
+   }
876
+
877
    if (this->target != this->follower) {
878
        this->driver = true;
879
 
880
@@ -1152,7 +1389,7 @@
881
    int res;
882
    struct impl *this = data;
883
 
884
-   if (this->target != this->follower && this->convert)
885
+   if (this->convert && this->target != this->follower)
886
        res = spa_node_port_reuse_buffer(this->convert, port_id, buffer_id);
887
    else
888
        res = spa_node_call_reuse_buffer(&this->callbacks, port_id, buffer_id);
889
@@ -1187,6 +1424,7 @@
890
    spa_log_trace(this->log, "%p: add listener %p", this, listener);
891
    spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
892
 
893
+
894
    if (events->info || events->port_info) {
895
        this->add_listener = true;
896
 
897
@@ -1199,6 +1437,7 @@
898
            spa_node_add_listener(this->convert, &l, &convert_node_events, this);
899
            spa_hook_remove(&l);
900
        }
901
+
902
        this->add_listener = false;
903
 
904
        emit_node_info(this, true);
905
@@ -1286,7 +1525,6 @@
906
             const struct spa_pod *param)
907
 {
908
    struct impl *this = object;
909
-   int res;
910
 
911
    spa_return_val_if_fail(this != NULL, -EINVAL);
912
 
913
@@ -1295,18 +1533,8 @@
914
    if (direction != this->direction)
915
        port_id++;
916
 
917
-   if ((res = spa_node_port_set_param(this->target, direction, port_id, id,
918
-           flags, param)) < 0)
919
-       return res;
920
-
921
-   if ((id == SPA_PARAM_Latency) &&
922
-       direction == this->direction) {
923
-       if ((res = spa_node_port_set_param(this->follower, direction, 0, id,
924
-               flags, param)) < 0)
925
-           return res;
926
-   }
927
-
928
-   return res;
929
+   return spa_node_port_set_param(this->target, direction, port_id, id,
930
+           flags, param);
931
 }
932
 
933
 static int
934
@@ -1369,6 +1597,13 @@
935
    struct impl *this = object;
936
    int status = 0, fstatus, retry = 8;
937
 
938
+   if (!this->ready) {
939
+       if (!this->warned)
940
+           spa_log_warn(this->log, "%p: scheduling stopped node", this);
941
+       this->warned = true;
942
+       return -EIO;
943
+   }
944
+
945
    spa_log_trace_fp(this->log, "%p: process convert:%p driver:%d",
946
            this, this->convert, this->driver);
947
 
948
@@ -1479,6 +1714,45 @@
949
    .process = impl_node_process,
950
 };
951
 
952
+static int load_plugin_from(struct impl *this, const struct spa_dict *info,
953
+       const char *convertname, struct spa_handle **handle, struct spa_node **iface)
954
+{
955
+   struct spa_handle *hnd_convert = NULL;
956
+   void *iface_conv = NULL;
957
+   hnd_convert = spa_plugin_loader_load(this->ploader, convertname, NULL);
958
+   if (!hnd_convert)
959
+       return -EINVAL;
960
+
961
+   spa_handle_get_interface(hnd_convert, SPA_TYPE_INTERFACE_Node, &iface_conv);
962
+   if (iface_conv == NULL) {
963
+       spa_plugin_loader_unload(this->ploader, hnd_convert);
964
+       return -EINVAL;
965
+   }
966
+
967
+   *handle = hnd_convert;
968
+   *iface = iface_conv;
969
+
970
+   return 0;
971
+}
972
+
973
+static int load_converter(struct impl *this, const struct spa_dict *info)
974
+{
975
+   int ret;
976
+   if (!this->ploader || !info)
977
+       return -EINVAL;
978
+
979
+   const char* factory_name = spa_dict_lookup(info, "video.adapt.converter");
980
+
981
+   if (factory_name) {
982
+       ret = load_plugin_from(this, info, factory_name, &this->hnd_convert, &this->convert);
983
+       if (ret >= 0) {
984
+           this->convertname = strdup(factory_name);
985
+           return ret;
986
+       }
987
+   }
988
+   return 0;
989
+}
990
+
991
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
992
 {
993
    struct impl *this;
994
@@ -1507,8 +1781,10 @@
995
    spa_hook_remove(&this->follower_listener);
996
    spa_node_set_callbacks(this->follower, NULL, NULL);
997
 
998
-   if (this->hnd_convert)
999
-       spa_handle_clear(this->hnd_convert);
1000
+   if (this->hnd_convert) {
1001
+       spa_plugin_loader_unload(this->ploader, this->hnd_convert);
1002
+       free(this->convertname);
1003
+   }
1004
 
1005
    if (this->buffers)
1006
        free(this->buffers);
1007
@@ -1522,12 +1798,7 @@
1008
 impl_get_size(const struct spa_handle_factory *factory,
1009
          const struct spa_dict *params)
1010
 {
1011
-   size_t size = 0;
1012
-
1013
-#if 0
1014
-   size += spa_handle_factory_get_size(&spa_videoconvert_factory, params);
1015
-#endif
1016
-   size += sizeof(struct impl);
1017
+   size_t size = sizeof(struct impl);
1018
 
1019
    return size;
1020
 }
1021
@@ -1540,10 +1811,8 @@
1022
      uint32_t n_support)
1023
 {
1024
    struct impl *this;
1025
-#if 0
1026
-   void *iface;
1027
-#endif
1028
    const char *str;
1029
+   int ret;
1030
 
1031
    spa_return_val_if_fail(factory != NULL, -EINVAL);
1032
    spa_return_val_if_fail(handle != NULL, -EINVAL);
1033
@@ -1558,6 +1827,8 @@
1034
 
1035
    this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
1036
 
1037
+   this->ploader = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_PluginLoader);
1038
+
1039
    if (info == NULL ||
1040
        (str = spa_dict_lookup(info, "video.adapt.follower")) == NULL)
1041
        return -EINVAL;
1042
@@ -1576,15 +1847,10 @@
1043
            SPA_VERSION_NODE,
1044
            &impl_node, this);
1045
 
1046
-#if 0
1047
-   this->hnd_convert = SPA_PTROFF(this, sizeof(struct impl), struct spa_handle);
1048
-   spa_handle_factory_init(&spa_videoconvert_factory,
1049
-               this->hnd_convert,
1050
-               info, support, n_support);
1051
-
1052
-   spa_handle_get_interface(this->hnd_convert, SPA_TYPE_INTERFACE_Node, &iface);
1053
-   this->convert = iface;
1054
-#endif
1055
+   ret = load_converter(this, info);
1056
+   spa_log_debug(this->log, "%p: loaded converter %s, hnd %p, convert %p", this, this->convertname, this->hnd_convert, this->convert);
1057
+   if (ret < 0)
1058
+       return ret;
1059
    this->target = this->convert;
1060
 
1061
    this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
1062
@@ -1600,6 +1866,7 @@
1063
    this->paramsIDX_PortConfig = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE);
1064
    this->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
1065
    this->paramsIDX_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE);
1066
+   this->paramsIDX_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE);
1067
    this->info.params = this->params;
1068
    this->info.n_params = N_NODE_PARAMS;
1069
 
1070
@@ -1607,11 +1874,15 @@
1071
            &this->follower_listener, &follower_node_events, this);
1072
    spa_node_set_callbacks(this->follower, &follower_node_callbacks, this);
1073
 
1074
-   if (this->convert)
1075
+   // TODO: adapt port bootstrap for arbitrary converter (incl. dummy)
1076
+   if (this->convert) {
1077
        spa_node_add_listener(this->convert,
1078
                &this->convert_listener, &convert_node_events, this);
1079
 
1080
-   reconfigure_mode(this, true, this->direction, NULL);
1081
+       configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_convert);
1082
+   } else {
1083
+       reconfigure_mode(this, true, this->direction, NULL);
1084
+   }
1085
 
1086
    link_io(this);
1087
 
1088
pipewire-1.0.1.tar.bz2/spa/plugins/videotestsrc/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/videotestsrc/plugin.c Changed
14
 
1
@@ -5,9 +5,12 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_videotestsrc_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/videotestsrc/videotestsrc.c -> pipewire-1.2.0.tar.gz/spa/plugins/videotestsrc/videotestsrc.c Changed
75
 
1
@@ -23,7 +23,9 @@
2
 #include <spa/param/param.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#define NAME "videotestsrc"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.videotestsrc");
9
 
10
 #define FRAMES_TO_TIME(port,f) ((port->current_format.info.raw.framerate.denom * (f) * SPA_NSEC_PER_SEC) / \
11
                                 (port->current_format.info.raw.framerate.num))
12
@@ -289,7 +291,7 @@
13
        if ((res = spa_system_timerfd_read(this->data_system,
14
                        this->timer_source.fd, &expirations)) < 0) {
15
            if (res != -EAGAIN)
16
-               spa_log_error(this->log, NAME " %p: timerfd error: %s",
17
+               spa_log_error(this->log, "%p: timerfd error: %s",
18
                        this, spa_strerror(res));
19
        }
20
    }
21
@@ -308,7 +310,7 @@
22
 
23
    if (spa_list_is_empty(&port->empty)) {
24
        set_timer(this, false);
25
-       spa_log_error(this->log, NAME " %p: out of buffers", this);
26
+       spa_log_error(this->log, "%p: out of buffers", this);
27
        return -EPIPE;
28
    }
29
    b = spa_list_first(&port->empty, struct buffer, link);
30
@@ -317,7 +319,7 @@
31
 
32
    n_bytes = b->outbuf->datas0.maxsize;
33
 
34
-   spa_log_trace(this->log, NAME " %p: dequeue buffer %d", this, b->id);
35
+   spa_log_trace(this->log, "%p: dequeue buffer %d", this, b->id);
36
 
37
    fill_buffer(this, b);
38
 
39
@@ -612,7 +614,7 @@
40
 static int clear_buffers(struct impl *this, struct port *port)
41
 {
42
    if (port->n_buffers > 0) {
43
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
44
+       spa_log_debug(this->log, "%p: clear buffers", this);
45
        port->n_buffers = 0;
46
        spa_list_init(&port->empty);
47
        this->started = false;
48
@@ -729,7 +731,7 @@
49
        b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
50
 
51
        if (d0.data == NULL) {
52
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
53
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
54
                      buffersi);
55
            return -EINVAL;
56
        }
57
@@ -769,7 +771,7 @@
58
    struct buffer *b = &port->buffersid;
59
    spa_return_if_fail(b->outstanding);
60
 
61
-   spa_log_trace(this->log, NAME " %p: reuse buffer %d", this, id);
62
+   spa_log_trace(this->log, "%p: reuse buffer %d", this, id);
63
 
64
    b->outstanding = false;
65
    spa_list_append(&port->empty, &b->link);
66
@@ -990,7 +992,7 @@
67
 
68
 const struct spa_handle_factory spa_videotestsrc_factory = {
69
    SPA_VERSION_HANDLE_FACTORY,
70
-   NAME,
71
+   "videotestsrc",
72
    &info,
73
    impl_get_size,
74
    impl_init,
75
pipewire-1.0.1.tar.bz2/spa/plugins/volume/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/volume/plugin.c Changed
14
 
1
@@ -5,9 +5,12 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_volume_factory;
8
 
9
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
10
+
11
 SPA_EXPORT
12
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
13
 {
14
pipewire-1.0.1.tar.bz2/spa/plugins/volume/volume.c -> pipewire-1.2.0.tar.gz/spa/plugins/volume/volume.c Changed
80
 
1
@@ -17,7 +17,9 @@
2
 #include <spa/param/param.h>
3
 #include <spa/pod/filter.h>
4
 
5
-#define NAME "volume"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.volume");
9
 
10
 #define DEFAULT_RATE       48000
11
 #define DEFAULT_CHANNELS   2
12
@@ -417,7 +419,7 @@
13
 static int clear_buffers(struct impl *this, struct port *port)
14
 {
15
    if (port->n_buffers > 0) {
16
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
17
+       spa_log_debug(this->log, "%p: clear buffers", this);
18
        port->n_buffers = 0;
19
        spa_list_init(&port->empty);
20
    }
21
@@ -529,7 +531,7 @@
22
            b->ptr = d0.data;
23
            b->size = d0.maxsize;
24
        } else {
25
-           spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this,
26
+           spa_log_error(this->log, "%p: invalid memory on buffer %p", this,
27
                      buffersi);
28
            return -EINVAL;
29
        }
30
@@ -572,13 +574,13 @@
31
    struct buffer *b = &port->buffersid;
32
 
33
    if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
34
-       spa_log_warn(this->log, NAME " %p: buffer %d not outstanding", this, id);
35
+       spa_log_warn(this->log, "%p: buffer %d not outstanding", this, id);
36
        return;
37
    }
38
 
39
    spa_list_append(&port->empty, &b->link);
40
    SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
41
-   spa_log_trace(this->log, NAME " %p: recycle buffer %d", this, id);
42
+   spa_log_trace(this->log, "%p: recycle buffer %d", this, id);
43
 }
44
 
45
 static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
46
@@ -649,7 +651,7 @@
47
 
48
        n_samples = n_bytes / sizeof(int16_t);
49
        for (i = 0; i < n_samples; i++)
50
-           dsti = srci * volume;
51
+           dsti = (int16_t)(srci * volume);
52
 
53
        sindex += n_bytes;
54
        dindex += n_bytes;
55
@@ -695,13 +697,13 @@
56
    }
57
 
58
    if ((dbuf = find_free_buffer(this, out_port)) == NULL) {
59
-                spa_log_error(this->log, NAME " %p: out of buffers", this);
60
+                spa_log_error(this->log, "%p: out of buffers", this);
61
        return -EPIPE;
62
    }
63
 
64
    sbuf = &in_port->buffersinput->buffer_id;
65
 
66
-   spa_log_trace(this->log, NAME " %p: do volume %d -> %d", this, sbuf->id, dbuf->id);
67
+   spa_log_trace(this->log, "%p: do volume %d -> %d", this, sbuf->id, dbuf->id);
68
    do_volume(this, dbuf->outbuf, sbuf->outbuf);
69
 
70
    output->buffer_id = dbuf->id;
71
@@ -866,7 +868,7 @@
72
 
73
 const struct spa_handle_factory spa_volume_factory = {
74
    SPA_VERSION_HANDLE_FACTORY,
75
-   NAME,
76
+   "volume",
77
    NULL,
78
    impl_get_size,
79
    impl_init,
80
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/dmabuf_fallback.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/dmabuf_fallback.c Changed
15
 
1
@@ -32,11 +32,11 @@
2
 }
3
 
4
 bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd) {
5
-   spa_log_error("DMA-BUF sync_file import IOCTL not available on this system");
6
+   spa_log_error(log, "DMA-BUF sync_file import IOCTL not available on this system");
7
    return false;
8
 }
9
 
10
 int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags) {
11
-   spa_log_error("DMA-BUF sync_file export IOCTL not available on this system");
12
+   spa_log_error(log, "DMA-BUF sync_file export IOCTL not available on this system");
13
    return false;
14
 }
15
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/meson.build -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/meson.build Changed
17
 
1
@@ -1,9 +1,14 @@
2
 spa_vulkan_sources = 
3
   'plugin.c',
4
+  'pixel-formats.c',
5
   'vulkan-compute-filter.c',
6
   'vulkan-compute-source.c',
7
   'vulkan-compute-utils.c',
8
-  'vulkan-utils.c'
9
+  'vulkan-blit-filter.c',
10
+  'vulkan-blit-dsp-filter.c',
11
+  'vulkan-blit-utils.c',
12
+  'vulkan-utils.c',
13
+  'utils.c',
14
 
15
 
16
 drm = dependency('libdrm')
17
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/pixel-formats.c Added
35
 
1
@@ -0,0 +1,33 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "pixel-formats.h"
7
+
8
+#include <spa/utils/defs.h>
9
+#include <spa/param/video/raw.h>
10
+
11
+struct pixel_info {
12
+   uint32_t format;
13
+   uint32_t bpp;
14
+} pixel_infos = {
15
+   { SPA_VIDEO_FORMAT_RGBA_F32, 16 },
16
+   { SPA_VIDEO_FORMAT_BGRA, 4 },
17
+   { SPA_VIDEO_FORMAT_RGBA, 4 },
18
+   { SPA_VIDEO_FORMAT_BGRx, 4 },
19
+   { SPA_VIDEO_FORMAT_RGBx, 4 },
20
+   { SPA_VIDEO_FORMAT_BGR, 3 },
21
+   { SPA_VIDEO_FORMAT_RGB, 3 },
22
+};
23
+
24
+bool get_pixel_format_info(uint32_t format, struct pixel_format_info *info)
25
+{
26
+   struct pixel_info *p;
27
+   SPA_FOR_EACH_ELEMENT(pixel_infos, p) {
28
+       if (p->format != format)
29
+           continue;
30
+       info->bpp = p->bpp;
31
+       return true;
32
+   }
33
+   return false;
34
+}
35
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/pixel-formats.h Added
14
 
1
@@ -0,0 +1,12 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <stdint.h>
7
+#include <stdbool.h>
8
+
9
+struct pixel_format_info {
10
+   uint32_t bpp;   // bytes per pixel
11
+};
12
+
13
+bool get_pixel_format_info(uint32_t format, struct pixel_format_info *info);
14
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/plugin.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/plugin.c Changed
29
 
1
@@ -5,9 +5,14 @@
2
 #include <errno.h>
3
 
4
 #include <spa/support/plugin.h>
5
+#include <spa/support/log.h>
6
 
7
 extern const struct spa_handle_factory spa_vulkan_compute_filter_factory;
8
 extern const struct spa_handle_factory spa_vulkan_compute_source_factory;
9
+extern const struct spa_handle_factory spa_vulkan_blit_filter_factory;
10
+extern const struct spa_handle_factory spa_vulkan_blit_dsp_filter_factory;
11
+
12
+SPA_LOG_TOPIC_ENUM_DEFINE_REGISTERED;
13
 
14
 SPA_EXPORT
15
 int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
16
@@ -22,6 +27,12 @@
17
    case 1:
18
        *factory = &spa_vulkan_compute_filter_factory;
19
        break;
20
+   case 2:
21
+       *factory = &spa_vulkan_blit_filter_factory;
22
+       break;
23
+   case 3:
24
+       *factory = &spa_vulkan_blit_dsp_filter_factory;
25
+       break;
26
    default:
27
        return 0;
28
    }
29
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/utils.c Added
93
 
1
@@ -0,0 +1,91 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "utils.h"
7
+
8
+#include <spa/param/param.h>
9
+#include <spa/param/video/dsp.h>
10
+#include <spa/param/video/raw.h>
11
+
12
+// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs
13
+// first and then falling back to those supported with SHM buffers.
14
+bool find_EnumFormatInfo(struct vulkan_format_infos *fmtInfos, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier)
15
+{
16
+   int64_t fmtIterator = 0;
17
+   int64_t maxIterator = 0;
18
+   if (caps & VULKAN_BUFFER_TYPE_CAP_SHM)
19
+       maxIterator += fmtInfos->formatCount;
20
+   if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF)
21
+       maxIterator += fmtInfos->formatCount;
22
+   // Count available formats until index underflows, while fmtIterator indexes the current format.
23
+   // Iterate twice over formats first time with modifiers, second time without if both caps are supported.
24
+   while (index < (uint32_t)-1 && fmtIterator < maxIterator) {
25
+       const struct vulkan_format_info *f_info = &fmtInfos->infosfmtIterator%fmtInfos->formatCount;
26
+       if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < fmtInfos->formatCount) {
27
+           // First round, check for modifiers
28
+           if (f_info->modifierCount > 0) {
29
+               index--;
30
+           }
31
+       } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) {
32
+           // Second round, every format should be supported.
33
+           index--;
34
+       }
35
+       fmtIterator++;
36
+   }
37
+
38
+   if (index != (uint32_t)-1) {
39
+       // No more formats available
40
+       return false;
41
+   }
42
+   // Undo end of loop increment
43
+   fmtIterator--;
44
+   *fmt_idx = fmtIterator%fmtInfos->formatCount;
45
+   // Loop finished in first round
46
+   *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < fmtInfos->formatCount;
47
+   return true;
48
+}
49
+
50
+struct spa_pod *build_dsp_EnumFormat(const struct vulkan_format_info *fmt, bool with_modifiers, struct spa_pod_builder *builder)
51
+{
52
+   struct spa_pod_frame f2;
53
+   uint32_t i, c;
54
+
55
+   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
56
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
57
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0);
58
+   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt->spa_format), 0);
59
+   if (with_modifiers && fmt->modifierCount > 0) {
60
+       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
61
+       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
62
+       for (i = 0, c = 0; i < fmt->modifierCount; i++) {
63
+           spa_pod_builder_long(builder, fmt->infosi.props.drmFormatModifier);
64
+           if (c++ == 0)
65
+               spa_pod_builder_long(builder, fmt->infosi.props.drmFormatModifier);
66
+       }
67
+       spa_pod_builder_pop(builder, &f1);
68
+   }
69
+   return spa_pod_builder_pop(builder, &f0);
70
+}
71
+
72
+struct spa_pod *build_raw_EnumFormat(const struct vulkan_format_info *fmt, bool with_modifiers, struct spa_pod_builder *builder)
73
+{
74
+   struct spa_pod_frame f2;
75
+   uint32_t i, c;
76
+
77
+   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
78
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
79
+   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
80
+   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt->spa_format), 0);
81
+   if (with_modifiers && fmt->modifierCount > 0) {
82
+       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
83
+       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
84
+       for (i = 0, c = 0; i < fmt->modifierCount; i++) {
85
+           spa_pod_builder_long(builder, fmt->infosi.props.drmFormatModifier);
86
+           if (c++ == 0)
87
+               spa_pod_builder_long(builder, fmt->infosi.props.drmFormatModifier);
88
+       }
89
+       spa_pod_builder_pop(builder, &f1);
90
+   }
91
+   return spa_pod_builder_pop(builder, &f0);
92
+}
93
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/utils.h Added
13
 
1
@@ -0,0 +1,11 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include "vulkan-types.h"
7
+#include "spa/pod/builder.h"
8
+
9
+bool find_EnumFormatInfo(struct vulkan_format_infos *fmtInfos, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier);
10
+
11
+struct spa_pod *build_dsp_EnumFormat(const struct vulkan_format_info *fmt, bool with_modifiers, struct spa_pod_builder *builder);
12
+struct spa_pod *build_raw_EnumFormat(const struct vulkan_format_info *fmt, bool with_modifiers, struct spa_pod_builder *builder);
13
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-blit-dsp-filter.c Added
956
 
1
@@ -0,0 +1,954 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <errno.h>
8
+#include <stddef.h>
9
+#include <unistd.h>
10
+#include <string.h>
11
+#include <stdio.h>
12
+#include <stdatomic.h>
13
+#include <pthread.h>
14
+
15
+#include <spa/support/plugin.h>
16
+#include <spa/support/log.h>
17
+#include <spa/support/loop.h>
18
+#include <spa/utils/list.h>
19
+#include <spa/utils/keys.h>
20
+#include <spa/utils/names.h>
21
+#include <spa/utils/string.h>
22
+#include <spa/node/node.h>
23
+#include <spa/node/utils.h>
24
+#include <spa/node/io.h>
25
+#include <spa/node/keys.h>
26
+#include <spa/param/video/format-utils.h>
27
+#include <spa/param/param.h>
28
+#include <spa/pod/filter.h>
29
+
30
+#include "pixel-formats.h"
31
+#include "vulkan-blit-utils.h"
32
+
33
+#undef SPA_LOG_TOPIC_DEFAULT
34
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
35
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.vulkan.blit-dsp-filter");
36
+
37
+struct buffer {
38
+   uint32_t id;
39
+#define BUFFER_FLAG_OUT (1<<0)
40
+   uint32_t flags;
41
+   struct spa_buffer *outbuf;
42
+   struct spa_meta_header *h;
43
+   struct spa_list link;
44
+};
45
+
46
+struct port {
47
+   uint64_t info_all;
48
+   struct spa_port_info info;
49
+
50
+   enum spa_direction direction;
51
+#define IDX_EnumFormat 0
52
+#define IDX_Meta   1
53
+#define IDX_IO     2
54
+#define IDX_Format 3
55
+#define IDX_Buffer 4
56
+#define N_PORT_PARAMS  5
57
+   struct spa_param_info paramsN_PORT_PARAMS;
58
+
59
+   struct spa_io_buffers *io;
60
+
61
+   bool have_format;
62
+   struct spa_video_info current_format;
63
+
64
+   struct buffer buffersMAX_BUFFERS;
65
+   uint32_t n_buffers;
66
+
67
+   struct spa_list empty;
68
+   struct spa_list ready;
69
+   uint32_t stream_id;
70
+};
71
+
72
+struct impl {
73
+   struct spa_handle handle;
74
+   struct spa_node node;
75
+
76
+   struct spa_log *log;
77
+
78
+   struct spa_io_position *position;
79
+
80
+   uint64_t info_all;
81
+   struct spa_node_info info;
82
+#define IDX_PropInfo   0
83
+#define IDX_Props  1
84
+#define N_NODE_PARAMS  2
85
+   struct spa_param_info paramsN_NODE_PARAMS;
86
+
87
+   struct spa_hook_list hooks;
88
+   struct spa_callbacks callbacks;
89
+
90
+   // Synchronization between main and data thread
91
+   atomic_bool started;
92
+   pthread_rwlock_t renderlock;
93
+
94
+   struct vulkan_blit_state state;
95
+   struct vulkan_pass pass;
96
+   struct port port2;
97
+};
98
+
99
+#define CHECK_PORT(this,d,p)  ((p) < 1)
100
+
101
+static int lock_init(struct impl *this)
102
+{
103
+   return pthread_rwlock_init(&this->renderlock, NULL);
104
+}
105
+
106
+static void lock_destroy(struct impl *this)
107
+{
108
+   pthread_rwlock_destroy(&this->renderlock);
109
+}
110
+
111
+static int lock_renderer(struct impl *this)
112
+{
113
+   spa_log_info(this->log, "Lock renderer");
114
+   return pthread_rwlock_wrlock(&this->renderlock);
115
+}
116
+
117
+static int unlock_renderer(struct impl *this)
118
+{
119
+   spa_log_info(this->log, "Unlock renderer");
120
+   return pthread_rwlock_unlock(&this->renderlock);
121
+}
122
+
123
+static int impl_node_enum_params(void *object, int seq,
124
+                uint32_t id, uint32_t start, uint32_t num,
125
+                const struct spa_pod *filter)
126
+{
127
+   struct impl *this = object;
128
+   struct spa_pod *param;
129
+   struct spa_pod_builder b = { 0 };
130
+   uint8_t buffer1024;
131
+   struct spa_result_node_params result;
132
+   uint32_t count = 0;
133
+
134
+   spa_return_val_if_fail(this != NULL, -EINVAL);
135
+   spa_return_val_if_fail(num != 0, -EINVAL);
136
+
137
+   result.id = id;
138
+   result.next = start;
139
+      next:
140
+   result.index = result.next++;
141
+
142
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
143
+
144
+   switch (id) {
145
+   default:
146
+       return -ENOENT;
147
+   }
148
+
149
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
150
+       goto next;
151
+
152
+   spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
153
+
154
+   if (++count != num)
155
+       goto next;
156
+
157
+   return 0;
158
+}
159
+
160
+static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
161
+{
162
+   struct impl *this = object;
163
+
164
+   spa_return_val_if_fail(this != NULL, -EINVAL);
165
+
166
+   switch (id) {
167
+   case SPA_IO_Position:
168
+       if (size > 0 && size < sizeof(struct spa_io_position))
169
+           return -EINVAL;
170
+       this->position = data;
171
+       break;
172
+   default:
173
+       return -ENOENT;
174
+   }
175
+   return 0;
176
+}
177
+static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
178
+                  const struct spa_pod *param)
179
+{
180
+   struct impl *this = object;
181
+
182
+   spa_return_val_if_fail(this != NULL, -EINVAL);
183
+
184
+   switch (id) {
185
+   default:
186
+       return -ENOENT;
187
+   }
188
+   return 0;
189
+}
190
+
191
+static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t id)
192
+{
193
+   struct buffer *b = &port->buffersid;
194
+
195
+   if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
196
+       spa_log_debug(this->log, "%p: reuse buffer %d", this, id);
197
+
198
+       SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
199
+       spa_list_append(&port->empty, &b->link);
200
+   }
201
+}
202
+
203
+static int impl_node_send_command(void *object, const struct spa_command *command)
204
+{
205
+   struct impl *this = object;
206
+
207
+   spa_return_val_if_fail(this != NULL, -EINVAL);
208
+   spa_return_val_if_fail(command != NULL, -EINVAL);
209
+
210
+   switch (SPA_NODE_COMMAND_ID(command)) {
211
+   case SPA_NODE_COMMAND_Start:
212
+       if (this->started)
213
+           return 0;
214
+
215
+       this->started = true;
216
+       spa_vulkan_blit_start(&this->state);
217
+       // The main thread needs to lock the renderer before changing its state
218
+       break;
219
+
220
+   case SPA_NODE_COMMAND_Suspend:
221
+   case SPA_NODE_COMMAND_Pause:
222
+       if (!this->started)
223
+           return 0;
224
+
225
+       lock_renderer(this);
226
+       spa_vulkan_blit_stop(&this->state);
227
+       this->started = false;
228
+       unlock_renderer(this);
229
+       // Locking the renderer from the renderer is no longer required
230
+       break;
231
+   default:
232
+       return -ENOTSUP;
233
+   }
234
+   return 0;
235
+}
236
+
237
+static const struct spa_dict_item node_info_items = {
238
+   { SPA_KEY_MEDIA_CLASS, "Video/Filter" },
239
+};
240
+
241
+static void emit_node_info(struct impl *this, bool full)
242
+{
243
+   uint64_t old = full ? this->info.change_mask : 0;
244
+   if (full)
245
+       this->info.change_mask = this->info_all;
246
+   if (this->info.change_mask) {
247
+       this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
248
+       spa_node_emit_info(&this->hooks, &this->info);
249
+       this->info.change_mask = old;
250
+   }
251
+}
252
+
253
+static void emit_port_info(struct impl *this, struct port *port, bool full)
254
+{
255
+   uint64_t old = full ? port->info.change_mask : 0;
256
+   if (full)
257
+       port->info.change_mask = port->info_all;
258
+   if (port->info.change_mask) {
259
+       struct spa_dict_item items1;
260
+
261
+       items0 = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "32 bit float RGBA video");
262
+       port->info.props = &SPA_DICT_INIT(items, 1);
263
+       spa_node_emit_port_info(&this->hooks,
264
+               port->direction, 0, &port->info);
265
+       port->info.change_mask = old;
266
+   }
267
+}
268
+
269
+static int
270
+impl_node_add_listener(void *object,
271
+       struct spa_hook *listener,
272
+       const struct spa_node_events *events,
273
+       void *data)
274
+{
275
+   struct impl *this = object;
276
+   struct spa_hook_list save;
277
+
278
+   spa_return_val_if_fail(this != NULL, -EINVAL);
279
+
280
+   spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
281
+
282
+   emit_node_info(this, true);
283
+   emit_port_info(this, &this->port0, true);
284
+   emit_port_info(this, &this->port1, true);
285
+
286
+   spa_hook_list_join(&this->hooks, &save);
287
+
288
+   return 0;
289
+}
290
+
291
+static int
292
+impl_node_set_callbacks(void *object,
293
+           const struct spa_node_callbacks *callbacks,
294
+           void *data)
295
+{
296
+   struct impl *this = object;
297
+
298
+   spa_return_val_if_fail(this != NULL, -EINVAL);
299
+
300
+   this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
301
+
302
+   return 0;
303
+}
304
+
305
+static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id,
306
+       const struct spa_dict *props)
307
+{
308
+   return -ENOTSUP;
309
+}
310
+
311
+static int
312
+impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id)
313
+{
314
+   return -ENOTSUP;
315
+}
316
+
317
+static bool port_has_fixated_format(struct port *p)
318
+{
319
+   if (!p->have_format)
320
+       return false;
321
+   return p->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
322
+       && p->current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
323
+}
324
+
325
+static int port_enum_formats(void *object,
326
+                enum spa_direction direction, uint32_t port_id,
327
+                uint32_t index,
328
+                const struct spa_pod *filter,
329
+                struct spa_pod **param,
330
+                struct spa_pod_builder *builder)
331
+{
332
+   struct impl *this = object;
333
+
334
+   if (port_has_fixated_format(&this->portport_id)) {
335
+       if (index == 0) {
336
+           spa_log_info(this->log, "enum_formats fixated format idx: %d, format %d, has_modifier 1",
337
+                   index, this->portport_id.current_format.info.dsp.format);
338
+           *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.dsp);
339
+           return 1;
340
+       }
341
+       return spa_vulkan_blit_enumerate_formats(&this->state, index-1, spa_vulkan_blit_get_buffer_caps(&this->state, direction), param, builder);
342
+   } else {
343
+       return spa_vulkan_blit_enumerate_formats(&this->state, index, spa_vulkan_blit_get_buffer_caps(&this->state, direction), param, builder);
344
+   }
345
+}
346
+
347
+static int port_get_buffer_props(struct impl *this, struct port *port,
348
+       uint32_t *blocks, uint32_t *size, uint32_t *stride, bool *is_dmabuf)
349
+{
350
+   if (this->position == NULL)
351
+       return -EIO;
352
+
353
+   spa_log_debug(this->log, "%p: %dx%d stride %d", this,
354
+           this->position->video.size.width,
355
+           this->position->video.size.height,
356
+           this->position->video.stride);
357
+
358
+   if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
359
+       *is_dmabuf = true;
360
+
361
+       struct vulkan_modifier_info *mod_info = spa_vulkan_blit_get_modifier_info(&this->state,
362
+           &port->current_format);
363
+       *blocks = mod_info->props.drmFormatModifierPlaneCount;
364
+   } else {
365
+       *is_dmabuf = false;
366
+       *blocks = 1;
367
+       *size = this->position->video.stride * this->position->video.size.height;
368
+       *stride = this->position->video.stride;
369
+   }
370
+   return 0;
371
+}
372
+
373
+static int
374
+impl_node_port_enum_params(void *object, int seq,
375
+           enum spa_direction direction, uint32_t port_id,
376
+           uint32_t id, uint32_t start, uint32_t num,
377
+           const struct spa_pod *filter)
378
+{
379
+   struct impl *this = object;
380
+   struct port *port;
381
+   struct spa_pod_builder b = { 0 };
382
+   uint8_t buffer1024;
383
+   struct spa_pod *param;
384
+   struct spa_result_node_params result;
385
+   uint32_t count = 0;
386
+   int res;
387
+
388
+   spa_return_val_if_fail(this != NULL, -EINVAL);
389
+   spa_return_val_if_fail(num != 0, -EINVAL);
390
+
391
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
392
+   port = &this->portdirection;
393
+
394
+   result.id = id;
395
+   result.next = start;
396
+      next:
397
+   result.index = result.next++;
398
+
399
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
400
+
401
+   switch (id) {
402
+   case SPA_PARAM_EnumFormat:
403
+       if ((res = port_enum_formats(this, direction, port_id,
404
+                       result.index, filter, &param, &b)) <= 0)
405
+           return res;
406
+       break;
407
+
408
+   case SPA_PARAM_Format:
409
+       if (!port->have_format)
410
+           return -EIO;
411
+       if (result.index > 0)
412
+           return 0;
413
+
414
+       param = spa_format_video_dsp_build(&b, id, &port->current_format.info.dsp);
415
+       break;
416
+
417
+   case SPA_PARAM_Buffers:
418
+   {
419
+       if (!port->have_format)
420
+           return -EIO;
421
+       if (result.index > 0)
422
+           return 0;
423
+
424
+       int ret;
425
+       uint32_t blocks, size, stride;
426
+       bool is_dmabuf;
427
+
428
+       if ((ret = port_get_buffer_props(this, port, &blocks, &size, &stride, &is_dmabuf)) < 0)
429
+           return ret;
430
+
431
+       if (is_dmabuf) {
432
+           param = spa_pod_builder_add_object(&b,
433
+               SPA_TYPE_OBJECT_ParamBuffers, id,
434
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
435
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(blocks),
436
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf));
437
+       } else {
438
+           param = spa_pod_builder_add_object(&b,
439
+               SPA_TYPE_OBJECT_ParamBuffers, id,
440
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
441
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(blocks),
442
+               SPA_PARAM_BUFFERS_size,  SPA_POD_Int(size),
443
+               SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(stride),
444
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr));
445
+       }
446
+
447
+       break;
448
+   }
449
+   case SPA_PARAM_Meta:
450
+       switch (result.index) {
451
+       case 0:
452
+           param = spa_pod_builder_add_object(&b,
453
+               SPA_TYPE_OBJECT_ParamMeta, id,
454
+               SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
455
+               SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
456
+           break;
457
+
458
+       default:
459
+           return 0;
460
+       }
461
+       break;
462
+   default:
463
+       return -ENOENT;
464
+   }
465
+
466
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
467
+       goto next;
468
+
469
+   spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
470
+
471
+   if (++count != num)
472
+       goto next;
473
+
474
+   return 0;
475
+}
476
+
477
+static int clear_buffers(struct impl *this, struct port *port)
478
+{
479
+   if (port->n_buffers > 0) {
480
+       spa_log_debug(this->log, "%p: clear buffers", this);
481
+       lock_renderer(this);
482
+       spa_vulkan_blit_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format, 0, NULL);
483
+       spa_vulkan_blit_clear_pass(&this->state, &this->pass);
484
+       unlock_renderer(this);
485
+       port->n_buffers = 0;
486
+       spa_list_init(&port->empty);
487
+       spa_list_init(&port->ready);
488
+   }
489
+   return 0;
490
+}
491
+
492
+static int port_set_dsp_format(struct impl *this, struct port *port,
493
+              uint32_t flags, struct spa_video_info *info,
494
+              bool *has_modifier, bool *modifier_fixed,
495
+              const struct spa_pod *format)
496
+{
497
+       if (spa_format_video_dsp_parse(format, &info->info.dsp) < 0)
498
+           return -EINVAL;
499
+
500
+       if (info->info.dsp.format != SPA_VIDEO_FORMAT_DSP_F32)
501
+           return -EINVAL;
502
+
503
+       this->state.streamsport->stream_id.dim.width = this->position->video.size.width;
504
+       this->state.streamsport->stream_id.dim.height = this->position->video.size.height;
505
+       this->state.streamsport->stream_id.bpp = 16;
506
+       *has_modifier = SPA_FLAG_IS_SET(info->info.dsp.flags, SPA_VIDEO_FLAG_MODIFIER);
507
+
508
+       // fixate modifier
509
+       if (port->direction == SPA_DIRECTION_OUTPUT
510
+               && info->info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
511
+               && info->info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
512
+           const struct spa_pod_prop *mod_prop;
513
+           if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL)
514
+               return -EINVAL;
515
+
516
+           const struct spa_pod *mod_pod = &mod_prop->value;
517
+           uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod);
518
+           uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod);
519
+           if (modifierCount <= 1)
520
+               return -EINVAL;
521
+           // SPA_POD_CHOICE carries the "preferred" value at position 0
522
+           modifierCount -= 1;
523
+           modifiers++;
524
+           uint64_t fixed_modifier;
525
+           if (spa_vulkan_blit_fixate_modifier(&this->state, &this->state.streamsport->stream_id, info, modifierCount, modifiers, &fixed_modifier) != 0)
526
+               return -EINVAL;
527
+
528
+           spa_log_info(this->log, "modifier fixated %"PRIu64, fixed_modifier);
529
+
530
+           info->info.dsp.modifier = fixed_modifier;
531
+           info->info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
532
+           *modifier_fixed = true;
533
+       }
534
+
535
+   return 0;
536
+}
537
+
538
+static int port_set_format(struct impl *this, struct port *port,
539
+              uint32_t flags,
540
+              const struct spa_pod *format)
541
+{
542
+   int res;
543
+
544
+   if (format == NULL) {
545
+       port->have_format = false;
546
+       clear_buffers(this, port);
547
+   } else {
548
+       struct spa_video_info info = { 0 };
549
+
550
+       if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
551
+           return res;
552
+
553
+       if (info.media_type != SPA_MEDIA_TYPE_video)
554
+           return -EINVAL;
555
+
556
+       bool has_modifier = false;
557
+       bool modifier_fixed = false;
558
+       if ((res = port_set_dsp_format(this, port, flags, &info, &has_modifier, &modifier_fixed, format)) < 0)
559
+           return res;
560
+
561
+       if (has_modifier) {
562
+           SPA_FLAG_SET(port->info.flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
563
+       } else {
564
+           SPA_FLAG_CLEAR(port->info.flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
565
+       }
566
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
567
+
568
+       port->current_format = info;
569
+       port->have_format = true;
570
+
571
+       if (modifier_fixed) {
572
+           port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
573
+           port->paramsIDX_EnumFormat.flags ^= SPA_PARAM_INFO_SERIAL;
574
+           emit_port_info(this, port, false);
575
+           return 0;
576
+       }
577
+   }
578
+
579
+   port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
580
+   if (port->have_format) {
581
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
582
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
583
+   } else {
584
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
585
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
586
+   }
587
+   emit_port_info(this, port, false);
588
+
589
+   return 0;
590
+}
591
+
592
+static int
593
+impl_node_port_set_param(void *object,
594
+            enum spa_direction direction, uint32_t port_id,
595
+            uint32_t id, uint32_t flags,
596
+            const struct spa_pod *param)
597
+{
598
+   struct impl *this = object;
599
+   struct port *port;
600
+   int res;
601
+
602
+   spa_return_val_if_fail(this != NULL, -EINVAL);
603
+   spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL);
604
+   port = &this->portdirection;
605
+
606
+   switch (id) {
607
+   case SPA_PARAM_Format:
608
+       res = port_set_format(this, port, flags, param);
609
+       break;
610
+   default:
611
+       return -ENOENT;
612
+   }
613
+   return res;
614
+}
615
+
616
+static int
617
+impl_node_port_use_buffers(void *object,
618
+              enum spa_direction direction,
619
+              uint32_t port_id,
620
+              uint32_t flags,
621
+              struct spa_buffer **buffers,
622
+              uint32_t n_buffers)
623
+{
624
+   struct impl *this = object;
625
+   struct port *port;
626
+   uint32_t i;
627
+
628
+   spa_return_val_if_fail(this != NULL, -EINVAL);
629
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
630
+   port = &this->portdirection;
631
+
632
+   clear_buffers(this, port);
633
+
634
+   if (n_buffers > 0 && !port->have_format)
635
+       return -EIO;
636
+   if (n_buffers > MAX_BUFFERS)
637
+       return -ENOSPC;
638
+
639
+   lock_renderer(this);
640
+   for (i = 0; i < n_buffers; i++) {
641
+       struct buffer *b;
642
+
643
+       b = &port->buffersi;
644
+       b->id = i;
645
+       b->outbuf = buffersi;
646
+       b->flags = 0;
647
+       b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
648
+
649
+       spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
650
+       spa_list_append(&port->empty, &b->link);
651
+   }
652
+   spa_vulkan_blit_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format, n_buffers, buffers);
653
+   port->n_buffers = n_buffers;
654
+   if (n_buffers > 0)
655
+       spa_vulkan_blit_init_pass(&this->state, &this->pass);
656
+   unlock_renderer(this);
657
+
658
+   return 0;
659
+}
660
+
661
+static int
662
+impl_node_port_set_io(void *object,
663
+             enum spa_direction direction,
664
+             uint32_t port_id,
665
+             uint32_t id,
666
+             void *data, size_t size)
667
+{
668
+   struct impl *this = object;
669
+   struct port *port;
670
+
671
+   spa_return_val_if_fail(this != NULL, -EINVAL);
672
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
673
+   port = &this->portdirection;
674
+
675
+   switch (id) {
676
+   case SPA_IO_Buffers:
677
+       port->io = data;
678
+       break;
679
+   default:
680
+       return -ENOENT;
681
+   }
682
+   return 0;
683
+}
684
+
685
+static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
686
+{
687
+   struct impl *this = object;
688
+   struct port *port;
689
+
690
+   spa_return_val_if_fail(this != NULL, -EINVAL);
691
+   spa_return_val_if_fail(port_id == 0, -EINVAL);
692
+
693
+   port = &this->portSPA_DIRECTION_OUTPUT;
694
+   spa_return_val_if_fail(buffer_id < port->n_buffers, -EINVAL);
695
+
696
+   reuse_buffer(this, port, buffer_id);
697
+
698
+   return 0;
699
+}
700
+
701
+static int impl_node_process(void *object)
702
+{
703
+   struct impl *this = object;
704
+   struct port *inport, *outport;
705
+   struct spa_io_buffers *inio, *outio;
706
+   struct buffer *b;
707
+
708
+   spa_return_val_if_fail(this != NULL, -EINVAL);
709
+   spa_return_val_if_fail(this->started, -EINVAL);
710
+
711
+   inport = &this->portSPA_DIRECTION_INPUT;
712
+   if ((inio = inport->io) == NULL)
713
+       return -EIO;
714
+
715
+   if (inio->status != SPA_STATUS_HAVE_DATA)
716
+       return inio->status;
717
+
718
+   if (inio->buffer_id >= inport->n_buffers) {
719
+       inio->status = -EINVAL;
720
+       return -EINVAL;
721
+   }
722
+
723
+   outport = &this->portSPA_DIRECTION_OUTPUT;
724
+   if ((outio = outport->io) == NULL)
725
+       return -EIO;
726
+
727
+   if (outio->status == SPA_STATUS_HAVE_DATA)
728
+       return SPA_STATUS_HAVE_DATA;
729
+
730
+   if (outio->buffer_id < outport->n_buffers) {
731
+       reuse_buffer(this, outport, outio->buffer_id);
732
+       outio->buffer_id = SPA_ID_INVALID;
733
+   }
734
+
735
+   if (spa_list_is_empty(&outport->empty)) {
736
+       spa_log_debug(this->log, "%p: out of buffers", this);
737
+       return -EPIPE;
738
+   }
739
+
740
+   if (pthread_rwlock_tryrdlock(&this->renderlock) < 0) {
741
+       return -EBUSY;
742
+   }
743
+
744
+   b = &inport->buffersinio->buffer_id;
745
+   this->pass.in_stream_id = SPA_DIRECTION_INPUT;
746
+   this->pass.in_buffer_id = b->id;
747
+   inio->status = SPA_STATUS_NEED_DATA;
748
+
749
+   b = spa_list_first(&outport->empty, struct buffer, link);
750
+   spa_list_remove(&b->link);
751
+   SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
752
+   this->pass.out_stream_id = SPA_DIRECTION_OUTPUT;
753
+   this->pass.out_buffer_id = b->id;
754
+
755
+   spa_log_debug(this->log, "filter into %d", b->id);
756
+
757
+   spa_vulkan_blit_process(&this->state, &this->pass);
758
+   spa_vulkan_blit_reset_pass(&this->state, &this->pass);
759
+
760
+   b->outbuf->datas0.chunk->offset = 0;
761
+   b->outbuf->datas0.chunk->size = b->outbuf->datas0.maxsize;
762
+   b->outbuf->datas0.chunk->stride = this->position->video.stride;
763
+
764
+   outio->buffer_id = b->id;
765
+   outio->status = SPA_STATUS_HAVE_DATA;
766
+
767
+   pthread_rwlock_unlock(&this->renderlock);
768
+
769
+   return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA;
770
+}
771
+
772
+static const struct spa_node_methods impl_node = {
773
+   SPA_VERSION_NODE_METHODS,
774
+   .add_listener = impl_node_add_listener,
775
+   .set_callbacks = impl_node_set_callbacks,
776
+   .enum_params = impl_node_enum_params,
777
+   .set_param = impl_node_set_param,
778
+   .set_io = impl_node_set_io,
779
+   .send_command = impl_node_send_command,
780
+   .add_port = impl_node_add_port,
781
+   .remove_port = impl_node_remove_port,
782
+   .port_enum_params = impl_node_port_enum_params,
783
+   .port_set_param = impl_node_port_set_param,
784
+   .port_use_buffers = impl_node_port_use_buffers,
785
+   .port_set_io = impl_node_port_set_io,
786
+   .port_reuse_buffer = impl_node_port_reuse_buffer,
787
+   .process = impl_node_process,
788
+};
789
+
790
+static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
791
+{
792
+   struct impl *this;
793
+
794
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
795
+   spa_return_val_if_fail(interface != NULL, -EINVAL);
796
+
797
+   this = (struct impl *) handle;
798
+
799
+   if (spa_streq(type, SPA_TYPE_INTERFACE_Node))
800
+       *interface = &this->node;
801
+   else
802
+       return -ENOENT;
803
+
804
+   return 0;
805
+}
806
+
807
+static int impl_clear(struct spa_handle *handle)
808
+{
809
+   struct impl *this;
810
+
811
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
812
+
813
+   this = (struct impl *) handle;
814
+
815
+   spa_vulkan_blit_unprepare(&this->state);
816
+   spa_vulkan_blit_deinit(&this->state);
817
+   lock_destroy(this);
818
+   return 0;
819
+}
820
+
821
+static size_t
822
+impl_get_size(const struct spa_handle_factory *factory,
823
+         const struct spa_dict *params)
824
+{
825
+   return sizeof(struct impl);
826
+}
827
+
828
+static int
829
+impl_init(const struct spa_handle_factory *factory,
830
+     struct spa_handle *handle,
831
+     const struct spa_dict *info,
832
+     const struct spa_support *support,
833
+     uint32_t n_support)
834
+{
835
+   struct impl *this;
836
+   struct port *port;
837
+
838
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
839
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
840
+
841
+   handle->get_interface = impl_get_interface;
842
+   handle->clear = impl_clear;
843
+
844
+   this = (struct impl *) handle;
845
+
846
+   this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
847
+   this->state.log = this->log;
848
+
849
+   spa_hook_list_init(&this->hooks);
850
+
851
+   this->node.iface = SPA_INTERFACE_INIT(
852
+           SPA_TYPE_INTERFACE_Node,
853
+           SPA_VERSION_NODE,
854
+           &impl_node, this);
855
+
856
+   this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
857
+           SPA_NODE_CHANGE_MASK_PROPS |
858
+           SPA_NODE_CHANGE_MASK_PARAMS;
859
+   this->info = SPA_NODE_INFO_INIT();
860
+   this->info.max_output_ports = 1;
861
+   this->info.max_input_ports = 1;
862
+   this->info.flags = SPA_NODE_FLAG_RT;
863
+   this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
864
+   this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
865
+   this->info.params = this->params;
866
+   this->info.n_params = N_NODE_PARAMS;
867
+
868
+   lock_init(this);
869
+
870
+   port = &this->portSPA_DIRECTION_INPUT;
871
+   port->stream_id = SPA_DIRECTION_INPUT;
872
+   port->direction = SPA_DIRECTION_INPUT;
873
+   port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
874
+           SPA_PORT_CHANGE_MASK_PARAMS |
875
+           SPA_PORT_CHANGE_MASK_PROPS;
876
+   port->info = SPA_PORT_INFO_INIT();
877
+   port->info.flags = SPA_PORT_FLAG_NO_REF;
878
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
879
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
880
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
881
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
882
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
883
+   port->info.params = port->params;
884
+   port->info.n_params = N_PORT_PARAMS;
885
+   spa_vulkan_blit_init_stream(&this->state, &this->state.streamsport->stream_id,
886
+           SPA_DIRECTION_INPUT, NULL);
887
+   spa_list_init(&port->empty);
888
+   spa_list_init(&port->ready);
889
+
890
+   port = &this->portSPA_DIRECTION_OUTPUT;
891
+   port->stream_id = SPA_DIRECTION_OUTPUT;
892
+   port->direction = SPA_DIRECTION_OUTPUT;
893
+   port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
894
+           SPA_PORT_CHANGE_MASK_PARAMS |
895
+           SPA_PORT_CHANGE_MASK_PROPS;
896
+   port->info = SPA_PORT_INFO_INIT();
897
+   port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
898
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
899
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
900
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
901
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
902
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
903
+   port->info.params = port->params;
904
+   port->info.n_params = N_PORT_PARAMS;
905
+   spa_list_init(&port->empty);
906
+   spa_list_init(&port->ready);
907
+   spa_vulkan_blit_init_stream(&this->state, &this->state.streamsport->stream_id,
908
+           SPA_DIRECTION_OUTPUT, NULL);
909
+
910
+   this->state.n_streams = 2;
911
+   spa_vulkan_blit_init(&this->state);
912
+   spa_vulkan_blit_prepare(&this->state);
913
+
914
+   return 0;
915
+}
916
+
917
+static const struct spa_interface_info impl_interfaces = {
918
+   {SPA_TYPE_INTERFACE_Node,},
919
+};
920
+
921
+static int
922
+impl_enum_interface_info(const struct spa_handle_factory *factory,
923
+            const struct spa_interface_info **info,
924
+            uint32_t *index)
925
+{
926
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
927
+   spa_return_val_if_fail(info != NULL, -EINVAL);
928
+   spa_return_val_if_fail(index != NULL, -EINVAL);
929
+
930
+   switch (*index) {
931
+   case 0:
932
+       *info = &impl_interfaces*index;
933
+       break;
934
+   default:
935
+       return 0;
936
+   }
937
+   (*index)++;
938
+   return 1;
939
+}
940
+
941
+static const struct spa_dict_item info_items = {
942
+   { SPA_KEY_FACTORY_AUTHOR, "Columbarius <co1umbarius@protonmail.com>" },
943
+   { SPA_KEY_FACTORY_DESCRIPTION, "Convert video frames using a vulkan blit" },
944
+};
945
+
946
+static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items);
947
+
948
+const struct spa_handle_factory spa_vulkan_blit_dsp_filter_factory = {
949
+   SPA_VERSION_HANDLE_FACTORY,
950
+   SPA_NAME_API_VULKAN_BLIT_DSP_FILTER,
951
+   &info,
952
+   impl_get_size,
953
+   impl_init,
954
+   impl_enum_interface_info,
955
+};
956
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-blit-filter.c Added
1058
 
1
@@ -0,0 +1,1056 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <errno.h>
8
+#include <stddef.h>
9
+#include <unistd.h>
10
+#include <string.h>
11
+#include <stdio.h>
12
+#include <stdatomic.h>
13
+#include <pthread.h>
14
+
15
+#include <spa/support/plugin.h>
16
+#include <spa/support/log.h>
17
+#include <spa/support/loop.h>
18
+#include <spa/utils/list.h>
19
+#include <spa/utils/keys.h>
20
+#include <spa/utils/names.h>
21
+#include <spa/utils/string.h>
22
+#include <spa/node/node.h>
23
+#include <spa/node/utils.h>
24
+#include <spa/node/io.h>
25
+#include <spa/node/keys.h>
26
+#include <spa/param/video/format-utils.h>
27
+#include <spa/param/param.h>
28
+#include <spa/pod/filter.h>
29
+
30
+#include "pixel-formats.h"
31
+#include "vulkan-blit-utils.h"
32
+
33
+#undef SPA_LOG_TOPIC_DEFAULT
34
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
35
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.vulkan.blit-filter");
36
+
37
+struct buffer {
38
+   uint32_t id;
39
+#define BUFFER_FLAG_OUT (1<<0)
40
+   uint32_t flags;
41
+   struct spa_buffer *outbuf;
42
+   struct spa_meta_header *h;
43
+   struct spa_list link;
44
+};
45
+
46
+struct port {
47
+   uint64_t info_all;
48
+   struct spa_port_info info;
49
+
50
+   enum spa_direction direction;
51
+#define IDX_EnumFormat 0
52
+#define IDX_Meta   1
53
+#define IDX_IO     2
54
+#define IDX_Format 3
55
+#define IDX_Buffer 4
56
+#define N_PORT_PARAMS  5
57
+   struct spa_param_info paramsN_PORT_PARAMS;
58
+
59
+   struct spa_io_buffers *io;
60
+
61
+   bool have_format;
62
+   struct spa_video_info current_format;
63
+
64
+   struct buffer buffersMAX_BUFFERS;
65
+   uint32_t n_buffers;
66
+
67
+   struct spa_list empty;
68
+   struct spa_list ready;
69
+   uint32_t stream_id;
70
+};
71
+
72
+struct impl {
73
+   struct spa_handle handle;
74
+   struct spa_node node;
75
+
76
+   struct spa_log *log;
77
+
78
+   struct spa_io_position *position;
79
+
80
+   uint64_t info_all;
81
+   struct spa_node_info info;
82
+#define IDX_PropInfo   0
83
+#define IDX_Props  1
84
+#define N_NODE_PARAMS  2
85
+   struct spa_param_info paramsN_NODE_PARAMS;
86
+
87
+   struct spa_hook_list hooks;
88
+   struct spa_callbacks callbacks;
89
+
90
+   // Synchronization between main and data thread
91
+   atomic_bool started;
92
+   pthread_rwlock_t renderlock;
93
+
94
+   struct vulkan_blit_state state;
95
+   struct vulkan_pass pass;
96
+   struct port port2;
97
+};
98
+
99
+#define CHECK_PORT(this,d,p)  ((p) < 1)
100
+
101
+static int lock_init(struct impl *this)
102
+{
103
+   return pthread_rwlock_init(&this->renderlock, NULL);
104
+}
105
+
106
+static void lock_destroy(struct impl *this)
107
+{
108
+   pthread_rwlock_destroy(&this->renderlock);
109
+}
110
+
111
+static int lock_renderer(struct impl *this)
112
+{
113
+   spa_log_info(this->log, "Lock renderer");
114
+   return pthread_rwlock_wrlock(&this->renderlock);
115
+}
116
+
117
+static int unlock_renderer(struct impl *this)
118
+{
119
+   spa_log_info(this->log, "Unlock renderer");
120
+   return pthread_rwlock_unlock(&this->renderlock);
121
+}
122
+
123
+static int impl_node_enum_params(void *object, int seq,
124
+                uint32_t id, uint32_t start, uint32_t num,
125
+                const struct spa_pod *filter)
126
+{
127
+   struct impl *this = object;
128
+   struct spa_pod *param;
129
+   struct spa_pod_builder b = { 0 };
130
+   uint8_t buffer1024;
131
+   struct spa_result_node_params result;
132
+   uint32_t count = 0;
133
+
134
+   spa_return_val_if_fail(this != NULL, -EINVAL);
135
+   spa_return_val_if_fail(num != 0, -EINVAL);
136
+
137
+   result.id = id;
138
+   result.next = start;
139
+      next:
140
+   result.index = result.next++;
141
+
142
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
143
+
144
+   switch (id) {
145
+   default:
146
+       return -ENOENT;
147
+   }
148
+
149
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
150
+       goto next;
151
+
152
+   spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
153
+
154
+   if (++count != num)
155
+       goto next;
156
+
157
+   return 0;
158
+}
159
+
160
+static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
161
+{
162
+   struct impl *this = object;
163
+
164
+   spa_return_val_if_fail(this != NULL, -EINVAL);
165
+
166
+   switch (id) {
167
+   case SPA_IO_Position:
168
+       if (size > 0 && size < sizeof(struct spa_io_position))
169
+           return -EINVAL;
170
+       this->position = data;
171
+       break;
172
+   default:
173
+       return -ENOENT;
174
+   }
175
+   return 0;
176
+}
177
+static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
178
+                  const struct spa_pod *param)
179
+{
180
+   struct impl *this = object;
181
+
182
+   spa_return_val_if_fail(this != NULL, -EINVAL);
183
+
184
+   switch (id) {
185
+   default:
186
+       return -ENOENT;
187
+   }
188
+   return 0;
189
+}
190
+
191
+static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t id)
192
+{
193
+   struct buffer *b = &port->buffersid;
194
+
195
+   if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
196
+       spa_log_debug(this->log, "%p: reuse buffer %d", this, id);
197
+
198
+       SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
199
+       spa_list_append(&port->empty, &b->link);
200
+   }
201
+}
202
+
203
+static int impl_node_send_command(void *object, const struct spa_command *command)
204
+{
205
+   struct impl *this = object;
206
+
207
+   spa_return_val_if_fail(this != NULL, -EINVAL);
208
+   spa_return_val_if_fail(command != NULL, -EINVAL);
209
+
210
+   switch (SPA_NODE_COMMAND_ID(command)) {
211
+   case SPA_NODE_COMMAND_Start:
212
+       if (this->started)
213
+           return 0;
214
+
215
+       this->started = true;
216
+       spa_vulkan_blit_start(&this->state);
217
+       // The main thread needs to lock the renderer before changing its state
218
+       break;
219
+
220
+   case SPA_NODE_COMMAND_Suspend:
221
+   case SPA_NODE_COMMAND_Pause:
222
+       if (!this->started)
223
+           return 0;
224
+
225
+       lock_renderer(this);
226
+       spa_vulkan_blit_stop(&this->state);
227
+       this->started = false;
228
+       unlock_renderer(this);
229
+       // Locking the renderer from the renderer is no longer required
230
+       break;
231
+   default:
232
+       return -ENOTSUP;
233
+   }
234
+   return 0;
235
+}
236
+
237
+static const struct spa_dict_item node_info_items = {
238
+   { SPA_KEY_MEDIA_CLASS, "Video/Filter" },
239
+};
240
+
241
+static void emit_node_info(struct impl *this, bool full)
242
+{
243
+   uint64_t old = full ? this->info.change_mask : 0;
244
+   if (full)
245
+       this->info.change_mask = this->info_all;
246
+   if (this->info.change_mask) {
247
+       this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
248
+       spa_node_emit_info(&this->hooks, &this->info);
249
+       this->info.change_mask = old;
250
+   }
251
+}
252
+
253
+static void emit_port_info(struct impl *this, struct port *port, bool full)
254
+{
255
+   uint64_t old = full ? port->info.change_mask : 0;
256
+   if (full)
257
+       port->info.change_mask = port->info_all;
258
+   if (port->info.change_mask) {
259
+       struct spa_dict_item items1;
260
+
261
+       items0 = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "32 bit float RGBA video");
262
+       port->info.props = &SPA_DICT_INIT(items, 1);
263
+       spa_node_emit_port_info(&this->hooks,
264
+               port->direction, 0, &port->info);
265
+       port->info.change_mask = old;
266
+   }
267
+}
268
+
269
+static int
270
+impl_node_add_listener(void *object,
271
+       struct spa_hook *listener,
272
+       const struct spa_node_events *events,
273
+       void *data)
274
+{
275
+   struct impl *this = object;
276
+   struct spa_hook_list save;
277
+
278
+   spa_return_val_if_fail(this != NULL, -EINVAL);
279
+
280
+   spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
281
+
282
+   emit_node_info(this, true);
283
+   emit_port_info(this, &this->port0, true);
284
+   emit_port_info(this, &this->port1, true);
285
+
286
+   spa_hook_list_join(&this->hooks, &save);
287
+
288
+   return 0;
289
+}
290
+
291
+static int
292
+impl_node_set_callbacks(void *object,
293
+           const struct spa_node_callbacks *callbacks,
294
+           void *data)
295
+{
296
+   struct impl *this = object;
297
+
298
+   spa_return_val_if_fail(this != NULL, -EINVAL);
299
+
300
+   this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
301
+
302
+   return 0;
303
+}
304
+
305
+static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id,
306
+       const struct spa_dict *props)
307
+{
308
+   return -ENOTSUP;
309
+}
310
+
311
+static int
312
+impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id)
313
+{
314
+   return -ENOTSUP;
315
+}
316
+
317
+static bool port_has_fixated_format(struct port *p)
318
+{
319
+   if (!p->have_format)
320
+       return false;
321
+   switch (p->current_format.media_subtype) {
322
+   case SPA_MEDIA_SUBTYPE_dsp:
323
+       return p->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
324
+           && p->current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
325
+   case SPA_MEDIA_SUBTYPE_raw:
326
+       return p->current_format.info.raw.flags & SPA_VIDEO_FLAG_MODIFIER
327
+           && p->current_format.info.raw.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
328
+   }
329
+   return false;
330
+}
331
+
332
+static int port_enum_formats(void *object,
333
+                enum spa_direction direction, uint32_t port_id,
334
+                uint32_t index,
335
+                const struct spa_pod *filter,
336
+                struct spa_pod **param,
337
+                struct spa_pod_builder *builder)
338
+{
339
+   struct impl *this = object;
340
+
341
+   if (port_has_fixated_format(&this->portport_id)) {
342
+       if (index == 0) {
343
+           if (this->portport_id.current_format.media_subtype == SPA_MEDIA_SUBTYPE_dsp) {
344
+               spa_log_info(this->log, "enum_formats fixated format idx: %d, format %d, has_modifier 1",
345
+                       index, this->portport_id.current_format.info.dsp.format);
346
+               *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.dsp);
347
+           } else {
348
+               spa_log_info(this->log, "enum_formats fixated format idx: %d, format %d, has_modifier 1",
349
+                       index, this->portport_id.current_format.info.raw.format);
350
+               *param = spa_format_video_raw_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.raw);
351
+           }
352
+           return 1;
353
+       }
354
+       return spa_vulkan_blit_enumerate_formats(&this->state, index-1, spa_vulkan_blit_get_buffer_caps(&this->state, direction), param, builder);
355
+   } else {
356
+       return spa_vulkan_blit_enumerate_formats(&this->state, index, spa_vulkan_blit_get_buffer_caps(&this->state, direction), param, builder);
357
+   }
358
+}
359
+
360
+static int port_get_buffer_props(struct impl *this, struct port *port,
361
+       uint32_t *blocks, uint32_t *size, uint32_t *stride, bool *is_dmabuf)
362
+{
363
+   if (port->current_format.media_subtype == SPA_MEDIA_SUBTYPE_dsp) {
364
+       if (this->position == NULL)
365
+           return -EIO;
366
+
367
+       spa_log_debug(this->log, "%p: %dx%d stride %d", this,
368
+               this->position->video.size.width,
369
+               this->position->video.size.height,
370
+               this->position->video.stride);
371
+
372
+       if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
373
+           *is_dmabuf = true;
374
+
375
+           struct vulkan_modifier_info *mod_info = spa_vulkan_blit_get_modifier_info(&this->state,
376
+               &port->current_format);
377
+           *blocks = mod_info->props.drmFormatModifierPlaneCount;
378
+       } else {
379
+           *is_dmabuf = false;
380
+           *blocks = 1;
381
+           *size = this->position->video.stride * this->position->video.size.height;
382
+           *stride = this->position->video.stride;
383
+       }
384
+       return 0;
385
+   } else if (port->current_format.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
386
+       spa_log_debug(this->log, "%p: %dx%d", this,
387
+               port->current_format.info.raw.size.width,
388
+               port->current_format.info.raw.size.height);
389
+
390
+       if (port->current_format.info.raw.flags & SPA_VIDEO_FLAG_MODIFIER) {
391
+           *is_dmabuf = true;
392
+
393
+           struct vulkan_modifier_info *mod_info = spa_vulkan_blit_get_modifier_info(&this->state,
394
+               &port->current_format);
395
+           *blocks = mod_info->props.drmFormatModifierPlaneCount;
396
+       } else {
397
+           struct pixel_format_info pInfo = {0};
398
+           if (!get_pixel_format_info(port->current_format.info.raw.format, &pInfo))
399
+               return -EINVAL;
400
+           uint32_t buffer_stride = pInfo.bpp * port->current_format.info.raw.size.width;
401
+           *is_dmabuf = false;
402
+           *blocks = 1;
403
+           *size = buffer_stride * port->current_format.info.raw.size.height;
404
+           *stride = buffer_stride;
405
+       }
406
+       return 0;
407
+   } else {
408
+       return -EINVAL;
409
+   }
410
+}
411
+
412
+static int
413
+impl_node_port_enum_params(void *object, int seq,
414
+           enum spa_direction direction, uint32_t port_id,
415
+           uint32_t id, uint32_t start, uint32_t num,
416
+           const struct spa_pod *filter)
417
+{
418
+   struct impl *this = object;
419
+   struct port *port;
420
+   struct spa_pod_builder b = { 0 };
421
+   uint8_t buffer1024;
422
+   struct spa_pod *param;
423
+   struct spa_result_node_params result;
424
+   uint32_t count = 0;
425
+   int res;
426
+
427
+   spa_return_val_if_fail(this != NULL, -EINVAL);
428
+   spa_return_val_if_fail(num != 0, -EINVAL);
429
+
430
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
431
+   port = &this->portdirection;
432
+
433
+   result.id = id;
434
+   result.next = start;
435
+      next:
436
+   result.index = result.next++;
437
+
438
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
439
+
440
+   switch (id) {
441
+   case SPA_PARAM_EnumFormat:
442
+       if ((res = port_enum_formats(this, direction, port_id,
443
+                       result.index, filter, &param, &b)) <= 0)
444
+           return res;
445
+       break;
446
+
447
+   case SPA_PARAM_Format:
448
+       if (!port->have_format)
449
+           return -EIO;
450
+       if (result.index > 0)
451
+           return 0;
452
+
453
+       if (port->current_format.media_subtype == SPA_MEDIA_SUBTYPE_dsp) {
454
+           param = spa_format_video_dsp_build(&b, id, &port->current_format.info.dsp);
455
+       } else if (port->current_format.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
456
+           param = spa_format_video_raw_build(&b, id, &port->current_format.info.raw);
457
+       } else {
458
+           return -EINVAL;
459
+       }
460
+       break;
461
+
462
+   case SPA_PARAM_Buffers:
463
+   {
464
+       if (!port->have_format)
465
+           return -EIO;
466
+       if (result.index > 0)
467
+           return 0;
468
+
469
+       int ret;
470
+       uint32_t blocks, size, stride;
471
+       bool is_dmabuf;
472
+
473
+       if ((ret = port_get_buffer_props(this, port, &blocks, &size, &stride, &is_dmabuf)) < 0)
474
+           return ret;
475
+
476
+       if (is_dmabuf) {
477
+           param = spa_pod_builder_add_object(&b,
478
+               SPA_TYPE_OBJECT_ParamBuffers, id,
479
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
480
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(blocks),
481
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf));
482
+       } else {
483
+           param = spa_pod_builder_add_object(&b,
484
+               SPA_TYPE_OBJECT_ParamBuffers, id,
485
+               SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
486
+               SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(blocks),
487
+               SPA_PARAM_BUFFERS_size,  SPA_POD_Int(size),
488
+               SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(stride),
489
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr));
490
+       }
491
+
492
+       break;
493
+   }
494
+   case SPA_PARAM_Meta:
495
+       switch (result.index) {
496
+       case 0:
497
+           param = spa_pod_builder_add_object(&b,
498
+               SPA_TYPE_OBJECT_ParamMeta, id,
499
+               SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
500
+               SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
501
+           break;
502
+
503
+       default:
504
+           return 0;
505
+       }
506
+       break;
507
+   default:
508
+       return -ENOENT;
509
+   }
510
+
511
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
512
+       goto next;
513
+
514
+   spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
515
+
516
+   if (++count != num)
517
+       goto next;
518
+
519
+   return 0;
520
+}
521
+
522
+static int clear_buffers(struct impl *this, struct port *port)
523
+{
524
+   if (port->n_buffers > 0) {
525
+       spa_log_debug(this->log, "%p: clear buffers", this);
526
+       lock_renderer(this);
527
+       spa_vulkan_blit_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format, 0, NULL);
528
+       spa_vulkan_blit_clear_pass(&this->state, &this->pass);
529
+       unlock_renderer(this);
530
+       port->n_buffers = 0;
531
+       spa_list_init(&port->empty);
532
+       spa_list_init(&port->ready);
533
+   }
534
+   return 0;
535
+}
536
+
537
+static int port_set_dsp_format(struct impl *this, struct port *port,
538
+              uint32_t flags, struct spa_video_info *info,
539
+              bool *has_modifier, bool *modifier_fixed,
540
+              const struct spa_pod *format)
541
+{
542
+       if (spa_format_video_dsp_parse(format, &info->info.dsp) < 0)
543
+           return -EINVAL;
544
+
545
+       if (info->info.dsp.format != SPA_VIDEO_FORMAT_DSP_F32)
546
+           return -EINVAL;
547
+
548
+       this->state.streamsport->stream_id.dim.width = this->position->video.size.width;
549
+       this->state.streamsport->stream_id.dim.height = this->position->video.size.height;
550
+       this->state.streamsport->stream_id.bpp = 16;
551
+       *has_modifier = SPA_FLAG_IS_SET(info->info.dsp.flags, SPA_VIDEO_FLAG_MODIFIER);
552
+
553
+       // fixate modifier
554
+       if (port->direction == SPA_DIRECTION_OUTPUT
555
+               && info->info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
556
+               && info->info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
557
+           const struct spa_pod_prop *mod_prop;
558
+           if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL)
559
+               return -EINVAL;
560
+
561
+           const struct spa_pod *mod_pod = &mod_prop->value;
562
+           uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod);
563
+           uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod);
564
+           if (modifierCount <= 1)
565
+               return -EINVAL;
566
+           // SPA_POD_CHOICE carries the "preferred" value at position 0
567
+           modifierCount -= 1;
568
+           modifiers++;
569
+           uint64_t fixed_modifier;
570
+           if (spa_vulkan_blit_fixate_modifier(&this->state, &this->state.streamsport->stream_id, info, modifierCount, modifiers, &fixed_modifier) != 0)
571
+               return -EINVAL;
572
+
573
+           spa_log_info(this->log, "modifier fixated %"PRIu64, fixed_modifier);
574
+
575
+           info->info.dsp.modifier = fixed_modifier;
576
+           info->info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
577
+           *modifier_fixed = true;
578
+       }
579
+
580
+   return 0;
581
+}
582
+
583
+static int port_set_raw_format(struct impl *this, struct port *port,
584
+              uint32_t flags, struct spa_video_info *info,
585
+              bool *has_modifier, bool *modifier_fixed,
586
+              const struct spa_pod *format)
587
+{
588
+       if (spa_format_video_raw_parse(format, &info->info.raw) < 0)
589
+           return -EINVAL;
590
+
591
+       struct pixel_format_info pInfo;
592
+       if (!get_pixel_format_info(info->info.raw.format, &pInfo))
593
+           return -EINVAL;
594
+       this->state.streamsport->stream_id.dim = info->info.raw.size;
595
+       this->state.streamsport->stream_id.bpp = pInfo.bpp;
596
+       *has_modifier = SPA_FLAG_IS_SET(info->info.raw.flags, SPA_VIDEO_FLAG_MODIFIER);
597
+
598
+       // fixate modifier
599
+       if (port->direction == SPA_DIRECTION_OUTPUT
600
+               && info->info.raw.flags & SPA_VIDEO_FLAG_MODIFIER
601
+               && info->info.raw.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
602
+           const struct spa_pod_prop *mod_prop;
603
+           if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL)
604
+               return -EINVAL;
605
+
606
+           const struct spa_pod *mod_pod = &mod_prop->value;
607
+           uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod);
608
+           uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod);
609
+           if (modifierCount <= 1)
610
+               return -EINVAL;
611
+           // SPA_POD_CHOICE carries the "preferred" value at position 0
612
+           modifierCount -= 1;
613
+           modifiers++;
614
+           uint64_t fixed_modifier;
615
+           if (spa_vulkan_blit_fixate_modifier(&this->state, &this->state.streamsport->stream_id, info, modifierCount, modifiers, &fixed_modifier) != 0)
616
+               return -EINVAL;
617
+
618
+           spa_log_info(this->log, "modifier fixated %"PRIu64, fixed_modifier);
619
+
620
+           info->info.raw.modifier = fixed_modifier;
621
+           info->info.raw.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
622
+           *modifier_fixed = true;
623
+       }
624
+
625
+   return 0;
626
+}
627
+
628
+static int port_set_format(struct impl *this, struct port *port,
629
+              uint32_t flags,
630
+              const struct spa_pod *format)
631
+{
632
+   int res;
633
+
634
+   if (format == NULL) {
635
+       port->have_format = false;
636
+       clear_buffers(this, port);
637
+   } else {
638
+       struct spa_video_info info = { 0 };
639
+
640
+       if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
641
+           return res;
642
+
643
+       if (info.media_type != SPA_MEDIA_TYPE_video)
644
+           return -EINVAL;
645
+
646
+       bool has_modifier = false;
647
+       bool modifier_fixed = false;
648
+       if (info.media_subtype == SPA_MEDIA_SUBTYPE_dsp) {
649
+           if ((res = port_set_dsp_format(this, port, flags, &info, &has_modifier, &modifier_fixed, format)) < 0)
650
+               return res;
651
+       } else if (info.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
652
+           if ((res = port_set_raw_format(this, port, flags, &info, &has_modifier, &modifier_fixed, format)) < 0)
653
+               return res;
654
+       } else {
655
+           return -EINVAL;
656
+       }
657
+
658
+       if (has_modifier) {
659
+           SPA_FLAG_SET(port->info.flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
660
+       } else {
661
+           SPA_FLAG_CLEAR(port->info.flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
662
+       }
663
+       port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
664
+
665
+       port->current_format = info;
666
+       port->have_format = true;
667
+
668
+       if (modifier_fixed) {
669
+           port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
670
+           port->paramsIDX_EnumFormat.flags ^= SPA_PARAM_INFO_SERIAL;
671
+           emit_port_info(this, port, false);
672
+           return 0;
673
+       }
674
+   }
675
+
676
+   port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
677
+   if (port->have_format) {
678
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
679
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
680
+   } else {
681
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
682
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
683
+   }
684
+   emit_port_info(this, port, false);
685
+
686
+   return 0;
687
+}
688
+
689
+static int
690
+impl_node_port_set_param(void *object,
691
+            enum spa_direction direction, uint32_t port_id,
692
+            uint32_t id, uint32_t flags,
693
+            const struct spa_pod *param)
694
+{
695
+   struct impl *this = object;
696
+   struct port *port;
697
+   int res;
698
+
699
+   spa_return_val_if_fail(this != NULL, -EINVAL);
700
+   spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL);
701
+   port = &this->portdirection;
702
+
703
+   switch (id) {
704
+   case SPA_PARAM_Format:
705
+       res = port_set_format(this, port, flags, param);
706
+       break;
707
+   default:
708
+       return -ENOENT;
709
+   }
710
+   return res;
711
+}
712
+
713
+static int
714
+impl_node_port_use_buffers(void *object,
715
+              enum spa_direction direction,
716
+              uint32_t port_id,
717
+              uint32_t flags,
718
+              struct spa_buffer **buffers,
719
+              uint32_t n_buffers)
720
+{
721
+   struct impl *this = object;
722
+   struct port *port;
723
+   uint32_t i;
724
+
725
+   spa_return_val_if_fail(this != NULL, -EINVAL);
726
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
727
+   port = &this->portdirection;
728
+
729
+   clear_buffers(this, port);
730
+
731
+   if (n_buffers > 0 && !port->have_format)
732
+       return -EIO;
733
+   if (n_buffers > MAX_BUFFERS)
734
+       return -ENOSPC;
735
+
736
+   lock_renderer(this);
737
+   for (i = 0; i < n_buffers; i++) {
738
+       struct buffer *b;
739
+
740
+       b = &port->buffersi;
741
+       b->id = i;
742
+       b->outbuf = buffersi;
743
+       b->flags = 0;
744
+       b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h));
745
+
746
+       spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
747
+       spa_list_append(&port->empty, &b->link);
748
+   }
749
+   spa_vulkan_blit_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format, n_buffers, buffers);
750
+   port->n_buffers = n_buffers;
751
+   if (n_buffers > 0)
752
+       spa_vulkan_blit_init_pass(&this->state, &this->pass);
753
+   unlock_renderer(this);
754
+
755
+   return 0;
756
+}
757
+
758
+static int
759
+impl_node_port_set_io(void *object,
760
+             enum spa_direction direction,
761
+             uint32_t port_id,
762
+             uint32_t id,
763
+             void *data, size_t size)
764
+{
765
+   struct impl *this = object;
766
+   struct port *port;
767
+
768
+   spa_return_val_if_fail(this != NULL, -EINVAL);
769
+   spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
770
+   port = &this->portdirection;
771
+
772
+   switch (id) {
773
+   case SPA_IO_Buffers:
774
+       port->io = data;
775
+       break;
776
+   default:
777
+       return -ENOENT;
778
+   }
779
+   return 0;
780
+}
781
+
782
+static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
783
+{
784
+   struct impl *this = object;
785
+   struct port *port;
786
+
787
+   spa_return_val_if_fail(this != NULL, -EINVAL);
788
+   spa_return_val_if_fail(port_id == 0, -EINVAL);
789
+
790
+   port = &this->portSPA_DIRECTION_OUTPUT;
791
+   spa_return_val_if_fail(buffer_id < port->n_buffers, -EINVAL);
792
+
793
+   reuse_buffer(this, port, buffer_id);
794
+
795
+   return 0;
796
+}
797
+
798
+static int impl_node_process(void *object)
799
+{
800
+   struct impl *this = object;
801
+   struct port *inport, *outport;
802
+   struct spa_io_buffers *inio, *outio;
803
+   struct buffer *b;
804
+
805
+   spa_return_val_if_fail(this != NULL, -EINVAL);
806
+   spa_return_val_if_fail(this->started, -EINVAL);
807
+
808
+   inport = &this->portSPA_DIRECTION_INPUT;
809
+   if ((inio = inport->io) == NULL)
810
+       return -EIO;
811
+
812
+   if (inio->status != SPA_STATUS_HAVE_DATA)
813
+       return inio->status;
814
+
815
+   if (inio->buffer_id >= inport->n_buffers) {
816
+       inio->status = -EINVAL;
817
+       return -EINVAL;
818
+   }
819
+
820
+   outport = &this->portSPA_DIRECTION_OUTPUT;
821
+   if ((outio = outport->io) == NULL)
822
+       return -EIO;
823
+
824
+   if (outio->status == SPA_STATUS_HAVE_DATA)
825
+       return SPA_STATUS_HAVE_DATA;
826
+
827
+   if (outio->buffer_id < outport->n_buffers) {
828
+       reuse_buffer(this, outport, outio->buffer_id);
829
+       outio->buffer_id = SPA_ID_INVALID;
830
+   }
831
+
832
+   if (spa_list_is_empty(&outport->empty)) {
833
+       spa_log_debug(this->log, "%p: out of buffers", this);
834
+       return -EPIPE;
835
+   }
836
+
837
+   if (pthread_rwlock_tryrdlock(&this->renderlock) < 0) {
838
+       return -EBUSY;
839
+   }
840
+
841
+   b = &inport->buffersinio->buffer_id;
842
+   this->pass.in_stream_id = SPA_DIRECTION_INPUT;
843
+   this->pass.in_buffer_id = b->id;
844
+   inio->status = SPA_STATUS_NEED_DATA;
845
+
846
+   b = spa_list_first(&outport->empty, struct buffer, link);
847
+   spa_list_remove(&b->link);
848
+   SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
849
+   this->pass.out_stream_id = SPA_DIRECTION_OUTPUT;
850
+   this->pass.out_buffer_id = b->id;
851
+
852
+   spa_log_debug(this->log, "filter into %d", b->id);
853
+
854
+   spa_vulkan_blit_process(&this->state, &this->pass);
855
+   spa_vulkan_blit_reset_pass(&this->state, &this->pass);
856
+
857
+   b->outbuf->datas0.chunk->offset = 0;
858
+   b->outbuf->datas0.chunk->size = b->outbuf->datas0.maxsize;
859
+   if (outport->current_format.media_subtype == SPA_MEDIA_SUBTYPE_raw) {
860
+       b->outbuf->datas0.chunk->stride =
861
+           this->state.streamsoutport->stream_id.bpp * outport->current_format.info.raw.size.width;
862
+   } else {
863
+       b->outbuf->datas0.chunk->stride = this->position->video.stride;
864
+   }
865
+
866
+   outio->buffer_id = b->id;
867
+   outio->status = SPA_STATUS_HAVE_DATA;
868
+
869
+   pthread_rwlock_unlock(&this->renderlock);
870
+
871
+   return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA;
872
+}
873
+
874
+static const struct spa_node_methods impl_node = {
875
+   SPA_VERSION_NODE_METHODS,
876
+   .add_listener = impl_node_add_listener,
877
+   .set_callbacks = impl_node_set_callbacks,
878
+   .enum_params = impl_node_enum_params,
879
+   .set_param = impl_node_set_param,
880
+   .set_io = impl_node_set_io,
881
+   .send_command = impl_node_send_command,
882
+   .add_port = impl_node_add_port,
883
+   .remove_port = impl_node_remove_port,
884
+   .port_enum_params = impl_node_port_enum_params,
885
+   .port_set_param = impl_node_port_set_param,
886
+   .port_use_buffers = impl_node_port_use_buffers,
887
+   .port_set_io = impl_node_port_set_io,
888
+   .port_reuse_buffer = impl_node_port_reuse_buffer,
889
+   .process = impl_node_process,
890
+};
891
+
892
+static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
893
+{
894
+   struct impl *this;
895
+
896
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
897
+   spa_return_val_if_fail(interface != NULL, -EINVAL);
898
+
899
+   this = (struct impl *) handle;
900
+
901
+   if (spa_streq(type, SPA_TYPE_INTERFACE_Node))
902
+       *interface = &this->node;
903
+   else
904
+       return -ENOENT;
905
+
906
+   return 0;
907
+}
908
+
909
+static int impl_clear(struct spa_handle *handle)
910
+{
911
+   struct impl *this;
912
+
913
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
914
+
915
+   this = (struct impl *) handle;
916
+
917
+   spa_vulkan_blit_unprepare(&this->state);
918
+   spa_vulkan_blit_deinit(&this->state);
919
+   lock_destroy(this);
920
+   return 0;
921
+}
922
+
923
+static size_t
924
+impl_get_size(const struct spa_handle_factory *factory,
925
+         const struct spa_dict *params)
926
+{
927
+   return sizeof(struct impl);
928
+}
929
+
930
+static int
931
+impl_init(const struct spa_handle_factory *factory,
932
+     struct spa_handle *handle,
933
+     const struct spa_dict *info,
934
+     const struct spa_support *support,
935
+     uint32_t n_support)
936
+{
937
+   struct impl *this;
938
+   struct port *port;
939
+
940
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
941
+   spa_return_val_if_fail(handle != NULL, -EINVAL);
942
+
943
+   handle->get_interface = impl_get_interface;
944
+   handle->clear = impl_clear;
945
+
946
+   this = (struct impl *) handle;
947
+
948
+   this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
949
+   this->state.log = this->log;
950
+
951
+   spa_hook_list_init(&this->hooks);
952
+
953
+   this->node.iface = SPA_INTERFACE_INIT(
954
+           SPA_TYPE_INTERFACE_Node,
955
+           SPA_VERSION_NODE,
956
+           &impl_node, this);
957
+
958
+   this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
959
+           SPA_NODE_CHANGE_MASK_PROPS |
960
+           SPA_NODE_CHANGE_MASK_PARAMS;
961
+   this->info = SPA_NODE_INFO_INIT();
962
+   this->info.max_output_ports = 1;
963
+   this->info.max_input_ports = 1;
964
+   this->info.flags = SPA_NODE_FLAG_RT;
965
+   this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
966
+   this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
967
+   this->info.params = this->params;
968
+   this->info.n_params = N_NODE_PARAMS;
969
+
970
+   lock_init(this);
971
+
972
+   port = &this->portSPA_DIRECTION_INPUT;
973
+   port->stream_id = SPA_DIRECTION_INPUT;
974
+   port->direction = SPA_DIRECTION_INPUT;
975
+   port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
976
+           SPA_PORT_CHANGE_MASK_PARAMS |
977
+           SPA_PORT_CHANGE_MASK_PROPS;
978
+   port->info = SPA_PORT_INFO_INIT();
979
+   port->info.flags = SPA_PORT_FLAG_NO_REF;
980
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
981
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
982
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
983
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
984
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
985
+   port->info.params = port->params;
986
+   port->info.n_params = N_PORT_PARAMS;
987
+   spa_vulkan_blit_init_stream(&this->state, &this->state.streamsport->stream_id,
988
+           SPA_DIRECTION_INPUT, NULL);
989
+   spa_list_init(&port->empty);
990
+   spa_list_init(&port->ready);
991
+
992
+   port = &this->portSPA_DIRECTION_OUTPUT;
993
+   port->stream_id = SPA_DIRECTION_OUTPUT;
994
+   port->direction = SPA_DIRECTION_OUTPUT;
995
+   port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
996
+           SPA_PORT_CHANGE_MASK_PARAMS |
997
+           SPA_PORT_CHANGE_MASK_PROPS;
998
+   port->info = SPA_PORT_INFO_INIT();
999
+   port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
1000
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
1001
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
1002
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
1003
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
1004
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
1005
+   port->info.params = port->params;
1006
+   port->info.n_params = N_PORT_PARAMS;
1007
+   spa_list_init(&port->empty);
1008
+   spa_list_init(&port->ready);
1009
+   spa_vulkan_blit_init_stream(&this->state, &this->state.streamsport->stream_id,
1010
+           SPA_DIRECTION_OUTPUT, NULL);
1011
+
1012
+   this->state.n_streams = 2;
1013
+   spa_vulkan_blit_init(&this->state);
1014
+   spa_vulkan_blit_prepare(&this->state);
1015
+
1016
+   return 0;
1017
+}
1018
+
1019
+static const struct spa_interface_info impl_interfaces = {
1020
+   {SPA_TYPE_INTERFACE_Node,},
1021
+};
1022
+
1023
+static int
1024
+impl_enum_interface_info(const struct spa_handle_factory *factory,
1025
+            const struct spa_interface_info **info,
1026
+            uint32_t *index)
1027
+{
1028
+   spa_return_val_if_fail(factory != NULL, -EINVAL);
1029
+   spa_return_val_if_fail(info != NULL, -EINVAL);
1030
+   spa_return_val_if_fail(index != NULL, -EINVAL);
1031
+
1032
+   switch (*index) {
1033
+   case 0:
1034
+       *info = &impl_interfaces*index;
1035
+       break;
1036
+   default:
1037
+       return 0;
1038
+   }
1039
+   (*index)++;
1040
+   return 1;
1041
+}
1042
+
1043
+static const struct spa_dict_item info_items = {
1044
+   { SPA_KEY_FACTORY_AUTHOR, "Columbarius <co1umbarius@protonmail.com>" },
1045
+   { SPA_KEY_FACTORY_DESCRIPTION, "Convert video frames using a vulkan blit" },
1046
+};
1047
+
1048
+static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items);
1049
+
1050
+const struct spa_handle_factory spa_vulkan_blit_filter_factory = {
1051
+   SPA_VERSION_HANDLE_FACTORY,
1052
+   SPA_NAME_API_VULKAN_BLIT_FILTER,
1053
+   &info,
1054
+   impl_get_size,
1055
+   impl_init,
1056
+   impl_enum_interface_info,
1057
+};
1058
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-blit-utils.c Added
685
 
1
@@ -0,0 +1,683 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <vulkan/vulkan.h>
8
+
9
+#include <unistd.h>
10
+#include <sys/types.h>
11
+#include <sys/stat.h>
12
+#include <sys/mman.h>
13
+#include <fcntl.h>
14
+#include <string.h>
15
+#include <vulkan/vulkan_core.h>
16
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
17
+#include <alloca.h>
18
+#endif
19
+#include <errno.h>
20
+#include <stdio.h>
21
+#include <assert.h>
22
+#include <math.h>
23
+#include <time.h>
24
+
25
+#include <spa/utils/result.h>
26
+#include <spa/utils/string.h>
27
+#include <spa/support/log.h>
28
+#include <spa/debug/mem.h>
29
+
30
+#include "vulkan-blit-utils.h"
31
+#include "vulkan-utils.h"
32
+#include "utils.h"
33
+
34
+#define VULKAN_INSTANCE_FUNCTION(name)                     \
35
+   PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->base.instance, #name)
36
+
37
+#define GET_BUFFER_ID_FROM_STREAM(s, pass) \
38
+   (s->direction == SPA_DIRECTION_INPUT ? pass->in_buffer_id : pass->out_buffer_id)
39
+
40
+static int runImportSHMBuffers(struct vulkan_blit_state *s, struct vulkan_pass *pass) {
41
+   struct vulkan_stream *p = &s->streamspass->in_stream_id;
42
+
43
+   if (p->buffer_type == SPA_DATA_MemPtr) {
44
+       struct spa_buffer *spa_buf = p->spa_bufferspass->in_buffer_id;
45
+       struct vulkan_write_pixels_info writeInfo = {
46
+           .data = spa_buf->datas0.data,
47
+           .offset = 0,
48
+           .stride = p->bpp * p->dim.width,
49
+           .bytes_per_pixel = p->bpp,
50
+           .size.width = p->dim.width,
51
+           .size.height = p->dim.height,
52
+           .copies = &pass->in_copy,
53
+       };
54
+       CHECK(vulkan_write_pixels(&s->base, &writeInfo, &pass->in_staging_buffer));
55
+   }
56
+
57
+   return 0;
58
+}
59
+
60
+static int runExportSHMBuffers(struct vulkan_blit_state *s, struct vulkan_pass *pass) {
61
+   struct vulkan_stream *p = &s->streamspass->out_stream_id;
62
+
63
+   if (p->buffer_type == SPA_DATA_MemPtr) {
64
+       struct spa_buffer *spa_buf = p->spa_bufferspass->out_buffer_id;
65
+       struct vulkan_read_pixels_info readInfo = {
66
+           .data = spa_buf->datas0.data,
67
+           .offset = 0,
68
+           .stride = p->bpp * p->dim.width,
69
+           .bytes_per_pixel = p->bpp,
70
+           .size.width = p->dim.width,
71
+           .size.height = p->dim.height,
72
+       };
73
+       CHECK(vulkan_read_pixels(&s->base, &readInfo, &p->bufferspass->out_buffer_id));
74
+   }
75
+
76
+   return 0;
77
+}
78
+
79
+static int runImportSync(struct vulkan_blit_state *s, struct vulkan_pass *pass)
80
+{
81
+   int ret = 0;
82
+   for (uint32_t i = 0; i < s->n_streams; i++) {
83
+       struct vulkan_stream *p = &s->streamsi;
84
+       uint32_t current_buffer_id = GET_BUFFER_ID_FROM_STREAM(p, pass);
85
+       struct vulkan_buffer *current_buffer = &p->bufferscurrent_buffer_id;
86
+
87
+       if (p->buffer_type != SPA_DATA_DmaBuf)
88
+           continue;
89
+
90
+       if (vulkan_buffer_import_implicit_syncfd(&s->base, current_buffer) >= 0)
91
+           continue;
92
+       if (vulkan_buffer_wait_dmabuf_fence(&s->base, current_buffer) < 0) {
93
+           spa_log_warn(s->log, "Failed to wait for foreign buffer DMA-BUF fence");
94
+           ret = -1;
95
+       }
96
+   }
97
+   return ret;
98
+}
99
+
100
+static int runExportSync(struct vulkan_blit_state *s, struct vulkan_pass *pass)
101
+{
102
+   int ret = 0;
103
+   for (uint32_t i = 0; i < s->n_streams; i++) {
104
+       struct vulkan_stream *p = &s->streamsi;
105
+
106
+       if (p->buffer_type != SPA_DATA_DmaBuf)
107
+           continue;
108
+
109
+       if (!vulkan_sync_export_dmabuf(&s->base, &p->buffersGET_BUFFER_ID_FROM_STREAM(p, pass), pass->sync_fd)) {
110
+           ret = -1;
111
+       }
112
+   }
113
+
114
+   return ret;
115
+}
116
+
117
+static int runCommandBuffer(struct vulkan_blit_state *s, struct vulkan_pass *pass)
118
+{
119
+   VULKAN_INSTANCE_FUNCTION(vkQueueSubmit2KHR);
120
+   VULKAN_INSTANCE_FUNCTION(vkGetSemaphoreFdKHR);
121
+
122
+   static const VkCommandBufferBeginInfo beginInfo = {
123
+       .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
124
+       .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
125
+   };
126
+   VK_CHECK_RESULT(vkBeginCommandBuffer(pass->commandBuffer, &beginInfo));
127
+
128
+   uint32_t i;
129
+   struct vulkan_stream *stream_input = &s->streamspass->in_stream_id;
130
+   struct vulkan_stream *stream_output = &s->streamspass->out_stream_id;
131
+
132
+   VkImage src_image = stream_input->bufferspass->in_buffer_id.image;
133
+   VkImage dst_image = stream_output->bufferspass->out_buffer_id.image;
134
+
135
+   if (stream_input->buffer_type == SPA_DATA_MemPtr) {
136
+       vkCmdCopyBufferToImage(pass->commandBuffer, pass->in_staging_buffer.buffer, src_image,
137
+               VK_IMAGE_LAYOUT_GENERAL, 1, &pass->in_copy);
138
+
139
+       VkImageMemoryBarrier copy_barrier = {
140
+           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
141
+           .srcQueueFamilyIndex = s->base.queueFamilyIndex,
142
+           .dstQueueFamilyIndex = s->base.queueFamilyIndex,
143
+           .image = src_image,
144
+           .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
145
+           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
146
+           .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
147
+           .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
148
+           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
149
+           .subresourceRange.levelCount = 1,
150
+           .subresourceRange.layerCount = 1,
151
+       };
152
+
153
+       vkCmdPipelineBarrier(pass->commandBuffer,
154
+                   VK_PIPELINE_STAGE_TRANSFER_BIT,
155
+                   VK_PIPELINE_STAGE_TRANSFER_BIT,
156
+                   0, 0, NULL, 0, NULL,
157
+                   1, &copy_barrier);
158
+   }
159
+
160
+   VkImageBlit imageBlitRegion = {
161
+       .srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
162
+       .srcSubresource.layerCount = 1,
163
+       .srcOffsets0 = {
164
+           .x = 0,
165
+           .y = 0,
166
+           .z = 0,
167
+       },
168
+       .srcOffsets1 = {
169
+           .x = stream_input->dim.width,
170
+           .y = stream_input->dim.height,
171
+           .z = 1,
172
+       },
173
+       .dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
174
+       .dstSubresource.layerCount = 1,
175
+       .dstOffsets1 = {
176
+           .x = stream_output->dim.width,
177
+           .y = stream_output->dim.height,
178
+           .z = 1,
179
+       }
180
+   };
181
+   spa_log_trace_fp(s->log, "Blitting from (%p, %d, %d) %d,%dx%d,%d to (%p, %d, %d) %d,%dx%d,%d",
182
+           stream_input, pass->in_buffer_id, stream_input->direction, 0, 0, stream_input->dim.width, stream_input->dim.height,
183
+           stream_output, pass->out_buffer_id, stream_output->direction, 0, 0, stream_output->dim.width, stream_output->dim.height);
184
+   vkCmdBlitImage(pass->commandBuffer, src_image, VK_IMAGE_LAYOUT_GENERAL,
185
+           dst_image, VK_IMAGE_LAYOUT_GENERAL,
186
+           1, &imageBlitRegion, VK_FILTER_NEAREST);
187
+
188
+   VkImageMemoryBarrier acquire_barriers->n_streams;
189
+   VkImageMemoryBarrier release_barriers->n_streams;
190
+   VkSemaphoreSubmitInfo semaphore_wait_infos->n_streams;
191
+   uint32_t semaphore_wait_info_len = 0;
192
+   VkSemaphoreSubmitInfo semaphore_signal_info1;
193
+   uint32_t semaphore_signal_info_len = 0;
194
+
195
+   for (i = 0; i < s->n_streams; i++) {
196
+       struct vulkan_stream *p = &s->streamsi;
197
+       struct vulkan_buffer *current_buffer = &p->buffersGET_BUFFER_ID_FROM_STREAM(p, pass);
198
+
199
+       VkAccessFlags access_flags;
200
+       VkAccessFlags release_flags;
201
+       if (p->direction == SPA_DIRECTION_INPUT) {
202
+           access_flags = p->buffer_type == SPA_DATA_DmaBuf
203
+               ? VK_ACCESS_TRANSFER_READ_BIT
204
+               : VK_ACCESS_TRANSFER_WRITE_BIT;
205
+           release_flags = VK_ACCESS_TRANSFER_READ_BIT;
206
+       } else {
207
+           access_flags = VK_ACCESS_TRANSFER_WRITE_BIT;
208
+           release_flags = VK_ACCESS_TRANSFER_WRITE_BIT;
209
+       }
210
+
211
+       acquire_barrieri= (VkImageMemoryBarrier) {
212
+           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
213
+           .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
214
+           .dstQueueFamilyIndex = s->base.queueFamilyIndex,
215
+           .image = current_buffer->image,
216
+           .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
217
+           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
218
+           .srcAccessMask = 0,
219
+           .dstAccessMask = access_flags,
220
+           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
221
+           .subresourceRange.levelCount = 1,
222
+           .subresourceRange.layerCount = 1,
223
+       };
224
+
225
+       release_barrieri= (VkImageMemoryBarrier) {
226
+           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
227
+           .srcQueueFamilyIndex = s->base.queueFamilyIndex,
228
+           .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
229
+           .image = current_buffer->image,
230
+           .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
231
+           .newLayout = VK_IMAGE_LAYOUT_GENERAL,
232
+           .srcAccessMask = release_flags,
233
+           .dstAccessMask = 0,
234
+           .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
235
+           .subresourceRange.levelCount = 1,
236
+           .subresourceRange.layerCount = 1,
237
+       };
238
+
239
+       if (current_buffer->foreign_semaphore != VK_NULL_HANDLE) {
240
+           semaphore_wait_infosemaphore_wait_info_len++ = (VkSemaphoreSubmitInfo) {
241
+               .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
242
+               .semaphore = current_buffer->foreign_semaphore,
243
+               .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
244
+           };
245
+       }
246
+   }
247
+
248
+        vkCmdPipelineBarrier(pass->commandBuffer,
249
+               VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
250
+               VK_PIPELINE_STAGE_2_TRANSFER_BIT,
251
+               0, 0, NULL, 0, NULL,
252
+               s->n_streams, acquire_barrier);
253
+
254
+        vkCmdPipelineBarrier(pass->commandBuffer,
255
+               VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
256
+               VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
257
+               0, 0, NULL, 0, NULL,
258
+               s->n_streams, release_barrier);
259
+
260
+   VK_CHECK_RESULT(vkEndCommandBuffer(pass->commandBuffer));
261
+
262
+   VK_CHECK_RESULT(vkResetFences(s->base.device, 1, &pass->fence));
263
+
264
+   semaphore_signal_infosemaphore_signal_info_len++ = (VkSemaphoreSubmitInfo) {
265
+       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
266
+       .semaphore = pass->pipelineSemaphore,
267
+   };
268
+
269
+   VkCommandBufferSubmitInfoKHR commandBufferInfo = {
270
+       .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
271
+       .commandBuffer = pass->commandBuffer,
272
+   };
273
+
274
+   const VkSubmitInfo2KHR submitInfo = {
275
+       .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR,
276
+       .commandBufferInfoCount = 1,
277
+       .pCommandBufferInfos = &commandBufferInfo,
278
+       .waitSemaphoreInfoCount = semaphore_wait_info_len,
279
+       .pWaitSemaphoreInfos = semaphore_wait_info,
280
+       .signalSemaphoreInfoCount = semaphore_signal_info_len,
281
+       .pSignalSemaphoreInfos = semaphore_signal_info,
282
+   };
283
+        VK_CHECK_RESULT(vkQueueSubmit2KHR(s->base.queue, 1, &submitInfo, pass->fence));
284
+   s->started = true;
285
+
286
+   VkSemaphoreGetFdInfoKHR get_fence_fd_info = {
287
+       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
288
+       .semaphore = pass->pipelineSemaphore,
289
+       .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
290
+   };
291
+   VK_CHECK_RESULT(vkGetSemaphoreFdKHR(s->base.device, &get_fence_fd_info, &pass->sync_fd));
292
+
293
+   return 0;
294
+}
295
+
296
+static void clear_buffers(struct vulkan_blit_state *s, struct vulkan_stream *p)
297
+{
298
+   uint32_t i;
299
+
300
+   for (i = 0; i < p->n_buffers; i++) {
301
+       vulkan_buffer_clear(&s->base, &p->buffersi);
302
+       p->spa_buffersi = NULL;
303
+   }
304
+   p->n_buffers = 0;
305
+   p->buffer_type = SPA_DATA_Invalid;
306
+   p->maxsize = 0;
307
+}
308
+
309
+static void clear_streams(struct vulkan_blit_state *s)
310
+{
311
+   uint32_t i;
312
+   for (i = 0; i < s->n_streams; i++) {
313
+       struct vulkan_stream *p = &s->streamsi;
314
+       clear_buffers(s, p);
315
+   }
316
+}
317
+
318
+int spa_vulkan_blit_fixate_modifier(struct vulkan_blit_state *s, struct vulkan_stream *p, struct spa_video_info *info,
319
+       uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier)
320
+{
321
+   VkFormat format;
322
+   struct spa_rectangle size;
323
+   switch (info->media_subtype) {
324
+   case SPA_MEDIA_SUBTYPE_dsp:
325
+       format = vulkan_id_to_vkformat(info->info.dsp.format);
326
+       size.width = p->dim.width;
327
+       size.height = p->dim.height;
328
+       break;
329
+   case SPA_MEDIA_SUBTYPE_raw:
330
+       format = vulkan_id_to_vkformat(info->info.raw.format);
331
+       size.width = p->dim.width;
332
+       size.height = p->dim.height;
333
+       break;
334
+   default:
335
+       spa_log_warn(s->log, "Unsupported media subtype %d", info->media_subtype);
336
+       return -1;
337
+   }
338
+   if (format == VK_FORMAT_UNDEFINED) {
339
+       return -1;
340
+   }
341
+
342
+   struct dmabuf_fixation_info fixation_info = {
343
+       .format = format,
344
+       .modifierCount = modifierCount,
345
+       .modifiers = modifiers,
346
+       .size = size,
347
+       .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
348
+   };
349
+   return vulkan_fixate_modifier(&s->base, &fixation_info, modifier);
350
+}
351
+
352
+int spa_vulkan_blit_use_buffers(struct vulkan_blit_state *s, struct vulkan_stream *p, uint32_t flags,
353
+       struct spa_video_info *info, uint32_t n_buffers, struct spa_buffer **buffers)
354
+{
355
+   struct external_buffer_info externalBufferInfo = {0};
356
+   switch (info->media_subtype) {
357
+   case SPA_MEDIA_SUBTYPE_dsp:
358
+       externalBufferInfo.format = vulkan_id_to_vkformat(info->info.dsp.format);
359
+       externalBufferInfo.size.width = p->dim.width;
360
+       externalBufferInfo.size.height = p->dim.height;
361
+       if (info->info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER)
362
+           externalBufferInfo.modifier = info->info.dsp.modifier;
363
+       break;
364
+   case SPA_MEDIA_SUBTYPE_raw:
365
+       externalBufferInfo.format = vulkan_id_to_vkformat(info->info.raw.format);
366
+       externalBufferInfo.size.width = p->dim.width;
367
+       externalBufferInfo.size.height = p->dim.height;
368
+       if (info->info.raw.flags & SPA_VIDEO_FLAG_MODIFIER)
369
+           externalBufferInfo.modifier = info->info.raw.modifier;
370
+       break;
371
+   default:
372
+       spa_log_warn(s->log, "Unsupported media subtype %d", info->media_subtype);
373
+       return -1;
374
+   }
375
+   if (externalBufferInfo.format == VK_FORMAT_UNDEFINED)
376
+       return -1;
377
+
378
+   vulkan_wait_idle(&s->base);
379
+   clear_buffers(s, p);
380
+
381
+   if (n_buffers == 0)
382
+       return 0;
383
+
384
+   bool alloc = flags & SPA_NODE_BUFFERS_FLAG_ALLOC;
385
+   int ret;
386
+   for (uint32_t i = 0; i < n_buffers; i++) {
387
+       if (p->buffer_type == SPA_DATA_Invalid) {
388
+           p->buffer_type = buffersi->datas->type;
389
+       } else {
390
+           if (p->buffer_type != buffersi->datas->type) {
391
+               spa_log_error(s->log, "Buffers are of different type %d:%d", p->buffer_type, buffersi->datas0.type);
392
+               return -1;
393
+           }
394
+       }
395
+       p->maxsize = SPA_MAX(p->maxsize, buffersi->datas0.maxsize);
396
+       externalBufferInfo.usage = p->direction == SPA_DIRECTION_OUTPUT
397
+           ? VK_IMAGE_USAGE_TRANSFER_DST_BIT
398
+           : VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
399
+       externalBufferInfo.spa_buf = buffersi;
400
+       if (alloc) {
401
+           if (SPA_FLAG_IS_SET(buffersi->datas0.type, 1<<SPA_DATA_DmaBuf)) {
402
+               ret = vulkan_create_dmabuf(&s->base, &externalBufferInfo, &p->buffersi);
403
+           } else {
404
+               spa_log_error(s->log, "Unsupported buffer type mask %d", buffersi->datas0.type);
405
+               return -1;
406
+           }
407
+       } else {
408
+           switch (buffersi->datas0.type) {
409
+           case SPA_DATA_DmaBuf:;
410
+               ret = vulkan_import_dmabuf(&s->base, &externalBufferInfo, &p->buffersi);
411
+               break;
412
+           case SPA_DATA_MemPtr:;
413
+               if (p->direction == SPA_DIRECTION_OUTPUT) {
414
+                   externalBufferInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
415
+               } else {
416
+                   externalBufferInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
417
+               }
418
+               ret = vulkan_import_memptr(&s->base, &externalBufferInfo, &p->buffersi);
419
+               break;
420
+           default:
421
+               spa_log_error(s->log, "Unsupported buffer type %d", buffersi->datas0.type);
422
+               return -1;
423
+           }
424
+       }
425
+       if (ret != 0) {
426
+           spa_log_error(s->log, "Failed to use buffer %d", i);
427
+           return ret;
428
+       }
429
+       p->spa_buffersi = buffersi;
430
+       p->n_buffers++;
431
+   }
432
+
433
+   return 0;
434
+}
435
+
436
+int spa_vulkan_blit_enumerate_raw_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
437
+       struct spa_pod **param, struct spa_pod_builder *builder)
438
+{
439
+   uint32_t fmt_idx;
440
+   bool has_modifier;
441
+   if (!find_EnumFormatInfo(&s->formatInfosRaw, index, caps, &fmt_idx, &has_modifier))
442
+           return 0;
443
+   *param = build_raw_EnumFormat(&s->formatInfosRaw.infosfmt_idx, has_modifier, builder);
444
+   return 1;
445
+}
446
+
447
+int spa_vulkan_blit_enumerate_dsp_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
448
+       struct spa_pod **param, struct spa_pod_builder *builder)
449
+{
450
+   uint32_t fmt_idx;
451
+   bool has_modifier;
452
+   if (!find_EnumFormatInfo(&s->formatInfosDSP, index, caps, &fmt_idx, &has_modifier))
453
+           return 0;
454
+   *param = build_dsp_EnumFormat(&s->formatInfosDSP.infosfmt_idx, has_modifier, builder);
455
+   return 1;
456
+}
457
+
458
+int spa_vulkan_blit_enumerate_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
459
+       struct spa_pod **param, struct spa_pod_builder *builder)
460
+{
461
+   uint32_t fmt_idx;
462
+   bool has_modifier;
463
+   uint32_t raw_offset = 0;
464
+   if ((caps & VULKAN_BUFFER_TYPE_CAP_SHM) > 0)
465
+       raw_offset += s->formatInfosDSP.formatCount;
466
+   if ((caps & VULKAN_BUFFER_TYPE_CAP_DMABUF) > 0)
467
+       raw_offset += s->formatInfosDSP.formatsWithModifiersCount;
468
+   if (index < raw_offset) {
469
+       if (find_EnumFormatInfo(&s->formatInfosDSP, index, caps, &fmt_idx, &has_modifier)) {
470
+           *param = build_dsp_EnumFormat(&s->formatInfosDSP.infosfmt_idx, has_modifier, builder);
471
+           return 1;
472
+       }
473
+   } else {
474
+       if (find_EnumFormatInfo(&s->formatInfosRaw, index - raw_offset, caps, &fmt_idx, &has_modifier)) {
475
+           *param = build_raw_EnumFormat(&s->formatInfosRaw.infosfmt_idx, has_modifier, builder);
476
+           return 1;
477
+       }
478
+   }
479
+   return 0;
480
+}
481
+
482
+static int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
483
+       struct spa_dict *props)
484
+{
485
+   spa_zero(*stream);
486
+   stream->direction = direction;
487
+   stream->maxsize = 0;
488
+   return 0;
489
+}
490
+
491
+int spa_vulkan_blit_init_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass)
492
+{
493
+   pass->in_buffer_id = SPA_ID_INVALID;
494
+   pass->in_stream_id = SPA_ID_INVALID;
495
+   pass->out_buffer_id = SPA_ID_INVALID;
496
+   pass->out_stream_id = SPA_ID_INVALID;
497
+
498
+   pass->sync_fd = -1;
499
+
500
+   CHECK(vulkan_fence_create(&s->base, &pass->fence));
501
+   CHECK(vulkan_commandBuffer_create(&s->base, s->commandPool, &pass->commandBuffer));
502
+
503
+   VkExportSemaphoreCreateInfo export_info = {
504
+       .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
505
+       .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
506
+   };
507
+   VkSemaphoreCreateInfo semaphore_info = {
508
+       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
509
+       .pNext = &export_info,
510
+   };
511
+   VK_CHECK_RESULT(vkCreateSemaphore(s->base.device, &semaphore_info, NULL, &pass->pipelineSemaphore));
512
+
513
+   for (uint32_t i = 0; i < s->n_streams; i++) {
514
+       struct vulkan_stream *p = &s->streamsi;
515
+
516
+       if (p->direction == SPA_DIRECTION_OUTPUT || p->buffer_type != SPA_DATA_MemPtr)
517
+           continue;
518
+       vulkan_staging_buffer_create(&s->base, p->maxsize, &pass->in_staging_buffer);
519
+   }
520
+
521
+   return 0;
522
+}
523
+
524
+int spa_vulkan_blit_reset_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass)
525
+{
526
+   pass->in_buffer_id = SPA_ID_INVALID;
527
+   pass->in_stream_id = SPA_ID_INVALID;
528
+   pass->out_buffer_id = SPA_ID_INVALID;
529
+   pass->out_stream_id = SPA_ID_INVALID;
530
+
531
+   if (pass->sync_fd != -1) {
532
+       close(pass->sync_fd);
533
+       pass->sync_fd = -1;
534
+   }
535
+
536
+   return 0;
537
+}
538
+
539
+int spa_vulkan_blit_clear_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass)
540
+{
541
+   pass->in_buffer_id = SPA_ID_INVALID;
542
+   pass->in_stream_id = SPA_ID_INVALID;
543
+   pass->out_buffer_id = SPA_ID_INVALID;
544
+   pass->out_stream_id = SPA_ID_INVALID;
545
+
546
+   if (pass->sync_fd != -1) {
547
+       close(pass->sync_fd);
548
+       pass->sync_fd = -1;
549
+   }
550
+
551
+   vkDestroyFence(s->base.device, pass->fence, NULL);
552
+   pass->fence = VK_NULL_HANDLE;
553
+   vkFreeCommandBuffers(s->base.device, s->commandPool, 1, &pass->commandBuffer);
554
+   pass->commandBuffer = VK_NULL_HANDLE;
555
+   vkDestroySemaphore(s->base.device, pass->pipelineSemaphore, NULL);
556
+   pass->pipelineSemaphore = VK_NULL_HANDLE;
557
+   vulkan_staging_buffer_destroy(&s->base, &pass->in_staging_buffer);
558
+   pass->in_staging_buffer.buffer = VK_NULL_HANDLE;
559
+
560
+   return 0;
561
+}
562
+
563
+int spa_vulkan_blit_init_stream(struct vulkan_blit_state *s, struct vulkan_stream *stream,
564
+       enum spa_direction direction, struct spa_dict *props)
565
+{
566
+   return vulkan_stream_init(stream, direction, props);
567
+}
568
+
569
+int spa_vulkan_blit_prepare(struct vulkan_blit_state *s)
570
+{
571
+   if (!s->prepared) {
572
+       CHECK(vulkan_commandPool_create(&s->base, &s->commandPool));
573
+       s->prepared = true;
574
+   }
575
+   return 0;
576
+}
577
+
578
+int spa_vulkan_blit_unprepare(struct vulkan_blit_state *s)
579
+{
580
+   if (s->prepared) {
581
+       vkDestroyCommandPool(s->base.device, s->commandPool, NULL);
582
+       s->prepared = false;
583
+   }
584
+   return 0;
585
+}
586
+
587
+int spa_vulkan_blit_start(struct vulkan_blit_state *s)
588
+{
589
+   return 0;
590
+}
591
+
592
+int spa_vulkan_blit_stop(struct vulkan_blit_state *s)
593
+{
594
+        VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device));
595
+   clear_streams(s);
596
+   s->started = false;
597
+   return 0;
598
+}
599
+
600
+int spa_vulkan_blit_process(struct vulkan_blit_state *s, struct vulkan_pass *pass)
601
+{
602
+   if (!s->initialized) {
603
+       spa_log_warn(s->log, "Renderer not initialized");
604
+       return -1;
605
+   }
606
+   if (!s->prepared) {
607
+       spa_log_warn(s->log, "Renderer not prepared");
608
+       return -1;
609
+   }
610
+   CHECK(runImportSync(s, pass));
611
+   CHECK(runImportSHMBuffers(s, pass));
612
+   CHECK(runCommandBuffer(s, pass));
613
+   if (pass->sync_fd != -1) {
614
+       runExportSync(s, pass);
615
+   }
616
+   CHECK(vulkan_wait_idle(&s->base));
617
+   CHECK(runExportSHMBuffers(s, pass));
618
+
619
+   return 0;
620
+}
621
+
622
+int spa_vulkan_blit_get_buffer_caps(struct vulkan_blit_state *s, enum spa_direction direction)
623
+{
624
+   switch (direction) {
625
+   case SPA_DIRECTION_INPUT:
626
+       return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM;
627
+   case SPA_DIRECTION_OUTPUT:
628
+       return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM;
629
+   }
630
+   return 0;
631
+}
632
+
633
+struct vulkan_modifier_info *spa_vulkan_blit_get_modifier_info(struct vulkan_blit_state *s, struct spa_video_info *info) {
634
+   VkFormat format;
635
+   uint64_t modifier;
636
+   switch (info->media_subtype) {
637
+   case SPA_MEDIA_SUBTYPE_dsp:
638
+       format = vulkan_id_to_vkformat(info->info.dsp.format);
639
+       modifier = info->info.dsp.modifier;
640
+       return vulkan_modifierInfo_find(&s->formatInfosDSP, format, modifier);
641
+   case SPA_MEDIA_SUBTYPE_raw:
642
+       format = vulkan_id_to_vkformat(info->info.raw.format);
643
+       modifier = info->info.raw.modifier;
644
+       return vulkan_modifierInfo_find(&s->formatInfosRaw, format, modifier);
645
+   default:
646
+       spa_log_warn(s->log, "Unsupported media subtype %d", info->media_subtype);
647
+       return NULL;
648
+   }
649
+}
650
+
651
+int spa_vulkan_blit_init(struct vulkan_blit_state *s)
652
+{
653
+   int ret;
654
+   s->base.log = s->log;
655
+   struct vulkan_base_info baseInfo = {
656
+       .queueFlags = VK_QUEUE_TRANSFER_BIT,
657
+   };
658
+   if ((ret = vulkan_base_init(&s->base, &baseInfo)) < 0)
659
+       return ret;
660
+
661
+   uint32_t dsp_formats  = {
662
+       SPA_VIDEO_FORMAT_DSP_F32
663
+   };
664
+   vulkan_format_infos_init(&s->base, SPA_N_ELEMENTS(dsp_formats), dsp_formats, &s->formatInfosDSP);
665
+   uint32_t raw_formats  = {
666
+       SPA_VIDEO_FORMAT_BGRA,
667
+       SPA_VIDEO_FORMAT_RGBA,
668
+       SPA_VIDEO_FORMAT_BGRx,
669
+       SPA_VIDEO_FORMAT_RGBx,
670
+       SPA_VIDEO_FORMAT_BGR,
671
+       SPA_VIDEO_FORMAT_RGB,
672
+   };
673
+   vulkan_format_infos_init(&s->base, SPA_N_ELEMENTS(raw_formats), raw_formats, &s->formatInfosRaw);
674
+   s->initialized = true;
675
+   return 0;
676
+}
677
+
678
+void spa_vulkan_blit_deinit(struct vulkan_blit_state *s)
679
+{
680
+   vulkan_format_infos_deinit(&s->formatInfosRaw);
681
+   vulkan_format_infos_deinit(&s->formatInfosDSP);
682
+   vulkan_base_deinit(&s->base);
683
+   s->initialized = false;
684
+}
685
pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-blit-utils.h Added
98
 
1
@@ -0,0 +1,96 @@
2
+/* Spa */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2023 columbarius */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <vulkan/vulkan.h>
8
+
9
+#include <spa/buffer/buffer.h>
10
+#include <spa/param/video/format.h>
11
+#include <spa/node/node.h>
12
+#include <spa/pod/builder.h>
13
+
14
+#include "vulkan-utils.h"
15
+
16
+#define MAX_STREAMS 2
17
+
18
+struct vulkan_pass {
19
+   uint32_t in_buffer_id;
20
+   uint32_t in_stream_id;
21
+
22
+   uint32_t out_buffer_id;
23
+   uint32_t out_stream_id;
24
+
25
+   VkBufferImageCopy in_copy;
26
+   struct vulkan_staging_buffer in_staging_buffer;
27
+
28
+   VkCommandBuffer commandBuffer;
29
+   VkSemaphore pipelineSemaphore;
30
+   VkFence fence;
31
+
32
+   int sync_fd;
33
+};
34
+
35
+struct vulkan_stream {
36
+   enum spa_direction direction;
37
+
38
+   enum spa_data_type buffer_type;
39
+   struct spa_rectangle dim;
40
+   uint32_t bpp;
41
+   uint32_t maxsize;
42
+
43
+   struct vulkan_buffer buffersMAX_BUFFERS;
44
+   struct spa_buffer *spa_buffersMAX_BUFFERS;
45
+   uint32_t n_buffers;
46
+};
47
+
48
+struct vulkan_blit_state {
49
+   struct spa_log *log;
50
+
51
+   struct vulkan_base base;
52
+
53
+   struct vulkan_format_infos formatInfosRaw;
54
+   struct vulkan_format_infos formatInfosDSP;
55
+
56
+   VkCommandPool commandPool;
57
+
58
+   unsigned int initialized:1;
59
+   unsigned int prepared:1;
60
+   unsigned int started:1;
61
+
62
+   uint32_t n_streams;
63
+   struct vulkan_stream streamsMAX_STREAMS;
64
+};
65
+
66
+int spa_vulkan_blit_init_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass);
67
+int spa_vulkan_blit_reset_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass);
68
+int spa_vulkan_blit_clear_pass(struct vulkan_blit_state *s, struct vulkan_pass *pass);
69
+
70
+int spa_vulkan_blit_init_stream(struct vulkan_blit_state *s, struct vulkan_stream *stream, enum spa_direction,
71
+       struct spa_dict *props);
72
+
73
+int spa_vulkan_blit_fixate_modifier(struct vulkan_blit_state *s, struct vulkan_stream *p, struct spa_video_info *info,
74
+       uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier);
75
+int spa_vulkan_blit_use_buffers(struct vulkan_blit_state *s, struct vulkan_stream *stream, uint32_t flags,
76
+       struct spa_video_info *info, uint32_t n_buffers, struct spa_buffer **buffers);
77
+int spa_vulkan_blit_enumerate_raw_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
78
+       struct spa_pod **param, struct spa_pod_builder *builder);
79
+int spa_vulkan_blit_enumerate_dsp_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
80
+       struct spa_pod **param, struct spa_pod_builder *builder);
81
+int spa_vulkan_blit_enumerate_formats(struct vulkan_blit_state *s, uint32_t index, uint32_t caps,
82
+       struct spa_pod **param, struct spa_pod_builder *builder);
83
+int spa_vulkan_blit_prepare(struct vulkan_blit_state *s);
84
+int spa_vulkan_blit_unprepare(struct vulkan_blit_state *s);
85
+
86
+int spa_vulkan_blit_start(struct vulkan_blit_state *s);
87
+int spa_vulkan_blit_stop(struct vulkan_blit_state *s);
88
+int spa_vulkan_blit_ready(struct vulkan_blit_state *s);
89
+int spa_vulkan_blit_process(struct vulkan_blit_state *s, struct vulkan_pass *pass);
90
+int spa_vulkan_blit_cleanup(struct vulkan_blit_state *s);
91
+
92
+int spa_vulkan_blit_get_buffer_caps(struct vulkan_blit_state *s, enum spa_direction direction);
93
+struct vulkan_modifier_info *spa_vulkan_blit_get_modifier_info(struct vulkan_blit_state *s,
94
+       struct spa_video_info *info);
95
+
96
+int spa_vulkan_blit_init(struct vulkan_blit_state *s);
97
+void spa_vulkan_blit_deinit(struct vulkan_blit_state *s);
98
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-compute-filter.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-compute-filter.c Changed
346
 
1
@@ -25,7 +25,9 @@
2
 
3
 #include "vulkan-compute-utils.h"
4
 
5
-#define NAME "vulkan-compute-filter"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.vulkan.compute-filter");
9
 
10
 struct buffer {
11
    uint32_t id;
12
@@ -41,7 +43,13 @@
13
    struct spa_port_info info;
14
 
15
    enum spa_direction direction;
16
-   struct spa_param_info params5;
17
+#define IDX_EnumFormat 0
18
+#define IDX_Meta   1
19
+#define IDX_IO     2
20
+#define IDX_Format 3
21
+#define IDX_Buffer 4
22
+#define N_PORT_PARAMS  5
23
+   struct spa_param_info paramsN_PORT_PARAMS;
24
 
25
    struct spa_io_buffers *io;
26
 
27
@@ -66,7 +74,10 @@
28
 
29
    uint64_t info_all;
30
    struct spa_node_info info;
31
-   struct spa_param_info params2;
32
+#define IDX_PropInfo   0
33
+#define IDX_Props  1
34
+#define N_NODE_PARAMS  2
35
+   struct spa_param_info paramsN_NODE_PARAMS;
36
 
37
    struct spa_hook_list hooks;
38
    struct spa_callbacks callbacks;
39
@@ -152,7 +163,7 @@
40
    struct buffer *b = &port->buffersid;
41
 
42
    if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
43
-       spa_log_debug(this->log, NAME " %p: reuse buffer %d", this, id);
44
+       spa_log_debug(this->log, "%p: reuse buffer %d", this, id);
45
 
46
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
47
        spa_list_append(&port->empty, &b->link);
48
@@ -172,7 +183,7 @@
49
            return 0;
50
 
51
        this->started = true;
52
-       spa_vulkan_start(&this->state);
53
+       spa_vulkan_compute_start(&this->state);
54
        break;
55
 
56
    case SPA_NODE_COMMAND_Suspend:
57
@@ -181,7 +192,7 @@
58
            return 0;
59
 
60
        this->started = false;
61
-       spa_vulkan_stop(&this->state);
62
+       spa_vulkan_compute_stop(&this->state);
63
        break;
64
    default:
65
        return -ENOTSUP;
66
@@ -269,64 +280,6 @@
67
    return -ENOTSUP;
68
 }
69
 
70
-static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) {
71
-   struct spa_pod_frame f2;
72
-   uint32_t i, c;
73
-
74
-   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
75
-   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
76
-   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0);
77
-   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0);
78
-   if (fmtInfo && fmtInfo->modifierCount > 0) {
79
-       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
80
-       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
81
-       for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) {
82
-           spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
83
-           if (c++ == 0)
84
-               spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
85
-       }
86
-       spa_pod_builder_pop(builder, &f1);
87
-   }
88
-   return spa_pod_builder_pop(builder, &f0);
89
-}
90
-
91
-// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs
92
-// first and then falling back to those supported with SHM buffers.
93
-static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) {
94
-   int64_t fmtIterator = 0;
95
-   int64_t maxIterator = 0;
96
-   if (caps & VULKAN_BUFFER_TYPE_CAP_SHM)
97
-       maxIterator += s->formatInfoCount;
98
-   if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF)
99
-       maxIterator += s->formatInfoCount;
100
-   // Count available formats until index underflows, while fmtIterator indexes the current format.
101
-   // Iterate twice over formats first time with modifiers, second time without if both caps are supported.
102
-   while (index < (uint32_t)-1 && fmtIterator < maxIterator) {
103
-       const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount;
104
-       if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) {
105
-           // First round, check for modifiers
106
-           if (f_info->modifierCount > 0) {
107
-               index--;
108
-           }
109
-       } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) {
110
-           // Second round, every format should be supported.
111
-           index--;
112
-       }
113
-       fmtIterator++;
114
-   }
115
-
116
-   if (index != (uint32_t)-1) {
117
-       // No more formats available
118
-       return false;
119
-   }
120
-   // Undo end of loop increment
121
-   fmtIterator--;
122
-   *fmt_idx = fmtIterator%s->formatInfoCount;
123
-   // Loop finished in first round
124
-   *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount;
125
-   return true;
126
-}
127
-
128
 static int port_enum_formats(void *object,
129
                 enum spa_direction direction, uint32_t port_id,
130
                 uint32_t index,
131
@@ -336,8 +289,6 @@
132
 {
133
    struct impl *this = object;
134
 
135
-   uint32_t fmt_index;
136
-   bool has_modifier;
137
    if (this->portport_id.have_format
138
            && this->portport_id.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
139
            && this->portport_id.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
140
@@ -347,18 +298,10 @@
141
            *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.dsp);
142
            return 1;
143
        }
144
-       if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
145
-           return 0;
146
+       return spa_vulkan_compute_enumerate_formats(&this->state, index-1, spa_vulkan_compute_get_buffer_caps(&this->state, direction), param, builder);
147
    } else {
148
-       if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
149
-           return 0;
150
+       return spa_vulkan_compute_enumerate_formats(&this->state, index, spa_vulkan_compute_get_buffer_caps(&this->state, direction), param, builder);
151
    }
152
-
153
-   const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index;
154
-   spa_log_info(this->log, "vulkan-compute-filter: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier);
155
-   *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder);
156
-
157
-   return 1;
158
 }
159
 
160
 static int
161
@@ -414,13 +357,13 @@
162
        if (result.index > 0)
163
            return 0;
164
 
165
-       spa_log_debug(this->log, NAME" %p: %dx%d stride %d", this,
166
+       spa_log_debug(this->log, "%p: %dx%d stride %d", this,
167
                this->position->video.size.width,
168
                this->position->video.size.height,
169
                this->position->video.stride);
170
 
171
        if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
172
-           struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state,
173
+           struct vulkan_modifier_info *mod_info = spa_vulkan_compute_get_modifier_info(&this->state,
174
                &port->current_format.info.dsp);
175
            param = spa_pod_builder_add_object(&b,
176
                SPA_TYPE_OBJECT_ParamBuffers, id,
177
@@ -471,9 +414,9 @@
178
 static int clear_buffers(struct impl *this, struct port *port)
179
 {
180
    if (port->n_buffers > 0) {
181
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
182
-       spa_vulkan_stop(&this->state);
183
-       spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format.info.dsp, 0, NULL);
184
+       spa_log_debug(this->log, "%p: clear buffers", this);
185
+       spa_vulkan_compute_stop(&this->state);
186
+       spa_vulkan_compute_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format.info.dsp, 0, NULL);
187
        port->n_buffers = 0;
188
        spa_list_init(&port->empty);
189
        spa_list_init(&port->ready);
190
@@ -491,7 +434,7 @@
191
    if (format == NULL) {
192
        port->have_format = false;
193
        clear_buffers(this, port);
194
-       spa_vulkan_unprepare(&this->state);
195
+       spa_vulkan_compute_unprepare(&this->state);
196
    } else {
197
        struct spa_video_info info = { 0 };
198
 
199
@@ -528,10 +471,10 @@
200
            modifierCount -= 1;
201
            modifiers++;
202
            uint64_t fixed_modifier;
203
-           if (spa_vulkan_fixate_modifier(&this->state, &this->state.streamsport->stream_id, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
204
+           if (spa_vulkan_compute_fixate_modifier(&this->state, &this->state.streamsport->stream_id, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
205
                return -EINVAL;
206
 
207
-           spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier);
208
+           spa_log_info(this->log, "modifier fixated %"PRIu64, fixed_modifier);
209
 
210
            info.info.dsp.modifier = fixed_modifier;
211
            info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
212
@@ -550,7 +493,7 @@
213
 
214
        if (modifier_fixed) {
215
            port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
216
-           port->params0.flags ^= SPA_PARAM_INFO_SERIAL;
217
+           port->paramsIDX_EnumFormat.flags ^= SPA_PARAM_INFO_SERIAL;
218
            emit_port_info(this, port, false);
219
            return 0;
220
        }
221
@@ -558,11 +501,11 @@
222
 
223
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
224
    if (port->have_format) {
225
-       port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
226
-       port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
227
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
228
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
229
    } else {
230
-       port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
231
-       port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
232
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
233
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
234
    }
235
    emit_port_info(this, port, false);
236
 
237
@@ -628,7 +571,7 @@
238
        spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
239
        spa_list_append(&port->empty, &b->link);
240
    }
241
-   spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format.info.dsp, n_buffers, buffers);
242
+   spa_vulkan_compute_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format.info.dsp, n_buffers, buffers);
243
    port->n_buffers = n_buffers;
244
 
245
    return 0;
246
@@ -708,7 +651,7 @@
247
    }
248
 
249
    if (spa_list_is_empty(&outport->empty)) {
250
-       spa_log_debug(this->log, NAME " %p: out of buffers", this);
251
+       spa_log_debug(this->log, "%p: out of buffers", this);
252
        return -EPIPE;
253
    }
254
    b = &inport->buffersinio->buffer_id;
255
@@ -720,12 +663,12 @@
256
    SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
257
    this->state.streamsoutport->stream_id.pending_buffer_id = b->id;
258
 
259
-   this->state.constants.time += 0.025;
260
+   this->state.constants.time += 0.025f;
261
    this->state.constants.frame++;
262
 
263
    spa_log_debug(this->log, "filter into %d", b->id);
264
 
265
-   spa_vulkan_process(&this->state);
266
+   spa_vulkan_compute_process(&this->state);
267
 
268
    b->outbuf->datas0.chunk->offset = 0;
269
    b->outbuf->datas0.chunk->size = b->outbuf->datas0.maxsize;
270
@@ -780,7 +723,7 @@
271
 
272
    this = (struct impl *) handle;
273
 
274
-   spa_vulkan_deinit(&this->state);
275
+   spa_vulkan_compute_deinit(&this->state);
276
    return 0;
277
 }
278
 
279
@@ -827,10 +770,10 @@
280
    this->info.max_output_ports = 1;
281
    this->info.max_input_ports = 1;
282
    this->info.flags = SPA_NODE_FLAG_RT;
283
-   this->params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
284
-   this->params1 = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
285
+   this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
286
+   this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
287
    this->info.params = this->params;
288
-   this->info.n_params = 2;
289
+   this->info.n_params = N_NODE_PARAMS;
290
 
291
    port = &this->port0;
292
    port->stream_id = 1;
293
@@ -840,14 +783,14 @@
294
            SPA_PORT_CHANGE_MASK_PROPS;
295
    port->info = SPA_PORT_INFO_INIT();
296
    port->info.flags = SPA_PORT_FLAG_NO_REF;
297
-   port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
298
-   port->params1 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
299
-   port->params2 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
300
-   port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
301
-   port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
302
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
303
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
304
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
305
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
306
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
307
    port->info.params = port->params;
308
-   port->info.n_params = 5;
309
-   spa_vulkan_init_stream(&this->state, &this->state.streamsport->stream_id,
310
+   port->info.n_params = N_PORT_PARAMS;
311
+   spa_vulkan_compute_init_stream(&this->state, &this->state.streamsport->stream_id,
312
            SPA_DIRECTION_INPUT, NULL);
313
    spa_list_init(&port->empty);
314
    spa_list_init(&port->ready);
315
@@ -860,21 +803,21 @@
316
            SPA_PORT_CHANGE_MASK_PROPS;
317
    port->info = SPA_PORT_INFO_INIT();
318
    port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
319
-   port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
320
-   port->params1 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
321
-   port->params2 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
322
-   port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
323
-   port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
324
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
325
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
326
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
327
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
328
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
329
    port->info.params = port->params;
330
-   port->info.n_params = 5;
331
+   port->info.n_params = N_PORT_PARAMS;
332
    spa_list_init(&port->empty);
333
    spa_list_init(&port->ready);
334
-   spa_vulkan_init_stream(&this->state, &this->state.streamsport->stream_id,
335
+   spa_vulkan_compute_init_stream(&this->state, &this->state.streamsport->stream_id,
336
            SPA_DIRECTION_OUTPUT, NULL);
337
 
338
    this->state.n_streams = 2;
339
-   spa_vulkan_init(&this->state);
340
-   spa_vulkan_prepare(&this->state);
341
+   spa_vulkan_compute_init(&this->state);
342
+   spa_vulkan_compute_prepare(&this->state);
343
 
344
    return 0;
345
 }
346
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-compute-source.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c Changed
356
 
1
@@ -26,7 +26,9 @@
2
 
3
 #include "vulkan-compute-utils.h"
4
 
5
-#define NAME "vulkan-compute-source"
6
+#undef SPA_LOG_TOPIC_DEFAULT
7
+#define SPA_LOG_TOPIC_DEFAULT &log_topic
8
+SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.vulkan.compute-source");
9
 
10
 #define FRAMES_TO_TIME(this,f) ((this->position->video.framerate.denom * (f) * SPA_NSEC_PER_SEC) / \
11
                                 (this->position->video.framerate.num))
12
@@ -54,7 +56,15 @@
13
 struct port {
14
    uint64_t info_all;
15
    struct spa_port_info info;
16
-   struct spa_param_info params5;
17
+
18
+   enum spa_direction direction;
19
+#define IDX_EnumFormat 0
20
+#define IDX_Meta   1
21
+#define IDX_IO     2
22
+#define IDX_Format 3
23
+#define IDX_Buffer 4
24
+#define N_PORT_PARAMS  5
25
+   struct spa_param_info paramsN_PORT_PARAMS;
26
 
27
    struct spa_io_buffers *io;
28
 
29
@@ -81,7 +91,10 @@
30
 
31
    uint64_t info_all;
32
    struct spa_node_info info;
33
-   struct spa_param_info params2;
34
+#define IDX_PropInfo   0
35
+#define IDX_Props  1
36
+#define N_NODE_PARAMS  2
37
+   struct spa_param_info paramsN_NODE_PARAMS;
38
    struct props props;
39
 
40
    struct spa_hook_list hooks;
41
@@ -257,7 +270,7 @@
42
        if ((res = spa_system_timerfd_read(this->data_system,
43
                        this->timer_source.fd, &expirations)) < 0) {
44
            if (res != -EAGAIN)
45
-               spa_log_error(this->log, NAME " %p: timerfd error: %s",
46
+               spa_log_error(this->log, "%p: timerfd error: %s",
47
                        this, spa_strerror(res));
48
        }
49
    }
50
@@ -274,14 +287,14 @@
51
    if (read_timer(this) < 0)
52
        return 0;
53
 
54
-   if ((res = spa_vulkan_ready(&this->state)) < 0) {
55
+   if ((res = spa_vulkan_compute_ready(&this->state)) < 0) {
56
        res = SPA_STATUS_OK;
57
        goto next;
58
    }
59
 
60
    if (spa_list_is_empty(&port->empty)) {
61
        set_timer(this, false);
62
-       spa_log_error(this->log, NAME " %p: out of buffers", this);
63
+       spa_log_error(this->log, "%p: out of buffers", this);
64
        return -EPIPE;
65
    }
66
    b = spa_list_first(&port->empty, struct buffer, link);
67
@@ -289,20 +302,20 @@
68
 
69
    n_bytes = b->outbuf->datas0.maxsize;
70
 
71
-   spa_log_trace(this->log, NAME " %p: dequeue buffer %d", this, b->id);
72
+   spa_log_trace(this->log, "%p: dequeue buffer %d", this, b->id);
73
 
74
    this->state.constants.time = this->elapsed_time / (float) SPA_NSEC_PER_SEC;
75
    this->state.constants.frame = this->frame_count;
76
 
77
    this->state.streams0.pending_buffer_id = b->id;
78
-   spa_vulkan_process(&this->state);
79
+   spa_vulkan_compute_process(&this->state);
80
 
81
    if (this->state.streams0.ready_buffer_id != SPA_ID_INVALID) {
82
        struct buffer *b = &port->buffersthis->state.streams0.ready_buffer_id;
83
 
84
        this->state.streams0.ready_buffer_id = SPA_ID_INVALID;
85
 
86
-       spa_log_trace(this->log, NAME " %p: ready buffer %d", this, b->id);
87
+       spa_log_trace(this->log, "%p: ready buffer %d", this, b->id);
88
 
89
        b->outbuf->datas0.chunk->offset = 0;
90
        b->outbuf->datas0.chunk->size = n_bytes;
91
@@ -331,7 +344,7 @@
92
    struct buffer *b = &port->buffersid;
93
 
94
    if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
95
-       spa_log_trace(this->log, NAME " %p: reuse buffer %d", this, id);
96
+       spa_log_trace(this->log, "%p: reuse buffer %d", this, id);
97
 
98
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
99
        spa_list_append(&port->empty, &b->link);
100
@@ -405,7 +418,7 @@
101
 
102
        this->started = true;
103
        set_timer(this, true);
104
-       spa_vulkan_start(&this->state);
105
+       spa_vulkan_compute_start(&this->state);
106
        break;
107
    }
108
    case SPA_NODE_COMMAND_Suspend:
109
@@ -415,7 +428,7 @@
110
 
111
        this->started = false;
112
        set_timer(this, false);
113
-       spa_vulkan_stop(&this->state);
114
+       spa_vulkan_compute_stop(&this->state);
115
        break;
116
    default:
117
        return -ENOTSUP;
118
@@ -503,64 +516,6 @@
119
    return -ENOTSUP;
120
 }
121
 
122
-static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) {
123
-   struct spa_pod_frame f2;
124
-   uint32_t i, c;
125
-
126
-   spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
127
-   spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
128
-   spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0);
129
-   spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0);
130
-   if (fmtInfo && fmtInfo->modifierCount > 0) {
131
-       spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
132
-       spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0);
133
-       for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) {
134
-           spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
135
-           if (c++ == 0)
136
-               spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier);
137
-       }
138
-       spa_pod_builder_pop(builder, &f1);
139
-   }
140
-   return spa_pod_builder_pop(builder, &f0);
141
-}
142
-
143
-// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs
144
-// first and then falling back to those supported with SHM buffers.
145
-static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) {
146
-   int64_t fmtIterator = 0;
147
-   int64_t maxIterator = 0;
148
-   if (caps & VULKAN_BUFFER_TYPE_CAP_SHM)
149
-       maxIterator += s->formatInfoCount;
150
-   if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF)
151
-       maxIterator += s->formatInfoCount;
152
-   // Count available formats until index underflows, while fmtIterator indexes the current format.
153
-   // Iterate twice over formats first time with modifiers, second time without if both caps are supported.
154
-   while (index < (uint32_t)-1 && fmtIterator < maxIterator) {
155
-       const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount;
156
-       if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) {
157
-           // First round, check for modifiers
158
-           if (f_info->modifierCount > 0) {
159
-               index--;
160
-           }
161
-       } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) {
162
-           // Second round, every format should be supported.
163
-           index--;
164
-       }
165
-       fmtIterator++;
166
-   }
167
-
168
-   if (index != (uint32_t)-1) {
169
-       // No more formats available
170
-       return false;
171
-   }
172
-   // Undo end of loop increment
173
-   fmtIterator--;
174
-   *fmt_idx = fmtIterator%s->formatInfoCount;
175
-   // Loop finished in first round
176
-   *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount;
177
-   return true;
178
-}
179
-
180
 static int port_enum_formats(void *object,
181
                 enum spa_direction direction, uint32_t port_id,
182
                 uint32_t index,
183
@@ -570,8 +525,6 @@
184
 {
185
    struct impl *this = object;
186
 
187
-   uint32_t fmt_index;
188
-   bool has_modifier;
189
    if (this->port.have_format
190
            && this->port.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER
191
            && this->port.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) {
192
@@ -581,18 +534,10 @@
193
            *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->port.current_format.info.dsp);
194
            return 1;
195
        }
196
-       if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
197
-           return 0;
198
+       return spa_vulkan_compute_enumerate_formats(&this->state, index-1, spa_vulkan_compute_get_buffer_caps(&this->state, direction), param, builder);
199
    } else {
200
-       if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier))
201
-           return 0;
202
+       return spa_vulkan_compute_enumerate_formats(&this->state, index, spa_vulkan_compute_get_buffer_caps(&this->state, direction), param, builder);
203
    }
204
-
205
-   const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index;
206
-   spa_log_info(this->log, "vulkan-compute-source: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier);
207
-   *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder);
208
-
209
-   return 1;
210
 }
211
 
212
 static int
213
@@ -648,14 +593,14 @@
214
        if (result.index > 0)
215
            return 0;
216
 
217
-       spa_log_debug(this->log, NAME" %p: %dx%d stride %d", this,
218
+       spa_log_debug(this->log, "%p: %dx%d stride %d", this,
219
                this->position->video.size.width,
220
                this->position->video.size.height,
221
                this->position->video.stride);
222
 
223
 
224
        if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) {
225
-           struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state,
226
+           struct vulkan_modifier_info *mod_info = spa_vulkan_compute_get_modifier_info(&this->state,
227
                &port->current_format.info.dsp);
228
            param = spa_pod_builder_add_object(&b,
229
                SPA_TYPE_OBJECT_ParamBuffers, id,
230
@@ -706,8 +651,8 @@
231
 static int clear_buffers(struct impl *this, struct port *port)
232
 {
233
    if (port->n_buffers > 0) {
234
-       spa_log_debug(this->log, NAME " %p: clear buffers", this);
235
-       spa_vulkan_use_buffers(&this->state, &this->state.streams0, 0, &port->current_format.info.dsp, 0, NULL);
236
+       spa_log_debug(this->log, "%p: clear buffers", this);
237
+       spa_vulkan_compute_use_buffers(&this->state, &this->state.streams0, 0, &port->current_format.info.dsp, 0, NULL);
238
        port->n_buffers = 0;
239
        spa_list_init(&port->empty);
240
        spa_list_init(&port->ready);
241
@@ -726,7 +671,7 @@
242
    if (format == NULL) {
243
        port->have_format = false;
244
        clear_buffers(this, port);
245
-       spa_vulkan_unprepare(&this->state);
246
+       spa_vulkan_compute_unprepare(&this->state);
247
    } else {
248
        struct spa_video_info info = { 0 };
249
 
250
@@ -763,10 +708,10 @@
251
            modifiers++;
252
 
253
            uint64_t fixed_modifier;
254
-           if (spa_vulkan_fixate_modifier(&this->state, &this->state.streams0, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
255
+           if (spa_vulkan_compute_fixate_modifier(&this->state, &this->state.streams0, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0)
256
                return -EINVAL;
257
 
258
-           spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier);
259
+           spa_log_info(this->log, "modifier fixated %"PRIu64, fixed_modifier);
260
 
261
            info.info.dsp.modifier = fixed_modifier;
262
            info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED;
263
@@ -782,11 +727,11 @@
264
 
265
        port->current_format = info;
266
        port->have_format = true;
267
-       spa_vulkan_prepare(&this->state);
268
+       spa_vulkan_compute_prepare(&this->state);
269
 
270
        if (modifier_fixed) {
271
            port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
272
-           port->params0.flags ^= SPA_PARAM_INFO_SERIAL;
273
+           port->paramsIDX_EnumFormat.flags ^= SPA_PARAM_INFO_SERIAL;
274
            emit_port_info(this, port, false);
275
            return 0;
276
        }
277
@@ -794,11 +739,11 @@
278
 
279
    port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
280
    if (port->have_format) {
281
-       port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
282
-       port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
283
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
284
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
285
    } else {
286
-       port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
287
-       port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
288
+       port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
289
+       port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
290
    }
291
    emit_port_info(this, port, false);
292
 
293
@@ -864,7 +809,7 @@
294
        spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b);
295
        spa_list_append(&port->empty, &b->link);
296
    }
297
-   spa_vulkan_use_buffers(&this->state, &this->state.streams0, flags, &port->current_format.info.dsp, n_buffers, buffers);
298
+   spa_vulkan_compute_use_buffers(&this->state, &this->state.streams0, flags, &port->current_format.info.dsp, n_buffers, buffers);
299
    port->n_buffers = n_buffers;
300
 
301
    return 0;
302
@@ -985,7 +930,7 @@
303
 
304
    this = (struct impl *) handle;
305
 
306
-   spa_vulkan_deinit(&this->state);
307
+   spa_vulkan_compute_deinit(&this->state);
308
 
309
    if (this->data_loop)
310
        spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
311
@@ -1036,10 +981,10 @@
312
    this->info = SPA_NODE_INFO_INIT();
313
    this->info.max_output_ports = 1;
314
    this->info.flags = SPA_NODE_FLAG_RT;
315
-   this->params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
316
-   this->params1 = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
317
+   this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
318
+   this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
319
    this->info.params = this->params;
320
-   this->info.n_params = 2;
321
+   this->info.n_params = N_NODE_PARAMS;
322
    reset_props(&this->props);
323
 
324
    this->timer_source.func = on_output;
325
@@ -1064,22 +1009,22 @@
326
    port->info.flags = SPA_PORT_FLAG_NO_REF;
327
    if (this->props.live)
328
        port->info.flags |= SPA_PORT_FLAG_LIVE;
329
-   port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
330
-   port->params1 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
331
-   port->params2 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
332
-   port->params3 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
333
-   port->params4 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
334
+   port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
335
+   port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
336
+   port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
337
+   port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
338
+   port->paramsIDX_Buffer = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
339
    port->info.params = port->params;
340
-   port->info.n_params = 5;
341
+   port->info.n_params = N_PORT_PARAMS;
342
    spa_list_init(&port->empty);
343
    spa_list_init(&port->ready);
344
 
345
    this->state.log = this->log;
346
-   spa_vulkan_init_stream(&this->state, &this->state.streams0,
347
+   spa_vulkan_compute_init_stream(&this->state, &this->state.streams0,
348
            SPA_DIRECTION_OUTPUT, NULL);
349
    this->state.shaderName = "spa/plugins/vulkan/shaders/main.spv";
350
    this->state.n_streams = 1;
351
-   spa_vulkan_init(&this->state);
352
+   spa_vulkan_compute_init(&this->state);
353
 
354
    return 0;
355
 }
356
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-compute-utils.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.c Changed
323
 
1
@@ -27,20 +27,12 @@
2
 
3
 #include "vulkan-compute-utils.h"
4
 #include "vulkan-utils.h"
5
+#include "utils.h"
6
+#include "pixel-formats.h"
7
 
8
 #define VULKAN_INSTANCE_FUNCTION(name)                     \
9
    PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->base.instance, #name)
10
 
11
-static int createFence(struct vulkan_compute_state *s) {
12
-   VkFenceCreateInfo createInfo = {
13
-       .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
14
-       .flags = 0,
15
-   };
16
-   VK_CHECK_RESULT(vkCreateFence(s->base.device, &createInfo, NULL, &s->fence));
17
-
18
-   return 0;
19
-};
20
-
21
 static int createDescriptors(struct vulkan_compute_state *s)
22
 {
23
    uint32_t i;
24
@@ -249,6 +241,39 @@
25
    return 0;
26
 }
27
 
28
+static int runImportSHMBuffers(struct vulkan_compute_state *s) {
29
+   for (uint32_t i = 0; i < s->n_streams; i++) {
30
+       struct vulkan_stream *p = &s->streamsi;
31
+
32
+       if (p->direction == SPA_DIRECTION_OUTPUT)
33
+           continue;
34
+
35
+       struct pixel_format_info pixel_info;
36
+       CHECK(get_pixel_format_info(p->format, &pixel_info));
37
+
38
+       if (p->spa_buffersp->current_buffer_id->datas0.type == SPA_DATA_MemPtr) {
39
+           struct vulkan_buffer *vk_buf = &p->buffersp->current_buffer_id;
40
+           struct spa_buffer *spa_buf = p->spa_buffersp->current_buffer_id;
41
+           VkBufferImageCopy copy;
42
+           struct vulkan_write_pixels_info writeInfo = {
43
+               .data = spa_buf->datas0.data,
44
+               .offset = spa_buf->datas0.chunk->offset,
45
+               .stride = spa_buf->datas0.chunk->stride,
46
+               .bytes_per_pixel = pixel_info.bpp,
47
+               .size.width = s->constants.width,
48
+               .size.height = s->constants.height,
49
+               .copies = &copy,
50
+           };
51
+           CHECK(vulkan_write_pixels(&s->base, &writeInfo, &s->staging_buffer));
52
+
53
+           vkCmdCopyBufferToImage(s->commandBuffer, s->staging_buffer.buffer, vk_buf->image,
54
+                   VK_IMAGE_LAYOUT_GENERAL, 1, &copy);
55
+       }
56
+   }
57
+
58
+   return 0;
59
+}
60
+
61
 static int runExportSHMBuffers(struct vulkan_compute_state *s) {
62
    for (uint32_t i = 0; i < s->n_streams; i++) {
63
        struct vulkan_stream *p = &s->streamsi;
64
@@ -256,13 +281,16 @@
65
        if (p->direction == SPA_DIRECTION_INPUT)
66
            continue;
67
 
68
+       struct pixel_format_info pixel_info;
69
+       CHECK(get_pixel_format_info(p->format, &pixel_info));
70
+
71
        if (p->spa_buffersp->current_buffer_id->datas0.type == SPA_DATA_MemPtr) {
72
            struct spa_buffer *spa_buf = p->spa_buffersp->current_buffer_id;
73
            struct vulkan_read_pixels_info readInfo = {
74
                .data = spa_buf->datas0.data,
75
                .offset = spa_buf->datas0.chunk->offset,
76
                .stride = spa_buf->datas0.chunk->stride,
77
-               .bytes_per_pixel = 16,
78
+               .bytes_per_pixel = pixel_info.bpp,
79
                .size.width = s->constants.width,
80
                .size.height = s->constants.height,
81
            };
82
@@ -276,8 +304,8 @@
83
 /** runCommandBuffer
84
  *  The return value of this functions means the following:
85
  *  ret < 0: Error
86
- *  ret = 0: queueSubmit was succsessful, but manual synchronization is required
87
- *  ret = 1: queueSubmit was succsessful and buffers can be released without synchronization
88
+ *  ret = 0: queueSubmit was successful, but manual synchronization is required
89
+ *  ret = 1: queueSubmit was successful and buffers can be released without synchronization
90
  */
91
 static int runCommandBuffer(struct vulkan_compute_state *s)
92
 {
93
@@ -290,6 +318,8 @@
94
    };
95
    VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo));
96
 
97
+   CHECK(runImportSHMBuffers(s));
98
+
99
    vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline);
100
    vkCmdPushConstants (s->commandBuffer,
101
            s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
102
@@ -449,6 +479,10 @@
103
        p->spa_buffersi = NULL;
104
    }
105
    p->n_buffers = 0;
106
+   if (p->direction == SPA_DIRECTION_INPUT) {
107
+       vulkan_staging_buffer_destroy(&s->base, &s->staging_buffer);
108
+       s->staging_buffer.buffer = VK_NULL_HANDLE;
109
+   }
110
 }
111
 
112
 static void clear_streams(struct vulkan_compute_state *s)
113
@@ -460,7 +494,7 @@
114
    }
115
 }
116
 
117
-int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
118
+int spa_vulkan_compute_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
119
        uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier)
120
 {
121
    VkFormat format = vulkan_id_to_vkformat(dsp_info->format);
122
@@ -479,7 +513,7 @@
123
    return vulkan_fixate_modifier(&s->base, &fixation_info, modifier);
124
 }
125
 
126
-int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p, uint32_t flags,
127
+int spa_vulkan_compute_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p, uint32_t flags,
128
        struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers)
129
 {
130
    VkFormat format = vulkan_id_to_vkformat(dsp_info->format);
131
@@ -488,10 +522,13 @@
132
 
133
    vulkan_wait_idle(&s->base);
134
    clear_buffers(s, p);
135
+   p->format = SPA_VIDEO_FORMAT_UNKNOWN;
136
+
137
+   if (n_buffers == 0)
138
+       return 0;
139
 
140
    bool alloc = flags & SPA_NODE_BUFFERS_FLAG_ALLOC;
141
    int ret;
142
-   p->n_buffers = 0;
143
    for (uint32_t i = 0; i < n_buffers; i++) {
144
        if (alloc) {
145
            if (SPA_FLAG_IS_SET(buffersi->datas0.type, 1<<SPA_DATA_DmaBuf)) {
146
@@ -505,6 +542,8 @@
147
                        : VK_IMAGE_USAGE_SAMPLED_BIT,
148
                    .spa_buf = buffersi,
149
                };
150
+               struct vulkan_modifier_info *modifierInfo = vulkan_modifierInfo_find(&s->formatInfos, format, dsp_info->modifier);
151
+               CHECK(vulkan_validate_dmabuf_properties(modifierInfo, &dmabufInfo.spa_buf->n_datas, &dmabufInfo.size));
152
                ret = vulkan_create_dmabuf(&s->base, &dmabufInfo, &p->buffersi);
153
            } else {
154
                spa_log_error(s->log, "Unsupported buffer type mask %d", buffersi->datas0.type);
155
@@ -523,6 +562,8 @@
156
                        : VK_IMAGE_USAGE_SAMPLED_BIT,
157
                    .spa_buf = buffersi,
158
                };
159
+               struct vulkan_modifier_info *modifierInfo = vulkan_modifierInfo_find(&s->formatInfos, format, dsp_info->modifier);
160
+               CHECK(vulkan_validate_dmabuf_properties(modifierInfo, &dmabufInfo.spa_buf->n_datas, &dmabufInfo.size));
161
                ret = vulkan_import_dmabuf(&s->base, &dmabufInfo, &p->buffersi);
162
                break;
163
            case SPA_DATA_MemPtr:;
164
@@ -532,7 +573,7 @@
165
                    .size.height = s->constants.height,
166
                    .usage = p->direction == SPA_DIRECTION_OUTPUT
167
                        ? VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
168
-                       : VK_IMAGE_USAGE_SAMPLED_BIT,
169
+                       : VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
170
                    .spa_buf = buffersi,
171
                };
172
                ret = vulkan_import_memptr(&s->base, &memptrInfo, &p->buffersi);
173
@@ -549,20 +590,50 @@
174
        p->spa_buffersi = buffersi;
175
        p->n_buffers++;
176
    }
177
+   if (p->direction == SPA_DIRECTION_INPUT && buffers0->datas0.type == SPA_DATA_MemPtr) {
178
+       ret = vulkan_staging_buffer_create(&s->base, buffers0->datas0.maxsize, &s->staging_buffer);
179
+       if (ret < 0) {
180
+           spa_log_error(s->log, "Failed to create staging buffer");
181
+           return ret;
182
+       }
183
+   }
184
+   p->format = dsp_info->format;
185
 
186
    return 0;
187
 }
188
 
189
-int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream,
190
+int spa_vulkan_compute_enumerate_formats(struct vulkan_compute_state *s, uint32_t index, uint32_t caps,
191
+       struct spa_pod **param, struct spa_pod_builder *builder)
192
+{
193
+   uint32_t fmt_idx;
194
+   bool has_modifier;
195
+   if (!find_EnumFormatInfo(&s->formatInfos, index, caps, &fmt_idx, &has_modifier))
196
+           return 0;
197
+   *param = build_dsp_EnumFormat(&s->formatInfos.infosfmt_idx, has_modifier, builder);
198
+   return 1;
199
+}
200
+
201
+static int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
202
+       struct spa_dict *props)
203
+{
204
+   spa_zero(*stream);
205
+   stream->direction = direction;
206
+   stream->current_buffer_id = SPA_ID_INVALID;
207
+   stream->busy_buffer_id = SPA_ID_INVALID;
208
+   stream->ready_buffer_id = SPA_ID_INVALID;
209
+   return 0;
210
+}
211
+
212
+int spa_vulkan_compute_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream,
213
        enum spa_direction direction, struct spa_dict *props)
214
 {
215
    return vulkan_stream_init(stream, direction, props);
216
 }
217
 
218
-int spa_vulkan_prepare(struct vulkan_compute_state *s)
219
+int spa_vulkan_compute_prepare(struct vulkan_compute_state *s)
220
 {
221
    if (!s->prepared) {
222
-       CHECK(createFence(s));
223
+       CHECK(vulkan_fence_create(&s->base, &s->fence));
224
        CHECK(createDescriptors(s));
225
        CHECK(createComputePipeline(s, s->shaderName));
226
        CHECK(createCommandBuffer(s));
227
@@ -571,7 +642,7 @@
228
    return 0;
229
 }
230
 
231
-int spa_vulkan_unprepare(struct vulkan_compute_state *s)
232
+int spa_vulkan_compute_unprepare(struct vulkan_compute_state *s)
233
 {
234
    if (s->prepared) {
235
        vkDestroyShaderModule(s->base.device, s->computeShaderModule, NULL);
236
@@ -587,7 +658,7 @@
237
    return 0;
238
 }
239
 
240
-int spa_vulkan_start(struct vulkan_compute_state *s)
241
+int spa_vulkan_compute_start(struct vulkan_compute_state *s)
242
 {
243
    uint32_t i;
244
 
245
@@ -600,7 +671,7 @@
246
    return 0;
247
 }
248
 
249
-int spa_vulkan_stop(struct vulkan_compute_state *s)
250
+int spa_vulkan_compute_stop(struct vulkan_compute_state *s)
251
 {
252
         VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device));
253
    clear_streams(s);
254
@@ -608,7 +679,7 @@
255
    return 0;
256
 }
257
 
258
-int spa_vulkan_ready(struct vulkan_compute_state *s)
259
+int spa_vulkan_compute_ready(struct vulkan_compute_state *s)
260
 {
261
    uint32_t i;
262
    VkResult result;
263
@@ -631,7 +702,7 @@
264
    return 0;
265
 }
266
 
267
-int spa_vulkan_process(struct vulkan_compute_state *s)
268
+int spa_vulkan_compute_process(struct vulkan_compute_state *s)
269
 {
270
    CHECK(updateDescriptors(s));
271
    CHECK(runCommandBuffer(s));
272
@@ -641,35 +712,40 @@
273
    return 0;
274
 }
275
 
276
-int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction)
277
+int spa_vulkan_compute_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction)
278
 {
279
    switch (direction) {
280
    case SPA_DIRECTION_INPUT:
281
-       return VULKAN_BUFFER_TYPE_CAP_DMABUF;
282
+       return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM;
283
    case SPA_DIRECTION_OUTPUT:
284
        return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM;
285
    }
286
    return 0;
287
 }
288
 
289
-struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s, struct spa_video_info_dsp *info) {
290
+struct vulkan_modifier_info *spa_vulkan_compute_get_modifier_info(struct vulkan_compute_state *s, struct spa_video_info_dsp *info) {
291
    VkFormat vk_format = vulkan_id_to_vkformat(info->format);
292
-   return vulkan_modifierInfo_find(&s->base, vk_format, info->modifier);
293
+   return vulkan_modifierInfo_find(&s->formatInfos, vk_format, info->modifier);
294
 }
295
 
296
-int spa_vulkan_init(struct vulkan_compute_state *s)
297
+int spa_vulkan_compute_init(struct vulkan_compute_state *s)
298
 {
299
+   int ret;
300
    s->base.log = s->log;
301
-   uint32_t dsp_format = SPA_VIDEO_FORMAT_DSP_F32;
302
+   uint32_t dsp_formats = {
303
+       SPA_VIDEO_FORMAT_DSP_F32
304
+   };
305
    struct vulkan_base_info baseInfo = {
306
        .queueFlags = VK_QUEUE_COMPUTE_BIT,
307
-       .formatInfo.formatCount = 1,
308
-       .formatInfo.formats = &dsp_format,
309
    };
310
-   return vulkan_base_init(&s->base, &baseInfo);
311
+   if ((ret = vulkan_base_init(&s->base, &baseInfo)) < 0)
312
+           return ret;
313
+   return vulkan_format_infos_init(&s->base, SPA_N_ELEMENTS(dsp_formats), dsp_formats, &s->formatInfos);
314
+
315
 }
316
 
317
-void spa_vulkan_deinit(struct vulkan_compute_state *s)
318
+void spa_vulkan_compute_deinit(struct vulkan_compute_state *s)
319
 {
320
+   vulkan_format_infos_deinit(&s->formatInfos);
321
    vulkan_base_deinit(&s->base);
322
 }
323
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-compute-utils.h -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.h Changed
91
 
1
@@ -7,6 +7,7 @@
2
 #include <spa/buffer/buffer.h>
3
 #include <spa/param/video/format.h>
4
 #include <spa/node/node.h>
5
+#include <spa/pod/builder.h>
6
 
7
 #include "vulkan-utils.h"
8
 
9
@@ -24,6 +25,20 @@
10
        int height;
11
 };
12
 
13
+struct vulkan_stream {
14
+   enum spa_direction direction;
15
+
16
+   uint32_t pending_buffer_id;
17
+   uint32_t current_buffer_id;
18
+   uint32_t busy_buffer_id;
19
+   uint32_t ready_buffer_id;
20
+
21
+   uint32_t format;
22
+
23
+   struct vulkan_buffer buffersMAX_BUFFERS;
24
+   struct spa_buffer *spa_buffersMAX_BUFFERS;
25
+   uint32_t n_buffers;
26
+};
27
 
28
 struct vulkan_compute_state {
29
    struct spa_log *log;
30
@@ -32,6 +47,8 @@
31
 
32
    struct vulkan_base base;
33
 
34
+   struct vulkan_format_infos formatInfos;
35
+
36
    VkPipeline pipeline;
37
    VkPipelineLayout pipelineLayout;
38
    const char *shaderName;
39
@@ -39,6 +56,7 @@
40
 
41
    VkCommandPool commandPool;
42
    VkCommandBuffer commandBuffer;
43
+   struct vulkan_staging_buffer staging_buffer;
44
 
45
    VkFence fence;
46
    VkSemaphore pipelineSemaphore;
47
@@ -56,25 +74,27 @@
48
    struct vulkan_stream streamsMAX_STREAMS;
49
 };
50
 
51
-int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream, enum spa_direction,
52
+int spa_vulkan_compute_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream, enum spa_direction,
53
        struct spa_dict *props);
54
 
55
-int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
56
+int spa_vulkan_compute_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info,
57
        uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier);
58
-int spa_vulkan_prepare(struct vulkan_compute_state *s);
59
-int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *stream, uint32_t flags,
60
+int spa_vulkan_compute_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *stream, uint32_t flags,
61
        struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers);
62
-int spa_vulkan_unprepare(struct vulkan_compute_state *s);
63
-
64
-int spa_vulkan_start(struct vulkan_compute_state *s);
65
-int spa_vulkan_stop(struct vulkan_compute_state *s);
66
-int spa_vulkan_ready(struct vulkan_compute_state *s);
67
-int spa_vulkan_process(struct vulkan_compute_state *s);
68
-int spa_vulkan_cleanup(struct vulkan_compute_state *s);
69
-
70
-int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction);
71
-struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s,
72
+int spa_vulkan_compute_enumerate_formats(struct vulkan_compute_state *s, uint32_t index, uint32_t caps,
73
+       struct spa_pod **param, struct spa_pod_builder *builder);
74
+int spa_vulkan_compute_prepare(struct vulkan_compute_state *s);
75
+int spa_vulkan_compute_unprepare(struct vulkan_compute_state *s);
76
+
77
+int spa_vulkan_compute_start(struct vulkan_compute_state *s);
78
+int spa_vulkan_compute_stop(struct vulkan_compute_state *s);
79
+int spa_vulkan_compute_ready(struct vulkan_compute_state *s);
80
+int spa_vulkan_compute_process(struct vulkan_compute_state *s);
81
+int spa_vulkan_compute_cleanup(struct vulkan_compute_state *s);
82
+
83
+int spa_vulkan_compute_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction);
84
+struct vulkan_modifier_info *spa_vulkan_compute_get_modifier_info(struct vulkan_compute_state *s,
85
        struct spa_video_info_dsp *dsp_info);
86
 
87
-int spa_vulkan_init(struct vulkan_compute_state *s);
88
-void spa_vulkan_deinit(struct vulkan_compute_state *s);
89
+int spa_vulkan_compute_init(struct vulkan_compute_state *s);
90
+void spa_vulkan_compute_deinit(struct vulkan_compute_state *s);
91
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-types.h -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-types.h Changed
55
 
1
@@ -25,6 +25,13 @@
2
    struct vulkan_modifier_info *infos;
3
 };
4
 
5
+struct vulkan_format_infos {
6
+   uint32_t formatCount;
7
+   struct vulkan_format_info *infos;
8
+
9
+   uint32_t formatsWithModifiersCount;
10
+};
11
+
12
 struct vulkan_buffer {
13
    int fd;
14
    VkImage image;
15
@@ -33,26 +40,13 @@
16
    VkSemaphore foreign_semaphore;
17
 };
18
 
19
-struct vulkan_stream {
20
-   enum spa_direction direction;
21
-
22
-   uint32_t pending_buffer_id;
23
-   uint32_t current_buffer_id;
24
-   uint32_t busy_buffer_id;
25
-   uint32_t ready_buffer_id;
26
-
27
-   struct vulkan_buffer buffersMAX_BUFFERS;
28
-   struct spa_buffer *spa_buffersMAX_BUFFERS;
29
-   uint32_t n_buffers;
30
+struct vulkan_staging_buffer {
31
+   VkBuffer buffer;
32
+   VkDeviceMemory memory;
33
 };
34
 
35
 struct vulkan_base_info {
36
    uint32_t queueFlags;
37
-
38
-   struct {
39
-       uint32_t formatCount;
40
-       uint32_t *formats;
41
-   } formatInfo;
42
 };
43
 
44
 struct vulkan_base {
45
@@ -66,9 +60,6 @@
46
    uint32_t queueFamilyIndex;
47
    VkDevice device;
48
 
49
-   uint32_t formatInfoCount;
50
-   struct vulkan_format_info *formatInfos;
51
-
52
    bool implicit_sync_interop;
53
 
54
    unsigned int initialized:1;
55
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-utils.c -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-utils.c Changed
603
 
1
@@ -103,6 +103,12 @@
2
    uint32_t id;
3
 } vk_video_format_convs = {
4
    { VK_FORMAT_R32G32B32A32_SFLOAT, SPA_VIDEO_FORMAT_RGBA_F32 },
5
+   { VK_FORMAT_B8G8R8A8_SRGB, SPA_VIDEO_FORMAT_BGRA },
6
+   { VK_FORMAT_R8G8B8A8_SRGB, SPA_VIDEO_FORMAT_RGBA },
7
+   { VK_FORMAT_B8G8R8A8_SRGB, SPA_VIDEO_FORMAT_BGRx },
8
+   { VK_FORMAT_R8G8B8A8_SRGB, SPA_VIDEO_FORMAT_RGBx },
9
+   { VK_FORMAT_B8G8R8_SRGB, SPA_VIDEO_FORMAT_BGR },
10
+   { VK_FORMAT_R8G8B8_SRGB, SPA_VIDEO_FORMAT_RGB },
11
 };
12
 
13
 static int createInstance(struct vulkan_base *s)
14
@@ -213,8 +219,10 @@
15
        VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
16
        VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
17
        VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
18
-       VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME
19
+       VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
20
+       VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
21
    };
22
+
23
    const VkDeviceCreateInfo deviceCreateInfo = {
24
        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
25
        .queueCreateInfoCount = 1,
26
@@ -231,113 +239,38 @@
27
    return 0;
28
 }
29
 
30
-static int queryFormatInfo(struct vulkan_base *s, struct vulkan_base_info *info)
31
+int vulkan_write_pixels(struct vulkan_base *s, struct vulkan_write_pixels_info *info, struct vulkan_staging_buffer *vk_sbuf)
32
 {
33
-   if (s->formatInfos)
34
-       return 0;
35
-
36
-   s->formatInfos = calloc(info->formatInfo.formatCount, sizeof(struct vulkan_format_info));
37
-   if (!s->formatInfos)
38
-       return -ENOMEM;
39
-
40
-   for (uint32_t i = 0; i < info->formatInfo.formatCount; i++) {
41
-       VkFormat format = vulkan_id_to_vkformat(info->formatInfo.formatsi);
42
-       if (format == VK_FORMAT_UNDEFINED)
43
-           continue;
44
-       struct vulkan_format_info *f_info = &s->formatInfoss->formatInfoCount++;
45
-       f_info->spa_format = info->formatInfo.formatsi;
46
-       f_info->vk_format = format;
47
-
48
-       VkDrmFormatModifierPropertiesListEXT modPropsList = {
49
-           .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
50
-       };
51
-       VkFormatProperties2 fmtProps = {
52
-           .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
53
-           .pNext = &modPropsList,
54
-       };
55
-       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
56
-
57
-       if (!modPropsList.drmFormatModifierCount)
58
-           continue;
59
-
60
-       modPropsList.pDrmFormatModifierProperties = calloc(modPropsList.drmFormatModifierCount,
61
-               sizeof(modPropsList.pDrmFormatModifierProperties0));
62
-       if (!modPropsList.pDrmFormatModifierProperties)
63
-           continue;
64
-       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
65
-
66
-       f_info->infos = calloc(modPropsList.drmFormatModifierCount, sizeof(f_info->infos0));
67
-       if (!f_info->infos) {
68
-           free(modPropsList.pDrmFormatModifierProperties);
69
-           continue;
70
-       }
71
-
72
-       for (uint32_t j = 0; j < modPropsList.drmFormatModifierCount; j++) {
73
-           VkDrmFormatModifierPropertiesEXT props = modPropsList.pDrmFormatModifierPropertiesj;
74
-
75
-           if (!(props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
76
-               continue;
77
-
78
-           if (props.drmFormatModifierPlaneCount > DMABUF_MAX_PLANES)
79
-               continue;
80
-
81
-           VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = {
82
-               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
83
-               .drmFormatModifier = props.drmFormatModifier,
84
-               .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
85
-           };
86
-           VkPhysicalDeviceExternalImageFormatInfo extImgFmtInfo = {
87
-               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
88
-               .pNext = &modInfo,
89
-               .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
90
-           };
91
-           VkPhysicalDeviceImageFormatInfo2 imgFmtInfo = {
92
-               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
93
-               .pNext = &extImgFmtInfo,
94
-               .type = VK_IMAGE_TYPE_2D,
95
-               .format = format,
96
-               .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
97
-               .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
98
-           };
99
-
100
-           VkExternalImageFormatProperties extImgFmtProps = {
101
-               .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
102
-           };
103
-           VkImageFormatProperties2 imgFmtProps = {
104
-               .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
105
-               .pNext = &extImgFmtProps,
106
-           };
107
+   void *vmap;
108
+   VK_CHECK_RESULT(vkMapMemory(s->device, vk_sbuf->memory, 0, VK_WHOLE_SIZE, 0, &vmap));
109
+
110
+   char *map = (char *)vmap;
111
+
112
+   // upload data
113
+   const char *pdata = info->data;
114
+   memcpy(map, pdata, info->stride * info->size.height);
115
+
116
+   info->copies0 = (VkBufferImageCopy) {
117
+       .imageExtent.width = info->size.width,
118
+       .imageExtent.height = info->size.height,
119
+       .imageExtent.depth = 1,
120
+       .imageOffset.x = 0,
121
+       .imageOffset.y = 0,
122
+       .imageOffset.z = 0,
123
+       .bufferOffset = 0,
124
+       .bufferRowLength = info->size.width,
125
+       .bufferImageHeight = info->size.height,
126
+       .imageSubresource.mipLevel = 0,
127
+       .imageSubresource.baseArrayLayer = 0,
128
+       .imageSubresource.layerCount = 1,
129
+       .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
130
+   };
131
 
132
-           VK_CHECK_RESULT_LOOP(vkGetPhysicalDeviceImageFormatProperties2(s->physicalDevice, &imgFmtInfo, &imgFmtProps))
133
+   vkUnmapMemory(s->device, vk_sbuf->memory);
134
 
135
-           VkExternalMemoryFeatureFlags extMemFeatures =
136
-               extImgFmtProps.externalMemoryProperties.externalMemoryFeatures;
137
-           if (!(extMemFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) {
138
-               continue;
139
-           }
140
-
141
-           VkExtent3D max_extent = imgFmtProps.imageFormatProperties.maxExtent;
142
-           f_info->infosf_info->modifierCount++ = (struct vulkan_modifier_info){
143
-               .props = props,
144
-               .max_extent = { .width = max_extent.width, .height = max_extent.height },
145
-           };
146
-
147
-       }
148
-       free(modPropsList.pDrmFormatModifierProperties);
149
-   }
150
    return 0;
151
 }
152
 
153
-static void destroyFormatInfo(struct vulkan_base *s)
154
-{
155
-   for (uint32_t i = 0; i < s->formatInfoCount; i++) {
156
-       free(s->formatInfosi.infos);
157
-   }
158
-   free(s->formatInfos);
159
-   s->formatInfos = NULL;
160
-   s->formatInfoCount = 0;
161
-}
162
-
163
 int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf)
164
 {
165
    VkImageSubresource img_sub_res = {
166
@@ -353,13 +286,13 @@
167
 
168
    const char *d = (const char *)v + img_sub_layout.offset;
169
    unsigned char *p = (unsigned char *)info->data + info->offset;
170
-   uint32_t bytes_per_pixel = 16;
171
    uint32_t pack_stride = img_sub_layout.rowPitch;
172
+   spa_log_trace_fp(s->log, "Read pixels: %p to %p, stride: %d, width %d, height %d, offset %d, pack_stride %d", d, p, info->stride, info->size.width, info->size.height, info->offset, pack_stride);
173
    if (pack_stride == info->stride) {
174
        memcpy(p, d, info->stride * info->size.height);
175
    } else {
176
        for (uint32_t i = 0; i < info->size.height; i++) {
177
-           memcpy(p + i * info->stride, d + i * pack_stride, info->size.width * bytes_per_pixel);
178
+           memcpy(p + i * info->stride, d + i * pack_stride, info->size.width * info->bytes_per_pixel);
179
        }
180
    }
181
    vkUnmapMemory(s->device, vk_buf->memory);
182
@@ -368,56 +301,27 @@
183
 
184
 int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf)
185
 {
186
-   VULKAN_INSTANCE_FUNCTION(vkImportSemaphoreFdKHR);
187
-
188
    if (!s->implicit_sync_interop) {
189
-       struct pollfd pollfd = {
190
-           .fd = vk_buf->fd,
191
-           .events = POLLIN,
192
-       };
193
-       int timeout_ms = 1000;
194
-       int ret = poll(&pollfd, 1, timeout_ms);
195
-       if (ret < 0) {
196
-           spa_log_error(s->log, "Failed to wait for DMA-BUF fence");
197
-           return -1;
198
-       } else if (ret == 0) {
199
-           spa_log_error(s->log, "Timed out waiting for DMA-BUF fence");
200
-           return -1;
201
-       }
202
-       return 0;
203
+       return vulkan_buffer_wait_dmabuf_fence(s, vk_buf);
204
    }
205
 
206
-   int sync_file_fd = dmabuf_export_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_READ);
207
-   if (sync_file_fd < 0) {
208
-       spa_log_error(s->log, "Failed to extract for DMA-BUF fence");
209
-       return -1;
210
-   }
211
-
212
-   if (vk_buf->foreign_semaphore == VK_NULL_HANDLE) {
213
-       VkSemaphoreCreateInfo semaphore_info = {
214
-           .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
215
-       };
216
-       VK_CHECK_RESULT_WITH_CLEANUP(vkCreateSemaphore(s->device, &semaphore_info, NULL, &vk_buf->foreign_semaphore), close(sync_file_fd));
217
-   }
218
-
219
-   VkImportSemaphoreFdInfoKHR import_info = {
220
-       .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
221
-       .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
222
-       .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR,
223
-       .semaphore = vk_buf->foreign_semaphore,
224
-       .fd = sync_file_fd,
225
-   };
226
-   VK_CHECK_RESULT_WITH_CLEANUP(vkImportSemaphoreFdKHR(s->device, &import_info), close(sync_file_fd));
227
-
228
-   return 0;
229
+   return vulkan_buffer_import_implicit_syncfd(s, vk_buf);
230
 }
231
 
232
 bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd)
233
 {
234
-   if (!s->implicit_sync_interop)
235
-       return false;
236
+   return vulkan_buffer_set_implicit_syncfd(s, vk_buf, sync_file_fd);
237
+}
238
 
239
-   return dmabuf_import_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_WRITE, sync_file_fd);
240
+int vulkan_fence_create(struct vulkan_base *s, VkFence *fence)
241
+{
242
+   VkFenceCreateInfo createInfo = {
243
+       .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
244
+       .flags = 0,
245
+   };
246
+   VK_CHECK_RESULT(vkCreateFence(s->device, &createInfo, NULL, fence));
247
+
248
+   return 0;
249
 }
250
 
251
 int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool)
252
@@ -465,18 +369,18 @@
253
    return -1;
254
 }
255
 
256
-struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format)
257
+struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_format_infos *fmtInfo, VkFormat format)
258
 {
259
-   for (uint32_t i = 0; i < s->formatInfoCount; i++) {
260
-       if (s->formatInfosi.vk_format == format)
261
-           return &s->formatInfosi;
262
+   for (uint32_t i = 0; i < fmtInfo->formatCount; i++) {
263
+       if (fmtInfo->infosi.vk_format == format)
264
+           return &fmtInfo->infosi;
265
    }
266
    return NULL;
267
 }
268
 
269
-struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t mod)
270
+struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_format_infos *fmtInfo, VkFormat format, uint64_t mod)
271
 {
272
-   struct vulkan_format_info *f_info = vulkan_formatInfo_find(s, format);
273
+   struct vulkan_format_info *f_info = vulkan_formatInfo_find(fmtInfo, format);
274
    if (!f_info)
275
        return NULL;
276
    for (uint32_t i = 0; i < f_info->modifierCount; i++) {
277
@@ -486,6 +390,73 @@
278
    return NULL;
279
 }
280
 
281
+int vulkan_buffer_get_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf)
282
+{
283
+   if (!s->implicit_sync_interop)
284
+       return -1;
285
+
286
+   return dmabuf_export_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_READ);
287
+}
288
+
289
+bool vulkan_buffer_set_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd)
290
+{
291
+   if (!s->implicit_sync_interop)
292
+       return false;
293
+
294
+   return dmabuf_import_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_WRITE, sync_file_fd);
295
+}
296
+
297
+int vulkan_buffer_import_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf)
298
+{
299
+   int sync_file_fd = vulkan_buffer_get_implicit_syncfd(s, vk_buf);
300
+   if (sync_file_fd < 0) {
301
+       spa_log_error(s->log, "Failed to extract for DMA-BUF fence");
302
+       return -1;
303
+   }
304
+   return vulkan_buffer_import_syncfd(s, vk_buf, sync_file_fd);
305
+}
306
+
307
+int vulkan_buffer_wait_dmabuf_fence(struct vulkan_base *s, struct vulkan_buffer *vk_buf)
308
+{
309
+   struct pollfd pollfd = {
310
+       .fd = vk_buf->fd,
311
+       .events = POLLIN,
312
+   };
313
+   int timeout_ms = 1000;
314
+   int ret = poll(&pollfd, 1, timeout_ms);
315
+   if (ret < 0) {
316
+       spa_log_error(s->log, "Failed to wait for DMA-BUF fence");
317
+       return -1;
318
+   } else if (ret == 0) {
319
+       spa_log_error(s->log, "Timed out waiting for DMA-BUF fence");
320
+       return -1;
321
+   }
322
+   return 0;
323
+}
324
+
325
+int vulkan_buffer_import_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd)
326
+{
327
+   VULKAN_INSTANCE_FUNCTION(vkImportSemaphoreFdKHR);
328
+
329
+   if (vk_buf->foreign_semaphore == VK_NULL_HANDLE) {
330
+       VkSemaphoreCreateInfo semaphore_info = {
331
+           .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
332
+       };
333
+       VK_CHECK_RESULT_WITH_CLEANUP(vkCreateSemaphore(s->device, &semaphore_info, NULL, &vk_buf->foreign_semaphore), close(sync_file_fd));
334
+   }
335
+
336
+   VkImportSemaphoreFdInfoKHR import_info = {
337
+       .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
338
+       .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
339
+       .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR,
340
+       .semaphore = vk_buf->foreign_semaphore,
341
+       .fd = sync_file_fd,
342
+   };
343
+   VK_CHECK_RESULT_WITH_CLEANUP(vkImportSemaphoreFdKHR(s->device, &import_info), close(sync_file_fd));
344
+
345
+   return 0;
346
+}
347
+
348
 void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer)
349
 {
350
    if (buffer->fd != -1)
351
@@ -506,6 +477,40 @@
352
    }
353
 }
354
 
355
+int vulkan_staging_buffer_create(struct vulkan_base *s, uint32_t size, struct vulkan_staging_buffer *s_buf) {
356
+   VkBufferCreateInfo buf_info = {
357
+       .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
358
+       .size = size,
359
+       .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT |
360
+           VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
361
+       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
362
+   };
363
+   VK_CHECK_RESULT(vkCreateBuffer(s->device, &buf_info, NULL, &s_buf->buffer));
364
+
365
+   VkMemoryRequirements memoryRequirements;
366
+   vkGetBufferMemoryRequirements(s->device, s_buf->buffer, &memoryRequirements);
367
+
368
+   VkMemoryAllocateInfo mem_info = {
369
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
370
+       .allocationSize = memoryRequirements.size,
371
+       .memoryTypeIndex = vulkan_memoryType_find(s,
372
+                     memoryRequirements.memoryTypeBits,
373
+                     VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
374
+                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
375
+   };
376
+   VK_CHECK_RESULT(vkAllocateMemory(s->device, &mem_info, NULL, &s_buf->memory));
377
+   VK_CHECK_RESULT(vkBindBufferMemory(s->device, s_buf->buffer, s_buf->memory, 0));
378
+
379
+   return 0;
380
+}
381
+
382
+void vulkan_staging_buffer_destroy(struct vulkan_base *s, struct vulkan_staging_buffer *s_buf) {
383
+   if (s_buf->buffer == VK_NULL_HANDLE)
384
+       return;
385
+   vkFreeMemory(s->device, s_buf->memory, NULL);
386
+   vkDestroyBuffer(s->device, s_buf->buffer, NULL);
387
+}
388
+
389
 static int allocate_dmabuf(struct vulkan_base *s, VkFormat format, uint32_t modifierCount, uint64_t *modifiers, VkImageUsageFlags usage, struct spa_rectangle *size, struct vulkan_buffer *vk_buf)
390
 {
391
    VkImageDrmFormatModifierListCreateInfoEXT imageDrmFormatModifierListCreateInfo = {
392
@@ -566,6 +571,19 @@
393
    return 0;
394
 }
395
 
396
+int vulkan_validate_dmabuf_properties(const struct vulkan_modifier_info *modInfo, uint32_t *planeCount, struct spa_rectangle *dim)
397
+{
398
+   if (planeCount) {
399
+       if (*planeCount != modInfo->props.drmFormatModifierPlaneCount)
400
+           return -1;
401
+   }
402
+   if (dim) {
403
+       if (dim->width > modInfo->max_extent.width || dim->height > modInfo->max_extent.height)
404
+           return -1;
405
+   }
406
+   return 0;
407
+}
408
+
409
 int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier)
410
 {
411
    VULKAN_INSTANCE_FUNCTION(vkGetImageDrmFormatModifierPropertiesEXT);
412
@@ -604,11 +622,6 @@
413
    int fd = -1;
414
    VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd));
415
 
416
-   const struct vulkan_modifier_info *modInfo = vulkan_modifierInfo_find(s, info->format, info->modifier);
417
-
418
-   if (info->spa_buf->n_datas != modInfo->props.drmFormatModifierPlaneCount)
419
-       return -1;
420
-
421
    VkMemoryRequirements memoryRequirements = {0};
422
    vkGetImageMemoryRequirements(s->device,
423
            vk_buf->image, &memoryRequirements);
424
@@ -658,18 +671,8 @@
425
    if (info->spa_buf->n_datas == 0 || info->spa_buf->n_datas > DMABUF_MAX_PLANES)
426
        return -1;
427
 
428
-   struct vulkan_modifier_info *modProps = vulkan_modifierInfo_find(s, info->format, info->modifier);
429
-   if (!modProps)
430
-       return -1;
431
-
432
    uint32_t planeCount = info->spa_buf->n_datas;
433
 
434
-   if (planeCount != modProps->props.drmFormatModifierPlaneCount)
435
-       return -1;
436
-
437
-   if (info->size.width > modProps->max_extent.width || info->size.height > modProps->max_extent.height)
438
-       return -1;
439
-
440
    VkSubresourceLayout planeLayoutsDMABUF_MAX_PLANES = {0};
441
    for (uint32_t i = 0; i < planeCount; i++) {
442
        planeLayoutsi.offset = info->spa_buf->datasi.chunk->offset;
443
@@ -817,17 +820,6 @@
444
    return 0;
445
 }
446
 
447
-int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
448
-       struct spa_dict *props)
449
-{
450
-   spa_zero(*stream);
451
-   stream->direction = direction;
452
-   stream->current_buffer_id = SPA_ID_INVALID;
453
-   stream->busy_buffer_id = SPA_ID_INVALID;
454
-   stream->ready_buffer_id = SPA_ID_INVALID;
455
-   return 0;
456
-}
457
-
458
 uint32_t vulkan_vkformat_to_id(VkFormat format)
459
 {
460
    SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) {
461
@@ -865,13 +857,132 @@
462
    return 0;
463
 }
464
 
465
+int vulkan_format_infos_init(struct vulkan_base *s, uint32_t formatCount, uint32_t *formats,
466
+       struct vulkan_format_infos *info)
467
+{
468
+   if (info->infos)
469
+       return 0;
470
+
471
+
472
+   info->infos = calloc(formatCount, sizeof(struct vulkan_format_info));
473
+   if (!info->infos)
474
+       return -ENOMEM;
475
+
476
+   uint32_t i;
477
+   for (i = 0; i < formatCount; i++) {
478
+       VkFormat format = vulkan_id_to_vkformat(formatsi);
479
+       if (format == VK_FORMAT_UNDEFINED)
480
+           continue;
481
+       struct vulkan_format_info *f_info = &info->infosinfo->formatCount++;
482
+       f_info->spa_format = formatsi;
483
+       f_info->vk_format = format;
484
+       spa_log_info(s->log, "Adding format %d (spa_format %d)", format, formatsi);
485
+
486
+       VkDrmFormatModifierPropertiesListEXT modPropsList = {
487
+           .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT,
488
+       };
489
+       VkFormatProperties2 fmtProps = {
490
+           .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2,
491
+           .pNext = &modPropsList,
492
+       };
493
+       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
494
+
495
+       if (modPropsList.drmFormatModifierCount == 0) {
496
+           spa_log_info(s->log, "Format has no modifiers");
497
+           continue;
498
+       }
499
+
500
+       modPropsList.pDrmFormatModifierProperties = calloc(modPropsList.drmFormatModifierCount,
501
+               sizeof(modPropsList.pDrmFormatModifierProperties0));
502
+       if (!modPropsList.pDrmFormatModifierProperties) {
503
+           spa_log_info(s->log, "Failed to allocate DrmFormatModifierProperties");
504
+           continue;
505
+       }
506
+       vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps);
507
+
508
+       f_info->infos = calloc(modPropsList.drmFormatModifierCount, sizeof(f_info->infos0));
509
+       if (!f_info->infos) {
510
+           spa_log_info(s->log, "Failed to allocate modifier infos");
511
+           free(modPropsList.pDrmFormatModifierProperties);
512
+           continue;
513
+       }
514
+
515
+       spa_log_info(s->log, "Found %d modifiers", modPropsList.drmFormatModifierCount);
516
+       for (uint32_t j = 0; j < modPropsList.drmFormatModifierCount; j++) {
517
+           VkDrmFormatModifierPropertiesEXT props = modPropsList.pDrmFormatModifierPropertiesj;
518
+
519
+           if (!(props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
520
+               continue;
521
+
522
+           if (props.drmFormatModifierPlaneCount > DMABUF_MAX_PLANES)
523
+               continue;
524
+
525
+           VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = {
526
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
527
+               .drmFormatModifier = props.drmFormatModifier,
528
+               .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
529
+           };
530
+           VkPhysicalDeviceExternalImageFormatInfo extImgFmtInfo = {
531
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
532
+               .pNext = &modInfo,
533
+               .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
534
+           };
535
+           VkPhysicalDeviceImageFormatInfo2 imgFmtInfo = {
536
+               .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
537
+               .pNext = &extImgFmtInfo,
538
+               .type = VK_IMAGE_TYPE_2D,
539
+               .format = format,
540
+               .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
541
+               .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
542
+           };
543
+
544
+           VkExternalImageFormatProperties extImgFmtProps = {
545
+               .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
546
+           };
547
+           VkImageFormatProperties2 imgFmtProps = {
548
+               .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
549
+               .pNext = &extImgFmtProps,
550
+           };
551
+
552
+           VK_CHECK_RESULT_LOOP(vkGetPhysicalDeviceImageFormatProperties2(s->physicalDevice, &imgFmtInfo, &imgFmtProps))
553
+
554
+           VkExternalMemoryFeatureFlags extMemFeatures =
555
+               extImgFmtProps.externalMemoryProperties.externalMemoryFeatures;
556
+           if (!(extMemFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) {
557
+               continue;
558
+           }
559
+
560
+           VkExtent3D max_extent = imgFmtProps.imageFormatProperties.maxExtent;
561
+           f_info->infosf_info->modifierCount++ = (struct vulkan_modifier_info){
562
+               .props = props,
563
+               .max_extent = { .width = max_extent.width, .height = max_extent.height },
564
+           };
565
+           spa_log_info(s->log, "Adding modifier %"PRIu64, props.drmFormatModifier);
566
+
567
+       }
568
+       free(modPropsList.pDrmFormatModifierProperties);
569
+   }
570
+   for (i = 0; i < info->formatCount; i++) {
571
+       if (info->infosi.modifierCount > 0)
572
+           info->formatsWithModifiersCount++;
573
+   }
574
+   return 0;
575
+}
576
+
577
+void vulkan_format_infos_deinit(struct vulkan_format_infos *info)
578
+{
579
+   for (uint32_t i = 0; i < info->formatCount; i++) {
580
+       free(info->infosi.infos);
581
+   }
582
+   free(info->infos);
583
+}
584
+
585
 int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info)
586
 {
587
    if (!s->initialized) {
588
        CHECK(createInstance(s));
589
        CHECK(findPhysicalDevice(s));
590
        CHECK(createDevice(s, info));
591
-       CHECK(queryFormatInfo(s, info));
592
        s->implicit_sync_interop = dmabuf_check_sync_file_import_export(s->log);
593
        s->initialized = true;
594
    }
595
@@ -881,7 +992,6 @@
596
 void vulkan_base_deinit(struct vulkan_base *s)
597
 {
598
    if (s->initialized) {
599
-       destroyFormatInfo(s);
600
        vkDestroyDevice(s->device, NULL);
601
        vkDestroyInstance(s->instance, NULL);
602
        s->initialized = false;
603
pipewire-1.0.1.tar.bz2/spa/plugins/vulkan/vulkan-utils.h -> pipewire-1.2.0.tar.gz/spa/plugins/vulkan/vulkan-utils.h Changed
71
 
1
@@ -46,6 +46,16 @@
2
        return _res;                                \
3
 }
4
 
5
+struct vulkan_write_pixels_info {
6
+   struct spa_rectangle size;
7
+   uint32_t offset;
8
+   uint32_t stride;
9
+   uint32_t bytes_per_pixel;
10
+
11
+   VkBufferImageCopy *copies;
12
+   void *data;
13
+};
14
+
15
 struct vulkan_read_pixels_info {
16
    struct spa_rectangle size;
17
    void *data;
18
@@ -70,29 +80,37 @@
19
    struct spa_buffer *spa_buf;
20
 };
21
 
22
+int vulkan_write_pixels(struct vulkan_base *s, struct vulkan_write_pixels_info *info, struct vulkan_staging_buffer *vk_sbuf);
23
 int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf);
24
 
25
 int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf);
26
 bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd);
27
 
28
+int vulkan_staging_buffer_create(struct vulkan_base *s, uint32_t size, struct vulkan_staging_buffer *s_buf);
29
+void vulkan_staging_buffer_destroy(struct vulkan_base *s, struct vulkan_staging_buffer *s_buf);
30
+
31
+int vulkan_validate_dmabuf_properties(const struct vulkan_modifier_info *modInfo, uint32_t *planeCount, struct spa_rectangle *dim);
32
 int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier);
33
 int vulkan_create_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
34
 int vulkan_import_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
35
 int vulkan_import_memptr(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf);
36
 
37
+int vulkan_fence_create(struct vulkan_base *s, VkFence *fence);
38
 int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool);
39
 int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer);
40
 
41
 uint32_t vulkan_memoryType_find(struct vulkan_base *s,
42
        uint32_t memoryTypeBits, VkMemoryPropertyFlags properties);
43
-struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format);
44
-struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t modifier);
45
-
46
+struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_format_infos *fmtInfo, VkFormat format);
47
+struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_format_infos *fmtInfo, VkFormat format, uint64_t modifier);
48
+
49
+int vulkan_buffer_get_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf);
50
+bool vulkan_buffer_set_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd);
51
+int vulkan_buffer_import_implicit_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf);
52
+int vulkan_buffer_wait_dmabuf_fence(struct vulkan_base *s, struct vulkan_buffer *vk_buf);
53
+int vulkan_buffer_import_syncfd(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd);
54
 void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer);
55
 
56
-int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
57
-       struct spa_dict *props);
58
-
59
 uint32_t vulkan_vkformat_to_id(VkFormat vkFormat);
60
 VkFormat vulkan_id_to_vkformat(uint32_t id);
61
 
62
@@ -101,5 +119,8 @@
63
 int vulkan_wait_fence(struct vulkan_base *s, VkFence fence);
64
 int vulkan_wait_idle(struct vulkan_base *s);
65
 
66
+int vulkan_format_infos_init(struct vulkan_base *s, uint32_t formatCount, uint32_t *formats,
67
+       struct vulkan_format_infos *info);
68
+void vulkan_format_infos_deinit(struct vulkan_format_infos *info);
69
 int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info);
70
 void vulkan_base_deinit(struct vulkan_base *s);
71
pipewire-1.0.1.tar.bz2/spa/tools/meson.build -> pipewire-1.2.0.tar.gz/spa/tools/meson.build Changed
9
 
1
@@ -6,6 +6,6 @@
2
            dependencies :  spa_dep, dl_lib ,
3
            install : true)
4
 
5
-executable('spa-json-dump', 'spa-json-dump.c',
6
+spa_json_dump_exe = executable('spa-json-dump', 'spa-json-dump.c',
7
            dependencies :  spa_dep, dl_lib, ,
8
            install : true)
9
pipewire-1.0.1.tar.bz2/spa/tools/spa-json-dump.c -> pipewire-1.2.0.tar.gz/spa/tools/spa-json-dump.c Changed
197
 
1
@@ -14,6 +14,7 @@
2
 
3
 #include <spa/utils/result.h>
4
 #include <spa/utils/json.h>
5
+#include <spa/debug/file.h>
6
 
7
 static void encode_string(FILE *f, const char *val, int len)
8
 {
9
@@ -54,22 +55,33 @@
10
 static int dump(FILE *file, int indent, struct spa_json *it, const char *value, int len)
11
 {
12
    struct spa_json sub;
13
-   int count = 0;
14
+   bool toplevel = false;
15
+   int count = 0, res;
16
    char key1024;
17
 
18
+   if (!value) {
19
+       toplevel = true;
20
+       value = "{";
21
+       len = 1;
22
+   }
23
+
24
    if (spa_json_is_array(value, len)) {
25
        fprintf(file, "");
26
        spa_json_enter(it, &sub);
27
        while ((len = spa_json_next(&sub, &value)) > 0) {
28
            fprintf(file, "%s\n%*s", count++ > 0 ? "," : "",
29
                    indent+2, "");
30
-           dump(file, indent+2, &sub, value, len);
31
+           if ((res = dump(file, indent+2, &sub, value, len)) < 0)
32
+               return res;
33
        }
34
        fprintf(file, "%s%*s", count > 0 ? "\n" : "",
35
                count > 0 ? indent : 0, "");
36
    } else if (spa_json_is_object(value, len)) {
37
        fprintf(file, "{");
38
-       spa_json_enter(it, &sub);
39
+       if (!toplevel)
40
+           spa_json_enter(it, &sub);
41
+       else
42
+           sub = *it;
43
        while (spa_json_get_string(&sub, key, sizeof(key)) > 0) {
44
            fprintf(file, "%s\n%*s",
45
                    count++ > 0 ? "," : "",
46
@@ -78,8 +90,15 @@
47
            fprintf(file, ": ");
48
            if ((len = spa_json_next(&sub, &value)) <= 0)
49
                break;
50
-           dump(file, indent+2, &sub, value, len);
51
+           res = dump(file, indent+2, &sub, value, len);
52
+           if (res < 0) {
53
+               if (toplevel)
54
+                   *it = sub;
55
+               return res;
56
+           }
57
        }
58
+       if (toplevel)
59
+           *it = sub;
60
        fprintf(file, "%s%*s}", count > 0 ? "\n" : "",
61
                count > 0 ? indent : 0, "");
62
    } else if (spa_json_is_string(value, len) ||
63
@@ -91,21 +110,98 @@
64
    } else {
65
        encode_string(file, value, len);
66
    }
67
+
68
+   if (spa_json_get_error(it, NULL, NULL))
69
+       return -EINVAL;
70
+
71
+   return 0;
72
+}
73
+
74
+static int process_json(const char *filename, void *buf, size_t size)
75
+{
76
+   int len, res;
77
+   struct spa_json it;
78
+   const char *value;
79
+
80
+   spa_json_init(&it, buf, size);
81
+   if ((len = spa_json_next(&it, &value)) <= 0) {
82
+                fprintf(stderr, "not a valid file '%s': %s\n", filename, spa_strerror(len));
83
+       return -EINVAL;
84
+   }
85
+   if (!spa_json_is_container(value, len)) {
86
+       spa_json_init(&it, buf, size);
87
+       value = NULL;
88
+       len = 0;
89
+   }
90
+   res = dump(stdout, 0, &it, value, len);
91
+   if (spa_json_next(&it, &value) < 0)
92
+       res = -EINVAL;
93
+
94
+   fprintf(stdout, "\n");
95
+   fflush(stdout);
96
+
97
+   if (res < 0) {
98
+       struct spa_error_location loc;
99
+
100
+       if (spa_json_get_error(&it, buf, &loc))
101
+           spa_debug_file_error_location(stderr, &loc,
102
+                   "syntax error in file '%s': %s",
103
+                   filename, loc.reason);
104
+       else
105
+           fprintf(stderr, "error parsing file '%s': %s\n", filename, spa_strerror(res));
106
+
107
+       return -EINVAL;
108
+   }
109
    return 0;
110
 }
111
 
112
+static int process_stdin(void)
113
+{
114
+   uint8_t *buf = NULL, *p;
115
+   size_t alloc = 0, size = 0, read_size, res;
116
+   int err;
117
+
118
+   do {
119
+       alloc += 1024 + alloc;
120
+       p = realloc(buf, alloc);
121
+       if (!p) {
122
+           fprintf(stderr, "error: %m\n");
123
+           goto error;
124
+       }
125
+       buf = p;
126
+
127
+       read_size = alloc - size;
128
+       res = fread(buf + size, 1, read_size, stdin);
129
+       size += res;
130
+   } while (res == read_size);
131
+
132
+   if (ferror(stdin)) {
133
+       fprintf(stderr, "error: %m\n");
134
+       goto error;
135
+   }
136
+
137
+   err = process_json("-", buf, size);
138
+   free(buf);
139
+
140
+   return (err == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
141
+
142
+error:
143
+   free(buf);
144
+   return EXIT_FAILURE;
145
+}
146
+
147
 int main(int argc, char *argv)
148
 {
149
-   int fd, len, res, exit_code = EXIT_FAILURE;
150
+   int fd, res, exit_code = EXIT_FAILURE;
151
    void *data;
152
    struct stat sbuf;
153
-   struct spa_json it;
154
-   const char *value;
155
 
156
-   if (argc < 2) {
157
-       fprintf(stderr, "usage: %s <spa-json-file>\n", argv0);
158
+   if (argc < 1) {
159
+       fprintf(stderr, "usage: %s spa-json-file\n", argv0);
160
        goto error;
161
    }
162
+   if (argc == 1)
163
+       return process_stdin();
164
    if ((fd = open(argv1,  O_CLOEXEC | O_RDONLY)) < 0)  {
165
                 fprintf(stderr, "error opening file '%s': %m\n", argv1);
166
        goto error;
167
@@ -119,24 +215,12 @@
168
                 goto error_close;
169
    }
170
 
171
-   spa_json_init(&it, data, sbuf.st_size);
172
-   if ((len = spa_json_next(&it, &value)) <= 0) {
173
-                fprintf(stderr, "not a valid file '%s': %s\n", argv1, spa_strerror(len));
174
-       goto error_unmap;
175
-   }
176
-   if (!spa_json_is_container(value, len)) {
177
-       spa_json_init(&it, data, sbuf.st_size);
178
-       value = "{";
179
-       len = 1;
180
-   }
181
-   if ((res = dump(stdout, 0, &it, value, len)) < 0) {
182
-                fprintf(stderr, "error parsing file '%s': %s\n", argv1, spa_strerror(res));
183
-       goto error_unmap;
184
-   }
185
-   fprintf(stdout, "\n");
186
-   exit_code = EXIT_SUCCESS;
187
+   res = process_json(argv1, data, sbuf.st_size);
188
+   if (res < 0)
189
+       exit_code = EXIT_FAILURE;
190
+   else
191
+       exit_code = EXIT_SUCCESS;
192
 
193
-error_unmap:
194
         munmap(data, sbuf.st_size);
195
 error_close:
196
         close(fd);
197
pipewire-1.0.1.tar.bz2/src/daemon/client-rt.conf.avail/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/client-rt.conf.avail/meson.build Changed
13
 
1
@@ -3,9 +3,10 @@
2
 
3
 
4
 foreach c : conf_files
5
-  configure_file(input : '@0@.in'.format(c),
6
+  res = configure_file(input : '@0@.in'.format(c),
7
                  output : c,
8
                  configuration : conf_config,
9
                  install_dir : pipewire_confdatadir / 'client-rt.conf.avail')
10
+  test(f'validate-json-client-rt-@c@', spa_json_dump_exe, args : res)
11
 endforeach
12
 
13
pipewire-1.0.1.tar.bz2/src/daemon/client-rt.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/client-rt.conf.in Changed
10
 
1
@@ -43,7 +43,7 @@
2
     # Uses realtime scheduling to boost the audio thread priorities
3
     { name = libpipewire-module-rt
4
         args = {
5
-            #rt.prio      = 88
6
+            #rt.prio      = @rtprio_client@
7
             #rt.time.soft = -1
8
             #rt.time.hard = -1
9
         }
10
pipewire-1.0.1.tar.bz2/src/daemon/client.conf.avail/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/client.conf.avail/meson.build Changed
13
 
1
@@ -3,9 +3,10 @@
2
 
3
 
4
 foreach c : conf_files
5
-  configure_file(input : '@0@.in'.format(c),
6
+  res = configure_file(input : '@0@.in'.format(c),
7
                  output : c,
8
                  configuration : conf_config,
9
                  install_dir : pipewire_confdatadir / 'client.conf.avail')
10
+  test(f'validate-json-client-@c@', spa_json_dump_exe, args : res)
11
 endforeach
12
 
13
pipewire-1.0.1.tar.bz2/src/daemon/filter-chain.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/filter-chain.conf.in Changed
10
 
1
@@ -42,7 +42,7 @@
2
     # Uses realtime scheduling to boost the audio thread priorities
3
     { name = libpipewire-module-rt
4
         args = {
5
-            #rt.prio      = 88
6
+            #rt.prio      = @rtprio_client@
7
             #rt.time.soft = -1
8
             #rt.time.hard = -1
9
         }
10
pipewire-1.0.1.tar.bz2/src/daemon/filter-chain/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/filter-chain/meson.build Changed
12
 
1
@@ -12,8 +12,9 @@
2
 
3
 
4
 foreach c : conf_files
5
-  configure_file(input : c.get(0),
6
+  res = configure_file(input : c.get(0),
7
                  output : c.get(1),
8
                  configuration : conf_config,
9
                  install_dir : pipewire_confdatadir / 'filter-chain')
10
+  test('validate-json-filter-chain-' + c.get(0), spa_json_dump_exe, args : res)
11
 endforeach
12
pipewire-1.0.1.tar.bz2/src/daemon/jack.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/jack.conf.in Changed
22
 
1
@@ -43,7 +43,7 @@
2
     # Boost the data thread priority.
3
     { name = libpipewire-module-rt
4
         args = {
5
-            #rt.prio      = 88
6
+            #rt.prio      = @rtprio_client@
7
             #rt.time.soft = -1
8
             #rt.time.hard = -1
9
         }
10
@@ -83,6 +83,11 @@
11
      # fail-all:        Fail all self connect requests
12
      # ignore-all:      Ignore all self connect requests
13
      #jack.self-connect-mode = allow
14
+     #
15
+     # allow:           Allow connect request of other ports
16
+     # fail:            Fail connect requests of other ports
17
+     # ignore:          Ignore connect requests of other ports
18
+     #jack.other-connect-mode = allow
19
      #jack.locked-process    = true
20
      #jack.default-as-system = false
21
      #jack.fix-midi-events   = true
22
pipewire-1.0.1.tar.bz2/src/daemon/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/meson.build Changed
30
 
1
@@ -11,6 +11,8 @@
2
 conf_config.set('pipewire_pulse_path', pipewire_bindir / 'pipewire-pulse')
3
 conf_config.set('sm_comment', '#')
4
 conf_config.set('pulse_comment', '#')
5
+conf_config.set('rtprio_server', get_option('rtprio-server'))
6
+conf_config.set('rtprio_client', get_option('rtprio-client'))
7
 
8
 conf_config_uninstalled = conf_config
9
 conf_config_uninstalled.set('pipewire_path',
10
@@ -78,15 +80,17 @@
11
 endif
12
 
13
 foreach c : conf_files
14
-  configure_file(input : '@0@.in'.format(c),
15
+  res = configure_file(input : '@0@.in'.format(c),
16
                  output : c,
17
                  configuration : conf_config,
18
                  install_dir : pipewire_confdatadir)
19
+  test(f'validate-json-@c@', spa_json_dump_exe, args : res)
20
 endforeach
21
 
22
-configure_file(input : 'pipewire.conf.in',
23
+res = configure_file(input : 'pipewire.conf.in',
24
                output : 'pipewire-uninstalled.conf',
25
                configuration : conf_config_uninstalled)
26
+test('validate-json-pipewire-uninstalled.conf', spa_json_dump_exe, args : res)
27
 
28
 conf_avail_folders = 
29
   'pipewire.conf.avail',
30
pipewire-1.0.1.tar.bz2/src/daemon/minimal.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/minimal.conf.in Changed
254
 
1
@@ -32,6 +32,7 @@
2
     #default.clock.min-quantum   = 32
3
     #default.clock.max-quantum   = 2048
4
     #default.clock.quantum-limit = 8192
5
+    #default.clock.quantum-floor = 4
6
     #default.video.width         = 640
7
     #default.video.height        = 480
8
     #default.video.rate.num      = 25
9
@@ -39,13 +40,26 @@
10
     #
11
     settings.check-quantum      = true
12
     settings.check-rate         = true
13
-    #
14
-    # These overrides are only applied when running in a vm.
15
-    vm.overrides = {
16
-        default.clock.min-quantum = 1024
17
-    }
18
+
19
+    # This config can use udev or hardcoded ALSA devices. Make sure to
20
+    # change the alsa device below when disabling udev
21
+    minimal.use-udev = true
22
+
23
+    # Load the pulseaudio emulation daemon
24
+    minimal.use-pulse = true
25
 }
26
 
27
+context.properties.rules = 
28
+    {   matches =  { cpu.vm.name = !null } 
29
+        actions = {
30
+            update-props = {
31
+                # These overrides are only applied when running in a vm.
32
+                default.clock.min-quantum = 1024
33
+       }
34
+        }
35
+    }
36
+
37
+
38
 context.spa-libs = {
39
     #<factory-name regex> = <library-name>
40
     #
41
@@ -54,6 +68,7 @@
42
     # that factory.
43
     #
44
     audio.convert.* = audioconvert/libspa-audioconvert
45
+    audio.adapt     = audioconvert/libspa-audioconvert
46
     api.alsa.*      = alsa/libspa-alsa
47
     support.*       = support/libspa-support
48
 }
49
@@ -76,7 +91,7 @@
50
     { name = libpipewire-module-rt
51
         args = {
52
             nice.level   = -11
53
-            #rt.prio      = 88
54
+            rt.prio      = @rtprio_server@
55
             #rt.time.soft = -1
56
             #rt.time.hard = -1
57
         }
58
@@ -99,6 +114,8 @@
59
     # context of the PipeWire server.
60
     { name = libpipewire-module-spa-node-factory }
61
 
62
+    { name = libpipewire-module-spa-device-factory }
63
+
64
     # Allows creating nodes that run in the context of the
65
     # client. Is used by all clients that want to provide
66
     # data to PipeWire.
67
@@ -132,8 +149,23 @@
68
 
69
     # Makes a factory for creating links between ports.
70
     { name = libpipewire-module-link-factory }
71
+
72
+    { name = libpipewire-module-protocol-pulse
73
+      condition =  { minimal.use-pulse = true } 
74
+    }
75
 
76
 
77
+pulse.properties = {
78
+    # the addresses this server listens on
79
+    server.address = 
80
+        "unix:native"
81
+    
82
+}
83
+
84
+stream.properties = {
85
+    adapter.auto-port-config = { mode = dsp }
86
+}
87
+
88
 context.objects = 
89
     #{ factory = <factory-name>
90
     #    ( args  = { <key> = <value> ... } )
91
@@ -179,9 +211,46 @@
92
             priority.driver = 19000
93
             node.group      = pipewire.freewheel
94
             node.freewheel  = true
95
+            #freewheel.wait = 10
96
         }
97
     }
98
 
99
+    # This creates a ALSA udev device that will enumerate all
100
+    # ALSA devices. Because it is using ACP and has the auto-profile
101
+    # property set, this will enable a profile and create associated
102
+    # nodes, which will be automatically configured to their best
103
+    # configuration with the auto-port-config settings.
104
+    # Extra node and device params can be given with node.param and
105
+    # device.param prefixes.
106
+    { factory = spa-device-factory
107
+        args = {
108
+            factory.name           = api.alsa.enum.udev
109
+            alsa.use-acp = true
110
+            device.object.properties = {
111
+                api.acp.auto-profile = true
112
+                api.acp.auto-port = true
113
+                device.object.properties = {
114
+                    node.adapter             = audio.adapt
115
+                    resample.disable         = false
116
+                    adapter.auto-port-config = {
117
+                        mode = dsp
118
+                        monitor = false
119
+                        control = false
120
+                        position = preserve   # unknown, aux
121
+                    }
122
+                    #node.param.Props    = {
123
+                    #    channelVolumes =  0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.6 
124
+                    #}
125
+                }
126
+                #device.param.Profile    = {
127
+                #    #idx = 0
128
+                #    name = pro-audio
129
+                #}
130
+            }
131
+        }
132
+        condition =  { minimal.use-udev = true } 
133
+    }
134
+
135
     # This creates a single PCM source device for the given
136
     # alsa device path hw:0. You can change source to sink
137
     # to make a sink in the same way.
138
@@ -191,7 +260,7 @@
139
             node.name              = "system"
140
             node.description       = "system"
141
             media.class            = "Audio/Source"
142
-            api.alsa.path          = "hw:0"
143
+            api.alsa.path          = "hw:4"
144
             #api.alsa.period-size   = 0
145
             #api.alsa.period-num    = 0
146
             #api.alsa.headroom      = 0
147
@@ -221,7 +290,7 @@
148
             #channelmix.rear-delay  = 12.0
149
             #channelmix.stereo-widen = 0.0
150
             #channelmix.hilbert-taps = 0
151
-            channelmix.disable     = true
152
+            #channelmix.disable     = false
153
             #dither.noise = 0
154
             #node.param.Props      = {
155
             #    params = 
156
@@ -246,7 +315,11 @@
157
             #        position =  FL FR RL RR 
158
             #    }
159
             #}
160
+            #node.param.Props    = {
161
+            #    channelVolumes =  0.5 0.4 0.3 0.5 
162
+            #}
163
         }
164
+        condition =  { minimal.use-udev = false } 
165
     }
166
     { factory = adapter
167
         args = {
168
@@ -254,7 +327,7 @@
169
             node.name              = "system"
170
             node.description       = "system"
171
             media.class            = "Audio/Sink"
172
-            api.alsa.path          = "hw:0"
173
+            api.alsa.path          = "hw:4"
174
             #api.alsa.period-size   = 0
175
             #api.alsa.period-num    = 0
176
             #api.alsa.headroom      = 0
177
@@ -283,7 +356,7 @@
178
             #channelmix.rear-delay = 12.0
179
             #channelmix.stereo-widen = 0.0
180
             #channelmix.hilbert-taps = 0
181
-            channelmix.disable     = true
182
+            #channelmix.disable     = false
183
             #dither.noise = 0
184
             #node.param.Props      = {
185
             #    params = 
186
@@ -308,7 +381,11 @@
187
             #        channels = 4
188
             #    }
189
             #}
190
+            #node.param.Props    = {
191
+            #    channelVolumes =  0.5 0.4 0.3 0.5 
192
+            #}
193
         }
194
+        condition =  { minimal.use-udev = false } 
195
     }
196
     # This creates a new Source node. It will have input ports
197
     # that you can link, to provide audio for this source.
198
@@ -319,6 +396,7 @@
199
     #        node.description = "Microphone"
200
     #        media.class      = "Audio/Source/Virtual"
201
     #        audio.position   = "FL,FR"
202
+    #        monitor.passthrough = true
203
     #        adapter.auto-port-config = {
204
     #            mode = dsp
205
     #            monitor = true
206
@@ -348,7 +426,7 @@
207
 
208
 context.exec = 
209
     #{   path = <program-name>
210
-    #    ( args = "<arguments>" )
211
+    #    ( args = "<arguments>" |  <arg1> <arg2> ...  )
212
     #    ( condition =  { <key> = <value> ... } ...  )
213
     #}
214
     #
215
@@ -361,3 +439,38 @@
216
     #
217
     #@pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf" }
218
 
219
+
220
+node.rules = 
221
+    {   matches = 
222
+            {
223
+                # all keys must match the value. ! negates. ~ starts regex.
224
+                #alsa.card_name = "ICUSBAUDIO7D"
225
+                #api.alsa.pcm.stream = "playback"
226
+            }
227
+        
228
+        actions = {
229
+            update-props = {
230
+                #node.name = "alsa_playback.ICUSBAUDIO7D"
231
+            }
232
+        }
233
+    }
234
+
235
+device.rules = 
236
+    {   matches = 
237
+            {
238
+                #alsa.card_name = "ICUSBAUDIO7D"
239
+            }
240
+        
241
+        actions = {
242
+            update-props = {
243
+                #device.name = "alsa_card.ICUSBAUDIO7D"
244
+                #api.acp.auto-profile = false
245
+                #api.acp.auto-port = false
246
+                #device.param.Profile    = {
247
+                #    #idx = 0
248
+                #    name = off
249
+                #}
250
+            }
251
+        }
252
+    }
253
+
254
pipewire-1.0.1.tar.bz2/src/daemon/pipewire-aes67.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/pipewire-aes67.conf.in Changed
105
 
1
@@ -36,9 +36,13 @@
2
             # This driver should only be used for network nodes marked with group
3
             priority.driver = 100000
4
             clock.name      = "clock.system.ptp0"
5
+            ### Please select the PTP hardware clock here
6
+            # Interface name is the preferred method of specifying the PHC
7
+            clock.interface = "eth0"
8
+            #clock.device    = "/dev/ptp0"
9
             #clock.id        = tai
10
-            clock.device    = "/dev/ptp0"
11
-            #clock.interface = "eth0"
12
+            # Lower this in case of periodic out-of-sync
13
+            resync.ms       = 1.5
14
             object.export   = true
15
         }
16
     }
17
@@ -48,7 +52,7 @@
18
     { name = libpipewire-module-rt
19
         args = {
20
             nice.level   = -11
21
-            #rt.prio      = 88
22
+            #rt.prio      = @rtprio_client@
23
             #rt.time.soft = -1
24
             #rt.time.hard = -1
25
         }
26
@@ -60,11 +64,15 @@
27
     { name = libpipewire-module-adapter }
28
     { name = libpipewire-module-rtp-sap
29
         args = {
30
+            ### Please select the interface here
31
             local.ifname = eth0
32
             sap.ip = 239.255.255.255
33
             sap.port = 9875
34
             net.ttl = 32
35
-            net.loop = true
36
+            net.loop = false
37
+            # If you use another PTPv2 daemon supporting management
38
+            # messages over a UNIX socket, specify its path here
39
+            ptp.management-socket = "/var/run/ptp4lro"
40
 
41
             stream.rules = 
42
                 {
43
@@ -78,7 +86,8 @@
44
                             node.virtual = false
45
                             media.class = "Audio/Source"
46
                             device.api = aes67
47
-                            sess.latency.msec = 10
48
+                            # You can adjust the latency buffering here. Use integer values only
49
+                            sess.latency.msec = 3
50
                             node.group = pipewire.ptp0
51
                         }
52
                     }
53
@@ -98,27 +107,37 @@
54
     },
55
     { name = libpipewire-module-rtp-sink
56
         args = {
57
+            ### Please select the interface here
58
             local.ifname = eth0
59
+            ### If you want to create multiple output streams, please copy the whole
60
+            ### module-rtp-sink block, but change this multicast IP to another unused
61
+            ### one keeping 239.69.x.x range unless you know you need another one
62
             destination.ip = 239.69.150.243
63
             destination.port = 5004
64
             net.mtu = 1280
65
             net.ttl = 32
66
-            net.loop = true
67
+            net.loop = false
68
+            # These should typically be equal
69
+            # You can customize packet length, but 1 ms should work for every device
70
+            # Consult receiver documentation to ensure it supports the value you set
71
             sess.min-ptime = 1
72
             sess.max-ptime = 1
73
+            ### Please change this, especially if you create multiple sinks
74
             sess.name = "PipeWire RTP stream"
75
             sess.media = "audio"
76
+            # This property is used if you aren't using ptp4l 4
77
             sess.ts-refclk = "ptp=traceable"
78
             sess.ts-offset = 0
79
-            sess.ptime = 1
80
-            sess.latency.msec = 1
81
-            sess.announce = true
82
+            # You can adjust the latency buffering here. Use integer values only
83
+            sess.latency.msec = 3
84
             audio.format = "S24BE"
85
             audio.rate = 48000
86
             audio.channels = 2
87
-            audio.position =  FL FR 
88
+            # These channel names will be visible both to applications and AES67 receivers
89
+            node.channel-names = "CH1", "CH2"
90
 
91
             stream.props = {
92
+                ### Please change the sink name, this is necessary when you create multiple sinks
93
                 node.name = "rtp-sink"
94
                 media.class = "Audio/Sink"
95
                 node.virtual = false
96
@@ -126,6 +145,8 @@
97
                 sess.sap.announce = true
98
                 node.always-process = true
99
                 node.group = pipewire.ptp0
100
+                rtp.ntp = 0
101
+                rtp.fetch-ts-refclk = true
102
             }
103
         }
104
     },
105
pipewire-1.0.1.tar.bz2/src/daemon/pipewire-avb.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/pipewire-avb.conf.in Changed
28
 
1
@@ -27,7 +27,7 @@
2
     { name = libpipewire-module-rt
3
         args = {
4
             nice.level   = -11
5
-            #rt.prio      = 88
6
+            #rt.prio      = @rtprio_client@
7
             #rt.time.soft = -1
8
             #rt.time.hard = -1
9
         }
10
@@ -67,7 +67,14 @@
11
     # the addresses this server listens on
12
     #ifname = "eth0.2"
13
     ifname = "enp3s0"
14
-    # These overrides are only applied when running in a vm.
15
-    vm.overrides = {
16
-    }
17
 }
18
+
19
+avb.properties.rules = 
20
+    {   matches =  { cpu.vm.name = !null } 
21
+        actions = {
22
+            update-props = {
23
+                # These overrides are only applied when running in a vm.
24
+       }
25
+        }
26
+    }
27
+
28
pipewire-1.0.1.tar.bz2/src/daemon/pipewire-pulse.conf.avail/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/pipewire-pulse.conf.avail/meson.build Changed
13
 
1
@@ -3,9 +3,10 @@
2
 
3
 
4
 foreach c : conf_files
5
-  configure_file(input : '@0@.in'.format(c),
6
+  res = configure_file(input : '@0@.in'.format(c),
7
                  output : c,
8
                  configuration : conf_config,
9
                  install_dir : pipewire_confdatadir / 'pipewire-pulse.conf.avail')
10
+  test(f'validate-json-pulse-@c@', spa_json_dump_exe, args : res)
11
 endforeach
12
 
13
pipewire-1.0.1.tar.bz2/src/daemon/pipewire-pulse.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/pipewire-pulse.conf.in Changed
53
 
1
@@ -27,8 +27,7 @@
2
     { name = libpipewire-module-rt
3
         args = {
4
             nice.level   = -11
5
-            #rt.prio      = 88
6
-            rt.prio      = 65
7
+            #rt.prio      = @rtprio_client@
8
             #rt.time.soft = -1
9
             #rt.time.hard = -1
10
             #uclamp.min = 0
11
@@ -63,6 +62,9 @@
12
 #      ( flags =  nofail  )
13
 pulse.cmd = 
14
     { cmd = "load-module" args = "module-always-sink" flags =   }
15
+    { cmd = "load-module" args = "module-device-manager" flags =   }
16
+    { cmd = "load-module" args = "module-device-restore" flags =   }
17
+    { cmd = "load-module" args = "module-stream-restore" flags =   }
18
     #{ cmd = "load-module" args = "module-switch-on-connect" }
19
     #{ cmd = "load-module" args = "module-gsettings" flags =  nofail  }
20
 
21
@@ -99,6 +101,7 @@
22
         #}
23
     
24
     #server.dbus-name       = "org.pulseaudio.Server"
25
+    #pulse.allow-module-loading = true
26
     #pulse.min.req          = 128/48000     # 2.7ms
27
     #pulse.default.req      = 960/48000     # 20 milliseconds
28
     #pulse.min.frag         = 128/48000     # 2.7ms
29
@@ -108,12 +111,19 @@
30
     #pulse.idle.timeout     = 0             # don't pause after underruns
31
     #pulse.default.format   = F32
32
     #pulse.default.position =  FL FR 
33
-    # These overrides are only applied when running in a vm.
34
-    vm.overrides = {
35
-        pulse.min.quantum = 1024/48000      # 22ms
36
-    }
37
 }
38
 
39
+pulse.properties.rules = 
40
+    {   matches =  { cpu.vm.name = !null } 
41
+        actions = {
42
+            update-props = {
43
+            # These overrides are only applied when running in a vm.
44
+                pulse.min.quantum = 1024/48000      # 22ms
45
+       }
46
+        }
47
+    }
48
+
49
+
50
 # client/stream specific properties
51
 pulse.rules = 
52
     {
53
pipewire-1.0.1.tar.bz2/src/daemon/pipewire-vulkan.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/pipewire-vulkan.conf.in Changed
34
 
1
@@ -9,8 +9,6 @@
2
     #library.name.system                   = support/libspa-support
3
     #context.data-loop.library.name.system = support/libspa-support
4
     #support.dbus                          = true
5
-    #link.max-buffers                      = 64
6
-    #link.max-buffers                       = 16                       # version < 3 clients can't handle more
7
     #mem.warn-mlock                        = false
8
     #mem.allow-mlock                       = true
9
     #mem.mlock-all                         = false
10
@@ -30,6 +28,7 @@
11
     #
12
     api.vulkan.*    = vulkan/libspa-vulkan
13
     support.*       = support/libspa-support
14
+    video.convert.* = videoconvert/libspa-videoconvert
15
 }
16
 
17
 context.modules = 
18
@@ -52,7 +51,7 @@
19
     { name = libpipewire-module-rt
20
         args = {
21
             nice.level    = -11
22
-            #rt.prio      = 88
23
+            #rt.prio      = @rtprio_client@
24
             #rt.time.soft = -1
25
             #rt.time.hard = -1
26
         }
27
@@ -95,4 +94,6 @@
28
     #{ factory = adapter            args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } }
29
     { factory = spa-node-factory   args = { factory.name = api.vulkan.compute.source node.name = vulkan-compute-source object.export = true } }
30
     { factory = spa-node-factory   args = { factory.name = api.vulkan.compute.filter node.name = vulkan-compute-filter object.export = true } }
31
+    { factory = spa-node-factory   args = { factory.name = api.vulkan.blit.filter node.name = vulkan-blit-filter object.export = true } }
32
+    { factory = spa-node-factory   args = { factory.name = api.vulkan.blit.dsp-filter node.name = vulkan-blit-dsp-filter object.export = true } }
33
 
34
pipewire-1.0.1.tar.bz2/src/daemon/pipewire.c -> pipewire-1.2.0.tar.gz/src/daemon/pipewire.c Changed
94
 
1
@@ -9,6 +9,9 @@
2
 #include <locale.h>
3
 
4
 #include <spa/utils/result.h>
5
+#include <spa/utils/cleanup.h>
6
+#include <spa/debug/file.h>
7
+
8
 #include <pipewire/pipewire.h>
9
 
10
 #include <pipewire/i18n.h>
11
@@ -25,8 +28,10 @@
12
 {
13
    fprintf(stdout, _("%s options\n"
14
        "  -h, --help                            Show this help\n"
15
+       "  -v, --verbose                         Increase verbosity by one level\n"
16
        "      --version                         Show version\n"
17
-       "  -c, --config                          Load config (Default %s)\n"),
18
+       "  -c, --config                          Load config (Default %s)\n"
19
+       "  -P  --properties                      Set context properties\n"),
20
        name,
21
        config_name);
22
 }
23
@@ -41,13 +46,15 @@
24
        { "version",    no_argument,        NULL, 'V' },
25
        { "config", required_argument,  NULL, 'c' },
26
        { "verbose",    no_argument,        NULL, 'v' },
27
+       { "properties", required_argument,  NULL, 'P' },
28
 
29
        { NULL, 0, NULL, 0}
30
    };
31
    int c, res = 0;
32
    char pathPATH_MAX;
33
    const char *config_name;
34
-   enum spa_log_level level = pw_log_level;
35
+   enum spa_log_level level;
36
+   struct spa_error_location loc;
37
 
38
    if (setenv("PIPEWIRE_INTERNAL", "1", 1) < 0)
39
        fprintf(stderr, "can't set PIPEWIRE_INTERNAL env: %m");
40
@@ -58,7 +65,13 @@
41
    setlocale(LC_ALL, "");
42
    pw_init(&argc, &argv);
43
 
44
-   while ((c = getopt_long(argc, argv, "hVc:v", long_options, NULL)) != -1) {
45
+   level = pw_log_level;
46
+
47
+   properties = pw_properties_new(
48
+               PW_KEY_CONFIG_NAME, config_name,
49
+               NULL);
50
+
51
+   while ((c = getopt_long(argc, argv, "hVc:vP:", long_options, NULL)) != -1) {
52
        switch (c) {
53
        case 'v':
54
            if (level < SPA_LOG_LEVEL_TRACE)
55
@@ -77,17 +90,24 @@
56
            return 0;
57
        case 'c':
58
            config_name = optarg;
59
+           pw_properties_set(properties, PW_KEY_CONFIG_NAME, config_name);
60
            break;
61
+
62
+       case 'P':
63
+           if (pw_properties_update_string_checked(properties, optarg, strlen(optarg), &loc) < 0) {
64
+               spa_debug_file_error_location(stderr, &loc,
65
+                       "error: syntax error in --properties: %s",
66
+                       loc.reason);
67
+               goto done;
68
+           }
69
+           break;
70
+
71
        default:
72
            res = -EINVAL;
73
            goto done;
74
        }
75
    }
76
 
77
-   properties = pw_properties_new(
78
-               PW_KEY_CONFIG_NAME, config_name,
79
-               NULL);
80
-
81
    loop = pw_main_loop_new(&properties->dict);
82
    if (loop == NULL) {
83
        pw_log_error("failed to create main-loop: %m");
84
@@ -98,8 +118,7 @@
85
    pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGINT, do_quit, loop);
86
    pw_loop_add_signal(pw_main_loop_get_loop(loop), SIGTERM, do_quit, loop);
87
 
88
-   context = pw_context_new(pw_main_loop_get_loop(loop), properties, 0);
89
-   properties = NULL;
90
+   context = pw_context_new(pw_main_loop_get_loop(loop), spa_steal_ptr(properties), 0);
91
 
92
    if (context == NULL) {
93
        pw_log_error("failed to create context: %m");
94
pipewire-1.0.1.tar.bz2/src/daemon/pipewire.conf.avail/meson.build -> pipewire-1.2.0.tar.gz/src/daemon/pipewire.conf.avail/meson.build Changed
13
 
1
@@ -4,9 +4,10 @@
2
 
3
 
4
 foreach c : conf_files
5
-  configure_file(input : '@0@.in'.format(c),
6
+  res = configure_file(input : '@0@.in'.format(c),
7
                  output : c,
8
                  configuration : conf_config,
9
                  install_dir : pipewire_confdatadir / 'pipewire.conf.avail')
10
+  test(f'validate-json-pipewire-@c@', spa_json_dump_exe, args : res)
11
 endforeach
12
 
13
pipewire-1.0.1.tar.bz2/src/daemon/pipewire.conf.in -> pipewire-1.2.0.tar.gz/src/daemon/pipewire.conf.in Changed
129
 
1
@@ -22,6 +22,20 @@
2
     #log.level                             = 2
3
     #cpu.zero.denormals                    = false
4
 
5
+    #loop.rt-prio = -1            # -1 = use module-rt prio, 0 disable rt
6
+    #loop.class = data.rt
7
+    #thread.affinity =  0 1     # optional array of CPUs
8
+    #context.num-data-loops = 1   # -1 = num-cpus, 0 = no data loops
9
+    #
10
+    #context.data-loops = 
11
+    #    {   loop.rt-prio = -1
12
+    #        loop.class =  data.rt audio.rt 
13
+    #        #library.name.system = support/libspa-support
14
+    #        thread.name = data-loop.0
15
+    #        #thread.affinity =  0 1     # optional array of CPUs
16
+    #    }
17
+    #
18
+
19
     core.daemon = true              # listening for socket connections
20
     core.name   = pipewire-0        # core name and socket name
21
 
22
@@ -32,6 +46,7 @@
23
     #default.clock.min-quantum   = 32
24
     #default.clock.max-quantum   = 2048
25
     #default.clock.quantum-limit = 8192
26
+    #default.clock.quantum-floor = 4
27
     #default.video.width         = 640
28
     #default.video.height        = 480
29
     #default.video.rate.num      = 25
30
@@ -39,11 +54,6 @@
31
     #
32
     #settings.check-quantum      = false
33
     #settings.check-rate         = false
34
-    #
35
-    # These overrides are only applied when running in a vm.
36
-    vm.overrides = {
37
-        default.clock.min-quantum = 1024
38
-    }
39
 
40
     # keys checked below to disable module loading
41
     module.x11.bell = true
42
@@ -54,6 +64,17 @@
43
     module.jackdbus-detect = true
44
 }
45
 
46
+context.properties.rules = 
47
+    {   matches =  { cpu.vm.name = !null } 
48
+        actions = {
49
+            update-props = {
50
+                # These overrides are only applied when running in a vm.
51
+                default.clock.min-quantum = 1024
52
+       }
53
+        }
54
+    }
55
+
56
+
57
 context.spa-libs = {
58
     #<factory-name regex> = <library-name>
59
     #
60
@@ -70,6 +91,7 @@
61
     api.vulkan.*    = vulkan/libspa-vulkan
62
     api.jack.*      = jack/libspa-jack
63
     support.*       = support/libspa-support
64
+    video.convert.* = videoconvert/libspa-videoconvert
65
     #videotestsrc   = videotestsrc/libspa-videotestsrc
66
     #audiotestsrc   = audiotestsrc/libspa-audiotestsrc
67
 }
68
@@ -95,7 +117,7 @@
69
     { name = libpipewire-module-rt
70
         args = {
71
             nice.level    = -11
72
-            #rt.prio      = 88
73
+            rt.prio       = @rtprio_server@
74
             #rt.time.soft = -1
75
             #rt.time.hard = -1
76
             #uclamp.min = 0
77
@@ -172,7 +194,7 @@
78
     # Use libcanberra to play X11 Bell
79
     { name = libpipewire-module-x11-bell
80
         args = {
81
-            #sink.name = "@DEFAULT_SINK@"
82
+            #sink.name = "\@DEFAULT_SINK\@"
83
             #sample.name = "bell-window-system"
84
             #x11.display = null
85
             #x11.xauthority = null
86
@@ -231,6 +253,7 @@
87
             factory.name    = support.node.driver
88
             node.name       = Dummy-Driver
89
             node.group      = pipewire.dummy
90
+            node.sync-group  = sync.dummy
91
             priority.driver = 20000
92
             #clock.id       = monotonic # realtime | tai | monotonic-raw | boottime
93
             #clock.name     = "clock.system.monotonic"
94
@@ -242,7 +265,9 @@
95
             node.name       = Freewheel-Driver
96
             priority.driver = 19000
97
             node.group      = pipewire.freewheel
98
+            node.sync-group  = sync.dummy
99
             node.freewheel  = true
100
+            #freewheel.wait = 10
101
         }
102
     }
103
 
104
@@ -255,6 +280,7 @@
105
     #        node.description = "Microphone"
106
     #        media.class      = "Audio/Source/Virtual"
107
     #        audio.position   = "FL,FR"
108
+    #        monitor.passthrough = true
109
     #    }
110
     #}
111
 
112
@@ -293,7 +319,7 @@
113
 
114
 context.exec = 
115
     #{   path = <program-name>
116
-    #    ( args = "<arguments>" )
117
+    #    ( args = "<arguments>" |  <arg1> <arg2> ...  )
118
     #    ( condition =  { <key> = <value> ... } ...  )
119
     #}
120
     #
121
@@ -313,6 +339,6 @@
122
     # It can be interesting to start another daemon here that listens
123
     # on another address with the -a option (eg. -a tcp:4713).
124
     #
125
-    @pulse_comment@{ path = "@pipewire_path@" args = "-c pipewire-pulse.conf"
126
+    @pulse_comment@{ path = "@pipewire_path@" args =  "-c" "pipewire-pulse.conf" 
127
     @pulse_comment@  condition =  { exec.pipewire-pulse = null } { exec.pipewire-pulse = true }  }
128
 
129
pipewire-1.0.1.tar.bz2/src/examples/audio-capture.c -> pipewire-1.2.0.tar.gz/src/examples/audio-capture.c Changed
10
 
1
@@ -63,7 +63,7 @@
2
        for (n = c; n < n_samples; n += n_channels)
3
            max = fmaxf(max, fabsf(samplesn));
4
 
5
-       peak = SPA_CLAMP(max * 30, 0, 39);
6
+       peak = (uint32_t)SPA_CLAMPF(max * 30, 0.f, 39.f);
7
 
8
        fprintf(stdout, "channel %d: |%*s%*s| peak:%f\n",
9
                c, peak+1, "*", 40 - peak, "", max);
10
pipewire-1.0.1.tar.bz2/src/examples/audio-dsp-src.c -> pipewire-1.2.0.tar.gz/src/examples/audio-dsp-src.c Changed
38
 
1
@@ -16,17 +16,17 @@
2
 #include <pipewire/pipewire.h>
3
 #include <pipewire/filter.h>
4
 
5
-#define M_PI_M2 ( M_PI + M_PI )
6
+#define M_PI_M2f (float)(M_PI+M_PI)
7
 
8
 #define DEFAULT_RATE       44100
9
 #define DEFAULT_FREQ       440
10
-#define DEFAULT_VOLUME     0.7
11
+#define DEFAULT_VOLUME     0.7f
12
 
13
 struct data;
14
 
15
 struct port {
16
    struct data *data;
17
-   double accumulator;
18
+   float accumulator;
19
 };
20
 
21
 struct data {
22
@@ -61,11 +61,11 @@
23
        return;
24
 
25
    for (i = 0; i < n_samples; i++) {
26
-       out_port->accumulator += M_PI_M2 * DEFAULT_FREQ / DEFAULT_RATE;
27
-       if (out_port->accumulator >= M_PI_M2)
28
-           out_port->accumulator -= M_PI_M2;
29
+       out_port->accumulator += M_PI_M2f * DEFAULT_FREQ / DEFAULT_RATE;
30
+       if (out_port->accumulator >= M_PI_M2f)
31
+           out_port->accumulator -= M_PI_M2f;
32
 
33
-       *out++ = sin(out_port->accumulator) * DEFAULT_VOLUME;
34
+       *out++ = sinf(out_port->accumulator) * DEFAULT_VOLUME;
35
    }
36
 }
37
 
38
pipewire-1.0.1.tar.bz2/src/examples/audio-src.c -> pipewire-1.2.0.tar.gz/src/examples/audio-src.c Changed
49
 
1
@@ -17,17 +17,17 @@
2
 
3
 #include <pipewire/pipewire.h>
4
 
5
-#define M_PI_M2 ( M_PI + M_PI )
6
+#define M_PI_M2f (float)(M_PI+M_PI)
7
 
8
 #define DEFAULT_RATE       44100
9
 #define DEFAULT_CHANNELS   2
10
-#define DEFAULT_VOLUME     0.7
11
+#define DEFAULT_VOLUME     0.7f
12
 
13
 struct data {
14
    struct pw_main_loop *loop;
15
    struct pw_stream *stream;
16
 
17
-   double accumulator;
18
+   float accumulator;
19
 };
20
 
21
 static void fill_f32(struct data *d, void *dest, int n_frames)
22
@@ -36,11 +36,11 @@
23
    int i, c;
24
 
25
         for (i = 0; i < n_frames; i++) {
26
-                d->accumulator += M_PI_M2 * 440 / DEFAULT_RATE;
27
-                if (d->accumulator >= M_PI_M2)
28
-                        d->accumulator -= M_PI_M2;
29
+                d->accumulator += M_PI_M2f * 440 / DEFAULT_RATE;
30
+                if (d->accumulator >= M_PI_M2f)
31
+                        d->accumulator -= M_PI_M2f;
32
 
33
-                val = sin(d->accumulator) * DEFAULT_VOLUME;
34
+                val = sinf(d->accumulator) * DEFAULT_VOLUME;
35
                 for (c = 0; c < DEFAULT_CHANNELS; c++)
36
                         *dst++ = val;
37
         }
38
@@ -73,7 +73,9 @@
39
        return;
40
 
41
    stride = sizeof(float) * DEFAULT_CHANNELS;
42
-   n_frames = SPA_MIN(b->requested, buf->datas0.maxsize / stride);
43
+   n_frames = buf->datas0.maxsize / stride;
44
+   if (b->requested)
45
+       n_frames = SPA_MIN((int)b->requested, n_frames);
46
 
47
    fill_f32(data, p, n_frames);
48
 
49
pipewire-1.0.1.tar.bz2/src/examples/bluez-session.c -> pipewire-1.2.0.tar.gz/src/examples/bluez-session.c Changed
19
 
1
@@ -58,8 +58,6 @@
2
 };
3
 
4
 struct impl {
5
-   struct timespec now;
6
-
7
    struct pw_main_loop *loop;
8
    struct pw_context *context;
9
 
10
@@ -353,8 +351,6 @@
11
    impl.loop = pw_main_loop_new(NULL);
12
    impl.context = pw_context_new(pw_main_loop_get_loop(impl.loop), NULL, 0);
13
 
14
-   clock_gettime(CLOCK_MONOTONIC, &impl.now);
15
-
16
    spa_list_init(&impl.device_list);
17
 
18
         impl.core = pw_context_connect(impl.context, NULL, 0);
19
pipewire-1.0.1.tar.bz2/src/examples/export-sink.c -> pipewire-1.2.0.tar.gz/src/examples/export-sink.c Changed
59
 
1
@@ -29,7 +29,7 @@
2
 
3
 #include "sdl.h"
4
 
5
-#define M_PI_M2 ( M_PI + M_PI )
6
+#define M_PI_M2f (float)(M_PI+M_PI)
7
 
8
 #define MAX_BUFFERS    64
9
 
10
@@ -65,7 +65,7 @@
11
    struct spa_io_buffers *io;
12
    struct spa_io_sequence *io_notify;
13
    uint32_t io_notify_size;
14
-   double param_accum;
15
+   float param_accum;
16
 
17
    uint8_t buffer1024;
18
 
19
@@ -106,13 +106,13 @@
20
    spa_pod_builder_control(&b, 0, SPA_CONTROL_Properties);
21
    spa_pod_builder_push_object(&b, &f1, SPA_TYPE_OBJECT_Props, 0);
22
    spa_pod_builder_prop(&b, SPA_PROP_contrast, 0);
23
-   spa_pod_builder_float(&b, (sin(data->param_accum) * 127.0) + 127.0);
24
+   spa_pod_builder_float(&b, (sinf(data->param_accum) * 127.0f) + 127.0f);
25
    spa_pod_builder_pop(&b, &f1);
26
    spa_pod_builder_pop(&b, &f0);
27
 
28
-        data->param_accum += M_PI_M2 / 30.0;
29
-        if (data->param_accum >= M_PI_M2)
30
-                data->param_accum -= M_PI_M2;
31
+        data->param_accum += M_PI_M2f / 30.0f;
32
+        if (data->param_accum >= M_PI_M2f)
33
+                data->param_accum -= M_PI_M2f;
34
 }
35
 
36
 static int impl_send_command(void *object, const struct spa_command *command)
37
@@ -369,9 +369,9 @@
38
 
39
    if (buf->datas0.type == SPA_DATA_MemFd ||
40
        buf->datas0.type == SPA_DATA_DmaBuf) {
41
-       map = mmap(NULL, buf->datas0.maxsize + buf->datas0.mapoffset, PROT_READ,
42
-              MAP_PRIVATE, buf->datas0.fd, 0);
43
-       sdata = SPA_PTROFF(map, buf->datas0.mapoffset, uint8_t);
44
+       map = mmap(NULL, buf->datas0.maxsize, PROT_READ,
45
+              MAP_PRIVATE, buf->datas0.fd, buf->datas0.mapoffset);
46
+       sdata = map;
47
    } else if (buf->datas0.type == SPA_DATA_MemPtr) {
48
        map = NULL;
49
        sdata = buf->datas0.data;
50
@@ -413,7 +413,7 @@
51
    SDL_RenderPresent(d->renderer);
52
 
53
    if (map)
54
-       munmap(map, buf->datas0.maxsize + buf->datas0.mapoffset);
55
+       munmap(map, buf->datas0.maxsize);
56
 
57
    return 0;
58
 }
59
pipewire-1.0.1.tar.bz2/src/examples/export-source.c -> pipewire-1.2.0.tar.gz/src/examples/export-source.c Changed
88
 
1
@@ -23,7 +23,7 @@
2
 
3
 #include <pipewire/pipewire.h>
4
 
5
-#define M_PI_M2 ( M_PI + M_PI )
6
+#define M_PI_M2f (float)(M_PI + M_PI)
7
 
8
 #define BUFFER_SAMPLES 128
9
 #define MAX_BUFFERS    32
10
@@ -64,8 +64,8 @@
11
    uint32_t n_buffers;
12
    struct spa_list empty;
13
 
14
-   double accumulator;
15
-   double volume_accum;
16
+   float accumulator;
17
+   float volume_accum;
18
 };
19
 
20
 static void update_volume(struct data *data)
21
@@ -81,13 +81,13 @@
22
    spa_pod_builder_control(&b, 0, SPA_CONTROL_Properties);
23
    spa_pod_builder_push_object(&b, &f1, SPA_TYPE_OBJECT_Props, 0);
24
    spa_pod_builder_prop(&b, SPA_PROP_volume, 0);
25
-   spa_pod_builder_float(&b, (sin(data->volume_accum) / 2.0) + 0.5);
26
+   spa_pod_builder_float(&b, (sinf(data->volume_accum) / 2.0f) + 0.5f);
27
    spa_pod_builder_pop(&b, &f1);
28
    spa_pod_builder_pop(&b, &f0);
29
 
30
-        data->volume_accum += M_PI_M2 / 1000.0;
31
-        if (data->volume_accum >= M_PI_M2)
32
-                data->volume_accum -= M_PI_M2;
33
+        data->volume_accum += M_PI_M2f / 1000.0f;
34
+        if (data->volume_accum >= M_PI_M2f)
35
+                data->volume_accum -= M_PI_M2f;
36
 }
37
 
38
 static int impl_send_command(void *object, const struct spa_command *command)
39
@@ -319,14 +319,13 @@
40
        }
41
        else if (datas0.type == SPA_DATA_MemFd ||
42
             datas0.type == SPA_DATA_DmaBuf) {
43
-           b->ptr = mmap(NULL, datas0.maxsize + datas0.mapoffset, PROT_WRITE,
44
-                     MAP_SHARED, datas0.fd, 0);
45
+           b->ptr = mmap(NULL, datas0.maxsize, PROT_WRITE,
46
+                     MAP_SHARED, datas0.fd, datas0.mapoffset);
47
            if (b->ptr == MAP_FAILED) {
48
                pw_log_error("failed to buffer mem");
49
                return -errno;
50
 
51
            }
52
-           b->ptr = SPA_PTROFF(b->ptr, datas0.mapoffset, void);
53
            b->mapped = true;
54
        }
55
        else {
56
@@ -365,11 +364,11 @@
57
         for (i = 0; i < n_samples; i++) {
58
        float val;
59
 
60
-                d->accumulator += M_PI_M2 * 440 / d->format.rate;
61
-                if (d->accumulator >= M_PI_M2)
62
-                        d->accumulator -= M_PI_M2;
63
+                d->accumulator += M_PI_M2f * 440 / d->format.rate;
64
+                if (d->accumulator >= M_PI_M2f)
65
+                        d->accumulator -= M_PI_M2f;
66
 
67
-                val = sin(d->accumulator);
68
+                val = sinf(d->accumulator);
69
 
70
                 for (c = 0; c < d->format.channels; c++)
71
                         *dst++ = val;
72
@@ -386,11 +385,11 @@
73
         for (i = 0; i < n_samples; i++) {
74
                 int16_t val;
75
 
76
-                d->accumulator += M_PI_M2 * 440 / d->format.rate;
77
-                if (d->accumulator >= M_PI_M2)
78
-                        d->accumulator -= M_PI_M2;
79
+                d->accumulator += M_PI_M2f * 440 / d->format.rate;
80
+                if (d->accumulator >= M_PI_M2f)
81
+                        d->accumulator -= M_PI_M2f;
82
 
83
-                val = (int16_t) (sin(d->accumulator) * 32767.0);
84
+                val = (int16_t) (sinf(d->accumulator) * 32767.0f);
85
 
86
                 for (c = 0; c < d->format.channels; c++)
87
                         *dst++ = val;
88
pipewire-1.0.1.tar.bz2/src/examples/local-v4l2.c -> pipewire-1.2.0.tar.gz/src/examples/local-v4l2.c Changed
23
 
1
@@ -269,9 +269,9 @@
2
 
3
    if (buf->datas0.type == SPA_DATA_MemFd ||
4
        buf->datas0.type == SPA_DATA_DmaBuf) {
5
-       map = mmap(NULL, buf->datas0.maxsize + buf->datas0.mapoffset, PROT_READ,
6
-              MAP_PRIVATE, buf->datas0.fd, 0);
7
-       sdata = SPA_PTROFF(map, buf->datas0.mapoffset, uint8_t);
8
+       map = mmap(NULL, buf->datas0.maxsize, PROT_READ,
9
+              MAP_PRIVATE, buf->datas0.fd, buf->datas0.mapoffset);
10
+       sdata = map;
11
    } else if (buf->datas0.type == SPA_DATA_MemPtr) {
12
        map = NULL;
13
        sdata = buf->datas0.data;
14
@@ -299,7 +299,7 @@
15
    SDL_RenderPresent(d->renderer);
16
 
17
    if (map)
18
-       munmap(map, buf->datas0.maxsize + buf->datas0.mapoffset);
19
+       munmap(map, buf->datas0.maxsize);
20
 
21
    return 0;
22
 }
23
pipewire-1.0.1.tar.bz2/src/examples/meson.build -> pipewire-1.2.0.tar.gz/src/examples/meson.build Changed
16
 
1
@@ -7,12 +7,14 @@
2
   'video-play',
3
   'video-src',
4
   'video-dsp-play',
5
+  'video-dsp-src',
6
   'video-play-pull',
7
   'video-play-reneg',
8
   'video-src-alloc',
9
   'video-src-reneg',
10
   'video-src-fixate',
11
   'video-play-fixate',
12
+  'midi-src',
13
   'internal',
14
   'export-sink',
15
   'export-source',
16
pipewire-1.2.0.tar.gz/src/examples/midi-src.c Added
266
 
1
@@ -0,0 +1,264 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Pauli Virtanen */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+/*
7
+ title
8
+ MIDI source using \ref pw_filter "pw_filter".
9
+ title
10
+ */
11
+
12
+#include <stdio.h>
13
+#include <errno.h>
14
+#include <math.h>
15
+#include <signal.h>
16
+#include <getopt.h>
17
+
18
+#include <pipewire/pipewire.h>
19
+#include <pipewire/filter.h>
20
+
21
+#include <spa/pod/builder.h>
22
+#include <spa/control/control.h>
23
+
24
+
25
+#define PERIOD_NSEC    (SPA_NSEC_PER_SEC/8)
26
+
27
+struct port {
28
+};
29
+
30
+struct data {
31
+   struct pw_main_loop *loop;
32
+   struct pw_filter *filter;
33
+   struct port *port;
34
+   uint32_t clock_id;
35
+   int64_t offset;
36
+   uint64_t position;
37
+};
38
+
39
+static void on_process(void *userdata, struct spa_io_position *position)
40
+{
41
+   struct data *data = userdata;
42
+   struct port *port = data->port;
43
+   struct pw_buffer *buf;
44
+   struct spa_data *d;
45
+   struct spa_pod_builder builder;
46
+   struct spa_pod_frame frame;
47
+   uint64_t sample_offset, sample_period, sample_position, cycle;
48
+
49
+   /*
50
+    * Use the clock sample position.
51
+    *
52
+    * If the playback switches to using a different clock, we reset
53
+    * playback as the sample position can then be discontinuous.
54
+    */
55
+   if (data->clock_id != position->clock.id) {
56
+       pw_log_info("switch to clock %u", position->clock.id);
57
+       data->offset = position->clock.position - data->position;
58
+       data->clock_id = position->clock.id;
59
+   }
60
+
61
+   sample_position = position->clock.position - data->offset;
62
+   data->position = sample_position + position->clock.duration;
63
+
64
+   /*
65
+    * Produce note on/off every `PERIOD_NSEC` nanoseconds (rounded down to
66
+    * samples, for simplicity).
67
+    *
68
+    * We want to place the notes on the playback timeline, so we use sample
69
+    * positions (not real time!).
70
+    */
71
+
72
+   sample_period = PERIOD_NSEC * position->clock.rate.denom
73
+       / position->clock.rate.num / SPA_NSEC_PER_SEC;
74
+
75
+   cycle = sample_position / sample_period;
76
+   if (sample_position % sample_period != 0)
77
+       ++cycle;
78
+
79
+   sample_offset = cycle*sample_period - sample_position;
80
+
81
+   if (sample_offset >= position->clock.duration)
82
+       return;  /* don't need to produce anything yet */
83
+
84
+   /* Get output buffer */
85
+   if ((buf = pw_filter_dequeue_buffer(port)) == NULL)
86
+       return;
87
+
88
+   /* Midi buffers always have exactly one data block */
89
+   spa_assert(buf->buffer->n_datas == 1);
90
+
91
+   d = &buf->buffer->datas0;
92
+   d->chunk->offset = 0;
93
+   d->chunk->size = 0;
94
+   d->chunk->stride = 1;
95
+   d->chunk->flags = 0;
96
+
97
+   /*
98
+    * MIDI buffers contain a SPA POD with a sequence of
99
+    * control messages and their raw MIDI data.
100
+    */
101
+   spa_pod_builder_init(&builder, d->data, d->maxsize);
102
+   spa_pod_builder_push_sequence(&builder, &frame, 0);
103
+
104
+   while (sample_offset < position->clock.duration) {
105
+       if (cycle % 2 == 0) {
106
+           /* MIDI note on, channel 0, middle C, max velocity */
107
+           uint8_t buf = { 0x90, 0x3c, 0x7f };
108
+
109
+           /* The time position of the message in the graph cycle
110
+            * is given as offset from the cycle start, in
111
+            * samples. The cycle has duration of `clock.duration`
112
+            * samples, and the sample offset should satisfy
113
+            * 0 <= sample_offset < position->clock.duration.
114
+            */
115
+           spa_pod_builder_control(&builder, sample_offset, SPA_CONTROL_Midi);
116
+
117
+           /* Raw MIDI data for the message */
118
+           spa_pod_builder_bytes(&builder, buf, sizeof(buf));
119
+
120
+           pw_log_info("note on at %"PRIu64, sample_position + sample_offset);
121
+       } else {
122
+           /* MIDI note off, channel 0, middle C, max velocity */
123
+           uint8_t buf = { 0x80, 0x3c, 0x7f };
124
+
125
+           spa_pod_builder_control(&builder, sample_offset, SPA_CONTROL_Midi);
126
+           spa_pod_builder_bytes(&builder, buf, sizeof(buf));
127
+
128
+           pw_log_info("note off at %"PRIu64, sample_position + sample_offset);
129
+       }
130
+
131
+       sample_offset += sample_period;
132
+       ++cycle;
133
+   }
134
+
135
+   /*
136
+    * Finish the sequence and queue buffer to output.
137
+    */
138
+        spa_pod_builder_pop(&builder, &frame);
139
+        d->chunk->size = builder.state.offset;
140
+
141
+   pw_log_trace("produced %u/%u bytes", d->chunk->size, d->maxsize);
142
+
143
+   pw_filter_queue_buffer(port, buf);
144
+}
145
+
146
+static void state_changed(void *userdata, enum pw_filter_state old,
147
+       enum pw_filter_state state, const char *error)
148
+{
149
+   struct data *data = userdata;
150
+
151
+   switch (state) {
152
+   case PW_FILTER_STATE_STREAMING:
153
+       /* reset playback position */
154
+       pw_log_info("start playback");
155
+       data->clock_id = SPA_ID_INVALID;
156
+       data->offset = 0;
157
+       data->position = 0;
158
+       break;
159
+   default:
160
+       break;
161
+   }
162
+}
163
+
164
+static const struct pw_filter_events filter_events = {
165
+   PW_VERSION_FILTER_EVENTS,
166
+   .process = on_process,
167
+   .state_changed = state_changed,
168
+};
169
+
170
+static void do_quit(void *userdata, int signal_number)
171
+{
172
+   struct data *data = userdata;
173
+   pw_main_loop_quit(data->loop);
174
+}
175
+
176
+int main(int argc, char *argv)
177
+{
178
+   struct data data = {};
179
+   uint8_t buffer1024;
180
+   struct spa_pod_builder builder;
181
+   struct spa_pod *params1;
182
+
183
+   pw_init(&argc, &argv);
184
+
185
+   /* make a main loop. If you already have another main loop, you can add
186
+    * the fd of this pipewire mainloop to it. */
187
+   data.loop = pw_main_loop_new(NULL);
188
+
189
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data);
190
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data);
191
+
192
+   /* Create a simple filter, the simple filter manages the core and remote
193
+    * objects for you if you don't need to deal with them.
194
+    *
195
+    * Pass your events and a user_data pointer as the last arguments. This
196
+    * will inform you about the filter state. The most important event
197
+    * you need to listen to is the process event where you need to process
198
+    * the data.
199
+    */
200
+   data.filter = pw_filter_new_simple(
201
+           pw_main_loop_get_loop(data.loop),
202
+           "midi-src",
203
+           pw_properties_new(
204
+               PW_KEY_MEDIA_TYPE, "Midi",
205
+               PW_KEY_MEDIA_CATEGORY, "Playback",
206
+               PW_KEY_MEDIA_CLASS, "Midi/Source",
207
+               NULL),
208
+           &filter_events,
209
+           &data);
210
+
211
+   /* Make a midi output port */
212
+   data.port = pw_filter_add_port(data.filter,
213
+           PW_DIRECTION_OUTPUT,
214
+           PW_FILTER_PORT_FLAG_MAP_BUFFERS,
215
+           sizeof(struct port),
216
+           pw_properties_new(
217
+               PW_KEY_FORMAT_DSP, "8 bit raw midi",
218
+               PW_KEY_PORT_NAME, "output",
219
+               NULL),
220
+           NULL, 0);
221
+
222
+   /* Update SPA_PARAM_Buffers to request a specific sizes and counts.
223
+    * This is not mandatory: if you skip this, you'll get default sized
224
+    * buffers, usually 4k or 32k bytes or so.
225
+    *
226
+    * We'll here ask for 4096 bytes as that's enough.
227
+    */
228
+   spa_pod_builder_init(&builder, buffer, sizeof(buffer));
229
+
230
+   params0 = spa_pod_builder_add_object(&builder,
231
+           /* POD Object for the buffer parameter */
232
+           SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
233
+           /* Default 1 buffer, minimum of 1, max of 32 buffers.
234
+            * We can do with 1 buffer as we dequeue and queue in the same
235
+            * cycle.
236
+            */
237
+           SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, 32),
238
+           /* MIDI buffers always have 1 data block */
239
+           SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
240
+           /* Buffer size: request default 4096 bytes, min 4096, no maximum */
241
+           SPA_PARAM_BUFFERS_size,    SPA_POD_CHOICE_RANGE_Int(4096, 4096, INT32_MAX),
242
+           /* MIDI buffers have stride 1 */
243
+           SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(1));
244
+
245
+   pw_filter_update_params(data.filter, data.port,
246
+           (const struct spa_pod **)params, SPA_N_ELEMENTS(params));
247
+
248
+   /* Now connect this filter. We ask that our process function is
249
+    * called in a realtime thread. */
250
+   if (pw_filter_connect(data.filter,
251
+               PW_FILTER_FLAG_RT_PROCESS,
252
+               NULL, 0) < 0) {
253
+       fprintf(stderr, "can't connect\n");
254
+       return -1;
255
+   }
256
+
257
+   /* and wait while we let things run */
258
+   pw_main_loop_run(data.loop);
259
+
260
+   pw_filter_destroy(data.filter);
261
+   pw_main_loop_destroy(data.loop);
262
+   pw_deinit();
263
+
264
+   return 0;
265
+}
266
pipewire-1.0.1.tar.bz2/src/examples/video-dsp-play.c -> pipewire-1.2.0.tar.gz/src/examples/video-dsp-play.c Changed
16
 
1
@@ -128,10 +128,10 @@
2
    for (i = 0; i < data->position->video.size.height; i++) {
3
        struct pixel *p = (struct pixel *) src;
4
        for (j = 0; j < data->position->video.size.width; j++) {
5
-           dstj * 4 + 0 = SPA_CLAMP(pj.r * 255.0f, 0, 255);
6
-           dstj * 4 + 1 = SPA_CLAMP(pj.g * 255.0f, 0, 255);
7
-           dstj * 4 + 2 = SPA_CLAMP(pj.b * 255.0f, 0, 255);
8
-           dstj * 4 + 3 = SPA_CLAMP(pj.a * 255.0f, 0, 255);
9
+           dstj * 4 + 0 = SPA_CLAMP((uint8_t)(pj.r * 255.0f), 0, 255);
10
+           dstj * 4 + 1 = SPA_CLAMP((uint8_t)(pj.g * 255.0f), 0, 255);
11
+           dstj * 4 + 2 = SPA_CLAMP((uint8_t)(pj.b * 255.0f), 0, 255);
12
+           dstj * 4 + 3 = SPA_CLAMP((uint8_t)(pj.a * 255.0f), 0, 255);
13
        }
14
        src += sstride;
15
        dst += dstride;
16
pipewire-1.2.0.tar.gz/src/examples/video-dsp-src.c Added
376
 
1
@@ -0,0 +1,374 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2023 Columbarius */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+/*
8
+ title
9
+ Video source using \ref pw_stream.
10
+ title
11
+ */
12
+
13
+#include <stdio.h>
14
+#include <errno.h>
15
+#include <signal.h>
16
+#include <math.h>
17
+
18
+#include <spa/param/video/format-utils.h>
19
+#include <spa/param/tag-utils.h>
20
+#include <spa/debug/pod.h>
21
+
22
+#include <pipewire/pipewire.h>
23
+
24
+#define BPP        16
25
+#define CURSOR_WIDTH   64
26
+#define CURSOR_HEIGHT  64
27
+#define CURSOR_BPP 4
28
+
29
+#define MAX_BUFFERS    64
30
+
31
+#define M_PI_M2 ( M_PI + M_PI )
32
+
33
+struct pixel {
34
+   float r, g, b, a;
35
+};
36
+
37
+struct data {
38
+   struct pw_main_loop *loop;
39
+   struct spa_source *timer;
40
+
41
+   struct pw_context *context;
42
+   struct pw_core *core;
43
+
44
+   struct pw_stream *stream;
45
+   struct spa_hook stream_listener;
46
+
47
+   struct spa_video_info_dsp format;
48
+   int32_t stride;
49
+   struct spa_io_position *position;
50
+
51
+   int counter;
52
+   uint32_t seq;
53
+
54
+   double crop;
55
+   double accumulator;
56
+   int res;
57
+};
58
+
59
+static void draw_elipse(uint32_t *dst, int width, int height, uint32_t color)
60
+{
61
+   int i, j, r1, r2, r12, r22, r122;
62
+
63
+   r1 = width/2;
64
+   r12 = r1 * r1;
65
+   r2 = height/2;
66
+   r22 = r2 * r2;
67
+   r122 = r12 * r22;
68
+
69
+   for (i = -r2; i < r2; i++) {
70
+       for (j = -r1; j < r1; j++) {
71
+           dst(i + r2)*width+(j+r1) =
72
+               (i * i * r12 + j * j * r22 <= r122) ? color : 0x00000000;
73
+       }
74
+   }
75
+}
76
+
77
+static inline float map_value(int value)
78
+{
79
+   return (value%256)/255.0f;
80
+}
81
+
82
+static void on_process(void *userdata)
83
+{
84
+   struct data *data = userdata;
85
+   struct pw_buffer *b;
86
+   struct spa_buffer *buf;
87
+   uint32_t i, j;
88
+   uint8_t *p;
89
+   struct pixel *px;
90
+   struct spa_meta *m;
91
+   struct spa_meta_header *h;
92
+   struct spa_meta_region *mc;
93
+   struct spa_meta_cursor *mcs;
94
+
95
+   if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) {
96
+       pw_log_warn("out of buffers: %m");
97
+       return;
98
+   }
99
+
100
+   buf = b->buffer;
101
+   if ((p = buf->datas0.data) == NULL)
102
+       return;
103
+
104
+   if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
105
+#if 0
106
+       h->pts = pw_stream_get_nsec(data->stream);
107
+#else
108
+       h->pts = -1;
109
+#endif
110
+       h->flags = 0;
111
+       h->seq = data->seq++;
112
+       h->dts_offset = 0;
113
+   }
114
+   if ((m = spa_buffer_find_meta(buf, SPA_META_VideoDamage))) {
115
+       struct spa_meta_region *r = spa_meta_first(m);
116
+
117
+       if (spa_meta_check(r, m)) {
118
+           r->region.position = SPA_POINT(0,0);
119
+           r->region.size = data->position->video.size;
120
+           r++;
121
+       }
122
+       if (spa_meta_check(r, m))
123
+           r->region = SPA_REGION(0,0,0,0);
124
+   }
125
+   if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
126
+       data->crop = (sin(data->accumulator) + 1.0) * 32.0;
127
+       mc->region.position.x = (int32_t)data->crop;
128
+       mc->region.position.y = (int32_t)data->crop;
129
+       mc->region.size.width = data->position->video.size.width - (int32_t)(data->crop*2);
130
+       mc->region.size.height = data->position->video.size.height - (int32_t)(data->crop*2);
131
+   }
132
+   if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
133
+       struct spa_meta_bitmap *mb;
134
+       uint32_t *bitmap, color;
135
+
136
+       mcs->id = 1;
137
+       mcs->position.x = (int32_t)((sin(data->accumulator) + 1.0) * 160.0 + 80);
138
+       mcs->position.y = (int32_t)((cos(data->accumulator) + 1.0) * 100.0 + 50);
139
+       mcs->hotspot.x = 0;
140
+       mcs->hotspot.y = 0;
141
+       mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
142
+
143
+       mb = SPA_PTROFF(mcs, mcs->bitmap_offset, struct spa_meta_bitmap);
144
+       mb->format = SPA_VIDEO_FORMAT_ARGB;
145
+       mb->size.width = CURSOR_WIDTH;
146
+       mb->size.height = CURSOR_HEIGHT;
147
+       mb->stride = CURSOR_WIDTH * CURSOR_BPP;
148
+       mb->offset = sizeof(struct spa_meta_bitmap);
149
+
150
+       bitmap = SPA_PTROFF(mb, mb->offset, uint32_t);
151
+       color = (uint32_t)((cos(data->accumulator) + 1.0) * (1 << 23));
152
+       color |= 0xff000000;
153
+
154
+       draw_elipse(bitmap, mb->size.width, mb->size.height, color);
155
+   }
156
+
157
+   for (i = 0; i < data->position->video.size.height; i++) {
158
+       px = (struct pixel *)p;
159
+       for (j = 0; j < data->position->video.size.width; j++) {
160
+           pxj = (struct pixel){map_value(data->counter + j * i), map_value(data->counter + j * (i + 1)), map_value(data->counter + j * (i + 2)),  1.0f};
161
+       }
162
+       p += data->stride;
163
+       data->counter += 13;
164
+   }
165
+
166
+   data->accumulator += M_PI_M2 / 50.0;
167
+   if (data->accumulator >= M_PI_M2)
168
+       data->accumulator -= M_PI_M2;
169
+
170
+   buf->datas0.chunk->offset = 0;
171
+   buf->datas0.chunk->size = data->position->video.size.height * data->stride;
172
+   buf->datas0.chunk->stride = data->stride;
173
+
174
+   pw_stream_queue_buffer(data->stream, b);
175
+}
176
+
177
+static void on_timeout(void *userdata, uint64_t expirations)
178
+{
179
+   struct data *data = userdata;
180
+   pw_log_trace("timeout");
181
+   pw_stream_trigger_process(data->stream);
182
+}
183
+
184
+static void on_stream_state_changed(void *_data, enum pw_stream_state old, enum pw_stream_state state,
185
+                   const char *error)
186
+{
187
+   struct data *data = _data;
188
+
189
+   printf("stream state: \"%s\"\n", pw_stream_state_as_string(state));
190
+
191
+   switch (state) {
192
+   case PW_STREAM_STATE_ERROR:
193
+   case PW_STREAM_STATE_UNCONNECTED:
194
+       pw_main_loop_quit(data->loop);
195
+       break;
196
+
197
+   case PW_STREAM_STATE_PAUSED:
198
+       printf("node id: %d\n", pw_stream_get_node_id(data->stream));
199
+       pw_loop_update_timer(pw_main_loop_get_loop(data->loop),
200
+               data->timer, NULL, NULL, false);
201
+       break;
202
+   case PW_STREAM_STATE_STREAMING:
203
+   {
204
+       struct timespec timeout, interval;
205
+
206
+       timeout.tv_sec = 0;
207
+       timeout.tv_nsec = 1;
208
+       interval.tv_sec = 0;
209
+       interval.tv_nsec = 40 * SPA_NSEC_PER_MSEC;
210
+
211
+       pw_loop_update_timer(pw_main_loop_get_loop(data->loop),
212
+               data->timer, &timeout, &interval, false);
213
+       break;
214
+   }
215
+   default:
216
+       break;
217
+   }
218
+}
219
+
220
+static void
221
+on_stream_io_changed(void *_data, uint32_t id, void *area, uint32_t size)
222
+{
223
+   struct data *data = _data;
224
+
225
+   switch (id) {
226
+   case SPA_IO_Position:
227
+       data->position = area;
228
+       if (data->position)
229
+           pw_log_info("Position: %ux%u", data->position->video.size.width, data->position->video.size.height);
230
+       break;
231
+   }
232
+}
233
+
234
+static void
235
+on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param)
236
+{
237
+   struct data *data = _data;
238
+   struct pw_stream *stream = data->stream;
239
+   uint8_t params_buffer1024;
240
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
241
+   const struct spa_pod *params5;
242
+
243
+   if (param != NULL && id == SPA_PARAM_Tag) {
244
+       spa_debug_pod(0, NULL, param);
245
+       return;
246
+   }
247
+   if (param == NULL || id != SPA_PARAM_Format)
248
+       return;
249
+
250
+   spa_format_video_dsp_parse(param, &data->format);
251
+
252
+   data->stride = SPA_ROUND_UP_N(data->position->video.size.width * BPP, 4);
253
+
254
+   params0 = spa_pod_builder_add_object(&b,
255
+       SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
256
+       SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS),
257
+       SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
258
+       SPA_PARAM_BUFFERS_size,    SPA_POD_Int(data->stride * data->position->video.size.height),
259
+       SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(data->stride));
260
+
261
+   params1 = spa_pod_builder_add_object(&b,
262
+       SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
263
+       SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
264
+       SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
265
+
266
+   params2 = spa_pod_builder_add_object(&b,
267
+       SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
268
+       SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoDamage),
269
+       SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int(
270
+                   sizeof(struct spa_meta_region) * 16,
271
+                   sizeof(struct spa_meta_region) * 1,
272
+                   sizeof(struct spa_meta_region) * 16));
273
+   params3 = spa_pod_builder_add_object(&b,
274
+       SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
275
+       SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoCrop),
276
+       SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_region)));
277
+#define CURSOR_META_SIZE(w,h)  (sizeof(struct spa_meta_cursor) + \
278
+                sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP)
279
+   params4 = spa_pod_builder_add_object(&b,
280
+       SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
281
+       SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Cursor),
282
+       SPA_PARAM_META_size, SPA_POD_Int(
283
+           CURSOR_META_SIZE(CURSOR_WIDTH,CURSOR_HEIGHT)));
284
+
285
+   pw_stream_update_params(stream, params, 5);
286
+}
287
+
288
+static void
289
+on_trigger_done(void *_data)
290
+{
291
+   pw_log_trace("trigger done");
292
+}
293
+
294
+static const struct pw_stream_events stream_events = {
295
+   PW_VERSION_STREAM_EVENTS,
296
+   .process = on_process,
297
+   .state_changed = on_stream_state_changed,
298
+   .param_changed = on_stream_param_changed,
299
+   .io_changed = on_stream_io_changed,
300
+   .trigger_done = on_trigger_done,
301
+};
302
+
303
+static void do_quit(void *userdata, int signal_number)
304
+{
305
+   struct data *data = userdata;
306
+   pw_main_loop_quit(data->loop);
307
+}
308
+
309
+int main(int argc, char *argv)
310
+{
311
+   struct data data = { 0, };
312
+   const struct spa_pod *params2;
313
+   uint8_t buffer1024;
314
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
315
+
316
+   pw_init(&argc, &argv);
317
+
318
+   data.loop = pw_main_loop_new(NULL);
319
+
320
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data);
321
+   pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data);
322
+
323
+   data.context = pw_context_new(pw_main_loop_get_loop(data.loop), NULL, 0);
324
+
325
+   data.timer = pw_loop_add_timer(pw_main_loop_get_loop(data.loop), on_timeout, &data);
326
+
327
+   data.core = pw_context_connect(data.context, NULL, 0);
328
+   if (data.core == NULL) {
329
+       fprintf(stderr, "can't connect: %m\n");
330
+       data.res = -errno;
331
+       goto cleanup;
332
+   }
333
+
334
+   data.stream = pw_stream_new(data.core, "video-src",
335
+       pw_properties_new(
336
+           PW_KEY_MEDIA_CLASS, "Video/Source",
337
+           NULL));
338
+
339
+   params0 = spa_pod_builder_add_object(&b,
340
+       SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
341
+       SPA_FORMAT_mediaType,       SPA_POD_Id(SPA_MEDIA_TYPE_video),
342
+       SPA_FORMAT_mediaSubtype,    SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
343
+       SPA_FORMAT_VIDEO_format,    SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
344
+
345
+   {
346
+       struct spa_pod_frame f;
347
+       struct spa_dict_item items1;
348
+       /* send a tag, output tags travel downstream */
349
+       spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_OUTPUT);
350
+       items0 = SPA_DICT_ITEM_INIT("my-tag-key", "my-special-tag-value");
351
+       spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, 1));
352
+       params1 = spa_tag_build_end(&b, &f);
353
+   }
354
+
355
+   pw_stream_add_listener(data.stream,
356
+                  &data.stream_listener,
357
+                  &stream_events,
358
+                  &data);
359
+
360
+   pw_stream_connect(data.stream,
361
+             PW_DIRECTION_OUTPUT,
362
+             PW_ID_ANY,
363
+             PW_STREAM_FLAG_DRIVER |
364
+             PW_STREAM_FLAG_MAP_BUFFERS,
365
+             params, 2);
366
+
367
+   pw_main_loop_run(data.loop);
368
+
369
+cleanup:
370
+   pw_context_destroy(data.context);
371
+   pw_main_loop_destroy(data.loop);
372
+   pw_deinit();
373
+
374
+   return data.res;
375
+}
376
pipewire-1.0.1.tar.bz2/src/examples/video-play-pull.c -> pipewire-1.2.0.tar.gz/src/examples/video-play-pull.c Changed
16
 
1
@@ -197,10 +197,10 @@
2
            for (i = 0; i < data->size.height; i++) {
3
                struct pixel *p = (struct pixel *) src;
4
                for (j = 0; j < data->size.width; j++) {
5
-                   dstj * 4 + 0 = SPA_CLAMP(pj.r * 255.0f, 0, 255);
6
-                   dstj * 4 + 1 = SPA_CLAMP(pj.g * 255.0f, 0, 255);
7
-                   dstj * 4 + 2 = SPA_CLAMP(pj.b * 255.0f, 0, 255);
8
-                   dstj * 4 + 3 = SPA_CLAMP(pj.a * 255.0f, 0, 255);
9
+                   dstj * 4 + 0 = SPA_CLAMP((uint8_t)(pj.r * 255.0f), 0u, 255u);
10
+                   dstj * 4 + 1 = SPA_CLAMP((uint8_t)(pj.g * 255.0f), 0u, 255u);
11
+                   dstj * 4 + 2 = SPA_CLAMP((uint8_t)(pj.b * 255.0f), 0u, 255u);
12
+                   dstj * 4 + 3 = SPA_CLAMP((uint8_t)(pj.a * 255.0f), 0u, 255u);
13
                }
14
                src += sstride;
15
                dst += dstride;
16
pipewire-1.0.1.tar.bz2/src/examples/video-play.c -> pipewire-1.2.0.tar.gz/src/examples/video-play.c Changed
58
 
1
@@ -16,6 +16,7 @@
2
 #include <spa/param/video/format-utils.h>
3
 #include <spa/param/tag-utils.h>
4
 #include <spa/param/props.h>
5
+#include <spa/param/latency-utils.h>
6
 #include <spa/debug/format.h>
7
 #include <spa/debug/pod.h>
8
 
9
@@ -89,6 +90,7 @@
10
    int sstride, dstride, ostride;
11
    struct spa_meta_region *mc;
12
    struct spa_meta_cursor *mcs;
13
+   struct spa_meta_header *h;
14
    uint32_t i, j;
15
    uint8_t *src, *dst;
16
    bool render_cursor = false;
17
@@ -116,6 +118,12 @@
18
    if ((sdata = buf->datas0.data) == NULL)
19
        goto done;
20
 
21
+   if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
22
+       uint64_t now = pw_stream_get_nsec(stream);
23
+       pw_log_debug("now:%"PRIu64" pts:%"PRIu64" diff:%"PRIi64,
24
+               now, h->pts, now - h->pts);
25
+   }
26
+
27
    /* get the videocrop metadata if any */
28
    if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc))) &&
29
        spa_meta_region_is_valid(mc)) {
30
@@ -197,10 +205,10 @@
31
            for (i = 0; i < data->size.height; i++) {
32
                struct pixel *p = (struct pixel *) src;
33
                for (j = 0; j < data->size.width; j++) {
34
-                   dstj * 4 + 0 = SPA_CLAMP(pj.r * 255.0f, 0, 255);
35
-                   dstj * 4 + 1 = SPA_CLAMP(pj.g * 255.0f, 0, 255);
36
-                   dstj * 4 + 2 = SPA_CLAMP(pj.b * 255.0f, 0, 255);
37
-                   dstj * 4 + 3 = SPA_CLAMP(pj.a * 255.0f, 0, 255);
38
+                   dstj * 4 + 0 = SPA_CLAMP((uint8_t)(pj.r * 255.0f), 0u, 255u);
39
+                   dstj * 4 + 1 = SPA_CLAMP((uint8_t)(pj.g * 255.0f), 0u, 255u);
40
+                   dstj * 4 + 2 = SPA_CLAMP((uint8_t)(pj.b * 255.0f), 0u, 255u);
41
+                   dstj * 4 + 3 = SPA_CLAMP((uint8_t)(pj.a * 255.0f), 0u, 255u);
42
                }
43
                src += sstride;
44
                dst += dstride;
45
@@ -284,6 +292,12 @@
46
        spa_debug_pod(0, NULL, param);
47
        return;
48
    }
49
+   if (param != NULL && id == SPA_PARAM_Latency) {
50
+       struct spa_latency_info info;
51
+       if (spa_latency_parse(param, &info) >= 0)
52
+           fprintf(stderr, "got latency: %"PRIu64"\n", (info.min_ns + info.max_ns) / 2);
53
+       return;
54
+   }
55
    /* NULL means to clear the format */
56
    if (param == NULL || id != SPA_PARAM_Format)
57
        return;
58
pipewire-1.0.1.tar.bz2/src/examples/video-src-alloc.c -> pipewire-1.2.0.tar.gz/src/examples/video-src-alloc.c Changed
55
 
1
@@ -90,9 +90,7 @@
2
 
3
    if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
4
 #if 0
5
-       struct timespec now;
6
-       clock_gettime(CLOCK_MONOTONIC, &now);
7
-       h->pts = SPA_TIMESPEC_TO_NSEC(&now);
8
+       h->pts = pw_stream_get_nsec(data->stream));
9
 #else
10
        h->pts = -1;
11
 #endif
12
@@ -113,18 +111,18 @@
13
    }
14
    if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
15
        data->crop = (sin(data->accumulator) + 1.0) * 32.0;
16
-       mc->region.position.x = data->crop;
17
-       mc->region.position.y = data->crop;
18
-       mc->region.size.width = data->format.size.width - data->crop*2;
19
-       mc->region.size.height = data->format.size.height - data->crop*2;
20
+       mc->region.position.x = (int32_t)data->crop;
21
+       mc->region.position.y = (int32_t)data->crop;
22
+       mc->region.size.width = data->format.size.width - (int32_t)(data->crop*2);
23
+       mc->region.size.height = data->format.size.height - (int32_t)(data->crop*2);
24
    }
25
    if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
26
        struct spa_meta_bitmap *mb;
27
        uint32_t *bitmap, color;
28
 
29
        mcs->id = 1;
30
-       mcs->position.x = (sin(data->accumulator) + 1.0) * 160.0 + 80;
31
-       mcs->position.y = (cos(data->accumulator) + 1.0) * 100.0 + 50;
32
+       mcs->position.x = (int32_t)((sin(data->accumulator) + 1.0) * 160.0 + 80);
33
+       mcs->position.y = (int32_t)((cos(data->accumulator) + 1.0) * 100.0 + 50);
34
        mcs->hotspot.x = 0;
35
        mcs->hotspot.y = 0;
36
        mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
37
@@ -137,7 +135,7 @@
38
        mb->offset = sizeof(struct spa_meta_bitmap);
39
 
40
        bitmap = SPA_PTROFF(mb, mb->offset, uint32_t);
41
-       color = (cos(data->accumulator) + 1.0) * (1 << 23);
42
+       color = (uint32_t)((cos(data->accumulator) + 1.0) * (1 << 23));
43
        color |= 0xff000000;
44
 
45
        draw_elipse(bitmap, mb->size.width, mb->size.height, color);
46
@@ -225,7 +223,7 @@
47
 
48
    /* create the memfd on the buffer, set the type and flags */
49
    d0.type = SPA_DATA_MemFd;
50
-   d0.flags = SPA_DATA_FLAG_READWRITE;
51
+   d0.flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_MAPPABLE;
52
 #ifdef HAVE_MEMFD_CREATE
53
    d0.fd = memfd_create("video-src-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
54
 #else
55
pipewire-1.0.1.tar.bz2/src/examples/video-src-fixate.c -> pipewire-1.2.0.tar.gz/src/examples/video-src-fixate.c Changed
55
 
1
@@ -164,9 +164,7 @@
2
 
3
    if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
4
 #if 0
5
-       struct timespec now;
6
-       clock_gettime(CLOCK_MONOTONIC, &now);
7
-       h->pts = SPA_TIMESPEC_TO_NSEC(&now);
8
+       h->pts = pw_stream_get_nsec(data->stream);
9
 #else
10
        h->pts = -1;
11
 #endif
12
@@ -187,18 +185,18 @@
13
    }
14
    if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
15
        data->crop = (sin(data->accumulator) + 1.0) * 32.0;
16
-       mc->region.position.x = data->crop;
17
-       mc->region.position.y = data->crop;
18
-       mc->region.size.width = data->format.size.width - data->crop*2;
19
-       mc->region.size.height = data->format.size.height - data->crop*2;
20
+       mc->region.position.x = (int32_t)data->crop;
21
+       mc->region.position.y = (int32_t)data->crop;
22
+       mc->region.size.width = data->format.size.width - (int32_t)(data->crop*2);
23
+       mc->region.size.height = data->format.size.height - (int32_t)(data->crop*2);
24
    }
25
    if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
26
        struct spa_meta_bitmap *mb;
27
        uint32_t *bitmap, color;
28
 
29
        mcs->id = 1;
30
-       mcs->position.x = (sin(data->accumulator) + 1.0) * 160.0 + 80;
31
-       mcs->position.y = (cos(data->accumulator) + 1.0) * 100.0 + 50;
32
+       mcs->position.x = (int32_t)((sin(data->accumulator) + 1.0) * 160.0 + 80);
33
+       mcs->position.y = (int32_t)((cos(data->accumulator) + 1.0) * 100.0 + 50);
34
        mcs->hotspot.x = 0;
35
        mcs->hotspot.y = 0;
36
        mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
37
@@ -211,7 +209,7 @@
38
        mb->offset = sizeof(struct spa_meta_bitmap);
39
 
40
        bitmap = SPA_PTROFF(mb, mb->offset, uint32_t);
41
-       color = (cos(data->accumulator) + 1.0) * (1 << 23);
42
+       color = (uint32_t)((cos(data->accumulator) + 1.0) * (1 << 23));
43
        color |= 0xff000000;
44
 
45
        draw_elipse(bitmap, mb->size.width, mb->size.height, color);
46
@@ -310,7 +308,7 @@
47
    printf("use memfd\n");
48
    /* create the memfd on the buffer, set the type and flags */
49
    d0.type = SPA_DATA_MemFd;
50
-   d0.flags = SPA_DATA_FLAG_READWRITE;
51
+   d0.flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_MAPPABLE;
52
 #ifdef HAVE_MEMFD_CREATE
53
    d0.fd = memfd_create("video-src-fixate-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
54
 #else
55
pipewire-1.0.1.tar.bz2/src/examples/video-src-reneg.c -> pipewire-1.2.0.tar.gz/src/examples/video-src-reneg.c Changed
55
 
1
@@ -94,9 +94,7 @@
2
 
3
    if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
4
 #if 0
5
-       struct timespec now;
6
-       clock_gettime(CLOCK_MONOTONIC, &now);
7
-       h->pts = SPA_TIMESPEC_TO_NSEC(&now);
8
+       h->pts = pw_stream_get_nsec(data->stream);
9
 #else
10
        h->pts = -1;
11
 #endif
12
@@ -117,18 +115,18 @@
13
    }
14
    if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
15
        data->crop = (sin(data->accumulator) + 1.0) * 32.0;
16
-       mc->region.position.x = data->crop;
17
-       mc->region.position.y = data->crop;
18
-       mc->region.size.width = data->format.size.width - data->crop*2;
19
-       mc->region.size.height = data->format.size.height - data->crop*2;
20
+       mc->region.position.x = (int32_t)data->crop;
21
+       mc->region.position.y = (int32_t)data->crop;
22
+       mc->region.size.width = data->format.size.width - (int32_t)(data->crop*2);
23
+       mc->region.size.height = data->format.size.height - (int32_t)(data->crop*2);
24
    }
25
    if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
26
        struct spa_meta_bitmap *mb;
27
        uint32_t *bitmap, color;
28
 
29
        mcs->id = 1;
30
-       mcs->position.x = (sin(data->accumulator) + 1.0) * 160.0 + 80;
31
-       mcs->position.y = (cos(data->accumulator) + 1.0) * 100.0 + 50;
32
+       mcs->position.x = (int32_t)((sin(data->accumulator) + 1.0) * 160.0 + 80);
33
+       mcs->position.y = (int32_t)((cos(data->accumulator) + 1.0) * 100.0 + 50);
34
        mcs->hotspot.x = 0;
35
        mcs->hotspot.y = 0;
36
        mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
37
@@ -141,7 +139,7 @@
38
        mb->offset = sizeof(struct spa_meta_bitmap);
39
 
40
        bitmap = SPA_PTROFF(mb, mb->offset, uint32_t);
41
-       color = (cos(data->accumulator) + 1.0) * (1 << 23);
42
+       color = (uint32_t)((cos(data->accumulator) + 1.0) * (1 << 23));
43
        color |= 0xff000000;
44
 
45
        draw_elipse(bitmap, mb->size.width, mb->size.height, color);
46
@@ -239,7 +237,7 @@
47
 
48
    /* create the memfd on the buffer, set the type and flags */
49
    d0.type = SPA_DATA_MemFd;
50
-   d0.flags = SPA_DATA_FLAG_READWRITE;
51
+   d0.flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_MAPPABLE;
52
 #ifdef HAVE_MEMFD_CREATE
53
    d0.fd = memfd_create("video-src-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
54
 #else
55
pipewire-1.0.1.tar.bz2/src/examples/video-src.c -> pipewire-1.2.0.tar.gz/src/examples/video-src.c Changed
46
 
1
@@ -90,9 +90,7 @@
2
 
3
    if ((h = spa_buffer_find_meta_data(buf, SPA_META_Header, sizeof(*h)))) {
4
 #if 0
5
-       struct timespec now;
6
-       clock_gettime(CLOCK_MONOTONIC, &now);
7
-       h->pts = SPA_TIMESPEC_TO_NSEC(&now);
8
+       h->pts = pw_stream_get_nsec(data->stream);
9
 #else
10
        h->pts = -1;
11
 #endif
12
@@ -113,18 +111,18 @@
13
    }
14
    if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
15
        data->crop = (sin(data->accumulator) + 1.0) * 32.0;
16
-       mc->region.position.x = data->crop;
17
-       mc->region.position.y = data->crop;
18
-       mc->region.size.width = data->format.size.width - data->crop*2;
19
-       mc->region.size.height = data->format.size.height - data->crop*2;
20
+       mc->region.position.x = (int32_t)data->crop;
21
+       mc->region.position.y = (int32_t)data->crop;
22
+       mc->region.size.width = data->format.size.width - (int32_t)(data->crop*2);
23
+       mc->region.size.height = data->format.size.height - (int32_t)(data->crop*2);
24
    }
25
    if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
26
        struct spa_meta_bitmap *mb;
27
        uint32_t *bitmap, color;
28
 
29
        mcs->id = 1;
30
-       mcs->position.x = (sin(data->accumulator) + 1.0) * 160.0 + 80;
31
-       mcs->position.y = (cos(data->accumulator) + 1.0) * 100.0 + 50;
32
+       mcs->position.x = (int32_t)((sin(data->accumulator) + 1.0) * 160.0 + 80);
33
+       mcs->position.y = (int32_t)((cos(data->accumulator) + 1.0) * 100.0 + 50);
34
        mcs->hotspot.x = 0;
35
        mcs->hotspot.y = 0;
36
        mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
37
@@ -137,7 +135,7 @@
38
        mb->offset = sizeof(struct spa_meta_bitmap);
39
 
40
        bitmap = SPA_PTROFF(mb, mb->offset, uint32_t);
41
-       color = (cos(data->accumulator) + 1.0) * (1 << 23);
42
+       color = (uint32_t)((cos(data->accumulator) + 1.0) * (1 << 23));
43
        color |= 0xff000000;
44
 
45
        draw_elipse(bitmap, mb->size.width, mb->size.height, color);
46
pipewire-1.0.1.tar.bz2/src/gst/gstpipewireclock.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewireclock.c Changed
66
 
1
@@ -14,12 +14,12 @@
2
 G_DEFINE_TYPE (GstPipeWireClock, gst_pipewire_clock, GST_TYPE_SYSTEM_CLOCK);
3
 
4
 GstClock *
5
-gst_pipewire_clock_new (struct pw_stream *stream, GstClockTime last_time)
6
+gst_pipewire_clock_new (GstPipeWireStream *stream, GstClockTime last_time)
7
 {
8
   GstPipeWireClock *clock;
9
 
10
   clock = g_object_new (GST_TYPE_PIPEWIRE_CLOCK, NULL);
11
-  clock->stream = stream;
12
+  g_weak_ref_set (&clock->stream, stream);
13
   clock->last_time = last_time;
14
   clock->time_offset = last_time;
15
 
16
@@ -30,28 +30,31 @@
17
 gst_pipewire_clock_get_internal_time (GstClock * clock)
18
 {
19
   GstPipeWireClock *pclock = (GstPipeWireClock *) clock;
20
+  g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&pclock->stream);
21
   GstClockTime result;
22
-  struct timespec ts;
23
+  uint64_t now;
24
 
25
-  clock_gettime(CLOCK_MONOTONIC, &ts);
26
+  if (G_UNLIKELY (!s))
27
+    return pclock->last_time;
28
+
29
+  now = pw_stream_get_nsec(s->pwstream);
30
 #if 0
31
   struct pw_time t;
32
-  if (pclock->stream == NULL ||
33
-      pw_stream_get_time (pclock->stream, &t) < 0 ||
34
+  if (s->pwstream == NULL ||
35
+      pw_stream_get_time_n (s->pwstream, &t, sizeof(t)) < 0 ||
36
       t.rate.denom == 0)
37
     return pclock->last_time;
38
 
39
   result = gst_util_uint64_scale_int (t.ticks, GST_SECOND * t.rate.num, t.rate.denom);
40
-  result += SPA_TIMESPEC_TO_NSEC(&ts) - t.now;
41
+  result += now - t.now;
42
 
43
   result += pclock->time_offset;
44
   pclock->last_time = result;
45
 
46
-  GST_DEBUG ("%"PRId64", %d/%d %"PRId64" %"PRId64,
47
-                t.ticks, t.rate.num, t.rate.denom, t.now, result);
48
+  GST_DEBUG ("%"PRId64", %d/%d %"PRId64" %"PRId64" %"PRId64,
49
+                t.ticks, t.rate.num, t.rate.denom, t.now, result, now);
50
 #else
51
-  result = SPA_TIMESPEC_TO_NSEC(&ts);
52
-  result += pclock->time_offset;
53
+  result = now + pclock->time_offset;
54
   pclock->last_time = result;
55
 #endif
56
 
57
@@ -65,6 +68,8 @@
58
 
59
   GST_DEBUG_OBJECT (clock, "finalize");
60
 
61
+  g_weak_ref_set (&clock->stream, NULL);
62
+
63
   G_OBJECT_CLASS (gst_pipewire_clock_parent_class)->finalize (object);
64
 }
65
 
66
pipewire-1.0.1.tar.bz2/src/gst/gstpipewireclock.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewireclock.h Changed
53
 
1
@@ -5,43 +5,27 @@
2
 #ifndef __GST_PIPEWIRE_CLOCK_H__
3
 #define __GST_PIPEWIRE_CLOCK_H__
4
 
5
-#include <gst/gst.h>
6
+#include "config.h"
7
+#include "gstpipewirestream.h"
8
 
9
+#include <gst/gst.h>
10
 #include <pipewire/pipewire.h>
11
 
12
 G_BEGIN_DECLS
13
 
14
-#define GST_TYPE_PIPEWIRE_CLOCK \
15
-  (gst_pipewire_clock_get_type())
16
-#define GST_PIPEWIRE_CLOCK(obj) \
17
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIPEWIRE_CLOCK,GstPipeWireClock))
18
-#define GST_PIPEWIRE_CLOCK_CLASS(klass) \
19
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIPEWIRE_CLOCK,GstPipeWireClockClass))
20
-#define GST_IS_PIPEWIRE_CLOCK(obj) \
21
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIPEWIRE_CLOCK))
22
-#define GST_IS_PIPEWIRE_CLOCK_CLASS(klass) \
23
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIPEWIRE_CLOCK))
24
-#define GST_PIPEWIRE_CLOCK_GET_CLASS(klass) \
25
-  (G_TYPE_INSTANCE_GET_CLASS ((klass), GST_TYPE_PIPEWIRE_CLOCK, GstPipeWireClockClass))
26
-
27
-typedef struct _GstPipeWireClock GstPipeWireClock;
28
-typedef struct _GstPipeWireClockClass GstPipeWireClockClass;
29
+#define GST_TYPE_PIPEWIRE_CLOCK (gst_pipewire_clock_get_type())
30
+G_DECLARE_FINAL_TYPE (GstPipeWireClock, gst_pipewire_clock, GST, PIPEWIRE_CLOCK, GstSystemClock)
31
 
32
 struct _GstPipeWireClock {
33
   GstSystemClock parent;
34
 
35
-  struct pw_stream *stream;
36
+  GWeakRef stream;
37
+
38
   GstClockTime last_time;
39
   GstClockTimeDiff time_offset;
40
 };
41
 
42
-struct _GstPipeWireClockClass {
43
-  GstSystemClockClass parent_class;
44
-};
45
-
46
-GType gst_pipewire_clock_get_type (void);
47
-
48
-GstClock *      gst_pipewire_clock_new           (struct pw_stream *stream,
49
+GstClock *      gst_pipewire_clock_new           (GstPipeWireStream *stream,
50
                                                   GstClockTime last_time);
51
 void            gst_pipewire_clock_reset         (GstPipeWireClock *clock,
52
                                                   GstClockTime time);
53
pipewire-1.0.1.tar.bz2/src/gst/gstpipewirecore.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewirecore.c Changed
33
 
1
@@ -51,7 +51,11 @@
2
   core->refcount = 1;
3
   core->fd = fd;
4
   core->loop = pw_thread_loop_new ("pipewire-main-loop", NULL);
5
+  if (core->loop == NULL)
6
+    goto loop_failed;
7
   core->context = pw_context_new (pw_thread_loop_get_loop(core->loop), NULL, 0);
8
+  if (core->context == NULL)
9
+    goto context_failed;
10
   core->last_seq = -1;
11
   core->last_error = 0;
12
   GST_DEBUG ("loop %p context %p", core->loop, core->context);
13
@@ -78,6 +82,19 @@
14
 
15
   return core;
16
 
17
+loop_failed:
18
+  {
19
+    GST_ERROR ("error creating threadloop");
20
+    g_free (core);
21
+    return NULL;
22
+  }
23
+context_failed:
24
+  {
25
+    GST_ERROR ("error creating context");
26
+    pw_thread_loop_destroy (core->loop);
27
+    g_free (core);
28
+    return NULL;
29
+  }
30
 mainloop_failed:
31
   {
32
     GST_ERROR ("error starting mainloop");
33
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiredeviceprovider.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiredeviceprovider.c Changed
13
 
1
@@ -486,7 +486,10 @@
2
 {
3
   struct port_data *pd = data;
4
   pw_log_debug("destroy %p", pd);
5
-  spa_list_remove(&pd->link);
6
+  if (pd->node_data != NULL) {
7
+    spa_list_remove(&pd->link);
8
+    pd->node_data = NULL;
9
+  }
10
 }
11
 
12
 static const struct pw_proxy_events proxy_port_events = {
13
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiredeviceprovider.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiredeviceprovider.h Changed
60
 
1
@@ -14,16 +14,9 @@
2
 
3
 G_BEGIN_DECLS
4
 
5
-typedef struct _GstPipeWireDevice GstPipeWireDevice;
6
-typedef struct _GstPipeWireDeviceClass GstPipeWireDeviceClass;
7
-
8
-#define GST_TYPE_PIPEWIRE_DEVICE                 (gst_pipewire_device_get_type())
9
-#define GST_IS_PIPEWIRE_DEVICE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PIPEWIRE_DEVICE))
10
-#define GST_IS_PIPEWIRE_DEVICE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PIPEWIRE_DEVICE))
11
-#define GST_PIPEWIRE_DEVICE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PIPEWIRE_DEVICE, GstPipeWireDeviceClass))
12
-#define GST_PIPEWIRE_DEVICE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PIPEWIRE_DEVICE, GstPipeWireDevice))
13
-#define GST_PIPEWIRE_DEVICE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstPipeWireDeviceClass))
14
-#define GST_PIPEWIRE_DEVICE_CAST(obj)            ((GstPipeWireDevice *)(obj))
15
+#define GST_TYPE_PIPEWIRE_DEVICE (gst_pipewire_device_get_type())
16
+#define GST_PIPEWIRE_DEVICE_CAST(obj) ((GstPipeWireDevice *)(obj))
17
+G_DECLARE_FINAL_TYPE (GstPipeWireDevice, gst_pipewire_device, GST, PIPEWIRE_DEVICE, GstDevice)
18
 
19
 typedef enum {
20
   GST_PIPEWIRE_DEVICE_TYPE_UNKNOWN,
21
@@ -42,22 +35,9 @@
22
   int                 priority;
23
 };
24
 
25
-struct _GstPipeWireDeviceClass {
26
-  GstDeviceClass    parent_class;
27
-};
28
-
29
-GType        gst_pipewire_device_get_type (void);
30
-
31
-typedef struct _GstPipeWireDeviceProvider GstPipeWireDeviceProvider;
32
-typedef struct _GstPipeWireDeviceProviderClass GstPipeWireDeviceProviderClass;
33
-
34
-#define GST_TYPE_PIPEWIRE_DEVICE_PROVIDER                 (gst_pipewire_device_provider_get_type())
35
-#define GST_IS_PIPEWIRE_DEVICE_PROVIDER(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PIPEWIRE_DEVICE_PROVIDER))
36
-#define GST_IS_PIPEWIRE_DEVICE_PROVIDER_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PIPEWIRE_DEVICE_PROVIDER))
37
-#define GST_PIPEWIRE_DEVICE_PROVIDER_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PIPEWIRE_DEVICE_PROVIDER, GstPipeWireDeviceProviderClass))
38
-#define GST_PIPEWIRE_DEVICE_PROVIDER(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PIPEWIRE_DEVICE_PROVIDER, GstPipeWireDeviceProvider))
39
-#define GST_PIPEWIRE_DEVICE_PROVIDER_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstPipeWireDeviceProviderClass))
40
-#define GST_PIPEWIRE_DEVICE_PROVIDER_CAST(obj)            ((GstPipeWireDeviceProvider *)(obj))
41
+#define GST_TYPE_PIPEWIRE_DEVICE_PROVIDER (gst_pipewire_device_provider_get_type())
42
+#define GST_PIPEWIRE_DEVICE_PROVIDER_CAST(obj) ((GstPipeWireDeviceProvider *)(obj))
43
+G_DECLARE_FINAL_TYPE (GstPipeWireDeviceProvider, gst_pipewire_device_provider, GST, PIPEWIRE_DEVICE_PROVIDER, GstDeviceProvider)
44
 
45
 struct _GstPipeWireDeviceProvider {
46
   GstDeviceProvider         parent;
47
@@ -78,12 +58,6 @@
48
   GList *devices;
49
 };
50
 
51
-struct _GstPipeWireDeviceProviderClass {
52
-  GstDeviceProviderClass    parent_class;
53
-};
54
-
55
-GType        gst_pipewire_device_provider_get_type (void);
56
-
57
 G_END_DECLS
58
 
59
 #endif /* __GST_PIPEWIRE_DEVICE_PROVIDER_H__ */
60
pipewire-1.0.1.tar.bz2/src/gst/gstpipewireformat.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewireformat.c Changed
857
 
1
@@ -2,6 +2,8 @@
2
 /* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
3
 /* SPDX-License-Identifier: MIT */
4
 
5
+#include "config.h"
6
+
7
 #include <stdio.h>
8
 
9
 #include <gst/gst.h>
10
@@ -13,10 +15,14 @@
11
 #include <spa/utils/type.h>
12
 #include <spa/param/video/format-utils.h>
13
 #include <spa/param/audio/format-utils.h>
14
-#include <spa/pod/builder.h>
15
+#include <spa/pod/dynamic.h>
16
 
17
 #include "gstpipewireformat.h"
18
 
19
+#ifndef DRM_FORMAT_INVALID
20
+#define DRM_FORMAT_INVALID 0
21
+#endif
22
+
23
 #ifndef DRM_FORMAT_MOD_INVALID
24
 #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
25
 #endif
26
@@ -169,9 +175,7 @@
27
 };
28
 
29
 typedef struct {
30
-  struct spa_pod_builder b;
31
   const struct media_type *type;
32
-  uint32_t id;
33
   const GstCapsFeatures *cf;
34
   const GstStructure *cs;
35
   GPtrArray *array;
36
@@ -358,48 +362,28 @@
37
   return SPA_CHOICE_Range;
38
 }
39
 
40
-static gboolean
41
-handle_video_fields (ConvertData *d)
42
+static void
43
+add_limits (struct spa_pod_dynamic_builder *b, ConvertData *d)
44
 {
45
-  const GValue *value, *value2;
46
-  int i;
47
   struct spa_pod_choice *choice;
48
   struct spa_pod_frame f;
49
+  const GValue *value, *value2;
50
+  int i;
51
 
52
-  value = gst_structure_get_value (d->cs, "format");
53
-  if (value) {
54
-    const char *v;
55
-    int idx;
56
-    for (i = 0; (v = get_nth_string (value, i)); i++) {
57
-      if (i == 0) {
58
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_format, 0);
59
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
60
-      }
61
-
62
-      idx = gst_video_format_from_string (v);
63
-      if (idx != GST_VIDEO_FORMAT_UNKNOWN && idx < (int)SPA_N_ELEMENTS (video_format_map))
64
-        spa_pod_builder_id (&d->b, video_format_mapidx);
65
-    }
66
-    if (i > 0) {
67
-      choice = spa_pod_builder_pop(&d->b, &f);
68
-      if (i == 1)
69
-        choice->body.type = SPA_CHOICE_None;
70
-    }
71
-  }
72
   value = gst_structure_get_value (d->cs, "width");
73
   value2 = gst_structure_get_value (d->cs, "height");
74
   if (value && value2) {
75
     struct spa_rectangle v;
76
     for (i = 0; get_nth_rectangle (value, value2, i, &v); i++) {
77
       if (i == 0) {
78
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_size, 0);
79
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type2 (value, value2), 0);
80
+        spa_pod_builder_prop (&b->b, SPA_FORMAT_VIDEO_size, 0);
81
+        spa_pod_builder_push_choice(&b->b, &f, get_range_type2 (value, value2), 0);
82
       }
83
 
84
-      spa_pod_builder_rectangle (&d->b, v.width, v.height);
85
+      spa_pod_builder_rectangle (&b->b, v.width, v.height);
86
     }
87
     if (i > 0) {
88
-      choice = spa_pod_builder_pop(&d->b, &f);
89
+      choice = spa_pod_builder_pop(&b->b, &f);
90
       if (i == 1)
91
         choice->body.type = SPA_CHOICE_None;
92
     }
93
@@ -410,14 +394,14 @@
94
     struct spa_fraction v;
95
     for (i = 0; get_nth_fraction (value, i, &v); i++) {
96
       if (i == 0) {
97
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_framerate, 0);
98
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
99
+        spa_pod_builder_prop (&b->b, SPA_FORMAT_VIDEO_framerate, 0);
100
+        spa_pod_builder_push_choice(&b->b, &f, get_range_type (value), 0);
101
       }
102
 
103
-      spa_pod_builder_fraction (&d->b, v.num, v.denom);
104
+      spa_pod_builder_fraction (&b->b, v.num, v.denom);
105
     }
106
     if (i > 0) {
107
-      choice = spa_pod_builder_pop(&d->b, &f);
108
+      choice = spa_pod_builder_pop(&b->b, &f);
109
       if (i == 1)
110
         choice->body.type = SPA_CHOICE_None;
111
     }
112
@@ -428,19 +412,178 @@
113
     struct spa_fraction v;
114
     for (i = 0; get_nth_fraction (value, i, &v); i++) {
115
       if (i == 0) {
116
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_maxFramerate, 0);
117
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
118
+        spa_pod_builder_prop (&b->b, SPA_FORMAT_VIDEO_maxFramerate, 0);
119
+        spa_pod_builder_push_choice(&b->b, &f, get_range_type (value), 0);
120
       }
121
 
122
-      spa_pod_builder_fraction (&d->b, v.num, v.denom);
123
+      spa_pod_builder_fraction (&b->b, v.num, v.denom);
124
     }
125
     if (i > 0) {
126
-      choice = spa_pod_builder_pop(&d->b, &f);
127
+      choice = spa_pod_builder_pop(&b->b, &f);
128
       if (i == 1)
129
         choice->body.type = SPA_CHOICE_None;
130
     }
131
   }
132
-  return TRUE;
133
+}
134
+
135
+static void
136
+add_video_format (gpointer format_ptr,
137
+                  gpointer modifiers_ptr,
138
+                  gpointer user_data)
139
+{
140
+  uint32_t format = GPOINTER_TO_UINT (format_ptr);
141
+  GHashTable *modifiers = modifiers_ptr;
142
+  ConvertData *d = user_data;
143
+  struct spa_pod_dynamic_builder b;
144
+  struct spa_pod_frame f;
145
+  int n_mods;
146
+
147
+  spa_pod_dynamic_builder_init (&b, NULL, 0, 1024);
148
+
149
+  spa_pod_builder_push_object (&b.b, &f, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
150
+
151
+  spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaType, 0);
152
+  spa_pod_builder_id(&b.b, d->type->media_type);
153
+
154
+  spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaSubtype, 0);
155
+  spa_pod_builder_id(&b.b, d->type->media_subtype);
156
+
157
+  spa_pod_builder_prop (&b.b, SPA_FORMAT_VIDEO_format, 0);
158
+  spa_pod_builder_id (&b.b, format);
159
+
160
+  n_mods = g_hash_table_size (modifiers);
161
+  if (n_mods > 0) {
162
+    struct spa_pod_frame f2;
163
+    GHashTableIter iter;
164
+    gpointer key, value;
165
+    uint32_t flags, choice_type;
166
+
167
+    flags = SPA_POD_PROP_FLAG_MANDATORY;
168
+    if (n_mods > 1) {
169
+      choice_type = SPA_CHOICE_Enum;
170
+      flags |= SPA_POD_PROP_FLAG_DONT_FIXATE;
171
+    } else {
172
+      choice_type = SPA_CHOICE_None;
173
+    }
174
+
175
+    spa_pod_builder_prop (&b.b, SPA_FORMAT_VIDEO_modifier, flags);
176
+    spa_pod_builder_push_choice (&b.b, &f2, choice_type, 0);
177
+
178
+    g_hash_table_iter_init (&iter, modifiers);
179
+    g_hash_table_iter_next (&iter, &key, &value);
180
+    spa_pod_builder_long (&b.b, (uint64_t) key);
181
+
182
+    if (n_mods > 1) {
183
+      do {
184
+        spa_pod_builder_long (&b.b, (uint64_t) key);
185
+      } while (g_hash_table_iter_next (&iter, &key, &value));
186
+    }
187
+
188
+    spa_pod_builder_pop (&b.b, &f2);
189
+  }
190
+
191
+  add_limits (&b, d);
192
+
193
+  g_ptr_array_add (d->array, spa_pod_builder_pop (&b.b, &f));
194
+}
195
+
196
+static void
197
+handle_video_fields (ConvertData *d)
198
+{
199
+  g_autoptr (GHashTable) formats = NULL;
200
+  const GValue *value;
201
+  gboolean dmabuf_caps;
202
+  int i;
203
+
204
+  formats = g_hash_table_new_full (NULL, NULL, NULL,
205
+                                   (GDestroyNotify) g_hash_table_unref);
206
+  dmabuf_caps = (d->cf &&
207
+                 gst_caps_features_contains (d->cf,
208
+                                             GST_CAPS_FEATURE_MEMORY_DMABUF));
209
+
210
+  value = gst_structure_get_value (d->cs, "format");
211
+  if (value) {
212
+    const char *v;
213
+
214
+    for (i = 0; (v = get_nth_string (value, i)); i++) {
215
+      int idx;
216
+
217
+      idx = gst_video_format_from_string (v);
218
+#ifdef HAVE_GSTREAMER_DMA_DRM
219
+      if (dmabuf_caps && idx == GST_VIDEO_FORMAT_DMA_DRM) {
220
+        const GValue *value2;
221
+
222
+        value2 = gst_structure_get_value (d->cs, "drm-format");
223
+        if (value2) {
224
+          const char *v2;
225
+          int j;
226
+
227
+          for (j = 0; (v2 = get_nth_string (value2, j)); j++) {
228
+            uint32_t fourcc;
229
+            uint64_t mod;
230
+            int idx2;
231
+
232
+            fourcc = gst_video_dma_drm_fourcc_from_string (v2, &mod);
233
+            idx2 = gst_video_dma_drm_fourcc_to_format (fourcc);
234
+
235
+            if (idx2 != GST_VIDEO_FORMAT_UNKNOWN &&
236
+                idx2 < (int)SPA_N_ELEMENTS (video_format_map)) {
237
+              GHashTable *modifiers =
238
+                  g_hash_table_lookup (formats,
239
+                                       GINT_TO_POINTER (video_format_mapidx2));
240
+              if (!modifiers) {
241
+                modifiers = g_hash_table_new (NULL, NULL);
242
+                g_hash_table_insert (formats,
243
+                                     GINT_TO_POINTER (video_format_mapidx2),
244
+                                     modifiers);
245
+              }
246
+
247
+              g_hash_table_add (modifiers, GINT_TO_POINTER (mod));
248
+            }
249
+          }
250
+        }
251
+      } else
252
+#endif
253
+      if (idx != GST_VIDEO_FORMAT_UNKNOWN &&
254
+          idx < (int)SPA_N_ELEMENTS (video_format_map)) {
255
+          GHashTable *modifiers =
256
+              g_hash_table_lookup (formats,
257
+                                   GINT_TO_POINTER (video_format_mapidx));
258
+          if (!modifiers) {
259
+            modifiers = g_hash_table_new (NULL, NULL);
260
+            g_hash_table_insert (formats,
261
+                                 GINT_TO_POINTER (video_format_mapidx),
262
+                                 modifiers);
263
+          }
264
+
265
+          if (dmabuf_caps) {
266
+            g_hash_table_add (modifiers, GINT_TO_POINTER (DRM_FORMAT_MOD_LINEAR));
267
+            g_hash_table_add (modifiers, GINT_TO_POINTER (DRM_FORMAT_MOD_INVALID));
268
+          }
269
+      }
270
+    }
271
+  }
272
+
273
+  if (g_hash_table_size (formats) > 0) {
274
+    g_hash_table_foreach (formats, add_video_format, d);
275
+  } else if (!dmabuf_caps) {
276
+    struct spa_pod_dynamic_builder b;
277
+    struct spa_pod_frame f;
278
+
279
+    spa_pod_dynamic_builder_init (&b, NULL, 0, 1024);
280
+
281
+    spa_pod_builder_push_object (&b.b, &f, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
282
+
283
+    spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaType, 0);
284
+    spa_pod_builder_id(&b.b, d->type->media_type);
285
+
286
+    spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaSubtype, 0);
287
+    spa_pod_builder_id(&b.b, d->type->media_subtype);
288
+
289
+    add_limits (&b, d);
290
+
291
+    g_ptr_array_add (d->array, spa_pod_builder_pop (&b.b, &f));
292
+  }
293
 }
294
 
295
 static void
296
@@ -481,45 +624,57 @@
297
         SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, channels, position), 0);
298
 }
299
 
300
-static gboolean
301
+static void
302
 handle_audio_fields (ConvertData *d)
303
 {
304
   const GValue *value;
305
+  struct spa_pod_dynamic_builder b;
306
   struct spa_pod_choice *choice;
307
-  struct spa_pod_frame f;
308
+  struct spa_pod_frame f, f0;
309
   int i = 0;
310
 
311
+  spa_pod_dynamic_builder_init (&b, NULL, 0, 1024);
312
+
313
+  spa_pod_builder_push_object (&b.b, &f0, SPA_TYPE_OBJECT_Format,
314
+                               SPA_PARAM_EnumFormat);
315
+
316
+  spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaType, 0);
317
+  spa_pod_builder_id(&b.b, d->type->media_type);
318
+
319
+  spa_pod_builder_prop (&b.b, SPA_FORMAT_mediaSubtype, 0);
320
+  spa_pod_builder_id(&b.b, d->type->media_subtype);
321
+
322
   value = gst_structure_get_value (d->cs, "format");
323
   if (value) {
324
     const char *v;
325
     int idx;
326
     for (i = 0; (v = get_nth_string (value, i)); i++) {
327
       if (i == 0) {
328
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
329
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
330
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_format, 0);
331
+        spa_pod_builder_push_choice(&b.b, &f, get_range_type (value), 0);
332
       }
333
 
334
       idx = gst_audio_format_from_string (v);
335
       if (idx < (int)SPA_N_ELEMENTS (audio_format_map))
336
-        spa_pod_builder_id (&d->b, audio_format_mapidx);
337
+        spa_pod_builder_id (&b.b, audio_format_mapidx);
338
     }
339
     if (i > 0) {
340
-      choice = spa_pod_builder_pop(&d->b, &f);
341
+      choice = spa_pod_builder_pop(&b.b, &f);
342
       if (i == 1)
343
         choice->body.type = SPA_CHOICE_None;
344
     }
345
   } else if (strcmp(d->type->name, "audio/x-mulaw") == 0) {
346
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
347
-        spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ULAW);
348
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_format, 0);
349
+        spa_pod_builder_id (&b.b, SPA_AUDIO_FORMAT_ULAW);
350
   } else if (strcmp(d->type->name, "audio/x-alaw") == 0) {
351
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
352
-        spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ALAW);
353
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_format, 0);
354
+        spa_pod_builder_id (&b.b, SPA_AUDIO_FORMAT_ALAW);
355
   } else if (strcmp(d->type->name, "audio/mpeg") == 0) {
356
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
357
-        spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ENCODED);
358
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_format, 0);
359
+        spa_pod_builder_id (&b.b, SPA_AUDIO_FORMAT_ENCODED);
360
   } else if (strcmp(d->type->name, "audio/x-flac") == 0) {
361
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_format, 0);
362
-        spa_pod_builder_id (&d->b, SPA_AUDIO_FORMAT_ENCODED);
363
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_format, 0);
364
+        spa_pod_builder_id (&b.b, SPA_AUDIO_FORMAT_ENCODED);
365
   }
366
 
367
 #if 0
368
@@ -537,14 +692,14 @@
369
         break;
370
 
371
       if (i == 0) {
372
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_layout, 0);
373
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
374
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_layout, 0);
375
+        spa_pod_builder_push_choice(&b.b, &f, get_range_type (value), 0);
376
       }
377
 
378
-      spa_pod_builder_id (&d->b, layout);
379
+      spa_pod_builder_id (&b.b, layout);
380
     }
381
     if (i > 0) {
382
-      choice = spa_pod_builder_pop(&d->b, &f);
383
+      choice = spa_pod_builder_pop(&b.b, &f);
384
       if (i == 1)
385
         choice->body.type = SPA_CHOICE_None;
386
     }
387
@@ -555,14 +710,14 @@
388
     int v;
389
     for (i = 0; get_nth_int (value, i, &v); i++) {
390
       if (i == 0) {
391
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_rate, 0);
392
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
393
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_rate, 0);
394
+        spa_pod_builder_push_choice(&b.b, &f, get_range_type (value), 0);
395
       }
396
 
397
-      spa_pod_builder_int (&d->b, v);
398
+      spa_pod_builder_int (&b.b, v);
399
     }
400
     if (i > 0) {
401
-      choice = spa_pod_builder_pop(&d->b, &f);
402
+      choice = spa_pod_builder_pop(&b.b, &f);
403
       if (i == 1)
404
         choice->body.type = SPA_CHOICE_None;
405
     }
406
@@ -572,132 +727,78 @@
407
     int v;
408
     for (i = 0; get_nth_int (value, i, &v); i++) {
409
       if (i == 0) {
410
-        spa_pod_builder_prop (&d->b, SPA_FORMAT_AUDIO_channels, 0);
411
-        spa_pod_builder_push_choice(&d->b, &f, get_range_type (value), 0);
412
+        spa_pod_builder_prop (&b.b, SPA_FORMAT_AUDIO_channels, 0);
413
+        spa_pod_builder_push_choice(&b.b, &f, get_range_type (value), 0);
414
       }
415
 
416
-      spa_pod_builder_int (&d->b, v);
417
+      spa_pod_builder_int (&b.b, v);
418
     }
419
     if (i > 0) {
420
-      choice = spa_pod_builder_pop(&d->b, &f);
421
+      choice = spa_pod_builder_pop(&b.b, &f);
422
       if (i == 1) {
423
         choice->body.type = SPA_CHOICE_None;
424
-        set_default_channels (&d->b, v);
425
+        set_default_channels (&b.b, v);
426
       }
427
     }
428
   }
429
-  return TRUE;
430
-}
431
 
432
-static int
433
-builder_overflow (void *event_data, uint32_t size)
434
-{
435
-  struct spa_pod_builder *b = event_data;
436
-  b->size = SPA_ROUND_UP_N (size, 512);
437
-  b->data = realloc (b->data, b->size);
438
-  if (b->data == NULL)
439
-    return -errno;
440
-  return 0;
441
+  g_ptr_array_add (d->array, spa_pod_builder_pop (&b.b, &f0));
442
 }
443
 
444
-static const struct spa_pod_builder_callbacks builder_callbacks = {
445
-        SPA_VERSION_POD_BUILDER_CALLBACKS,
446
-        .overflow = builder_overflow
447
-};
448
-
449
-static struct spa_pod *
450
-convert_1 (ConvertData *d)
451
+static void
452
+handle_fields (ConvertData *d)
453
 {
454
-  struct spa_pod_frame f;
455
-
456
   if (!(d->type = find_media_types (gst_structure_get_name (d->cs))))
457
-    return NULL;
458
-
459
-  spa_pod_builder_set_callbacks(&d->b, &builder_callbacks, &d->b);
460
-
461
-  spa_pod_builder_push_object (&d->b, &f, SPA_TYPE_OBJECT_Format, d->id);
462
-
463
-  spa_pod_builder_prop (&d->b, SPA_FORMAT_mediaType, 0);
464
-  spa_pod_builder_id(&d->b, d->type->media_type);
465
-
466
-  spa_pod_builder_prop (&d->b, SPA_FORMAT_mediaSubtype, 0);
467
-  spa_pod_builder_id(&d->b, d->type->media_subtype);
468
-
469
-  if (d->cf && gst_caps_features_contains (d->cf, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
470
-    struct spa_pod_frame f2;
471
-
472
-    spa_pod_builder_prop (&d->b, SPA_FORMAT_VIDEO_modifier,
473
-                          (SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE));
474
-    spa_pod_builder_push_choice (&d->b, &f2, SPA_CHOICE_Enum, 0);
475
-    spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_INVALID);
476
-    spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_INVALID);
477
-    spa_pod_builder_long (&d->b, DRM_FORMAT_MOD_LINEAR);
478
-    spa_pod_builder_pop (&d->b, &f2);
479
-  }
480
+    return;
481
 
482
   if (d->type->media_type == SPA_MEDIA_TYPE_video)
483
     handle_video_fields (d);
484
   else if (d->type->media_type == SPA_MEDIA_TYPE_audio)
485
     handle_audio_fields (d);
486
-
487
-  spa_pod_builder_pop (&d->b, &f);
488
-
489
-  return SPA_PTROFF (d->b.data, 0, struct spa_pod);
490
 }
491
 
492
-struct spa_pod *
493
-gst_caps_to_format (GstCaps *caps, guint index, uint32_t id)
494
+static gboolean
495
+foreach_func_dmabuf (GstCapsFeatures *features,
496
+                     GstStructure    *structure,
497
+                     ConvertData     *d)
498
 {
499
-  ConvertData d;
500
-  struct spa_pod *res;
501
+  if (!features || !gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF))
502
+    return TRUE;
503
 
504
-  g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
505
-  g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
506
-
507
-  spa_zero (d);
508
-  d.cf = gst_caps_get_features (caps, index);
509
-  d.cs = gst_caps_get_structure (caps, index);
510
-  d.id = id;
511
+  d->cf = features;
512
+  d->cs = structure;
513
 
514
-  res = convert_1 (&d);
515
+  handle_fields (d);
516
 
517
-  return res;
518
+  return TRUE;
519
 }
520
 
521
 static gboolean
522
-foreach_func (GstCapsFeatures *features,
523
-              GstStructure    *structure,
524
-              ConvertData     *d)
525
+foreach_func_no_dmabuf (GstCapsFeatures *features,
526
+                        GstStructure    *structure,
527
+                        ConvertData     *d)
528
 {
529
-  struct spa_pod *fmt;
530
-  int idx;
531
+  if (features && gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF))
532
+    return TRUE;
533
 
534
-  spa_zero(d->b);
535
   d->cf = features;
536
   d->cs = structure;
537
 
538
-  if (d->cf && gst_caps_features_contains (d->cf, GST_CAPS_FEATURE_MEMORY_DMABUF))
539
-    idx = 0;
540
-  else
541
-    idx = -1;
542
-
543
-  if ((fmt = convert_1 (d)))
544
-    g_ptr_array_insert (d->array, idx, fmt);
545
+  handle_fields (d);
546
 
547
   return TRUE;
548
 }
549
 
550
 
551
 GPtrArray *
552
-gst_caps_to_format_all (GstCaps *caps, uint32_t id)
553
+gst_caps_to_format_all (GstCaps *caps)
554
 {
555
   ConvertData d;
556
 
557
-  spa_zero (d);
558
-  d.id = id;
559
   d.array = g_ptr_array_new_full (gst_caps_get_size (caps), (GDestroyNotify)g_free);
560
 
561
-  gst_caps_foreach (caps, (GstCapsForeachFunc) foreach_func, &d);
562
+  gst_caps_foreach (caps, (GstCapsForeachFunc) foreach_func_dmabuf, &d);
563
+  gst_caps_foreach (caps, (GstCapsForeachFunc) foreach_func_no_dmabuf, &d);
564
 
565
   return d.array;
566
 }
567
@@ -712,6 +813,18 @@
568
   return gst_video_format_to_string(idx);
569
 }
570
 
571
+#ifdef HAVE_GSTREAMER_DMA_DRM
572
+static char *video_id_to_dma_drm_fourcc(uint32_t id, uint64_t mod)
573
+{
574
+  int idx;
575
+  guint32 fourcc;
576
+  if ((idx = find_index(video_format_map, SPA_N_ELEMENTS(video_format_map), id)) == -1)
577
+    return NULL;
578
+  fourcc = gst_video_dma_drm_fourcc_from_format(idx);
579
+  return gst_video_dma_drm_fourcc_to_string(fourcc, mod);
580
+}
581
+#endif
582
+
583
 static const char *audio_id_to_string(uint32_t id)
584
 {
585
   int idx;
586
@@ -729,7 +842,7 @@
587
   uint32_t i, n_items, choice;
588
 
589
   val = spa_pod_get_values(&prop->value, &n_items, &choice);
590
-  if (val->type != SPA_TYPE_Id)
591
+  if (val->type != SPA_TYPE_Id || n_items == 0)
592
           return;
593
 
594
   id = SPA_POD_BODY(val);
595
@@ -763,6 +876,114 @@
596
 }
597
 
598
 static void
599
+handle_dmabuf_prop (const struct spa_pod_prop *prop,
600
+    const struct spa_pod_prop *prop_modifier, GstCaps *res)
601
+{
602
+  g_autoptr (GPtrArray) fmt_array = NULL;
603
+  g_autoptr (GPtrArray) drm_fmt_array = NULL;
604
+  const struct spa_pod *pod_modifier;
605
+  struct spa_pod *val;
606
+  uint32_t *id, n_fmts, n_mods, choice, i, j;
607
+  uint64_t *mods;
608
+
609
+  val = spa_pod_get_values (&prop->value, &n_fmts, &choice);
610
+  if (val->type != SPA_TYPE_Id || n_fmts == 0)
611
+    return;
612
+  if (choice != SPA_CHOICE_None && choice != SPA_CHOICE_Enum)
613
+    return;
614
+
615
+  id = SPA_POD_BODY (val);
616
+  if (n_fmts > 1) {
617
+    n_fmts--;
618
+    id++;
619
+  }
620
+
621
+  pod_modifier = spa_pod_get_values (&prop_modifier->value, &n_mods, &choice);
622
+  if (pod_modifier->type != SPA_TYPE_Long || n_mods == 0)
623
+    return;
624
+  if (choice != SPA_CHOICE_None && choice != SPA_CHOICE_Enum)
625
+    return;
626
+
627
+  mods = SPA_POD_BODY (pod_modifier);
628
+  if (n_mods > 1) {
629
+    n_mods--;
630
+    mods++;
631
+  }
632
+
633
+  fmt_array = g_ptr_array_new_with_free_func (g_free);
634
+  drm_fmt_array = g_ptr_array_new_with_free_func (g_free);
635
+
636
+  for (i = 0; i < n_fmts; i++) {
637
+    for (j = 0; j < n_mods; j++) {
638
+      const char *fmt_str;
639
+
640
+      if ((modsj == DRM_FORMAT_MOD_LINEAR ||
641
+           modsj == DRM_FORMAT_MOD_INVALID) &&
642
+          (fmt_str = video_id_to_string(idi)))
643
+        g_ptr_array_add(fmt_array, g_strdup_printf ("%s", fmt_str));
644
+
645
+#ifdef HAVE_GSTREAMER_DMA_DRM
646
+      {
647
+        char *drm_str;
648
+
649
+        if ((drm_str = video_id_to_dma_drm_fourcc(idi, modsj)))
650
+          g_ptr_array_add(drm_fmt_array, drm_str);
651
+      }
652
+#endif
653
+    }
654
+  }
655
+
656
+#ifdef HAVE_GSTREAMER_DMA_DRM
657
+  if (drm_fmt_array->len > 0) {
658
+    g_ptr_array_add (fmt_array, g_strdup_printf ("DMA_DRM"));
659
+
660
+    if (drm_fmt_array->len == 1) {
661
+      gst_caps_set_simple (res, "drm-format", G_TYPE_STRING,
662
+          g_ptr_array_index (drm_fmt_array, 0), NULL);
663
+    } else {
664
+      GValue list = { 0 };
665
+
666
+      g_value_init (&list, GST_TYPE_LIST);
667
+      for (i = 0; i < drm_fmt_array->len; i++) {
668
+        GValue v = { 0 };
669
+
670
+        g_value_init (&v, G_TYPE_STRING);
671
+        g_value_set_string (&v, g_ptr_array_index (drm_fmt_array, i));
672
+        gst_value_list_append_and_take_value (&list, &v);
673
+      }
674
+
675
+      gst_caps_set_value (res, "drm-format", &list);
676
+      g_value_unset (&list);
677
+    }
678
+  }
679
+#endif
680
+
681
+  if (fmt_array->len > 0) {
682
+    gst_caps_set_features_simple (res,
683
+        gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF));
684
+
685
+    if (fmt_array->len == 1) {
686
+      gst_caps_set_simple (res, "format", G_TYPE_STRING,
687
+          g_ptr_array_index (fmt_array, 0), NULL);
688
+    } else {
689
+      GValue list = { 0 };
690
+
691
+      g_value_init (&list, GST_TYPE_LIST);
692
+      for (i = 0; i < fmt_array->len; i++) {
693
+        GValue v = { 0 };
694
+
695
+        g_value_init (&v, G_TYPE_STRING);
696
+        g_value_set_string (&v, g_ptr_array_index (fmt_array, i));
697
+        gst_value_list_append_and_take_value (&list, &v);
698
+      }
699
+
700
+      gst_caps_set_value (res, "format", &list);
701
+      g_value_unset (&list);
702
+    }
703
+  }
704
+}
705
+
706
+static void
707
 handle_int_prop (const struct spa_pod_prop *prop, const char *key, GstCaps *res)
708
 {
709
   struct spa_pod *val;
710
@@ -770,7 +991,7 @@
711
   uint32_t i, n_items, choice;
712
 
713
   val = spa_pod_get_values(&prop->value, &n_items, &choice);
714
-  if (val->type != SPA_TYPE_Int)
715
+  if (val->type != SPA_TYPE_Int || n_items == 0)
716
           return;
717
 
718
   ints = SPA_POD_BODY(val);
719
@@ -814,7 +1035,7 @@
720
   uint32_t i, n_items, choice;
721
 
722
   val = spa_pod_get_values(&prop->value, &n_items, &choice);
723
-  if (val->type != SPA_TYPE_Rectangle)
724
+  if (val->type != SPA_TYPE_Rectangle || n_items == 0)
725
           return;
726
 
727
   rect = SPA_POD_BODY(val);
728
@@ -867,7 +1088,7 @@
729
   uint32_t i, n_items, choice;
730
 
731
   val = spa_pod_get_values(&prop->value, &n_items, &choice);
732
-  if (val->type != SPA_TYPE_Fraction)
733
+  if (val->type != SPA_TYPE_Fraction || n_items == 0)
734
           return;
735
 
736
   fract = SPA_POD_BODY(val);
737
@@ -916,9 +1137,17 @@
738
 
739
   if (media_type == SPA_MEDIA_TYPE_video) {
740
     if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
741
+      const struct spa_pod_prop *prop_modifier;
742
+
743
       res = gst_caps_new_empty_simple ("video/x-raw");
744
-      if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_format))) {
745
-        handle_id_prop (prop, "format", video_id_to_string, res);
746
+
747
+      if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_format)) &&
748
+          (prop_modifier = spa_pod_object_find_prop (obj, NULL, SPA_FORMAT_VIDEO_modifier))) {
749
+        handle_dmabuf_prop (prop, prop_modifier, res);
750
+      } else {
751
+        if ((prop = spa_pod_object_find_prop (obj, prop, SPA_FORMAT_VIDEO_format))) {
752
+          handle_id_prop (prop, "format", video_id_to_string, res);
753
+        }
754
       }
755
     }
756
     else if (media_subtype == SPA_MEDIA_SUBTYPE_mjpg) {
757
@@ -959,3 +1188,99 @@
758
   }
759
   return res;
760
 }
761
+
762
+static gboolean
763
+filter_dmabuf_caps (GstCapsFeatures *features,
764
+                    GstStructure    *structure,
765
+                    gpointer         user_data)
766
+{
767
+  const GValue *value;
768
+  const char *v;
769
+
770
+  if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF))
771
+    return TRUE;
772
+
773
+  if (!(value = gst_structure_get_value (structure, "format")) ||
774
+      !(v = get_nth_string (value, 0)))
775
+    return FALSE;
776
+
777
+#ifdef HAVE_GSTREAMER_DMA_DRM
778
+  {
779
+    int idx;
780
+
781
+    idx = gst_video_format_from_string (v);
782
+    if (idx == GST_VIDEO_FORMAT_UNKNOWN)
783
+      return FALSE;
784
+
785
+    if (idx == GST_VIDEO_FORMAT_DMA_DRM &&
786
+        !gst_structure_get_value (structure, "drm-format"))
787
+      return FALSE;
788
+  }
789
+#endif
790
+
791
+  return TRUE;
792
+}
793
+
794
+void
795
+gst_caps_sanitize (GstCaps **caps)
796
+{
797
+  g_return_if_fail (GST_IS_CAPS (*caps));
798
+
799
+  *caps = gst_caps_make_writable (*caps);
800
+  gst_caps_filter_and_map_in_place (*caps, filter_dmabuf_caps, NULL);
801
+}
802
+
803
+void
804
+gst_caps_maybe_fixate_dma_format (GstCaps *caps)
805
+{
806
+#ifdef HAVE_GSTREAMER_DMA_DRM
807
+  GstCapsFeatures *features;
808
+  GstStructure *structure;
809
+  const GValue *format_value;
810
+  const GValue *drm_format_value;
811
+  const char *format_string;
812
+  const char *drm_format_string;
813
+  uint32_t fourcc;
814
+  uint64_t mod;
815
+  int drm_idx;
816
+  int i;
817
+
818
+  g_return_if_fail (GST_IS_CAPS (caps));
819
+
820
+  if (gst_caps_is_fixed (caps) || gst_caps_get_size(caps) != 1)
821
+    return;
822
+
823
+  features = gst_caps_get_features (caps, 0);
824
+  if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF))
825
+    return;
826
+
827
+  structure = gst_caps_get_structure (caps, 0);
828
+  if (!gst_structure_has_field (structure, "format") ||
829
+      !gst_structure_has_field (structure, "drm-format"))
830
+    return;
831
+
832
+  format_value = gst_structure_get_value (structure, "format");
833
+  drm_format_value = gst_structure_get_value (structure, "drm-format");
834
+  if (G_VALUE_TYPE (format_value) != GST_TYPE_LIST ||
835
+      ((GArray *) g_value_peek_pointer (format_value))->len != 2 ||
836
+      G_VALUE_TYPE (drm_format_value) != G_TYPE_STRING)
837
+    return;
838
+
839
+  drm_format_string = g_value_get_string (drm_format_value);
840
+  fourcc = gst_video_dma_drm_fourcc_from_string (drm_format_string, &mod);
841
+  drm_idx = gst_video_dma_drm_fourcc_to_format (fourcc);
842
+  if (drm_idx == GST_VIDEO_FORMAT_UNKNOWN || mod != DRM_FORMAT_MOD_LINEAR)
843
+    return;
844
+
845
+  for (i = 0; (format_string = get_nth_string (format_value, i)); i++) {
846
+    int idx;
847
+
848
+    idx = gst_video_format_from_string (format_string);
849
+    if (idx != GST_VIDEO_FORMAT_DMA_DRM && idx != drm_idx)
850
+      return;
851
+  }
852
+
853
+  gst_caps_set_simple (caps, "format", G_TYPE_STRING, "DMA_DRM", NULL);
854
+  g_warn_if_fail (gst_caps_is_fixed (caps));
855
+#endif
856
+}
857
pipewire-1.0.1.tar.bz2/src/gst/gstpipewireformat.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewireformat.h Changed
19
 
1
@@ -11,12 +11,14 @@
2
 
3
 G_BEGIN_DECLS
4
 
5
-struct spa_pod * gst_caps_to_format      (GstCaps *caps,
6
-                                          guint index, uint32_t id);
7
-GPtrArray *      gst_caps_to_format_all  (GstCaps *caps, uint32_t id);
8
+GPtrArray *      gst_caps_to_format_all  (GstCaps *caps);
9
 
10
 GstCaps *        gst_caps_from_format    (const struct spa_pod *format);
11
 
12
+void             gst_caps_sanitize       (GstCaps **caps);
13
+
14
+void             gst_caps_maybe_fixate_dma_format (GstCaps *caps);
15
+
16
 G_END_DECLS
17
 
18
 #endif
19
pipewire-1.0.1.tar.bz2/src/gst/gstpipewirepool.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewirepool.c Changed
187
 
1
@@ -15,6 +15,8 @@
2
 
3
 #include "gstpipewirepool.h"
4
 
5
+#include <spa/debug/types.h>
6
+
7
 GST_DEBUG_CATEGORY_STATIC (gst_pipewire_pool_debug_category);
8
 #define GST_CAT_DEFAULT gst_pipewire_pool_debug_category
9
 
10
@@ -33,11 +35,12 @@
11
 static GQuark pool_data_quark;
12
 
13
 GstPipeWirePool *
14
-gst_pipewire_pool_new (void)
15
+gst_pipewire_pool_new (GstPipeWireStream *stream)
16
 {
17
   GstPipeWirePool *pool;
18
 
19
   pool = g_object_new (GST_TYPE_PIPEWIRE_POOL, NULL);
20
+  g_weak_ref_set (&pool->stream, stream);
21
 
22
   return pool;
23
 }
24
@@ -57,7 +60,7 @@
25
   uint32_t i;
26
   GstPipeWirePoolData *data;
27
 
28
-  GST_LOG_OBJECT (pool, "wrap buffer");
29
+  GST_DEBUG_OBJECT (pool, "wrap buffer");
30
 
31
   data = g_slice_new (GstPipeWirePoolData);
32
 
33
@@ -67,21 +70,20 @@
34
     struct spa_data *d = &b->buffer->datasi;
35
     GstMemory *gmem = NULL;
36
 
37
-    GST_LOG_OBJECT (pool, "wrap buffer %d %d", d->mapoffset, d->maxsize);
38
+    GST_DEBUG_OBJECT (pool, "wrap data (%s %d) %d %d",
39
+        spa_debug_type_find_short_name(spa_type_data_type, d->type), d->type,
40
+        d->mapoffset, d->maxsize);
41
     if (d->type == SPA_DATA_MemFd) {
42
-      GST_LOG_OBJECT (pool, "memory type MemFd");
43
       gmem = gst_fd_allocator_alloc (pool->fd_allocator, dup(d->fd),
44
                 d->mapoffset + d->maxsize, GST_FD_MEMORY_FLAG_NONE);
45
       gst_memory_resize (gmem, d->mapoffset, d->maxsize);
46
     }
47
     else if(d->type == SPA_DATA_DmaBuf) {
48
-      GST_LOG_OBJECT (pool, "memory type DmaBuf");
49
       gmem = gst_fd_allocator_alloc (pool->dmabuf_allocator, dup(d->fd),
50
                 d->mapoffset + d->maxsize, GST_FD_MEMORY_FLAG_NONE);
51
       gst_memory_resize (gmem, d->mapoffset, d->maxsize);
52
     }
53
     else if (d->type == SPA_DATA_MemPtr) {
54
-      GST_LOG_OBJECT (pool, "memory type MemPtr");
55
       gmem = gst_memory_new_wrapped (0, d->data, d->maxsize, 0,
56
                                      d->maxsize, NULL, NULL);
57
     }
58
@@ -116,58 +118,50 @@
59
                              data,
60
                              pool_data_destroy);
61
   b->user_data = data;
62
-}
63
 
64
-GstPipeWirePoolData *gst_pipewire_pool_get_data (GstBuffer *buffer)
65
-{
66
-  return gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), pool_data_quark);
67
+  pool->n_buffers++;
68
 }
69
 
70
-#if 0
71
-gboolean
72
-gst_pipewire_pool_add_buffer (GstPipeWirePool *pool, GstBuffer *buffer)
73
+void gst_pipewire_pool_remove_buffer (GstPipeWirePool *pool, struct pw_buffer *b)
74
 {
75
-  g_return_val_if_fail (GST_IS_PIPEWIRE_POOL (pool), FALSE);
76
-  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
77
+  GstPipeWirePoolData *data = b->user_data;
78
 
79
-  GST_OBJECT_LOCK (pool);
80
-  g_queue_push_tail (&pool->available, buffer);
81
-  g_cond_signal (&pool->cond);
82
-  GST_OBJECT_UNLOCK (pool);
83
+  data->b = NULL;
84
+  data->header = NULL;
85
+  data->crop = NULL;
86
+  data->videotransform = NULL;
87
 
88
-  return TRUE;
89
-}
90
+  gst_buffer_remove_all_memory (data->buf);
91
 
92
-gboolean
93
-gst_pipewire_pool_remove_buffer (GstPipeWirePool *pool, GstBuffer *buffer)
94
-{
95
-  gboolean res;
96
+  /* this will also destroy the pool data, if this is the last reference */
97
+  gst_clear_buffer (&data->buf);
98
 
99
-  g_return_val_if_fail (GST_IS_PIPEWIRE_POOL (pool), FALSE);
100
-  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
101
-
102
-  GST_OBJECT_LOCK (pool);
103
-  res = g_queue_remove (&pool->available, buffer);
104
-  GST_OBJECT_UNLOCK (pool);
105
+  pool->n_buffers--;
106
+}
107
 
108
-  return res;
109
+GstPipeWirePoolData *gst_pipewire_pool_get_data (GstBuffer *buffer)
110
+{
111
+  return gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), pool_data_quark);
112
 }
113
-#endif
114
 
115
 static GstFlowReturn
116
 acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer,
117
         GstBufferPoolAcquireParams * params)
118
 {
119
   GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool);
120
+  g_autoptr (GstPipeWireStream) s = g_weak_ref_get (&p->stream);
121
   GstPipeWirePoolData *data;
122
   struct pw_buffer *b;
123
 
124
+  if (G_UNLIKELY (!s))
125
+    return GST_FLOW_ERROR;
126
+
127
   GST_OBJECT_LOCK (pool);
128
   while (TRUE) {
129
     if (G_UNLIKELY (GST_BUFFER_POOL_IS_FLUSHING (pool)))
130
       goto flushing;
131
 
132
-    if ((b = pw_stream_dequeue_buffer(p->stream)))
133
+    if ((b = pw_stream_dequeue_buffer(s->pwstream)))
134
       break;
135
 
136
     if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT))
137
@@ -181,7 +175,7 @@
138
   *buffer = data->buf;
139
 
140
   GST_OBJECT_UNLOCK (pool);
141
-  GST_DEBUG ("acquire buffer %p", *buffer);
142
+  GST_LOG_OBJECT (pool, "acquire buffer %p", *buffer);
143
 
144
   return GST_FLOW_OK;
145
 
146
@@ -210,6 +204,7 @@
147
 {
148
   GstPipeWirePool *p = GST_PIPEWIRE_POOL (pool);
149
   GstCaps *caps;
150
+  GstStructure *structure;
151
   guint size, min_buffers, max_buffers;
152
   gboolean has_video;
153
 
154
@@ -223,7 +218,14 @@
155
     return FALSE;
156
   }
157
 
158
-  has_video = gst_video_info_from_caps (&p->video_info, caps);
159
+  structure = gst_caps_get_structure (caps, 0);
160
+  if (g_str_has_prefix (gst_structure_get_name (structure), "video/") ||
161
+      g_str_has_prefix (gst_structure_get_name (structure), "image/")) {
162
+    has_video = TRUE;
163
+    gst_video_info_from_caps (&p->video_info, caps);
164
+  } else {
165
+    has_video = FALSE;
166
+  }
167
 
168
   p->add_metavideo = has_video && gst_buffer_pool_config_has_option (config,
169
       GST_BUFFER_POOL_OPTION_VIDEO_META);
170
@@ -250,7 +252,7 @@
171
 static void
172
 release_buffer (GstBufferPool * pool, GstBuffer *buffer)
173
 {
174
-  GST_DEBUG ("release buffer %p", buffer);
175
+  GST_LOG_OBJECT (pool, "release buffer %p", buffer);
176
 }
177
 
178
 static gboolean
179
@@ -266,6 +268,7 @@
180
   GstPipeWirePool *pool = GST_PIPEWIRE_POOL (object);
181
 
182
   GST_DEBUG_OBJECT (pool, "finalize");
183
+  g_weak_ref_set (&pool->stream, NULL);
184
   g_object_unref (pool->fd_allocator);
185
   g_object_unref (pool->dmabuf_allocator);
186
 
187
pipewire-1.0.1.tar.bz2/src/gst/gstpipewirepool.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewirepool.h Changed
76
 
1
@@ -5,6 +5,8 @@
2
 #ifndef __GST_PIPEWIRE_POOL_H__
3
 #define __GST_PIPEWIRE_POOL_H__
4
 
5
+#include "gstpipewirestream.h"
6
+
7
 #include <gst/gst.h>
8
 
9
 #include <gst/video/video.h>
10
@@ -13,23 +15,10 @@
11
 
12
 G_BEGIN_DECLS
13
 
14
-#define GST_TYPE_PIPEWIRE_POOL \
15
-  (gst_pipewire_pool_get_type())
16
-#define GST_PIPEWIRE_POOL(obj) \
17
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIPEWIRE_POOL,GstPipeWirePool))
18
-#define GST_PIPEWIRE_POOL_CLASS(klass) \
19
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIPEWIRE_POOL,GstPipeWirePoolClass))
20
-#define GST_IS_PIPEWIRE_POOL(obj) \
21
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIPEWIRE_POOL))
22
-#define GST_IS_PIPEWIRE_POOL_CLASS(klass) \
23
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIPEWIRE_POOL))
24
-#define GST_PIPEWIRE_POOL_GET_CLASS(klass) \
25
-  (G_TYPE_INSTANCE_GET_CLASS ((klass), GST_TYPE_PIPEWIRE_POOL, GstPipeWirePoolClass))
26
+#define GST_TYPE_PIPEWIRE_POOL (gst_pipewire_pool_get_type())
27
+G_DECLARE_FINAL_TYPE (GstPipeWirePool, gst_pipewire_pool, GST, PIPEWIRE_POOL, GstBufferPool)
28
 
29
 typedef struct _GstPipeWirePoolData GstPipeWirePoolData;
30
-typedef struct _GstPipeWirePool GstPipeWirePool;
31
-typedef struct _GstPipeWirePoolClass GstPipeWirePoolClass;
32
-
33
 struct _GstPipeWirePoolData {
34
   GstPipeWirePool *pool;
35
   void *owner;
36
@@ -45,8 +34,8 @@
37
 struct _GstPipeWirePool {
38
   GstBufferPool parent;
39
 
40
-  struct pw_stream *stream;
41
-  struct pw_type *t;
42
+  GWeakRef stream;
43
+  guint n_buffers;
44
 
45
   gboolean add_metavideo;
46
   GstVideoInfo video_info;
47
@@ -57,20 +46,18 @@
48
   GCond cond;
49
 };
50
 
51
-struct _GstPipeWirePoolClass {
52
-  GstBufferPoolClass parent_class;
53
-};
54
-
55
-GType gst_pipewire_pool_get_type (void);
56
-
57
-GstPipeWirePool *  gst_pipewire_pool_new           (void);
58
+GstPipeWirePool *  gst_pipewire_pool_new (GstPipeWireStream *stream);
59
 
60
 void gst_pipewire_pool_wrap_buffer (GstPipeWirePool *pool, struct pw_buffer *buffer);
61
+void gst_pipewire_pool_remove_buffer (GstPipeWirePool *pool, struct pw_buffer *buffer);
62
 
63
-GstPipeWirePoolData *gst_pipewire_pool_get_data (GstBuffer *buffer);
64
+static inline gboolean
65
+gst_pipewire_pool_has_buffers (GstPipeWirePool *pool)
66
+{
67
+  return pool->n_buffers > 0;
68
+}
69
 
70
-//gboolean        gst_pipewire_pool_add_buffer    (GstPipeWirePool *pool, GstBuffer *buffer);
71
-//gboolean        gst_pipewire_pool_remove_buffer (GstPipeWirePool *pool, GstBuffer *buffer);
72
+GstPipeWirePoolData *gst_pipewire_pool_get_data (GstBuffer *buffer);
73
 
74
 G_END_DECLS
75
 
76
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiresink.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiresink.c Changed
811
 
1
@@ -29,6 +29,7 @@
2
 
3
 #include <gst/video/video.h>
4
 
5
+#include "gstpipewireclock.h"
6
 #include "gstpipewireformat.h"
7
 
8
 GST_DEBUG_CATEGORY_STATIC (pipewire_sink_debug);
9
@@ -95,23 +96,40 @@
10
 
11
 static GstFlowReturn gst_pipewire_sink_render (GstBaseSink * psink,
12
     GstBuffer * buffer);
13
-static gboolean gst_pipewire_sink_start (GstBaseSink * basesink);
14
-static gboolean gst_pipewire_sink_stop (GstBaseSink * basesink);
15
+
16
+static GstClock *
17
+gst_pipewire_sink_provide_clock (GstElement * elem)
18
+{
19
+  GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (elem);
20
+  GstClock *clock;
21
+
22
+  GST_OBJECT_LOCK (pwsink);
23
+  if (!GST_OBJECT_FLAG_IS_SET (pwsink, GST_ELEMENT_FLAG_PROVIDE_CLOCK))
24
+    goto clock_disabled;
25
+
26
+  if (pwsink->stream->clock)
27
+    clock = GST_CLOCK_CAST (gst_object_ref (pwsink->stream->clock));
28
+  else
29
+    clock = NULL;
30
+  GST_OBJECT_UNLOCK (pwsink);
31
+
32
+  return clock;
33
+
34
+  /* ERRORS */
35
+clock_disabled:
36
+  {
37
+    GST_DEBUG_OBJECT (pwsink, "clock provide disabled");
38
+    GST_OBJECT_UNLOCK (pwsink);
39
+    return NULL;
40
+  }
41
+}
42
 
43
 static void
44
 gst_pipewire_sink_finalize (GObject * object)
45
 {
46
   GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (object);
47
 
48
-  g_object_unref (pwsink->pool);
49
-
50
-  if (pwsink->stream_properties)
51
-    gst_structure_free (pwsink->stream_properties);
52
-  if (pwsink->client_properties)
53
-    gst_structure_free (pwsink->client_properties);
54
-  g_free (pwsink->path);
55
-  g_free (pwsink->target_object);
56
-  g_free (pwsink->client_name);
57
+  gst_clear_object (&pwsink->stream);
58
 
59
   G_OBJECT_CLASS (parent_class)->finalize (object);
60
 }
61
@@ -121,7 +139,7 @@
62
 {
63
   GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (bsink);
64
 
65
-  gst_query_add_allocation_pool (query, GST_BUFFER_POOL_CAST (pwsink->pool), 0, 0, 0);
66
+  gst_query_add_allocation_pool (query, GST_BUFFER_POOL_CAST (pwsink->stream->pool), 0, 0, 0);
67
   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
68
   return TRUE;
69
 }
70
@@ -206,11 +224,12 @@
71
                                                       G_PARAM_READWRITE |
72
                                                       G_PARAM_STATIC_STRINGS));
73
 
74
+  gstelement_class->provide_clock = gst_pipewire_sink_provide_clock;
75
   gstelement_class->change_state = gst_pipewire_sink_change_state;
76
 
77
   gst_element_class_set_static_metadata (gstelement_class,
78
-      "PipeWire sink", "Sink/Video",
79
-      "Send video to PipeWire", "Wim Taymans <wim.taymans@gmail.com>");
80
+      "PipeWire sink", "Sink/Audio/Video",
81
+      "Send audio/video to PipeWire", "Wim Taymans <wim.taymans@gmail.com>");
82
 
83
   gst_element_class_add_pad_template (gstelement_class,
84
       gst_static_pad_template_get (&gst_pipewire_sink_template));
85
@@ -218,8 +237,6 @@
86
   gstbasesink_class->set_caps = gst_pipewire_sink_setcaps;
87
   gstbasesink_class->fixate = gst_pipewire_sink_sink_fixate;
88
   gstbasesink_class->propose_allocation = gst_pipewire_sink_propose_allocation;
89
-  gstbasesink_class->start = gst_pipewire_sink_start;
90
-  gstbasesink_class->stop = gst_pipewire_sink_stop;
91
   gstbasesink_class->render = gst_pipewire_sink_render;
92
 
93
   GST_DEBUG_CATEGORY_INIT (pipewire_sink_debug, "pipewiresink", 0,
94
@@ -227,8 +244,9 @@
95
 }
96
 
97
 static void
98
-pool_activated (GstPipeWirePool *pool, GstPipeWireSink *sink)
99
+gst_pipewire_sink_update_params (GstPipeWireSink *sink)
100
 {
101
+  GstPipeWirePool *pool = sink->stream->pool;
102
   GstStructure *config;
103
   GstCaps *caps;
104
   guint size;
105
@@ -244,24 +262,19 @@
106
 
107
   spa_pod_builder_init (&b, buffer, sizeof (buffer));
108
   spa_pod_builder_push_object (&b, &f, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers);
109
-  if (size == 0)
110
-    spa_pod_builder_add (&b,
111
-        SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(0, 0, INT32_MAX),
112
-        0);
113
-  else
114
-    spa_pod_builder_add (&b,
115
-        SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(size, size, INT32_MAX),
116
-        0);
117
+  spa_pod_builder_add (&b,
118
+      SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(size, size, INT32_MAX),
119
+      0);
120
 
121
   spa_pod_builder_add (&b,
122
       SPA_PARAM_BUFFERS_stride,  SPA_POD_CHOICE_RANGE_Int(0, 0, INT32_MAX),
123
       SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(
124
-         SPA_MAX(MIN_BUFFERS, min_buffers),
125
-         SPA_MAX(MIN_BUFFERS, min_buffers),
126
-         max_buffers ? max_buffers : INT32_MAX),
127
+              SPA_MAX(MIN_BUFFERS, min_buffers),
128
+              SPA_MAX(MIN_BUFFERS, min_buffers),
129
+              max_buffers ? max_buffers : INT32_MAX),
130
       SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(
131
-                       (1<<SPA_DATA_MemFd) |
132
-                       (1<<SPA_DATA_MemPtr)),
133
+                                                (1<<SPA_DATA_MemFd) |
134
+                                                (1<<SPA_DATA_MemPtr)),
135
       0);
136
   port_params0 = spa_pod_builder_pop (&b, &f);
137
 
138
@@ -275,20 +288,28 @@
139
       SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoCrop),
140
       SPA_PARAM_META_size, SPA_POD_Int(sizeof (struct spa_meta_region)));
141
 
142
-  pw_thread_loop_lock (sink->core->loop);
143
-  pw_stream_update_params (sink->stream, port_params, 3);
144
-  pw_thread_loop_unlock (sink->core->loop);
145
+  pw_thread_loop_lock (sink->stream->core->loop);
146
+  pw_stream_update_params (sink->stream->pwstream, port_params, 3);
147
+  pw_thread_loop_unlock (sink->stream->core->loop);
148
+}
149
+
150
+static void
151
+pool_activated (GstPipeWirePool *pool, GstPipeWireSink *sink)
152
+{
153
+  GST_DEBUG_OBJECT (pool, "activated");
154
+  g_cond_signal (&sink->stream->pool->cond);
155
 }
156
 
157
 static void
158
 gst_pipewire_sink_init (GstPipeWireSink * sink)
159
 {
160
-  sink->pool =  gst_pipewire_pool_new ();
161
-  sink->client_name = g_strdup(pw_get_client_name());
162
+  sink->stream =  gst_pipewire_stream_new (GST_ELEMENT (sink));
163
+
164
   sink->mode = DEFAULT_PROP_MODE;
165
-  sink->fd = -1;
166
 
167
-  g_signal_connect (sink->pool, "activated", G_CALLBACK (pool_activated), sink);
168
+  GST_OBJECT_FLAG_SET (sink, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
169
+
170
+  g_signal_connect (sink->stream->pool, "activated", G_CALLBACK (pool_activated), sink);
171
 }
172
 
173
 static GstCaps *
174
@@ -350,31 +371,31 @@
175
 
176
   switch (prop_id) {
177
     case PROP_PATH:
178
-      g_free (pwsink->path);
179
-      pwsink->path = g_value_dup_string (value);
180
+      g_free (pwsink->stream->path);
181
+      pwsink->stream->path = g_value_dup_string (value);
182
       break;
183
 
184
     case PROP_TARGET_OBJECT:
185
-      g_free (pwsink->target_object);
186
-      pwsink->target_object = g_value_dup_string (value);
187
+      g_free (pwsink->stream->target_object);
188
+      pwsink->stream->target_object = g_value_dup_string (value);
189
       break;
190
 
191
     case PROP_CLIENT_NAME:
192
-      g_free (pwsink->client_name);
193
-      pwsink->client_name = g_value_dup_string (value);
194
+      g_free (pwsink->stream->client_name);
195
+      pwsink->stream->client_name = g_value_dup_string (value);
196
       break;
197
 
198
     case PROP_CLIENT_PROPERTIES:
199
-      if (pwsink->client_properties)
200
-        gst_structure_free (pwsink->client_properties);
201
-      pwsink->client_properties =
202
+      if (pwsink->stream->client_properties)
203
+        gst_structure_free (pwsink->stream->client_properties);
204
+      pwsink->stream->client_properties =
205
           gst_structure_copy (gst_value_get_structure (value));
206
       break;
207
 
208
     case PROP_STREAM_PROPERTIES:
209
-      if (pwsink->stream_properties)
210
-        gst_structure_free (pwsink->stream_properties);
211
-      pwsink->stream_properties =
212
+      if (pwsink->stream->stream_properties)
213
+        gst_structure_free (pwsink->stream->stream_properties);
214
+      pwsink->stream->stream_properties =
215
           gst_structure_copy (gst_value_get_structure (value));
216
       break;
217
 
218
@@ -383,7 +404,7 @@
219
       break;
220
 
221
     case PROP_FD:
222
-      pwsink->fd = g_value_get_int (value);
223
+      pwsink->stream->fd = g_value_get_int (value);
224
       break;
225
 
226
     default:
227
@@ -400,23 +421,23 @@
228
 
229
   switch (prop_id) {
230
     case PROP_PATH:
231
-      g_value_set_string (value, pwsink->path);
232
+      g_value_set_string (value, pwsink->stream->path);
233
       break;
234
 
235
     case PROP_TARGET_OBJECT:
236
-      g_value_set_string (value, pwsink->target_object);
237
+      g_value_set_string (value, pwsink->stream->target_object);
238
       break;
239
 
240
     case PROP_CLIENT_NAME:
241
-      g_value_set_string (value, pwsink->client_name);
242
+      g_value_set_string (value, pwsink->stream->client_name);
243
       break;
244
 
245
     case PROP_CLIENT_PROPERTIES:
246
-      gst_value_set_structure (value, pwsink->client_properties);
247
+      gst_value_set_structure (value, pwsink->stream->client_properties);
248
       break;
249
 
250
     case PROP_STREAM_PROPERTIES:
251
-      gst_value_set_structure (value, pwsink->stream_properties);
252
+      gst_value_set_structure (value, pwsink->stream->stream_properties);
253
       break;
254
 
255
     case PROP_MODE:
256
@@ -424,7 +445,7 @@
257
       break;
258
 
259
     case PROP_FD:
260
-      g_value_set_int (value, pwsink->fd);
261
+      g_value_set_int (value, pwsink->stream->fd);
262
       break;
263
 
264
     default:
265
@@ -437,18 +458,23 @@
266
 on_add_buffer (void *_data, struct pw_buffer *b)
267
 {
268
   GstPipeWireSink *pwsink = _data;
269
-  gst_pipewire_pool_wrap_buffer (pwsink->pool, b);
270
+  GST_DEBUG_OBJECT (pwsink, "add pw_buffer %p", b);
271
+  gst_pipewire_pool_wrap_buffer (pwsink->stream->pool, b);
272
 }
273
 
274
 static void
275
 on_remove_buffer (void *_data, struct pw_buffer *b)
276
 {
277
   GstPipeWireSink *pwsink = _data;
278
-  GstPipeWirePoolData *data = b->user_data;
279
-
280
-  GST_LOG_OBJECT (pwsink, "remove buffer");
281
-
282
-  gst_buffer_unref (data->buf);
283
+  GST_DEBUG_OBJECT (pwsink, "remove pw_buffer %p", b);
284
+  gst_pipewire_pool_remove_buffer (pwsink->stream->pool, b);
285
+
286
+  if (!gst_pipewire_pool_has_buffers (pwsink->stream->pool) &&
287
+      !GST_BUFFER_POOL_IS_FLUSHING (GST_BUFFER_POOL_CAST (pwsink->stream->pool))) {
288
+    GST_ELEMENT_ERROR (pwsink, RESOURCE, NOT_FOUND,
289
+        ("all buffers have been removed"),
290
+        ("PipeWire link to remote node was destroyed"));
291
+  }
292
 }
293
 
294
 static void
295
@@ -461,6 +487,8 @@
296
 
297
   data = gst_pipewire_pool_get_data(buffer);
298
 
299
+  GST_LOG_OBJECT (pwsink, "queue buffer %p, pw_buffer %p", buffer, data->b);
300
+
301
   b = data->b->buffer;
302
 
303
   if (data->header) {
304
@@ -485,7 +513,7 @@
305
     GstMemory *mem = gst_buffer_peek_memory (buffer, i);
306
     d->chunk->offset = mem->offset;
307
     d->chunk->size = mem->size;
308
-    d->chunk->stride = pwsink->pool->video_info.stridei;
309
+    d->chunk->stride = pwsink->stream->pool->video_info.stridei;
310
   }
311
 
312
   GstVideoMeta *meta = gst_buffer_get_video_meta (buffer);
313
@@ -500,11 +528,11 @@
314
         video_size += d->chunk->size;
315
       }
316
     } else {
317
-      GST_ERROR ("plane num not matching, meta:%u buffer:%u", meta->n_planes, b->n_datas);
318
+      GST_ERROR_OBJECT (pwsink, "plane num not matching, meta:%u buffer:%u", meta->n_planes, b->n_datas);
319
     }
320
   }
321
 
322
-  if ((res = pw_stream_queue_buffer (pwsink->stream, data->b)) < 0) {
323
+  if ((res = pw_stream_queue_buffer (pwsink->stream->pwstream, data->b)) < 0) {
324
     g_warning ("can't send buffer %s", spa_strerror(res));
325
   }
326
 }
327
@@ -514,8 +542,8 @@
328
 on_process (void *data)
329
 {
330
   GstPipeWireSink *pwsink = data;
331
-  GST_DEBUG ("signal");
332
-  g_cond_signal (&pwsink->pool->cond);
333
+  GST_LOG_OBJECT (pwsink, "signal");
334
+  g_cond_signal (&pwsink->stream->pool->cond);
335
 }
336
 
337
 static void
338
@@ -523,7 +551,8 @@
339
 {
340
   GstPipeWireSink *pwsink = data;
341
 
342
-  GST_DEBUG ("got stream state %d", state);
343
+  GST_DEBUG_OBJECT (pwsink, "got stream state \"%s\" (%d)",
344
+      pw_stream_state_as_string(state), state);
345
 
346
   switch (state) {
347
     case PW_STREAM_STATE_UNCONNECTED:
348
@@ -531,54 +560,67 @@
349
     case PW_STREAM_STATE_PAUSED:
350
       break;
351
     case PW_STREAM_STATE_STREAMING:
352
-      if (pw_stream_is_driving (pwsink->stream))
353
-        pw_stream_trigger_process (pwsink->stream);
354
+      if (pw_stream_is_driving (pwsink->stream->pwstream))
355
+        pw_stream_trigger_process (pwsink->stream->pwstream);
356
       break;
357
     case PW_STREAM_STATE_ERROR:
358
       /* make the error permanent, if it is not already;
359
          pw_stream_set_error() will recursively call us again */
360
-      if (pw_stream_get_state (pwsink->stream, NULL) != PW_STREAM_STATE_ERROR)
361
-        pw_stream_set_error (pwsink->stream, -EPIPE, "%s", error);
362
+      if (pw_stream_get_state (pwsink->stream->pwstream, NULL) != PW_STREAM_STATE_ERROR)
363
+        pw_stream_set_error (pwsink->stream->pwstream, -EPIPE, "%s", error);
364
       else
365
         GST_ELEMENT_ERROR (pwsink, RESOURCE, FAILED,
366
             ("stream error: %s", error), (NULL));
367
       break;
368
   }
369
-  pw_thread_loop_signal (pwsink->core->loop, FALSE);
370
+  pw_thread_loop_signal (pwsink->stream->core->loop, FALSE);
371
 }
372
 
373
 static void
374
 on_param_changed (void *data, uint32_t id, const struct spa_pod *param)
375
 {
376
   GstPipeWireSink *pwsink = data;
377
+  GstPipeWirePool *pool = pwsink->stream->pool;
378
 
379
   if (param == NULL || id != SPA_PARAM_Format)
380
-          return;
381
+    return;
382
 
383
-  if (gst_buffer_pool_is_active (GST_BUFFER_POOL_CAST (pwsink->pool)))
384
-    pool_activated (pwsink->pool, pwsink);
385
+  GST_OBJECT_LOCK (pool);
386
+  while (!gst_buffer_pool_is_active (GST_BUFFER_POOL (pool))) {
387
+    GST_DEBUG_OBJECT (pool, "waiting for pool to become active");
388
+    g_cond_wait(&pool->cond, GST_OBJECT_GET_LOCK (pool));
389
+  }
390
+  GST_OBJECT_UNLOCK (pool);
391
+
392
+  gst_pipewire_sink_update_params (pwsink);
393
 }
394
 
395
 static gboolean
396
 gst_pipewire_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
397
 {
398
   GstPipeWireSink *pwsink;
399
-  GPtrArray *possible;
400
+  g_autoptr(GPtrArray) possible = NULL;
401
   enum pw_stream_state state;
402
   const char *error = NULL;
403
   gboolean res = FALSE;
404
-  GstStructure *config;
405
+  GstStructure *config, *s;
406
   guint size;
407
   guint min_buffers;
408
   guint max_buffers;
409
   struct timespec abstime;
410
+  gint rate;
411
 
412
   pwsink = GST_PIPEWIRE_SINK (bsink);
413
 
414
-  possible = gst_caps_to_format_all (caps, SPA_PARAM_EnumFormat);
415
+  s = gst_caps_get_structure (caps, 0);
416
+  rate = 0;
417
+  if (gst_structure_has_name (s, "audio/x-raw"))
418
+    gst_structure_get_int (s, "rate", &rate);
419
 
420
-  pw_thread_loop_lock (pwsink->core->loop);
421
-  state = pw_stream_get_state (pwsink->stream, &error);
422
+  possible = gst_caps_to_format_all (caps);
423
+
424
+  pw_thread_loop_lock (pwsink->stream->core->loop);
425
+  state = pw_stream_get_state (pwsink->stream->pwstream, &error);
426
 
427
   if (state == PW_STREAM_STATE_ERROR)
428
     goto start_error;
429
@@ -586,6 +628,9 @@
430
   if (state == PW_STREAM_STATE_UNCONNECTED) {
431
     enum pw_stream_flags flags;
432
     uint32_t target_id;
433
+    struct spa_dict_item items3;
434
+    uint32_t n_items = 0;
435
+    char buf64;
436
 
437
     flags = PW_STREAM_FLAG_ASYNC;
438
     if (pwsink->mode != GST_PIPEWIRE_SINK_MODE_PROVIDE)
439
@@ -593,41 +638,40 @@
440
     else
441
       flags |= PW_STREAM_FLAG_DRIVER;
442
 
443
-    target_id = pwsink->path ? (uint32_t)atoi(pwsink->path) : PW_ID_ANY;
444
+    target_id = pwsink->stream->path ? (uint32_t)atoi(pwsink->stream->path) : PW_ID_ANY;
445
 
446
-    if (pwsink->target_object) {
447
-      struct spa_dict_item items2 = {
448
-        SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsink->target_object),
449
-   /* XXX deprecated but the portal and some example apps only
450
-    * provide the object id */
451
-        SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL),
452
-      };
453
-      struct spa_dict dict = SPA_DICT_INIT_ARRAY(items);
454
+    if (pwsink->stream->target_object) {
455
       uint64_t serial;
456
 
457
+      itemsn_items++ = SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsink->stream->target_object);
458
+
459
       /* If target.object is a name, set it also to node.target */
460
-      if (spa_atou64(pwsink->target_object, &serial, 0)) {
461
-        dict.n_items = 1;
462
-      } else {
463
+      if (!spa_atou64(pwsink->stream->target_object, &serial, 0)) {
464
         target_id = PW_ID_ANY;
465
-        items1.value = pwsink->target_object;
466
+        /* XXX deprecated but the portal and some example apps only
467
+         * provide the object id */
468
+        itemsn_items++ = SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, pwsink->stream->target_object);
469
       }
470
-
471
-      pw_stream_update_properties (pwsink->stream, &dict);
472
     }
473
+    if (rate != 0) {
474
+      snprintf(buf, sizeof(buf), "1/%u", rate);
475
+      itemsn_items++ = SPA_DICT_ITEM_INIT(PW_KEY_NODE_RATE, buf);
476
+    }
477
+    if (n_items > 0)
478
+       pw_stream_update_properties (pwsink->stream->pwstream, &SPA_DICT_INIT(items, n_items));
479
 
480
-    pw_stream_connect (pwsink->stream,
481
+    pw_stream_connect (pwsink->stream->pwstream,
482
                           PW_DIRECTION_OUTPUT,
483
                           target_id,
484
                           flags,
485
                           (const struct spa_pod **) possible->pdata,
486
                           possible->len);
487
 
488
-    pw_thread_loop_get_time (pwsink->core->loop, &abstime,
489
+    pw_thread_loop_get_time (pwsink->stream->core->loop, &abstime,
490
               GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC);
491
 
492
     while (TRUE) {
493
-      state = pw_stream_get_state (pwsink->stream, &error);
494
+      state = pw_stream_get_state (pwsink->stream->pwstream, &error);
495
 
496
       if (state >= PW_STREAM_STATE_PAUSED)
497
         break;
498
@@ -635,7 +679,7 @@
499
       if (state == PW_STREAM_STATE_ERROR)
500
         goto start_error;
501
 
502
-      if (pw_thread_loop_timed_wait_full (pwsink->core->loop, &abstime) < 0) {
503
+      if (pw_thread_loop_timed_wait_full (pwsink->stream->core->loop, &abstime) < 0) {
504
         error = "timeout";
505
         goto start_error;
506
       }
507
@@ -643,12 +687,14 @@
508
   }
509
   res = TRUE;
510
 
511
-  config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pwsink->pool));
512
+  gst_pipewire_clock_reset (GST_PIPEWIRE_CLOCK (pwsink->stream->clock), 0);
513
+
514
+  config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pwsink->stream->pool));
515
   gst_buffer_pool_config_get_params (config, NULL, &size, &min_buffers, &max_buffers);
516
   gst_buffer_pool_config_set_params (config, caps, size, min_buffers, max_buffers);
517
-  gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pwsink->pool), config);
518
+  gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pwsink->stream->pool), config);
519
 
520
-  pw_thread_loop_unlock (pwsink->core->loop);
521
+  pw_thread_loop_unlock (pwsink->stream->core->loop);
522
 
523
   pwsink->negotiated = res;
524
 
525
@@ -656,9 +702,8 @@
526
 
527
 start_error:
528
   {
529
-    GST_ERROR ("could not start stream: %s", error);
530
-    pw_thread_loop_unlock (pwsink->core->loop);
531
-    g_ptr_array_unref (possible);
532
+    GST_ERROR_OBJECT (pwsink, "could not start stream: %s", error);
533
+    pw_thread_loop_unlock (pwsink->stream->core->loop);
534
     return FALSE;
535
   }
536
 }
537
@@ -676,13 +721,13 @@
538
   if (!pwsink->negotiated)
539
     goto not_negotiated;
540
 
541
-  if (buffer->pool != GST_BUFFER_POOL_CAST (pwsink->pool) &&
542
-      !gst_buffer_pool_is_active (GST_BUFFER_POOL_CAST (pwsink->pool))) {
543
+  if (buffer->pool != GST_BUFFER_POOL_CAST (pwsink->stream->pool) &&
544
+      !gst_buffer_pool_is_active (GST_BUFFER_POOL_CAST (pwsink->stream->pool))) {
545
     GstStructure *config;
546
     GstCaps *caps;
547
     guint size, min_buffers, max_buffers;
548
 
549
-    config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pwsink->pool));
550
+    config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pwsink->stream->pool));
551
     gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers, &max_buffers);
552
 
553
     if (size == 0) {
554
@@ -692,23 +737,23 @@
555
     }
556
 
557
     gst_buffer_pool_config_set_params (config, caps, size, min_buffers, max_buffers);
558
-    gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pwsink->pool), config);
559
+    gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pwsink->stream->pool), config);
560
 
561
-    gst_buffer_pool_set_active (GST_BUFFER_POOL_CAST (pwsink->pool), TRUE);
562
+    gst_buffer_pool_set_active (GST_BUFFER_POOL_CAST (pwsink->stream->pool), TRUE);
563
   }
564
 
565
-  pw_thread_loop_lock (pwsink->core->loop);
566
-  if (pw_stream_get_state (pwsink->stream, &error) != PW_STREAM_STATE_STREAMING)
567
+  pw_thread_loop_lock (pwsink->stream->core->loop);
568
+  if (pw_stream_get_state (pwsink->stream->pwstream, &error) != PW_STREAM_STATE_STREAMING)
569
     goto done_unlock;
570
 
571
-  if (buffer->pool != GST_BUFFER_POOL_CAST (pwsink->pool)) {
572
+  if (buffer->pool != GST_BUFFER_POOL_CAST (pwsink->stream->pool)) {
573
     GstBuffer *b = NULL;
574
     GstMapInfo info = { 0, };
575
     GstBufferPoolAcquireParams params = { 0, };
576
 
577
-    pw_thread_loop_unlock (pwsink->core->loop);
578
+    pw_thread_loop_unlock (pwsink->stream->core->loop);
579
 
580
-    if ((res = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL_CAST (pwsink->pool), &b, &params)) != GST_FLOW_OK)
581
+    if ((res = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL_CAST (pwsink->stream->pool), &b, &params)) != GST_FLOW_OK)
582
       goto done;
583
 
584
     gst_buffer_map (b, &info, GST_MAP_WRITE);
585
@@ -719,21 +764,20 @@
586
     buffer = b;
587
     unref_buffer = TRUE;
588
 
589
-    pw_thread_loop_lock (pwsink->core->loop);
590
-    if (pw_stream_get_state (pwsink->stream, &error) != PW_STREAM_STATE_STREAMING)
591
+    pw_thread_loop_lock (pwsink->stream->core->loop);
592
+    if (pw_stream_get_state (pwsink->stream->pwstream, &error) != PW_STREAM_STATE_STREAMING)
593
       goto done_unlock;
594
   }
595
 
596
-  GST_DEBUG ("push buffer");
597
   do_send_buffer (pwsink, buffer);
598
   if (unref_buffer)
599
     gst_buffer_unref (buffer);
600
 
601
-  if (pw_stream_is_driving (pwsink->stream))
602
-    pw_stream_trigger_process (pwsink->stream);
603
+  if (pw_stream_is_driving (pwsink->stream->pwstream))
604
+    pw_stream_trigger_process (pwsink->stream->pwstream);
605
 
606
 done_unlock:
607
-  pw_thread_loop_unlock (pwsink->core->loop);
608
+  pw_thread_loop_unlock (pwsink->stream->core->loop);
609
 done:
610
   return res;
611
 
612
@@ -743,26 +787,6 @@
613
   }
614
 }
615
 
616
-static gboolean
617
-copy_properties (GQuark field_id,
618
-                 const GValue *value,
619
-                 gpointer user_data)
620
-{
621
-  struct pw_properties *properties = user_data;
622
-  GValue dst = { 0 };
623
-
624
-  if (g_value_type_transformable (G_VALUE_TYPE(value), G_TYPE_STRING)) {
625
-    g_value_init(&dst, G_TYPE_STRING);
626
-    if (g_value_transform(value, &dst)) {
627
-      pw_properties_set (properties,
628
-                         g_quark_to_string (field_id),
629
-                         g_value_get_string (&dst));
630
-    }
631
-    g_value_unset(&dst);
632
-  }
633
-  return TRUE;
634
-}
635
-
636
 static const struct pw_stream_events stream_events = {
637
         PW_VERSION_STREAM_EVENTS,
638
         .state_changed = on_state_changed,
639
@@ -772,114 +796,6 @@
640
         .process = on_process,
641
 };
642
 
643
-static gboolean
644
-gst_pipewire_sink_start (GstBaseSink * basesink)
645
-{
646
-  GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (basesink);
647
-  struct pw_properties *props;
648
-
649
-  pwsink->negotiated = FALSE;
650
-
651
-  pw_thread_loop_lock (pwsink->core->loop);
652
-
653
-  props = pw_properties_new (NULL, NULL);
654
-  if (pwsink->client_name) {
655
-    pw_properties_set (props, PW_KEY_NODE_NAME, pwsink->client_name);
656
-    pw_properties_set (props, PW_KEY_NODE_DESCRIPTION, pwsink->client_name);
657
-  }
658
-  if (pwsink->stream_properties) {
659
-    gst_structure_foreach (pwsink->stream_properties, copy_properties, props);
660
-  }
661
-
662
-  if ((pwsink->stream = pw_stream_new (pwsink->core->core, pwsink->client_name, props)) == NULL)
663
-    goto no_stream;
664
-
665
-  pwsink->pool->stream = pwsink->stream;
666
-
667
-  pw_stream_add_listener(pwsink->stream,
668
-                         &pwsink->stream_listener,
669
-                         &stream_events,
670
-                         pwsink);
671
-
672
-  pw_thread_loop_unlock (pwsink->core->loop);
673
-
674
-  return TRUE;
675
-
676
-no_stream:
677
-  {
678
-    GST_ELEMENT_ERROR (pwsink, RESOURCE, FAILED, ("can't create stream"), (NULL));
679
-    pw_thread_loop_unlock (pwsink->core->loop);
680
-    return FALSE;
681
-  }
682
-}
683
-
684
-static gboolean
685
-gst_pipewire_sink_stop (GstBaseSink * basesink)
686
-{
687
-  GstPipeWireSink *pwsink = GST_PIPEWIRE_SINK (basesink);
688
-
689
-  pw_thread_loop_lock (pwsink->core->loop);
690
-  if (pwsink->stream) {
691
-    pw_stream_destroy (pwsink->stream);
692
-    pwsink->stream = NULL;
693
-    pwsink->pool->stream = NULL;
694
-  }
695
-  pw_thread_loop_unlock (pwsink->core->loop);
696
-
697
-  pwsink->negotiated = FALSE;
698
-
699
-  return TRUE;
700
-}
701
-
702
-static gboolean
703
-gst_pipewire_sink_open (GstPipeWireSink * pwsink)
704
-{
705
-  struct pw_properties *props;
706
-
707
-  GST_DEBUG_OBJECT (pwsink, "open");
708
-
709
-  pwsink->core = gst_pipewire_core_get(pwsink->fd);
710
-  if (pwsink->core == NULL)
711
-      goto connect_error;
712
-
713
-  pw_thread_loop_lock (pwsink->core->loop);
714
-
715
-  props = pw_properties_new (NULL, NULL);
716
-  if (pwsink->client_properties) {
717
-    gst_structure_foreach (pwsink->client_properties, copy_properties, props);
718
-    pw_core_update_properties (pwsink->core->core, &props->dict);
719
-  }
720
-  pw_properties_free(props);
721
-  pw_thread_loop_unlock (pwsink->core->loop);
722
-
723
-  return TRUE;
724
-
725
-  /* ERRORS */
726
-connect_error:
727
-  {
728
-    GST_ELEMENT_ERROR (pwsink, RESOURCE, FAILED,
729
-        ("Failed to connect"), (NULL));
730
-    return FALSE;
731
-  }
732
-}
733
-
734
-static gboolean
735
-gst_pipewire_sink_close (GstPipeWireSink * pwsink)
736
-{
737
-  pw_thread_loop_lock (pwsink->core->loop);
738
-  if (pwsink->stream) {
739
-    pw_stream_destroy (pwsink->stream);
740
-    pwsink->stream = NULL;
741
-  }
742
-  pw_thread_loop_unlock (pwsink->core->loop);
743
-
744
-  if (pwsink->core) {
745
-    gst_pipewire_core_release (pwsink->core);
746
-    pwsink->core = NULL;
747
-  }
748
-  return TRUE;
749
-}
750
-
751
 static GstStateChangeReturn
752
 gst_pipewire_sink_change_state (GstElement * element, GstStateChange transition)
753
 {
754
@@ -888,24 +804,33 @@
755
 
756
   switch (transition) {
757
     case GST_STATE_CHANGE_NULL_TO_READY:
758
-      if (!gst_pipewire_sink_open (this))
759
+      if (!gst_pipewire_stream_open (this->stream, &stream_events))
760
         goto open_failed;
761
       break;
762
     case GST_STATE_CHANGE_READY_TO_PAUSED:
763
+      /* the initial stream state is active, which is needed for linking and
764
+       * negotiation to happen and the bufferpool to be set up. We don't know
765
+       * if we'll go to plaing, so we deactivate the stream until that
766
+       * transition happens. This is janky, but because of how bins propagate
767
+       * state changes one transition at a time, there may not be a better way
768
+       * to do this. PAUSED -> READY -> PAUSED transitions, this is a noop */
769
+      pw_thread_loop_lock (this->stream->core->loop);
770
+      pw_stream_set_active(this->stream->pwstream, false);
771
+      pw_thread_loop_unlock (this->stream->core->loop);
772
       break;
773
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
774
       /* uncork and start play */
775
-      pw_thread_loop_lock (this->core->loop);
776
-      pw_stream_set_active(this->stream, true);
777
-      pw_thread_loop_unlock (this->core->loop);
778
-      gst_buffer_pool_set_flushing(GST_BUFFER_POOL_CAST(this->pool), FALSE);
779
+      pw_thread_loop_lock (this->stream->core->loop);
780
+      pw_stream_set_active(this->stream->pwstream, true);
781
+      pw_thread_loop_unlock (this->stream->core->loop);
782
+      gst_buffer_pool_set_flushing(GST_BUFFER_POOL_CAST(this->stream->pool), FALSE);
783
       break;
784
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
785
       /* stop play ASAP by corking */
786
-      pw_thread_loop_lock (this->core->loop);
787
-      pw_stream_set_active(this->stream, false);
788
-      pw_thread_loop_unlock (this->core->loop);
789
-      gst_buffer_pool_set_flushing(GST_BUFFER_POOL_CAST(this->pool), TRUE);
790
+      pw_thread_loop_lock (this->stream->core->loop);
791
+      pw_stream_set_active(this->stream->pwstream, false);
792
+      pw_thread_loop_unlock (this->stream->core->loop);
793
+      gst_buffer_pool_set_flushing(GST_BUFFER_POOL_CAST(this->stream->pool), TRUE);
794
       break;
795
     default:
796
       break;
797
@@ -917,10 +842,11 @@
798
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
799
       break;
800
     case GST_STATE_CHANGE_PAUSED_TO_READY:
801
-      gst_buffer_pool_set_active(GST_BUFFER_POOL_CAST(this->pool), FALSE);
802
+      gst_buffer_pool_set_active(GST_BUFFER_POOL_CAST(this->stream->pool), FALSE);
803
+      this->negotiated = FALSE;
804
       break;
805
     case GST_STATE_CHANGE_READY_TO_NULL:
806
-      gst_pipewire_sink_close (this);
807
+      gst_pipewire_stream_close (this->stream);
808
       break;
809
     default:
810
       break;
811
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiresink.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiresink.h Changed
70
 
1
@@ -5,6 +5,8 @@
2
 #ifndef __GST_PIPEWIRE_SINK_H__
3
 #define __GST_PIPEWIRE_SINK_H__
4
 
5
+#include "gstpipewirestream.h"
6
+
7
 #include <gst/gst.h>
8
 #include <gst/base/gstbasesink.h>
9
 
10
@@ -14,22 +16,9 @@
11
 
12
 G_BEGIN_DECLS
13
 
14
-#define GST_TYPE_PIPEWIRE_SINK \
15
-  (gst_pipewire_sink_get_type())
16
-#define GST_PIPEWIRE_SINK(obj) \
17
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIPEWIRE_SINK,GstPipeWireSink))
18
-#define GST_PIPEWIRE_SINK_CLASS(klass) \
19
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIPEWIRE_SINK,GstPipeWireSinkClass))
20
-#define GST_IS_PIPEWIRE_SINK(obj) \
21
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIPEWIRE_SINK))
22
-#define GST_IS_PIPEWIRE_SINK_CLASS(klass) \
23
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIPEWIRE_SINK))
24
-#define GST_PIPEWIRE_SINK_CAST(obj) \
25
-  ((GstPipeWireSink *) (obj))
26
-
27
-typedef struct _GstPipeWireSink GstPipeWireSink;
28
-typedef struct _GstPipeWireSinkClass GstPipeWireSinkClass;
29
-
30
+#define GST_TYPE_PIPEWIRE_SINK (gst_pipewire_sink_get_type())
31
+#define GST_PIPEWIRE_SINK_CAST(obj) ((GstPipeWireSink *) (obj))
32
+G_DECLARE_FINAL_TYPE (GstPipeWireSink, gst_pipewire_sink, GST, PIPEWIRE_SINK, GstBaseSink)
33
 
34
 /**
35
  * GstPipeWireSinkMode:
36
@@ -57,32 +46,14 @@
37
   GstBaseSink element;
38
 
39
   /*< private >*/
40
-  gchar *path;
41
-  gchar *target_object;
42
-  gchar *client_name;
43
-  int fd;
44
+  GstPipeWireStream *stream;
45
 
46
   /* video state */
47
   gboolean negotiated;
48
 
49
-  GstPipeWireCore *core;
50
-  struct spa_hook core_listener;
51
-  GstStructure *client_properties;
52
-
53
-  struct pw_stream *stream;
54
-  struct spa_hook stream_listener;
55
-
56
-  GstStructure *stream_properties;
57
   GstPipeWireSinkMode mode;
58
-
59
-  GstPipeWirePool *pool;
60
-};
61
-
62
-struct _GstPipeWireSinkClass {
63
-  GstBaseSinkClass parent_class;
64
 };
65
 
66
-GType gst_pipewire_sink_get_type (void);
67
 GType gst_pipewire_sink_mode_get_type (void);
68
 
69
 G_END_DECLS
70
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiresrc.c -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiresrc.c Changed
1080
 
1
@@ -15,7 +15,6 @@
2
 
3
 #define PW_ENABLE_DEPRECATED
4
 
5
-#include "config.h"
6
 #include "gstpipewiresrc.h"
7
 #include "gstpipewireformat.h"
8
 
9
@@ -102,31 +101,31 @@
10
 
11
   switch (prop_id) {
12
     case PROP_PATH:
13
-      g_free (pwsrc->path);
14
-      pwsrc->path = g_value_dup_string (value);
15
+      g_free (pwsrc->stream->path);
16
+      pwsrc->stream->path = g_value_dup_string (value);
17
       break;
18
 
19
     case PROP_TARGET_OBJECT:
20
-      g_free (pwsrc->target_object);
21
-      pwsrc->target_object = g_value_dup_string (value);
22
+      g_free (pwsrc->stream->target_object);
23
+      pwsrc->stream->target_object = g_value_dup_string (value);
24
       break;
25
 
26
     case PROP_CLIENT_NAME:
27
-      g_free (pwsrc->client_name);
28
-      pwsrc->client_name = g_value_dup_string (value);
29
+      g_free (pwsrc->stream->client_name);
30
+      pwsrc->stream->client_name = g_value_dup_string (value);
31
       break;
32
 
33
     case PROP_CLIENT_PROPERTIES:
34
-      if (pwsrc->client_properties)
35
-        gst_structure_free (pwsrc->client_properties);
36
-      pwsrc->client_properties =
37
+      if (pwsrc->stream->client_properties)
38
+        gst_structure_free (pwsrc->stream->client_properties);
39
+      pwsrc->stream->client_properties =
40
           gst_structure_copy (gst_value_get_structure (value));
41
       break;
42
 
43
     case PROP_STREAM_PROPERTIES:
44
-      if (pwsrc->stream_properties)
45
-        gst_structure_free (pwsrc->stream_properties);
46
-      pwsrc->stream_properties =
47
+      if (pwsrc->stream->stream_properties)
48
+        gst_structure_free (pwsrc->stream->stream_properties);
49
+      pwsrc->stream->stream_properties =
50
           gst_structure_copy (gst_value_get_structure (value));
51
       break;
52
 
53
@@ -143,7 +142,7 @@
54
       break;
55
 
56
     case PROP_FD:
57
-      pwsrc->fd = g_value_get_int (value);
58
+      pwsrc->stream->fd = g_value_get_int (value);
59
       break;
60
 
61
     case PROP_RESEND_LAST:
62
@@ -172,23 +171,23 @@
63
 
64
   switch (prop_id) {
65
     case PROP_PATH:
66
-      g_value_set_string (value, pwsrc->path);
67
+      g_value_set_string (value, pwsrc->stream->path);
68
       break;
69
 
70
     case PROP_TARGET_OBJECT:
71
-      g_value_set_string (value, pwsrc->target_object);
72
+      g_value_set_string (value, pwsrc->stream->target_object);
73
       break;
74
 
75
     case PROP_CLIENT_NAME:
76
-      g_value_set_string (value, pwsrc->client_name);
77
+      g_value_set_string (value, pwsrc->stream->client_name);
78
       break;
79
 
80
     case PROP_CLIENT_PROPERTIES:
81
-      gst_value_set_structure (value, pwsrc->client_properties);
82
+      gst_value_set_structure (value, pwsrc->stream->client_properties);
83
       break;
84
 
85
     case PROP_STREAM_PROPERTIES:
86
-      gst_value_set_structure (value, pwsrc->stream_properties);
87
+      gst_value_set_structure (value, pwsrc->stream->stream_properties);
88
       break;
89
 
90
     case PROP_ALWAYS_COPY:
91
@@ -204,7 +203,7 @@
92
       break;
93
 
94
     case PROP_FD:
95
-      g_value_set_int (value, pwsrc->fd);
96
+      g_value_set_int (value, pwsrc->stream->fd);
97
       break;
98
 
99
     case PROP_RESEND_LAST:
100
@@ -235,8 +234,8 @@
101
   if (!GST_OBJECT_FLAG_IS_SET (pwsrc, GST_ELEMENT_FLAG_PROVIDE_CLOCK))
102
     goto clock_disabled;
103
 
104
-  if (pwsrc->clock && pwsrc->is_live)
105
-    clock = GST_CLOCK_CAST (gst_object_ref (pwsrc->clock));
106
+  if (pwsrc->stream->clock && pwsrc->is_live)
107
+    clock = GST_CLOCK_CAST (gst_object_ref (pwsrc->stream->clock));
108
   else
109
     clock = NULL;
110
   GST_OBJECT_UNLOCK (pwsrc);
111
@@ -257,16 +256,7 @@
112
 {
113
   GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (object);
114
 
115
-  if (pwsrc->stream_properties)
116
-    gst_structure_free (pwsrc->stream_properties);
117
-  if (pwsrc->client_properties)
118
-    gst_structure_free (pwsrc->client_properties);
119
-  if (pwsrc->clock)
120
-    gst_object_unref (pwsrc->clock);
121
-  g_free (pwsrc->path);
122
-  g_free (pwsrc->target_object);
123
-  g_free (pwsrc->client_name);
124
-  g_object_unref(pwsrc->pool);
125
+  gst_clear_object (&pwsrc->stream);
126
 
127
   G_OBJECT_CLASS (parent_class)->finalize (object);
128
 }
129
@@ -402,8 +392,8 @@
130
   gstelement_class->send_event = gst_pipewire_src_send_event;
131
 
132
   gst_element_class_set_static_metadata (gstelement_class,
133
-      "PipeWire source", "Source/Video",
134
-      "Uses PipeWire to create video", "Wim Taymans <wim.taymans@gmail.com>");
135
+      "PipeWire source", "Source/Audio/Video",
136
+      "Uses PipeWire to create audio/video", "Wim Taymans <wim.taymans@gmail.com>");
137
 
138
   gst_element_class_add_pad_template (gstelement_class,
139
       gst_static_pad_template_get (&gst_pipewire_src_template));
140
@@ -435,17 +425,16 @@
141
 
142
   GST_OBJECT_FLAG_SET (src, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
143
 
144
+  src->stream = gst_pipewire_stream_new (GST_ELEMENT (src));
145
+
146
   src->always_copy = DEFAULT_ALWAYS_COPY;
147
   src->min_buffers = DEFAULT_MIN_BUFFERS;
148
   src->max_buffers = DEFAULT_MAX_BUFFERS;
149
-  src->fd = -1;
150
   src->resend_last = DEFAULT_RESEND_LAST;
151
   src->keepalive_time = DEFAULT_KEEPALIVE_TIME;
152
   src->autoconnect = DEFAULT_AUTOCONNECT;
153
-
154
-  src->client_name = g_strdup(pw_get_client_name ());
155
-
156
-  src->pool =  gst_pipewire_pool_new ();
157
+  src->min_latency = 0;
158
+  src->max_latency = GST_CLOCK_TIME_NONE;
159
 }
160
 
161
 static gboolean
162
@@ -466,9 +455,9 @@
163
   GST_BUFFER_FLAGS (obj) = data->flags;
164
   src = data->owner;
165
 
166
-  pw_thread_loop_lock (src->core->loop);
167
+  pw_thread_loop_lock (src->stream->core->loop);
168
   if (!obj->dispose) {
169
-    pw_thread_loop_unlock (src->core->loop);
170
+    pw_thread_loop_unlock (src->stream->core->loop);
171
     GST_OBJECT_UNLOCK (data->pool);
172
     return TRUE;
173
   }
174
@@ -477,12 +466,12 @@
175
 
176
   data->queued = TRUE;
177
 
178
-  if ((res = pw_stream_queue_buffer (src->stream, data->b)) < 0)
179
+  if ((res = pw_stream_queue_buffer (src->stream->pwstream, data->b)) < 0)
180
     GST_WARNING_OBJECT (src, "can't queue recycled buffer %p, %s", obj, spa_strerror(res));
181
   else
182
     GST_LOG_OBJECT (src, "recycle buffer %p", obj);
183
 
184
-  pw_thread_loop_unlock (src->core->loop);
185
+  pw_thread_loop_unlock (src->stream->core->loop);
186
 
187
   GST_OBJECT_UNLOCK (data->pool);
188
 
189
@@ -495,7 +484,7 @@
190
   GstPipeWireSrc *pwsrc = _data;
191
   GstPipeWirePoolData *data;
192
 
193
-  gst_pipewire_pool_wrap_buffer (pwsrc->pool, b);
194
+  gst_pipewire_pool_wrap_buffer (pwsrc->stream->pool, b);
195
   data = b->user_data;
196
   GST_DEBUG_OBJECT (pwsrc, "add buffer %p", data->buf);
197
   data->owner = pwsrc;
198
@@ -518,7 +507,7 @@
199
   if (data->queued) {
200
     gst_buffer_unref (buf);
201
   } else {
202
-    if ((res = pw_stream_queue_buffer (pwsrc->stream, b)) < 0)
203
+    if ((res = pw_stream_queue_buffer (pwsrc->stream->pwstream, b)) < 0)
204
       GST_WARNING_OBJECT (pwsrc, "can't queue removed buffer %p, %s", buf, spa_strerror(res));
205
   }
206
 }
207
@@ -550,9 +539,10 @@
208
   struct spa_meta_header *h;
209
   struct spa_meta_region *crop;
210
   struct spa_meta_videotransform *videotransform;
211
+  struct pw_time time;
212
   guint i;
213
 
214
-  b = pw_stream_dequeue_buffer (pwsrc->stream);
215
+  b = pw_stream_dequeue_buffer (pwsrc->stream->pwstream);
216
   if (b == NULL)
217
           return NULL;
218
 
219
@@ -568,6 +558,17 @@
220
     return NULL;
221
   }
222
 
223
+  pw_stream_get_time_n(pwsrc->stream->pwstream, &time, sizeof(time));
224
+
225
+  if (pwsrc->delay != time.delay && time.rate.denom != 0) {
226
+    pwsrc->min_latency = time.delay * GST_SECOND * time.rate.num / time.rate.denom;
227
+    GST_LOG_OBJECT (pwsrc, "latency changed %"PRIi64" -> %"PRIi64" %"PRIu64,
228
+           pwsrc->delay, time.delay, pwsrc->min_latency);
229
+    pwsrc->delay = time.delay;
230
+    gst_element_post_message (GST_ELEMENT_CAST (pwsrc),
231
+      gst_message_new_latency (GST_OBJECT_CAST (pwsrc)));
232
+  }
233
+
234
   GST_LOG_OBJECT (pwsrc, "got new buffer %p", data->buf);
235
 
236
   buf = gst_buffer_new ();
237
@@ -586,7 +587,21 @@
238
         GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) + h->dts_offset;
239
     }
240
     GST_BUFFER_OFFSET (buf) = h->seq;
241
+  } else {
242
+    GST_BUFFER_PTS (buf) = b->time - pwsrc->delay;
243
+    GST_BUFFER_DTS (buf) = b->time - pwsrc->delay;
244
   }
245
+
246
+  if (pwsrc->is_video) {
247
+    if (pwsrc->video_info.fps_n) {
248
+      GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (GST_SECOND,
249
+          pwsrc->video_info.fps_d, pwsrc->video_info.fps_n);
250
+    }
251
+  } else {
252
+    GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (GST_SECOND,
253
+        time.size * time.rate.num, time.rate.denom);
254
+  }
255
+
256
   crop = data->crop;
257
   if (crop) {
258
     GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buf);
259
@@ -662,7 +677,7 @@
260
 on_process (void *_data)
261
 {
262
   GstPipeWireSrc *pwsrc = _data;
263
-  pw_thread_loop_signal (pwsrc->core->loop, FALSE);
264
+  pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
265
 }
266
 
267
 static void
268
@@ -683,14 +698,14 @@
269
     case PW_STREAM_STATE_ERROR:
270
       /* make the error permanent, if it is not already;
271
          pw_stream_set_error() will recursively call us again */
272
-      if (pw_stream_get_state (pwsrc->stream, NULL) != PW_STREAM_STATE_ERROR)
273
-        pw_stream_set_error (pwsrc->stream, -EPIPE, "%s", error);
274
+      if (pw_stream_get_state (pwsrc->stream->pwstream, NULL) != PW_STREAM_STATE_ERROR)
275
+        pw_stream_set_error (pwsrc->stream->pwstream, -EPIPE, "%s", error);
276
       else
277
         GST_ELEMENT_ERROR (pwsrc, RESOURCE, FAILED,
278
             ("stream error: %s", error), (NULL));
279
       break;
280
   }
281
-  pw_thread_loop_signal (pwsrc->core->loop, FALSE);
282
+  pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
283
 }
284
 
285
 static void
286
@@ -702,12 +717,6 @@
287
   GST_OBJECT_LOCK (pwsrc);
288
   var = pw_properties_get (props, PW_KEY_STREAM_IS_LIVE);
289
   is_live = pwsrc->is_live = var ? pw_properties_parse_bool(var) : TRUE;
290
-
291
-  var = pw_properties_get (props, PW_KEY_STREAM_LATENCY_MIN);
292
-  pwsrc->min_latency = var ? (GstClockTime) atoi (var) : 0;
293
-
294
-  var = pw_properties_get (props, PW_KEY_STREAM_LATENCY_MAX);
295
-  pwsrc->max_latency = var ? (GstClockTime) atoi (var) : GST_CLOCK_TIME_NONE;
296
   GST_OBJECT_UNLOCK (pwsrc);
297
 
298
   GST_DEBUG_OBJECT (pwsrc, "live %d", is_live);
299
@@ -721,14 +730,14 @@
300
   const char *error = NULL;
301
   struct timespec abstime;
302
 
303
-  pw_thread_loop_lock (pwsrc->core->loop);
304
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
305
   GST_DEBUG_OBJECT (pwsrc, "doing stream start");
306
 
307
-  pw_thread_loop_get_time (pwsrc->core->loop, &abstime,
308
+  pw_thread_loop_get_time (pwsrc->stream->core->loop, &abstime,
309
                   GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC);
310
 
311
   while (TRUE) {
312
-    enum pw_stream_state state = pw_stream_get_state (pwsrc->stream, &error);
313
+    enum pw_stream_state state = pw_stream_get_state (pwsrc->stream->pwstream, &error);
314
 
315
     GST_DEBUG_OBJECT (pwsrc, "waiting for STREAMING, now %s", pw_stream_state_as_string (state));
316
     if (state == PW_STREAM_STATE_STREAMING)
317
@@ -742,25 +751,26 @@
318
       goto start_error;
319
     }
320
 
321
-    if (pw_thread_loop_timed_wait_full (pwsrc->core->loop, &abstime) < 0) {
322
+    if (pw_thread_loop_timed_wait_full (pwsrc->stream->core->loop, &abstime) < 0) {
323
       error = "timeout";
324
       goto start_error;
325
     }
326
   }
327
 
328
-  parse_stream_properties (pwsrc, pw_stream_get_properties (pwsrc->stream));
329
+  parse_stream_properties (pwsrc, pw_stream_get_properties (pwsrc->stream->pwstream));
330
   GST_DEBUG_OBJECT (pwsrc, "signal started");
331
   pwsrc->started = TRUE;
332
-  pw_thread_loop_signal (pwsrc->core->loop, FALSE);
333
-  pw_thread_loop_unlock (pwsrc->core->loop);
334
+  pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
335
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
336
 
337
   return TRUE;
338
 
339
 start_error:
340
   {
341
     GST_DEBUG_OBJECT (pwsrc, "error starting stream: %s", error);
342
-    pw_thread_loop_signal (pwsrc->core->loop, FALSE);
343
-    pw_thread_loop_unlock (pwsrc->core->loop);
344
+    pwsrc->started = FALSE;
345
+    pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
346
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
347
     return FALSE;
348
   }
349
 }
350
@@ -771,20 +781,29 @@
351
   enum pw_stream_state state, prev_state = PW_STREAM_STATE_UNCONNECTED;
352
   const char *error = NULL;
353
   struct timespec abstime;
354
+  gboolean restart = FALSE;
355
 
356
-  pw_thread_loop_lock (this->core->loop);
357
+  pw_thread_loop_lock (this->stream->core->loop);
358
 
359
-  pw_thread_loop_get_time (this->core->loop, &abstime,
360
+  pw_thread_loop_get_time (this->stream->core->loop, &abstime,
361
                   GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC);
362
 
363
+  /* when started already is true then expects a re-start, so allow prev_state
364
+   * degrade until turned around. */
365
+  if (this->started) {
366
+    GST_DEBUG_OBJECT (this, "restart in progress");
367
+    restart = TRUE;
368
+    this->started = FALSE;
369
+  }
370
+
371
   while (TRUE) {
372
-    state = pw_stream_get_state (this->stream, &error);
373
+    state = pw_stream_get_state (this->stream->pwstream, &error);
374
 
375
     GST_DEBUG_OBJECT (this, "waiting for started signal, state now %s",
376
         pw_stream_state_as_string (state));
377
 
378
     if (state == PW_STREAM_STATE_ERROR ||
379
-        (state == PW_STREAM_STATE_UNCONNECTED && prev_state > PW_STREAM_STATE_UNCONNECTED) ||
380
+        (state == PW_STREAM_STATE_UNCONNECTED && prev_state > PW_STREAM_STATE_UNCONNECTED && !restart) ||
381
         this->flushing) {
382
       state = PW_STREAM_STATE_ERROR;
383
       break;
384
@@ -793,16 +812,22 @@
385
     if (this->started)
386
       break;
387
 
388
-    if (pw_thread_loop_timed_wait_full (this->core->loop, &abstime) < 0) {
389
-      state = PW_STREAM_STATE_ERROR;
390
-      break;
391
+    if (this->autoconnect) {
392
+      if (pw_thread_loop_timed_wait_full (this->stream->core->loop, &abstime) < 0) {
393
+        state = PW_STREAM_STATE_ERROR;
394
+        break;
395
+      }
396
+    } else {
397
+      pw_thread_loop_wait (this->stream->core->loop);
398
     }
399
 
400
+    if (restart)
401
+      restart = state != PW_STREAM_STATE_UNCONNECTED;
402
     prev_state = state;
403
   }
404
   GST_DEBUG_OBJECT (this, "got started signal: %s",
405
                   pw_stream_state_as_string (state));
406
-  pw_thread_loop_unlock (this->core->loop);
407
+  pw_thread_loop_unlock (this->stream->core->loop);
408
 
409
   return state;
410
 }
411
@@ -811,11 +836,12 @@
412
 gst_pipewire_src_negotiate (GstBaseSrc * basesrc)
413
 {
414
   GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (basesrc);
415
-  GstCaps *thiscaps;
416
-  GstCaps *caps = NULL;
417
-  GstCaps *peercaps = NULL;
418
+  g_autoptr (GstCaps) thiscaps = NULL;
419
+  g_autoptr (GstCaps) possible_caps = NULL;
420
+  g_autoptr (GstCaps) negotiated_caps = NULL;
421
+  g_autoptr (GstCaps) peercaps = NULL;
422
+  g_autoptr (GPtrArray) possible = NULL;
423
   gboolean result = FALSE;
424
-  GPtrArray *possible;
425
   const char *error = NULL;
426
   struct timespec abstime;
427
   uint32_t target_id;
428
@@ -835,47 +861,62 @@
429
   GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
430
   if (peercaps) {
431
     /* The result is already a subset of our caps */
432
-    caps = peercaps;
433
-    gst_caps_unref (thiscaps);
434
+    possible_caps = g_steal_pointer (&peercaps);
435
   } else {
436
     /* no peer, work with our own caps then */
437
-    caps = thiscaps;
438
+    possible_caps = g_steal_pointer (&thiscaps);
439
   }
440
-  if (caps == NULL || gst_caps_is_empty (caps))
441
+
442
+  GST_DEBUG_OBJECT (basesrc, "have common caps: %" GST_PTR_FORMAT, possible_caps);
443
+  gst_caps_sanitize (&possible_caps);
444
+
445
+  if (gst_caps_is_empty (possible_caps))
446
     goto no_common_caps;
447
 
448
-  GST_DEBUG_OBJECT (basesrc, "have common caps: %" GST_PTR_FORMAT, caps);
449
+  GST_DEBUG_OBJECT (basesrc, "have common caps (sanitized): %" GST_PTR_FORMAT, possible_caps);
450
+
451
+  if (pw_stream_get_state(pwsrc->stream->pwstream, NULL) == PW_STREAM_STATE_STREAMING) {
452
+    g_autoptr (GstCaps) current_caps = NULL;
453
+    g_autoptr (GstCaps) preferred_new_caps = NULL;
454
+
455
+    current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (pwsrc));
456
+    preferred_new_caps = gst_caps_copy_nth (possible_caps, 0);
457
+
458
+    if (current_caps && gst_caps_is_equal (current_caps, preferred_new_caps)) {
459
+      GST_DEBUG_OBJECT (pwsrc,
460
+                        "Stream running and new caps equal current ones. "
461
+                        "Skipping renegotiation.");
462
+      goto no_nego_needed;
463
+    }
464
+  }
465
 
466
   /* open a connection with these caps */
467
-  possible = gst_caps_to_format_all (caps, SPA_PARAM_EnumFormat);
468
-  gst_caps_unref (caps);
469
+  possible = gst_caps_to_format_all (possible_caps);
470
 
471
   /* first disconnect */
472
-  pw_thread_loop_lock (pwsrc->core->loop);
473
-  if (pw_stream_get_state(pwsrc->stream, &error) != PW_STREAM_STATE_UNCONNECTED) {
474
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
475
+  if (pw_stream_get_state(pwsrc->stream->pwstream, &error) != PW_STREAM_STATE_UNCONNECTED) {
476
     GST_DEBUG_OBJECT (basesrc, "disconnect capture");
477
-    pw_stream_disconnect (pwsrc->stream);
478
+    pw_stream_disconnect (pwsrc->stream->pwstream);
479
     while (TRUE) {
480
-      enum pw_stream_state state = pw_stream_get_state (pwsrc->stream, &error);
481
+      enum pw_stream_state state = pw_stream_get_state (pwsrc->stream->pwstream, &error);
482
 
483
       GST_DEBUG_OBJECT (basesrc, "waiting for UNCONNECTED, now %s", pw_stream_state_as_string (state));
484
       if (state == PW_STREAM_STATE_UNCONNECTED)
485
         break;
486
 
487
-      if (state == PW_STREAM_STATE_ERROR || pwsrc->flushing) {
488
-        g_ptr_array_unref (possible);
489
+      if (state == PW_STREAM_STATE_ERROR || pwsrc->flushing)
490
         goto connect_error;
491
-      }
492
 
493
-      pw_thread_loop_wait (pwsrc->core->loop);
494
+      pw_thread_loop_wait (pwsrc->stream->core->loop);
495
     }
496
   }
497
 
498
-  target_id = pwsrc->path ? (uint32_t)atoi(pwsrc->path) : PW_ID_ANY;
499
+  target_id = pwsrc->stream->path ? (uint32_t)atoi(pwsrc->stream->path) : PW_ID_ANY;
500
 
501
-  if (pwsrc->target_object) {
502
+  if (pwsrc->stream->target_object) {
503
       struct spa_dict_item items2 = {
504
-        SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsrc->target_object),
505
+        SPA_DICT_ITEM_INIT(PW_KEY_TARGET_OBJECT, pwsrc->stream->target_object),
506
    /* XXX deprecated but the portal and some example apps only
507
     * provide the object id */
508
         SPA_DICT_ITEM_INIT(PW_KEY_NODE_TARGET, NULL),
509
@@ -884,37 +925,39 @@
510
       uint64_t serial;
511
 
512
       /* If target.object is a name, set it also to node.target */
513
-      if (spa_atou64(pwsrc->target_object, &serial, 0)) {
514
+      if (spa_atou64(pwsrc->stream->target_object, &serial, 0)) {
515
         dict.n_items = 1;
516
       } else {
517
         target_id = PW_ID_ANY;
518
-        items1.value = pwsrc->target_object;
519
+        items1.value = pwsrc->stream->target_object;
520
       }
521
 
522
-      pw_stream_update_properties (pwsrc->stream, &dict);
523
+      pw_stream_update_properties (pwsrc->stream->pwstream, &dict);
524
   }
525
 
526
   GST_DEBUG_OBJECT (basesrc, "connect capture with path %s, target-object %s",
527
-                    pwsrc->path, pwsrc->target_object);
528
-  pwsrc->negotiated = FALSE;
529
+                    pwsrc->stream->path, pwsrc->stream->target_object);
530
+
531
   enum pw_stream_flags flags;
532
   flags = PW_STREAM_FLAG_DONT_RECONNECT |
533
      PW_STREAM_FLAG_ASYNC;
534
   if (pwsrc->autoconnect)
535
     flags |= PW_STREAM_FLAG_AUTOCONNECT;
536
-  pw_stream_connect (pwsrc->stream,
537
+  pw_stream_connect (pwsrc->stream->pwstream,
538
                      PW_DIRECTION_INPUT,
539
                      target_id,
540
                      flags,
541
                      (const struct spa_pod **)possible->pdata,
542
                      possible->len);
543
-  g_ptr_array_free (possible, TRUE);
544
 
545
-  pw_thread_loop_get_time (pwsrc->core->loop, &abstime,
546
+  pw_thread_loop_get_time (pwsrc->stream->core->loop, &abstime,
547
                   GST_PIPEWIRE_DEFAULT_TIMEOUT * SPA_NSEC_PER_SEC);
548
 
549
+  pwsrc->possible_caps = possible_caps;
550
+  pwsrc->negotiated = FALSE;
551
+
552
   while (TRUE) {
553
-    enum pw_stream_state state = pw_stream_get_state (pwsrc->stream, &error);
554
+    enum pw_stream_state state = pw_stream_get_state (pwsrc->stream->pwstream, &error);
555
 
556
     GST_DEBUG_OBJECT (basesrc, "waiting for NEGOTIATED, now %s", pw_stream_state_as_string (state));
557
     if (state == PW_STREAM_STATE_ERROR || pwsrc->flushing)
558
@@ -923,33 +966,35 @@
559
     if (pwsrc->negotiated)
560
       break;
561
 
562
-    if (pw_thread_loop_timed_wait_full (pwsrc->core->loop, &abstime) < 0)
563
+    if (pwsrc->autoconnect) {
564
+      if (pw_thread_loop_timed_wait_full (pwsrc->stream->core->loop, &abstime) < 0)
565
         goto connect_error;
566
+    } else {
567
+      pw_thread_loop_wait (pwsrc->stream->core->loop);
568
+    }
569
   }
570
-  caps = pwsrc->caps;
571
-  pwsrc->caps = NULL;
572
-  pw_thread_loop_unlock (pwsrc->core->loop);
573
 
574
-  if (caps == NULL)
575
+  negotiated_caps = g_steal_pointer (&pwsrc->caps);
576
+  pwsrc->possible_caps = NULL;
577
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
578
+
579
+  if (negotiated_caps == NULL)
580
     goto no_caps;
581
 
582
-  gst_pipewire_clock_reset (GST_PIPEWIRE_CLOCK (pwsrc->clock), 0);
583
+  gst_pipewire_clock_reset (GST_PIPEWIRE_CLOCK (pwsrc->stream->clock), 0);
584
 
585
-  GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, caps);
586
-  result = gst_base_src_set_caps (GST_BASE_SRC (pwsrc), caps);
587
-  gst_caps_unref (caps);
588
+  GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, negotiated_caps);
589
+  result = gst_base_src_set_caps (GST_BASE_SRC (pwsrc), negotiated_caps);
590
+  if (!result)
591
+    goto no_caps;
592
 
593
   result = gst_pipewire_src_stream_start (pwsrc);
594
 
595
-  pwsrc->started = result;
596
-
597
   return result;
598
 
599
 no_nego_needed:
600
   {
601
     GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
602
-    if (thiscaps)
603
-      gst_caps_unref (thiscaps);
604
     return TRUE;
605
   }
606
 no_caps:
607
@@ -959,10 +1004,7 @@
608
     GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT,
609
         ("%s", error_string),
610
         ("This element did not produce valid caps"));
611
-    pw_stream_set_error (pwsrc->stream, -EINVAL, "%s", error_string);
612
-
613
-    if (thiscaps)
614
-      gst_caps_unref (thiscaps);
615
+    pw_stream_set_error (pwsrc->stream->pwstream, -EINVAL, "%s", error_string);
616
     return FALSE;
617
   }
618
 no_common_caps:
619
@@ -972,41 +1014,78 @@
620
     GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT,
621
         ("%s", error_string),
622
         ("This element does not have formats in common with the peer"));
623
-    pw_stream_set_error (pwsrc->stream, -EPIPE, "%s", error_string);
624
-
625
-    if (caps)
626
-      gst_caps_unref (caps);
627
+    pw_stream_set_error (pwsrc->stream->pwstream, -EPIPE, "%s", error_string);
628
     return FALSE;
629
   }
630
 connect_error:
631
   {
632
+    g_clear_pointer (&pwsrc->caps, gst_caps_unref);
633
+    pwsrc->possible_caps = NULL;
634
     GST_DEBUG_OBJECT (basesrc, "connect error");
635
-    pw_thread_loop_unlock (pwsrc->core->loop);
636
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
637
     return FALSE;
638
   }
639
 }
640
 
641
 static void
642
-on_param_changed (void *data, uint32_t id,
643
+handle_format_change (GstPipeWireSrc *pwsrc,
644
                    const struct spa_pod *param)
645
 {
646
-  GstPipeWireSrc *pwsrc = data;
647
+  GstStructure *structure;
648
+  g_autoptr (GstCaps) pw_peer_caps = NULL;
649
 
650
-  if (param == NULL || id != SPA_PARAM_Format) {
651
+  g_clear_pointer (&pwsrc->caps, gst_caps_unref);
652
+  if (param == NULL) {
653
     GST_DEBUG_OBJECT (pwsrc, "clear format");
654
+    pwsrc->negotiated = FALSE;
655
+    pwsrc->is_video = FALSE;
656
     return;
657
   }
658
-  if (pwsrc->caps)
659
-          gst_caps_unref(pwsrc->caps);
660
-  pwsrc->caps = gst_caps_from_format (param);
661
 
662
-  pwsrc->is_video = pwsrc->caps != NULL
663
-                      ? gst_video_info_from_caps (&pwsrc->video_info, pwsrc->caps)
664
-                      : FALSE;
665
+  pw_peer_caps = gst_caps_from_format (param);
666
+  if (pw_peer_caps) {
667
+    pwsrc->caps = gst_caps_intersect_full (pw_peer_caps,
668
+                                           pwsrc->possible_caps,
669
+                                           GST_CAPS_INTERSECT_FIRST);
670
+    gst_caps_maybe_fixate_dma_format (pwsrc->caps);
671
+  }
672
 
673
-  pwsrc->negotiated = pwsrc->caps != NULL;
674
+  if (pwsrc->caps && gst_caps_is_fixed (pwsrc->caps)) {
675
+    pwsrc->negotiated = TRUE;
676
+
677
+    structure = gst_caps_get_structure (pwsrc->caps, 0);
678
+    if (g_str_has_prefix (gst_structure_get_name (structure), "video/") ||
679
+        g_str_has_prefix (gst_structure_get_name (structure), "image/")) {
680
+      pwsrc->is_video = TRUE;
681
+
682
+#ifdef HAVE_GSTREAMER_DMA_DRM
683
+      if (gst_video_is_dma_drm_caps (pwsrc->caps)) {
684
+        if (!gst_video_info_dma_drm_from_caps (&pwsrc->drm_info, pwsrc->caps)) {
685
+          GST_WARNING_OBJECT (pwsrc, "Can't create drm video info from caps");
686
+          pw_stream_set_error (pwsrc->stream->pwstream, -EINVAL, "internal error");
687
+          return;
688
+        }
689
+
690
+        if (!gst_video_info_dma_drm_to_video_info (&pwsrc->drm_info,
691
+                                                   &pwsrc->video_info)) {
692
+          GST_WARNING_OBJECT (pwsrc, "Can't create video info from drm video info");
693
+          pw_stream_set_error (pwsrc->stream->pwstream, -EINVAL, "internal error");
694
+          return;
695
+        }
696
+      } else {
697
+        gst_video_info_dma_drm_init (&pwsrc->drm_info);
698
+#endif
699
+        gst_video_info_from_caps (&pwsrc->video_info, pwsrc->caps);
700
+#ifdef HAVE_GSTREAMER_DMA_DRM
701
+      }
702
+#endif
703
+    }
704
+  } else {
705
+    pwsrc->negotiated = FALSE;
706
+    pwsrc->is_video = FALSE;
707
+  }
708
 
709
-  if (pwsrc->negotiated) {
710
+  if (pwsrc->caps) {
711
     const struct spa_pod *params4;
712
     struct spa_pod_builder b = { NULL };
713
     uint8_t buffer512;
714
@@ -1014,12 +1093,7 @@
715
     int buffertypes;
716
 
717
     buffertypes = (1<<SPA_DATA_DmaBuf);
718
-    if (spa_pod_find_prop (param, NULL, SPA_FORMAT_VIDEO_modifier)) {
719
-      gst_caps_features_remove (gst_caps_get_features (pwsrc->caps, 0),
720
-          GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
721
-      gst_caps_features_add (gst_caps_get_features (pwsrc->caps, 0),
722
-          GST_CAPS_FEATURE_MEMORY_DMABUF);
723
-    } else {
724
+    if (!spa_pod_find_prop (param, NULL, SPA_FORMAT_VIDEO_modifier)) {
725
       buffertypes |= ((1<<SPA_DATA_MemFd) | (1<<SPA_DATA_MemPtr));
726
     }
727
 
728
@@ -1050,12 +1124,24 @@
729
         SPA_PARAM_META_size, SPA_POD_Int(sizeof (struct spa_meta_videotransform)));
730
 
731
     GST_DEBUG_OBJECT (pwsrc, "doing finish format");
732
-    pw_stream_update_params (pwsrc->stream, params, SPA_N_ELEMENTS(params));
733
+    pw_stream_update_params (pwsrc->stream->pwstream, params, SPA_N_ELEMENTS(params));
734
   } else {
735
     GST_WARNING_OBJECT (pwsrc, "finish format with error");
736
-    pw_stream_set_error (pwsrc->stream, -EINVAL, "unhandled format");
737
+    pw_stream_set_error (pwsrc->stream->pwstream, -EINVAL, "unhandled format");
738
+  }
739
+  pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
740
+}
741
+
742
+static void
743
+on_param_changed (void *data, uint32_t id,
744
+                   const struct spa_pod *param)
745
+{
746
+  GstPipeWireSrc *pwsrc = data;
747
+  switch (id) {
748
+    case SPA_PARAM_Format:
749
+      handle_format_change(pwsrc, param);
750
+      break;
751
   }
752
-  pw_thread_loop_signal (pwsrc->core->loop, FALSE);
753
 }
754
 
755
 static gboolean
756
@@ -1063,11 +1149,11 @@
757
 {
758
   GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (basesrc);
759
 
760
-  pw_thread_loop_lock (pwsrc->core->loop);
761
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
762
   GST_DEBUG_OBJECT (pwsrc, "setting flushing");
763
   pwsrc->flushing = TRUE;
764
-  pw_thread_loop_signal (pwsrc->core->loop, FALSE);
765
-  pw_thread_loop_unlock (pwsrc->core->loop);
766
+  pw_thread_loop_signal (pwsrc->stream->core->loop, FALSE);
767
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
768
 
769
   return TRUE;
770
 }
771
@@ -1077,10 +1163,10 @@
772
 {
773
   GstPipeWireSrc *pwsrc = GST_PIPEWIRE_SRC (basesrc);
774
 
775
-  pw_thread_loop_lock (pwsrc->core->loop);
776
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
777
   GST_DEBUG_OBJECT (pwsrc, "unsetting flushing");
778
   pwsrc->flushing = FALSE;
779
-  pw_thread_loop_unlock (pwsrc->core->loop);
780
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
781
 
782
   return TRUE;
783
 }
784
@@ -1123,8 +1209,6 @@
785
   switch (GST_QUERY_TYPE (query)) {
786
     case GST_QUERY_LATENCY:
787
       GST_OBJECT_LOCK (pwsrc);
788
-      pwsrc->min_latency = 10000000;
789
-      pwsrc->max_latency = GST_CLOCK_TIME_NONE;
790
       gst_query_set_latency (query, pwsrc->is_live, pwsrc->min_latency, pwsrc->max_latency);
791
       GST_OBJECT_UNLOCK (pwsrc);
792
       res = TRUE;
793
@@ -1176,7 +1260,7 @@
794
 
795
   pwsrc = GST_PIPEWIRE_SRC (psrc);
796
 
797
-  pw_thread_loop_lock (pwsrc->core->loop);
798
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
799
   if (!pwsrc->negotiated)
800
     goto not_negotiated;
801
 
802
@@ -1189,7 +1273,7 @@
803
     if (pwsrc->stream == NULL)
804
       goto streaming_error;
805
 
806
-    state = pw_stream_get_state (pwsrc->stream, &error);
807
+    state = pw_stream_get_state (pwsrc->stream->pwstream, &error);
808
     if (state == PW_STREAM_STATE_ERROR)
809
       goto streaming_error;
810
 
811
@@ -1198,13 +1282,13 @@
812
 
813
     if ((caps = pwsrc->caps) != NULL) {
814
       pwsrc->caps = NULL;
815
-      pw_thread_loop_unlock (pwsrc->core->loop);
816
+      pw_thread_loop_unlock (pwsrc->stream->core->loop);
817
 
818
       GST_DEBUG_OBJECT (pwsrc, "set format %" GST_PTR_FORMAT, caps);
819
       gst_base_src_set_caps (GST_BASE_SRC (pwsrc), caps);
820
       gst_caps_unref (caps);
821
 
822
-      pw_thread_loop_lock (pwsrc->core->loop);
823
+      pw_thread_loop_lock (pwsrc->stream->core->loop);
824
       continue;
825
     }
826
 
827
@@ -1235,15 +1319,15 @@
828
     timeout = FALSE;
829
     if (pwsrc->keepalive_time > 0) {
830
       struct timespec abstime;
831
-      pw_thread_loop_get_time(pwsrc->core->loop, &abstime,
832
+      pw_thread_loop_get_time(pwsrc->stream->core->loop, &abstime,
833
                       pwsrc->keepalive_time * SPA_NSEC_PER_MSEC);
834
-      if (pw_thread_loop_timed_wait_full (pwsrc->core->loop, &abstime) == -ETIMEDOUT)
835
+      if (pw_thread_loop_timed_wait_full (pwsrc->stream->core->loop, &abstime) == -ETIMEDOUT)
836
         timeout = TRUE;
837
     } else {
838
-      pw_thread_loop_wait (pwsrc->core->loop);
839
+      pw_thread_loop_wait (pwsrc->stream->core->loop);
840
     }
841
   }
842
-  pw_thread_loop_unlock (pwsrc->core->loop);
843
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
844
 
845
   *buffer = buf;
846
 
847
@@ -1270,22 +1354,22 @@
848
 
849
 not_negotiated:
850
   {
851
-    pw_thread_loop_unlock (pwsrc->core->loop);
852
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
853
     return GST_FLOW_NOT_NEGOTIATED;
854
   }
855
 streaming_eos:
856
   {
857
-    pw_thread_loop_unlock (pwsrc->core->loop);
858
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
859
     return GST_FLOW_EOS;
860
   }
861
 streaming_error:
862
   {
863
-    pw_thread_loop_unlock (pwsrc->core->loop);
864
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
865
     return GST_FLOW_ERROR;
866
   }
867
 streaming_stopped:
868
   {
869
-    pw_thread_loop_unlock (pwsrc->core->loop);
870
+    pw_thread_loop_unlock (pwsrc->stream->core->loop);
871
     return GST_FLOW_FLUSHING;
872
   }
873
 }
874
@@ -1303,32 +1387,12 @@
875
 
876
   pwsrc = GST_PIPEWIRE_SRC (basesrc);
877
 
878
-  pw_thread_loop_lock (pwsrc->core->loop);
879
+  pw_thread_loop_lock (pwsrc->stream->core->loop);
880
   pwsrc->eos = false;
881
   gst_buffer_replace (&pwsrc->last_buffer, NULL);
882
   gst_caps_replace(&pwsrc->caps, NULL);
883
-  pw_thread_loop_unlock (pwsrc->core->loop);
884
-
885
-  return TRUE;
886
-}
887
+  pw_thread_loop_unlock (pwsrc->stream->core->loop);
888
 
889
-static gboolean
890
-copy_properties (GQuark field_id,
891
-                 const GValue *value,
892
-                 gpointer user_data)
893
-{
894
-  struct pw_properties *properties = user_data;
895
-  GValue dst = { 0 };
896
-
897
-  if (g_value_type_transformable (G_VALUE_TYPE(value), G_TYPE_STRING)) {
898
-    g_value_init(&dst, G_TYPE_STRING);
899
-    if (g_value_transform(value, &dst)) {
900
-      pw_properties_set (properties,
901
-                         g_quark_to_string (field_id),
902
-                         g_value_get_string (&dst));
903
-    }
904
-    g_value_unset(&dst);
905
-  }
906
   return TRUE;
907
 }
908
 
909
@@ -1342,94 +1406,6 @@
910
 };
911
 
912
 static gboolean
913
-gst_pipewire_src_open (GstPipeWireSrc * pwsrc)
914
-{
915
-  struct pw_properties *props;
916
-
917
-  GST_DEBUG_OBJECT (pwsrc, "open");
918
-
919
-  pwsrc->core = gst_pipewire_core_get(pwsrc->fd);
920
-  if (pwsrc->core == NULL)
921
-      goto connect_error;
922
-
923
-  pw_thread_loop_lock (pwsrc->core->loop);
924
-
925
-  props = pw_properties_new (NULL, NULL);
926
-  if (pwsrc->client_properties) {
927
-    gst_structure_foreach (pwsrc->client_properties, copy_properties, props);
928
-    pw_core_update_properties (pwsrc->core->core, &props->dict);
929
-    pw_properties_clear(props);
930
-  }
931
-  if (pwsrc->client_name) {
932
-    pw_properties_set (props, PW_KEY_NODE_NAME, pwsrc->client_name);
933
-    pw_properties_set (props, PW_KEY_NODE_DESCRIPTION, pwsrc->client_name);
934
-  }
935
-  if (pwsrc->stream_properties) {
936
-    gst_structure_foreach (pwsrc->stream_properties, copy_properties, props);
937
-  }
938
-
939
-  if ((pwsrc->stream = pw_stream_new (pwsrc->core->core,
940
-                                  pwsrc->client_name, props)) == NULL)
941
-    goto no_stream;
942
-
943
-
944
-  pw_stream_add_listener(pwsrc->stream,
945
-                         &pwsrc->stream_listener,
946
-                         &stream_events,
947
-                         pwsrc);
948
-
949
-  pwsrc->clock = gst_pipewire_clock_new (pwsrc->stream, pwsrc->last_time);
950
-  pw_thread_loop_unlock (pwsrc->core->loop);
951
-
952
-  return TRUE;
953
-
954
-  /* ERRORS */
955
-connect_error:
956
-  {
957
-    GST_ELEMENT_ERROR (pwsrc, RESOURCE, FAILED, ("can't connect"), (NULL));
958
-    return FALSE;
959
-  }
960
-no_stream:
961
-  {
962
-    GST_ELEMENT_ERROR (pwsrc, RESOURCE, FAILED, ("can't create stream"), (NULL));
963
-    pw_thread_loop_unlock (pwsrc->core->loop);
964
-    gst_pipewire_core_release (pwsrc->core);
965
-    pwsrc->core = NULL;
966
-    return FALSE;
967
-  }
968
-}
969
-
970
-static void
971
-gst_pipewire_src_close (GstPipeWireSrc * pwsrc)
972
-{
973
-  pwsrc->last_time = gst_clock_get_time (pwsrc->clock);
974
-
975
-  GST_DEBUG_OBJECT (pwsrc, "close");
976
-
977
-  gst_element_post_message (GST_ELEMENT (pwsrc),
978
-    gst_message_new_clock_lost (GST_OBJECT_CAST (pwsrc), pwsrc->clock));
979
-
980
-  GST_OBJECT_LOCK (pwsrc);
981
-  GST_PIPEWIRE_CLOCK (pwsrc->clock)->stream = NULL;
982
-  g_clear_object (&pwsrc->clock);
983
-  GST_OBJECT_UNLOCK (pwsrc);
984
-
985
-  GST_OBJECT_LOCK (pwsrc->pool);
986
-  pw_thread_loop_lock (pwsrc->core->loop);
987
-  if (pwsrc->stream) {
988
-    pw_stream_destroy (pwsrc->stream);
989
-    pwsrc->stream = NULL;
990
-  }
991
-  pw_thread_loop_unlock (pwsrc->core->loop);
992
-  GST_OBJECT_UNLOCK (pwsrc->pool);
993
-
994
-  if (pwsrc->core) {
995
-    gst_pipewire_core_release (pwsrc->core);
996
-    pwsrc->core = NULL;
997
-  }
998
-}
999
-
1000
-static gboolean
1001
 gst_pipewire_src_send_event (GstElement * elem, GstEvent * event)
1002
 {
1003
   GstPipeWireSrc *this = GST_PIPEWIRE_SRC_CAST (elem);
1004
@@ -1438,10 +1414,10 @@
1005
   switch (GST_EVENT_TYPE (event)) {
1006
     case GST_EVENT_EOS:
1007
       GST_DEBUG_OBJECT (this, "got EOS");
1008
-      pw_thread_loop_lock (this->core->loop);
1009
+      pw_thread_loop_lock (this->stream->core->loop);
1010
       this->eos = true;
1011
-      pw_thread_loop_signal (this->core->loop, FALSE);
1012
-      pw_thread_loop_unlock (this->core->loop);
1013
+      pw_thread_loop_signal (this->stream->core->loop, FALSE);
1014
+      pw_thread_loop_unlock (this->stream->core->loop);
1015
       ret = TRUE;
1016
       break;
1017
     default:
1018
@@ -1459,22 +1435,22 @@
1019
 
1020
   switch (transition) {
1021
     case GST_STATE_CHANGE_NULL_TO_READY:
1022
-      if (!gst_pipewire_src_open (this))
1023
+      if (!gst_pipewire_stream_open (this->stream, &stream_events))
1024
         goto open_failed;
1025
       break;
1026
     case GST_STATE_CHANGE_READY_TO_PAUSED:
1027
       break;
1028
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1029
       /* uncork and start recording */
1030
-      pw_thread_loop_lock (this->core->loop);
1031
-      pw_stream_set_active(this->stream, true);
1032
-      pw_thread_loop_unlock (this->core->loop);
1033
+      pw_thread_loop_lock (this->stream->core->loop);
1034
+      pw_stream_set_active (this->stream->pwstream, true);
1035
+      pw_thread_loop_unlock (this->stream->core->loop);
1036
       break;
1037
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1038
       /* stop recording ASAP by corking */
1039
-      pw_thread_loop_lock (this->core->loop);
1040
-      pw_stream_set_active(this->stream, false);
1041
-      pw_thread_loop_unlock (this->core->loop);
1042
+      pw_thread_loop_lock (this->stream->core->loop);
1043
+      pw_stream_set_active (this->stream->pwstream, false);
1044
+      pw_thread_loop_unlock (this->stream->core->loop);
1045
       break;
1046
     default:
1047
       break;
1048
@@ -1487,18 +1463,28 @@
1049
       if (wait_started (this) == PW_STREAM_STATE_ERROR)
1050
         goto open_failed;
1051
 
1052
+      pw_thread_loop_lock (this->stream->core->loop);
1053
+      /* the initial stream state is active, which is needed for linking and
1054
+       * negotiation to happen and the bufferpool to be set up. We don't know
1055
+       * if we'll go to playing, so we deactivate the stream until that
1056
+       * transition happens. This is janky, but because of how bins propagate
1057
+       * state changes one transition at a time, there may not be a better way
1058
+       * to do this. */
1059
+      pw_stream_set_active (this->stream->pwstream, false);
1060
+      pw_thread_loop_unlock (this->stream->core->loop);
1061
+
1062
       if (gst_base_src_is_live (GST_BASE_SRC (element)))
1063
         ret = GST_STATE_CHANGE_NO_PREROLL;
1064
       break;
1065
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1066
       break;
1067
     case GST_STATE_CHANGE_PAUSED_TO_READY:
1068
-      pw_thread_loop_lock (this->core->loop);
1069
+      pw_thread_loop_lock (this->stream->core->loop);
1070
       this->negotiated = FALSE;
1071
-      pw_thread_loop_unlock (this->core->loop);
1072
+      pw_thread_loop_unlock (this->stream->core->loop);
1073
       break;
1074
     case GST_STATE_CHANGE_READY_TO_NULL:
1075
-      gst_pipewire_src_close (this);
1076
+      gst_pipewire_stream_close (this->stream);
1077
       break;
1078
     default:
1079
       break;
1080
pipewire-1.0.1.tar.bz2/src/gst/gstpipewiresrc.h -> pipewire-1.2.0.tar.gz/src/gst/gstpipewiresrc.h Changed
103
 
1
@@ -5,6 +5,10 @@
2
 #ifndef __GST_PIPEWIRE_SRC_H__
3
 #define __GST_PIPEWIRE_SRC_H__
4
 
5
+#include "config.h"
6
+
7
+#include "gstpipewirestream.h"
8
+
9
 #include <gst/gst.h>
10
 #include <gst/base/gstpushsrc.h>
11
 
12
@@ -16,21 +20,10 @@
13
 
14
 G_BEGIN_DECLS
15
 
16
-#define GST_TYPE_PIPEWIRE_SRC \
17
-  (gst_pipewire_src_get_type())
18
-#define GST_PIPEWIRE_SRC(obj) \
19
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PIPEWIRE_SRC,GstPipeWireSrc))
20
-#define GST_PIPEWIRE_SRC_CLASS(klass) \
21
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PIPEWIRE_SRC,GstPipeWireSrcClass))
22
-#define GST_IS_PIPEWIRE_SRC(obj) \
23
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PIPEWIRE_SRC))
24
-#define GST_IS_PIPEWIRE_SRC_CLASS(klass) \
25
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PIPEWIRE_SRC))
26
-#define GST_PIPEWIRE_SRC_CAST(obj) \
27
-  ((GstPipeWireSrc *) (obj))
28
-
29
-typedef struct _GstPipeWireSrc GstPipeWireSrc;
30
-typedef struct _GstPipeWireSrcClass GstPipeWireSrcClass;
31
+#define GST_TYPE_PIPEWIRE_SRC (gst_pipewire_src_get_type())
32
+#define GST_PIPEWIRE_SRC_CAST(obj) ((GstPipeWireSrc *) (obj))
33
+G_DECLARE_FINAL_TYPE (GstPipeWireSrc, gst_pipewire_src, GST, PIPEWIRE_SRC, GstPushSrc)
34
+
35
 
36
 /**
37
  * GstPipeWireSrc:
38
@@ -40,22 +33,24 @@
39
 struct _GstPipeWireSrc {
40
   GstPushSrc element;
41
 
42
+  GstPipeWireStream *stream;
43
+
44
   /*< private >*/
45
-  gchar *path;
46
-  gchar *target_object;
47
-  gchar *client_name;
48
   gboolean always_copy;
49
   gint min_buffers;
50
   gint max_buffers;
51
-  int fd;
52
   gboolean resend_last;
53
   gint keepalive_time;
54
   gboolean autoconnect;
55
 
56
   GstCaps *caps;
57
+  GstCaps *possible_caps;
58
 
59
   gboolean is_video;
60
   GstVideoInfo video_info;
61
+#ifdef HAVE_GSTREAMER_DMA_DRM
62
+  GstVideoInfoDmaDrm drm_info;
63
+#endif
64
 
65
   gboolean negotiated;
66
   gboolean flushing;
67
@@ -63,34 +58,15 @@
68
   gboolean eos;
69
 
70
   gboolean is_live;
71
+  int64_t delay;
72
   GstClockTime min_latency;
73
   GstClockTime max_latency;
74
 
75
-  GstStructure *client_properties;
76
-  GstPipeWireCore *core;
77
-  struct spa_hook core_listener;
78
-  int last_seq;
79
-  int pending_seq;
80
-
81
-  struct pw_stream *stream;
82
-  struct spa_hook stream_listener;
83
-
84
   GstBuffer *last_buffer;
85
-  GstStructure *stream_properties;
86
-
87
-  GstPipeWirePool *pool;
88
-  GstClock *clock;
89
-  GstClockTime last_time;
90
 
91
   enum spa_meta_videotransform_value transform_value;
92
 };
93
 
94
-struct _GstPipeWireSrcClass {
95
-  GstPushSrcClass parent_class;
96
-};
97
-
98
-GType gst_pipewire_src_get_type (void);
99
-
100
 G_END_DECLS
101
 
102
 #endif /* __GST_PIPEWIRE_SRC_H__ */
103
pipewire-1.2.0.tar.gz/src/gst/gstpipewirestream.c Added
168
 
1
@@ -0,0 +1,166 @@
2
+/* GStreamer */
3
+/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2024 Collabora Ltd. */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include "gstpipewirestream.h"
8
+
9
+#include "gstpipewirepool.h"
10
+#include "gstpipewireclock.h"
11
+
12
+GST_DEBUG_CATEGORY_STATIC (pipewire_stream_debug);
13
+#define GST_CAT_DEFAULT pipewire_stream_debug
14
+
15
+G_DEFINE_TYPE (GstPipeWireStream, gst_pipewire_stream, GST_TYPE_OBJECT)
16
+
17
+static void
18
+gst_pipewire_stream_init (GstPipeWireStream * self)
19
+{
20
+  self->fd = -1;
21
+  self->client_name = g_strdup (pw_get_client_name());
22
+  self->pool = gst_pipewire_pool_new (self);
23
+}
24
+
25
+static void
26
+gst_pipewire_stream_finalize (GObject * object)
27
+{
28
+  GstPipeWireStream * self = GST_PIPEWIRE_STREAM (object);
29
+
30
+  g_clear_object (&self->pool);
31
+  g_free (self->path);
32
+  g_free (self->target_object);
33
+  g_free (self->client_name);
34
+  gst_clear_structure (&self->client_properties);
35
+  gst_clear_structure (&self->stream_properties);
36
+
37
+  G_OBJECT_CLASS(gst_pipewire_stream_parent_class)->finalize (object);
38
+}
39
+
40
+void
41
+gst_pipewire_stream_class_init (GstPipeWireStreamClass * klass)
42
+{
43
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
44
+
45
+  gobject_class->finalize = gst_pipewire_stream_finalize;
46
+
47
+  GST_DEBUG_CATEGORY_INIT (pipewire_stream_debug, "pipewirestream", 0,
48
+      "PipeWire Stream");
49
+}
50
+
51
+GstPipeWireStream *
52
+gst_pipewire_stream_new (GstElement * element)
53
+{
54
+  GstPipeWireStream *stream;
55
+
56
+  stream = g_object_new (GST_TYPE_PIPEWIRE_STREAM, NULL);
57
+  stream->element = element;
58
+
59
+  return stream;
60
+}
61
+
62
+static gboolean
63
+copy_properties (GQuark field_id,
64
+                 const GValue *value,
65
+                 gpointer user_data)
66
+{
67
+  struct pw_properties *properties = user_data;
68
+  GValue dst = { 0 };
69
+
70
+  if (g_value_type_transformable (G_VALUE_TYPE(value), G_TYPE_STRING)) {
71
+    g_value_init (&dst, G_TYPE_STRING);
72
+    if (g_value_transform (value, &dst)) {
73
+      pw_properties_set (properties,
74
+                         g_quark_to_string (field_id),
75
+                         g_value_get_string (&dst));
76
+    }
77
+    g_value_unset (&dst);
78
+  }
79
+  return TRUE;
80
+}
81
+
82
+gboolean
83
+gst_pipewire_stream_open (GstPipeWireStream * self,
84
+    const struct pw_stream_events * pwstream_events)
85
+{
86
+  struct pw_properties *props;
87
+
88
+  g_return_val_if_fail (self->core == NULL, FALSE);
89
+
90
+  GST_DEBUG_OBJECT (self, "open");
91
+
92
+  /* acquire the core */
93
+  self->core = gst_pipewire_core_get (self->fd);
94
+  if (self->core == NULL)
95
+      goto connect_error;
96
+
97
+  pw_thread_loop_lock (self->core->loop);
98
+
99
+  /* update the client properties */
100
+  if (self->client_properties) {
101
+    props = pw_properties_new (NULL, NULL);
102
+    gst_structure_foreach (self->client_properties, copy_properties, props);
103
+    pw_core_update_properties (self->core->core, &props->dict);
104
+    pw_properties_free (props);
105
+  }
106
+
107
+  /* create stream */
108
+  props = pw_properties_new (NULL, NULL);
109
+  if (self->client_name) {
110
+    pw_properties_set (props, PW_KEY_NODE_NAME, self->client_name);
111
+    pw_properties_set (props, PW_KEY_NODE_DESCRIPTION, self->client_name);
112
+  }
113
+  if (self->stream_properties) {
114
+    gst_structure_foreach (self->stream_properties, copy_properties, props);
115
+  }
116
+
117
+  if ((self->pwstream = pw_stream_new (self->core->core,
118
+                                       self->client_name, props)) == NULL)
119
+    goto no_stream;
120
+
121
+  pw_stream_add_listener(self->pwstream,
122
+                         &self->pwstream_listener,
123
+                         pwstream_events,
124
+                         self->element);
125
+
126
+  /* create clock */
127
+  self->clock = gst_pipewire_clock_new (self, 0);
128
+
129
+  pw_thread_loop_unlock (self->core->loop);
130
+
131
+  return TRUE;
132
+
133
+  /* ERRORS */
134
+connect_error:
135
+  {
136
+    GST_ELEMENT_ERROR (self->element, RESOURCE, FAILED,
137
+        ("Failed to connect"), (NULL));
138
+    return FALSE;
139
+  }
140
+no_stream:
141
+  {
142
+    GST_ELEMENT_ERROR (self->element, RESOURCE, FAILED,
143
+        ("can't create stream"), (NULL));
144
+    pw_thread_loop_unlock (self->core->loop);
145
+    return FALSE;
146
+  }
147
+}
148
+
149
+void
150
+gst_pipewire_stream_close (GstPipeWireStream * self)
151
+{
152
+  GST_DEBUG_OBJECT (self, "close");
153
+
154
+  /* destroy the clock */
155
+  gst_element_post_message (GST_ELEMENT (self->element),
156
+    gst_message_new_clock_lost (GST_OBJECT_CAST (self->element), self->clock));
157
+  g_weak_ref_set (&GST_PIPEWIRE_CLOCK (self->clock)->stream, NULL);
158
+  g_clear_object (&self->clock);
159
+
160
+  /* destroy the pw stream */
161
+  pw_thread_loop_lock (self->core->loop);
162
+  g_clear_pointer (&self->pwstream, pw_stream_destroy);
163
+  pw_thread_loop_unlock (self->core->loop);
164
+
165
+  /* release the core */
166
+  g_clear_pointer (&self->core, gst_pipewire_core_release);
167
+}
168
pipewire-1.2.0.tar.gz/src/gst/gstpipewirestream.h Added
55
 
1
@@ -0,0 +1,53 @@
2
+/* GStreamer */
3
+/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
4
+/* SPDX-FileCopyrightText: Copyright © 2024 Collabora Ltd. */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#ifndef __GST_PIPEWIRE_STREAM_H__
8
+#define __GST_PIPEWIRE_STREAM_H__
9
+
10
+#include "config.h"
11
+
12
+#include "gstpipewirecore.h"
13
+
14
+#include <gst/gst.h>
15
+#include <pipewire/pipewire.h>
16
+
17
+G_BEGIN_DECLS
18
+
19
+typedef struct _GstPipeWirePool GstPipeWirePool;
20
+
21
+#define GST_TYPE_PIPEWIRE_STREAM (gst_pipewire_stream_get_type())
22
+G_DECLARE_FINAL_TYPE(GstPipeWireStream, gst_pipewire_stream, GST, PIPEWIRE_STREAM, GstObject)
23
+
24
+struct _GstPipeWireStream {
25
+  GstObject parent;
26
+
27
+  /* relatives */
28
+  GstElement *element;
29
+  GstPipeWireCore *core;
30
+  GstPipeWirePool *pool;
31
+  GstClock *clock;
32
+
33
+  /* the actual pw stream */
34
+  struct pw_stream *pwstream;
35
+  struct spa_hook pwstream_listener;
36
+
37
+  /* common properties */
38
+  int fd;
39
+  gchar *path;
40
+  gchar *target_object;
41
+  gchar *client_name;
42
+  GstStructure *client_properties;
43
+  GstStructure *stream_properties;
44
+};
45
+
46
+GstPipeWireStream * gst_pipewire_stream_new (GstElement * element);
47
+
48
+gboolean gst_pipewire_stream_open (GstPipeWireStream * self,
49
+    const struct pw_stream_events * pwstream_events);
50
+void gst_pipewire_stream_close (GstPipeWireStream * self);
51
+
52
+G_END_DECLS
53
+
54
+#endif /* __GST_PIPEWIRE_STREAM_H__ */
55
pipewire-1.0.1.tar.bz2/src/gst/meson.build -> pipewire-1.2.0.tar.gz/src/gst/meson.build Changed
17
 
1
@@ -6,6 +6,7 @@
2
   'gstpipewirepool.c',
3
   'gstpipewiresink.c',
4
   'gstpipewiresrc.c',
5
+  'gstpipewirestream.c',
6
 
7
 
8
 if get_option('gstreamer-device-provider').allowed()
9
@@ -20,6 +21,7 @@
10
   'gstpipewirepool.h',
11
   'gstpipewiresink.h',
12
   'gstpipewiresrc.h',
13
+  'gstpipewirestream.h',
14
 
15
 
16
 pipewire_gst = shared_library('gstpipewire',
17
pipewire-1.0.1.tar.bz2/src/modules/meson.build -> pipewire-1.2.0.tar.gz/src/modules/meson.build Changed
149
 
1
@@ -24,6 +24,7 @@
2
   'module-metadata.c',
3
   'module-netjack2-driver.c',
4
   'module-netjack2-manager.c',
5
+  'module-parametric-equalizer.c',
6
   'module-pipe-tunnel.c',
7
   'module-portal.c',
8
   'module-profiler.c',
9
@@ -38,6 +39,7 @@
10
   'module-rtp-session.c',
11
   'module-rtp-source.c',
12
   'module-rtp-sink.c',
13
+  'module-snapcast-discover.c',
14
   'module-vban-recv.c',
15
   'module-vban-send.c',
16
   'module-session-manager.c',
17
@@ -69,6 +71,11 @@
18
   dependencies : spa_dep, mathlib, dl_lib, pipewire_dep,
19
 )
20
 
21
+plugin_dependencies = 
22
+if get_option('spa-plugins').allowed()
23
+  plugin_dependencies += audioconvert_dep
24
+endif
25
+
26
 simd_cargs = 
27
 simd_dependencies = 
28
 
29
@@ -122,7 +129,7 @@
30
   'module-filter-chain/convolver.c'
31
 
32
 filter_chain_dependencies = 
33
-  mathlib, dl_lib, pipewire_dep, sndfile_dep, audioconvert_dep
34
+  mathlib, dl_lib, pipewire_dep, sndfile_dep, plugin_dependencies
35
 
36
 
37
 pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain',
38
@@ -175,7 +182,7 @@
39
   install : true,
40
   install_dir : modules_install_dir,
41
   install_rpath: modules_install_dir,
42
-  dependencies : mathlib, dl_lib, pipewire_dep, audioconvert_dep,
43
+  dependencies : mathlib, dl_lib, pipewire_dep, plugin_dependencies,
44
 )
45
 
46
 build_module_jack_tunnel = jack_dep.found()
47
@@ -246,6 +253,15 @@
48
   dependencies : spa_dep, mathlib, dl_lib, pipewire_dep, opus_custom_dep,
49
 )
50
 
51
+pipewire_module_parametric_equalizer = shared_library('pipewire-module-parametric-equalizer',
52
+   'module-parametric-equalizer.c' ,
53
+  include_directories : configinc,
54
+  install : true,
55
+  install_dir : modules_install_dir,
56
+  install_rpath: modules_install_dir,
57
+  dependencies : filter_chain_dependencies,
58
+)
59
+
60
 pipewire_module_profiler = shared_library('pipewire-module-profiler',
61
    'module-profiler.c',
62
     'module-profiler/protocol-native.c', ,
63
@@ -326,6 +342,7 @@
64
     'module-protocol-native/protocol-native.c',
65
     'module-protocol-native/v0/protocol-native.c',
66
     'module-protocol-native/protocol-footer.c',
67
+    'module-protocol-native/security-context.c',
68
     'module-protocol-native/connection.c' ,
69
   include_directories : configinc,
70
   install : true,
71
@@ -342,9 +359,6 @@
72
   'module-protocol-pulse/collect.c',
73
   'module-protocol-pulse/cmd.c',
74
   'module-protocol-pulse/extension.c',
75
-  'module-protocol-pulse/extensions/ext-device-manager.c',
76
-  'module-protocol-pulse/extensions/ext-device-restore.c',
77
-  'module-protocol-pulse/extensions/ext-stream-restore.c',
78
   'module-protocol-pulse/format.c',
79
   'module-protocol-pulse/manager.c',
80
   'module-protocol-pulse/message.c',
81
@@ -366,6 +380,8 @@
82
   'module-protocol-pulse/modules/module-alsa-source.c',
83
   'module-protocol-pulse/modules/module-always-sink.c',
84
   'module-protocol-pulse/modules/module-combine-sink.c',
85
+  'module-protocol-pulse/modules/module-device-manager.c',
86
+  'module-protocol-pulse/modules/module-device-restore.c',
87
   'module-protocol-pulse/modules/module-echo-cancel.c',
88
   'module-protocol-pulse/modules/module-jackdbus-detect.c',
89
   'module-protocol-pulse/modules/module-ladspa-sink.c',
90
@@ -384,6 +400,7 @@
91
   'module-protocol-pulse/modules/module-rtp-recv.c',
92
   'module-protocol-pulse/modules/module-rtp-send.c',
93
   'module-protocol-pulse/modules/module-simple-protocol-tcp.c',
94
+  'module-protocol-pulse/modules/module-stream-restore.c',
95
   'module-protocol-pulse/modules/module-switch-on-connect.c',
96
   'module-protocol-pulse/modules/module-tunnel-sink.c',
97
   'module-protocol-pulse/modules/module-tunnel-source.c',
98
@@ -393,6 +410,13 @@
99
   'module-protocol-pulse/modules/module-zeroconf-discover.c',
100
 
101
 
102
+if snap_dep.found() and glib2_snap_dep.found() and gio2_snap_dep.found() and apparmor_snap_dep.found()
103
+  pipewire_module_protocol_pulse_sources += 
104
+    'module-protocol-pulse/snap-policy.c',
105
+  
106
+  pipewire_module_protocol_pulse_deps += snap_deps
107
+endif
108
+
109
 if dbus_dep.found()
110
   pipewire_module_protocol_pulse_sources += 
111
     'module-protocol-pulse/dbus-name.c',
112
@@ -415,6 +439,15 @@
113
   
114
   pipewire_module_protocol_pulse_deps += gio_dep
115
   cdata.set('HAVE_GIO', true)
116
+  if get_option('gsettings-pulse-schema').enabled()
117
+    install_data('module-protocol-pulse/modules/org.freedesktop.pulseaudio.gschema.xml',
118
+      install_dir: pipewire_datadir / 'glib-2.0' / 'schemas'
119
+    )
120
+    gnome = import('gnome')
121
+    gnome.post_install(
122
+      glib_compile_schemas: true
123
+    )
124
+  endif
125
 endif
126
 
127
 if flatpak_support
128
@@ -609,6 +642,20 @@
129
 endif
130
 summary({'raop-discover (needs Avahi)': build_module_raop_discover}, bool_yn: true, section: 'Optional Modules')
131
 
132
+build_module_snapcast_discover = avahi_dep.found()
133
+if build_module_snapcast_discover
134
+  pipewire_module_snapcast_discover = shared_library('pipewire-module-snapcast-discover',
135
+     'module-snapcast-discover.c',
136
+      'module-zeroconf-discover/avahi-poll.c' ,
137
+    include_directories : configinc,
138
+    install : true,
139
+    install_dir : modules_install_dir,
140
+    install_rpath: modules_install_dir,
141
+    dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep,
142
+  )
143
+endif
144
+summary({'snapcast-discover (needs Avahi)': build_module_snapcast_discover}, bool_yn: true, section: 'Optional Modules')
145
+
146
 build_module_raop = openssl_lib.found()
147
 if build_module_raop
148
   pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
149
pipewire-1.0.1.tar.bz2/src/modules/module-access.c -> pipewire-1.2.0.tar.gz/src/modules/module-access.c Changed
15
 
1
@@ -20,12 +20,12 @@
2
 #include <sys/mount.h>
3
 #endif
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/utils/json.h>
9
 
10
 #include <pipewire/impl.h>
11
-#include <pipewire/cleanup.h>
12
 
13
 #include "flatpak-utils.h"
14
 
15
pipewire-1.0.1.tar.bz2/src/modules/module-adapter/adapter.c -> pipewire-1.2.0.tar.gz/src/modules/module-adapter/adapter.c Changed
209
 
1
@@ -70,30 +70,6 @@
2
    .free = node_free,
3
 };
4
 
5
-static int handle_node_param(struct pw_impl_node *node, const char *key, const char *value)
6
-{
7
-   const struct spa_type_info *ti;
8
-   uint8_t buffer1024;
9
-   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
10
-   struct spa_pod *pod;
11
-   int res;
12
-
13
-   ti = spa_debug_type_find_short(spa_type_param, key);
14
-   if (ti == NULL)
15
-       return -ENOENT;
16
-
17
-   if ((res = spa_json_to_pod(&b, 0, ti, value, strlen(value))) < 0)
18
-       return res;
19
-
20
-   if ((pod = spa_pod_builder_deref(&b, 0)) == NULL)
21
-       return -ENOSPC;
22
-
23
-   if ((res = pw_impl_node_set_param(node, ti->type, 0, pod)) < 0)
24
-       return res;
25
-
26
-   return 0;
27
-}
28
-
29
 static int find_format(struct spa_node *node, enum pw_direction direction,
30
        uint32_t *media_type, uint32_t *media_subtype)
31
 {
32
@@ -124,117 +100,6 @@
33
    return 0;
34
 }
35
 
36
-static int do_auto_port_config(struct node *n, const char *str)
37
-{
38
-   uint32_t state = 0, i;
39
-   uint8_t buffer4096;
40
-   struct spa_pod_builder b;
41
-#define POSITION_PRESERVE 0
42
-#define POSITION_AUX 1
43
-#define POSITION_UNKNOWN 2
44
-   int res, position = POSITION_PRESERVE;
45
-   struct spa_pod *param;
46
-   uint32_t media_type, media_subtype;
47
-   bool have_format = false, monitor = false, control = false;
48
-   struct spa_audio_info format = { 0, };
49
-   enum spa_param_port_config_mode mode = SPA_PARAM_PORT_CONFIG_MODE_none;
50
-   struct spa_json it2;
51
-   char key1024, val256;
52
-
53
-   spa_json_init(&it0, str, strlen(str));
54
-   if (spa_json_enter_object(&it0, &it1) <= 0)
55
-       return -EINVAL;
56
-
57
-   while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
58
-       if (spa_json_get_string(&it1, val, sizeof(val)) <= 0)
59
-           break;
60
-
61
-       if (spa_streq(key, "mode")) {
62
-           mode = spa_debug_type_find_type_short(spa_type_param_port_config_mode, val);
63
-           if (mode == SPA_ID_INVALID)
64
-               mode = SPA_PARAM_PORT_CONFIG_MODE_none;
65
-       } else if (spa_streq(key, "monitor")) {
66
-           monitor = spa_atob(val);
67
-       } else if (spa_streq(key, "control")) {
68
-           control = spa_atob(val);
69
-       } else if (spa_streq(key, "position")) {
70
-           if (spa_streq(val, "unknown"))
71
-               position = POSITION_UNKNOWN;
72
-           else if (spa_streq(val, "aux"))
73
-               position = POSITION_AUX;
74
-           else
75
-               position = POSITION_PRESERVE;
76
-       }
77
-        }
78
-
79
-   while (true) {
80
-       struct spa_audio_info info = { 0, };
81
-       struct spa_pod *position = NULL;
82
-       uint32_t n_position = 0;
83
-
84
-       spa_pod_builder_init(&b, buffer, sizeof(buffer));
85
-       if ((res = spa_node_port_enum_params_sync(n->follower,
86
-                   n->direction == PW_DIRECTION_INPUT ?
87
-                       SPA_DIRECTION_INPUT :
88
-                       SPA_DIRECTION_OUTPUT, 0,
89
-                   SPA_PARAM_EnumFormat, &state,
90
-                   NULL, &param, &b)) != 1)
91
-           break;
92
-
93
-       if ((res = spa_format_parse(param, &media_type, &media_subtype)) < 0)
94
-           continue;
95
-
96
-       if (media_type != SPA_MEDIA_TYPE_audio ||
97
-           media_subtype != SPA_MEDIA_SUBTYPE_raw)
98
-           continue;
99
-
100
-       spa_pod_object_fixate((struct spa_pod_object*)param);
101
-
102
-       if (spa_pod_parse_object(param,
103
-               SPA_TYPE_OBJECT_Format, NULL,
104
-               SPA_FORMAT_AUDIO_format,        SPA_POD_Id(&info.info.raw.format),
105
-               SPA_FORMAT_AUDIO_rate,          SPA_POD_Int(&info.info.raw.rate),
106
-               SPA_FORMAT_AUDIO_channels,      SPA_POD_Int(&info.info.raw.channels),
107
-               SPA_FORMAT_AUDIO_position,      SPA_POD_OPT_Pod(&position)) < 0)
108
-           continue;
109
-
110
-       if (position != NULL)
111
-           n_position = spa_pod_copy_array(position, SPA_TYPE_Id,
112
-                   info.info.raw.position, SPA_AUDIO_MAX_CHANNELS);
113
-       if (n_position == 0 || n_position != info.info.raw.channels)
114
-           SPA_FLAG_SET(info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED);
115
-
116
-       if (format.info.raw.channels >= info.info.raw.channels)
117
-           continue;
118
-
119
-       format = info;
120
-       have_format = true;
121
-   }
122
-   if (!have_format)
123
-       return -ENOENT;
124
-
125
-   if (position == POSITION_AUX) {
126
-       for (i = 0; i < format.info.raw.channels; i++)
127
-           format.info.raw.positioni = SPA_AUDIO_CHANNEL_START_Aux + i;
128
-   } else if (position == POSITION_UNKNOWN) {
129
-       for (i = 0; i < format.info.raw.channels; i++)
130
-           format.info.raw.positioni = SPA_AUDIO_CHANNEL_UNKNOWN;
131
-   }
132
-
133
-   spa_pod_builder_init(&b, buffer, sizeof(buffer));
134
-   param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &format.info.raw);
135
-   param = spa_pod_builder_add_object(&b,
136
-       SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
137
-       SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(n->direction),
138
-       SPA_PARAM_PORT_CONFIG_mode,      SPA_POD_Id(mode),
139
-       SPA_PARAM_PORT_CONFIG_monitor,   SPA_POD_Bool(monitor),
140
-       SPA_PARAM_PORT_CONFIG_control,   SPA_POD_Bool(control),
141
-       SPA_PARAM_PORT_CONFIG_format,    SPA_POD_Pod(param));
142
-   pw_impl_node_set_param(n->node, SPA_PARAM_PortConfig, 0, param);
143
-
144
-   return 0;
145
-}
146
-
147
 struct info_data {
148
    struct spa_hook listener;
149
    struct spa_node *node;
150
@@ -283,8 +148,6 @@
151
    enum pw_direction direction;
152
    int res;
153
    uint32_t media_type, media_subtype;
154
-   const struct spa_dict_item *it;
155
-   struct pw_properties *copy;
156
    struct info_data info;
157
 
158
    spa_zero(info);
159
@@ -313,13 +176,8 @@
160
    if ((str = pw_properties_get(props, PW_KEY_NODE_ID)) != NULL)
161
        pw_properties_set(props, PW_KEY_NODE_SESSION, str);
162
 
163
-   if (pw_properties_get(props, "factory.mode") == NULL) {
164
-       if (direction == PW_DIRECTION_INPUT)
165
-           str = "merge";
166
-       else
167
-           str = "split";
168
-       pw_properties_set(props, "factory.mode", str);
169
-   }
170
+   if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL)
171
+       pw_properties_setf(props, PW_KEY_PORT_GROUP, "stream.0");
172
 
173
    if ((res = find_format(follower, direction, &media_type, &media_subtype)) < 0)
174
        goto error;
175
@@ -344,16 +202,10 @@
176
        goto error;
177
    }
178
 
179
-   copy = pw_properties_new(NULL, NULL);
180
-   spa_dict_for_each(it, &props->dict) {
181
-       if (!spa_strstartswith(it->key, "node.param.") &&
182
-           !spa_strstartswith(it->key, "port.param."))
183
-           pw_properties_set(copy, it->key, it->value);
184
-   }
185
    node = pw_spa_node_load(context,
186
                factory_name,
187
                PW_SPA_NODE_FLAG_ACTIVATE | PW_SPA_NODE_FLAG_NO_REGISTER,
188
-               copy, sizeof(struct node) + user_data_size);
189
+               pw_properties_copy(props), sizeof(struct node) + user_data_size);
190
         if (node == NULL) {
191
        res = -errno;
192
        pw_log_error("can't load spa node: %m");
193
@@ -375,15 +227,6 @@
194
 
195
    pw_impl_node_add_listener(node, &n->node_listener, &node_events, n);
196
 
197
-   if ((str = pw_properties_get(props, "adapter.auto-port-config")) != NULL)
198
-       do_auto_port_config(n, str);
199
-
200
-   spa_dict_for_each(it, &props->dict) {
201
-       if (spa_strstartswith(it->key, "node.param.")) {
202
-           if ((res = handle_node_param(node, &it->key11, it->value)) < 0)
203
-               pw_log_warn("can't set node param: %s", spa_strerror(res));
204
-       }
205
-   }
206
    return node;
207
 
208
 error:
209
pipewire-1.0.1.tar.bz2/src/modules/module-avb/avb.c -> pipewire-1.2.0.tar.gz/src/modules/module-avb/avb.c Changed
10
 
1
@@ -32,6 +32,8 @@
2
    pw_context_conf_update_props(context, "avb.properties", props);
3
 
4
    if ((str = pw_properties_get(props, "vm.overrides")) != NULL) {
5
+       pw_log_warn("vm.overrides in avb.properties are deprecated, "
6
+               "use avb.properties.rules instead");
7
        if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
8
            pw_properties_update_string(props, str, strlen(str));
9
        pw_properties_set(props, "vm.overrides", NULL);
10
pipewire-1.0.1.tar.bz2/src/modules/module-avb/maap.c -> pipewire-1.2.0.tar.gz/src/modules/module-avb/maap.c Changed
16
 
1
@@ -78,10 +78,10 @@
2
    pw_log_info("  conflict-count: %d", AVB_PACKET_MAAP_GET_CONFLICT_COUNT(p));
3
 }
4
 
5
-#define PROBE_TIMEOUT(n) ((n) + (MAAP_PROBE_INTERVAL_MS + \
6
-                        drand48() * MAAP_PROBE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC)
7
-#define ANNOUNCE_TIMEOUT(n) ((n) + (MAAP_ANNOUNCE_INTERVAL_MS + \
8
-                        drand48() * MAAP_ANNOUNCE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC)
9
+#define PROBE_TIMEOUT(n) (uint64_t)(((n) + (MAAP_PROBE_INTERVAL_MS + \
10
+                        drand48() * MAAP_PROBE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC))
11
+#define ANNOUNCE_TIMEOUT(n) (uint64_t)(((n) + (MAAP_ANNOUNCE_INTERVAL_MS + \
12
+                        drand48() * MAAP_ANNOUNCE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC))
13
 
14
 static int make_new_address(struct maap *maap, uint64_t now, int range)
15
 {
16
pipewire-1.0.1.tar.bz2/src/modules/module-client-node/client-node.c -> pipewire-1.2.0.tar.gz/src/modules/module-client-node/client-node.c Changed
432
 
1
@@ -31,7 +31,8 @@
2
 #define MAX_BUFFERS    64
3
 #define MAX_METAS  16u
4
 #define MAX_DATAS  64u
5
-#define AREA_SIZE  (4096u / sizeof(struct spa_io_buffers))
6
+#define AREA_SLOT  (sizeof(struct spa_io_async_buffers))
7
+#define AREA_SIZE  (4096u / AREA_SLOT)
8
 #define MAX_AREAS  32
9
 
10
 #define CHECK_FREE_PORT(impl,d,p)  (p <= pw_map_get_size(&impl->portsd) && !CHECK_PORT(impl,d,p))
11
@@ -53,6 +54,7 @@
12
    struct port *port;
13
    uint32_t peer_id;
14
    uint32_t n_buffers;
15
+   uint32_t impl_mix_id;
16
    struct buffer buffersMAX_BUFFERS;
17
 };
18
 
19
@@ -69,6 +71,7 @@
20
    uint32_t id;
21
 
22
    struct spa_node mix_node;
23
+   struct spa_hook_list mix_hooks;
24
 
25
    struct spa_port_info info;
26
    struct pw_properties *properties;
27
@@ -228,6 +231,7 @@
28
    mix->mix_id = mix_id;
29
    mix->port = p;
30
    mix->n_buffers = 0;
31
+   mix->impl_mix_id = SPA_ID_INVALID;
32
    return mix;
33
 
34
 fail:
35
@@ -236,18 +240,6 @@
36
    return NULL;
37
 }
38
 
39
-static void free_mix(struct port *p, struct mix *mix)
40
-{
41
-   if (mix == NULL)
42
-       return;
43
-
44
-   /* never realloc so it's safe to call from pw_map_foreach */
45
-   if (mix->mix_id < pw_map_get_size(&p->mix))
46
-       pw_map_insert_at(&p->mix, mix->mix_id, NULL);
47
-
48
-   free(mix);
49
-}
50
-
51
 static void clear_data(struct impl *impl, struct spa_data *d)
52
 {
53
    switch (d->type) {
54
@@ -295,6 +287,27 @@
55
    return 0;
56
 }
57
 
58
+static void free_mix(struct port *p, struct mix *mix)
59
+{
60
+   struct impl *impl = p->impl;
61
+
62
+   if (mix == NULL)
63
+       return;
64
+
65
+   if (mix->n_buffers) {
66
+       /* this shouldn't happen */
67
+       spa_log_warn(impl->log, "%p: mix port-id:%u freeing leaked buffers", impl, mix->mix_id - 1u);
68
+   }
69
+
70
+   clear_buffers(impl, mix);
71
+
72
+   /* never realloc so it's safe to call from pw_map_foreach */
73
+   if (mix->mix_id < pw_map_get_size(&p->mix))
74
+       pw_map_insert_at(&p->mix, mix->mix_id, NULL);
75
+
76
+   free(mix);
77
+}
78
+
79
 static void mix_clear(struct impl *impl, struct mix *mix)
80
 {
81
    struct port *port = mix->port;
82
@@ -572,12 +585,11 @@
83
 }
84
 
85
 static int
86
-impl_node_port_enum_params(void *object, int seq,
87
+node_port_enum_params(struct impl *impl, int seq,
88
               enum spa_direction direction, uint32_t port_id,
89
               uint32_t id, uint32_t start, uint32_t num,
90
-              const struct spa_pod *filter)
91
+              const struct spa_pod *filter, struct spa_hook_list *hooks)
92
 {
93
-   struct impl *impl = object;
94
    struct port *port;
95
    uint8_t buffer1024;
96
    struct spa_pod_dynamic_builder b;
97
@@ -617,7 +629,7 @@
98
        spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
99
        if (spa_pod_filter(&b.b, &result.param, param, filter) == 0) {
100
            pw_log_debug("%p: %d param %u", impl, seq, result.index);
101
-           spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
102
+           spa_node_emit_result(hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
103
            count++;
104
        }
105
        spa_pod_dynamic_builder_clean(&b);
106
@@ -628,6 +640,17 @@
107
    return found ? 0 : -ENOENT;
108
 }
109
 
110
+static int
111
+impl_node_port_enum_params(void *object, int seq,
112
+              enum spa_direction direction, uint32_t port_id,
113
+              uint32_t id, uint32_t start, uint32_t num,
114
+              const struct spa_pod *filter)
115
+{
116
+   struct impl *impl = object;
117
+   return node_port_enum_params(impl, seq, direction, port_id, id,
118
+           start, num, filter, &impl->hooks);
119
+}
120
+
121
 static int clear_buffers_cb(void *item, void *data)
122
 {
123
    if (item)
124
@@ -681,14 +704,18 @@
125
            direction == SPA_DIRECTION_INPUT ? "input" : "output",
126
            port_id, mix_id, data, size);
127
 
128
+   old = pw_mempool_find_tag(impl->client_pool, tag, sizeof(tag));
129
+
130
    port = GET_PORT(impl, direction, port_id);
131
-   if (port == NULL)
132
+   if (port == NULL) {
133
+       pw_memmap_free(old);
134
        return data == NULL ? 0 : -EINVAL;
135
+   }
136
 
137
-   if ((mix = find_mix(port, mix_id)) == NULL)
138
+   if ((mix = find_mix(port, mix_id)) == NULL) {
139
+       pw_memmap_free(old);
140
        return -EINVAL;
141
-
142
-   old = pw_mempool_find_tag(impl->client_pool, tag, sizeof(tag));
143
+   }
144
 
145
    if (data) {
146
        mm = pw_mempool_import_map(impl->client_pool,
147
@@ -840,9 +867,12 @@
148
            switch (d->type) {
149
            case SPA_DATA_DmaBuf:
150
            case SPA_DATA_MemFd:
151
+           case SPA_DATA_SyncObj:
152
            {
153
                uint32_t flags = PW_MEMBLOCK_FLAG_DONT_CLOSE;
154
 
155
+               if (!(d->flags & SPA_DATA_FLAG_MAPPABLE))
156
+                   flags |= PW_MEMBLOCK_FLAG_UNMAPPABLE;
157
                if (d->flags & SPA_DATA_FLAG_READABLE)
158
                    flags |= PW_MEMBLOCK_FLAG_READABLE;
159
                if (d->flags & SPA_DATA_FLAG_WRITABLE)
160
@@ -1069,16 +1099,27 @@
161
        for (j = 0; j < b->buffer.n_datas; j++) {
162
            struct spa_chunk *oldchunk = oldbuf->datasj.chunk;
163
            struct spa_data *d = &newbuf->datasj;
164
+           uint32_t flags = d->flags;
165
+
166
+           if (d->type == SPA_DATA_MemFd &&
167
+               !SPA_FLAG_IS_SET(flags, SPA_DATA_FLAG_MAPPABLE)) {
168
+               spa_log_debug(impl->log, "buffer:%d data:%d has non mappable MemFd, "
169
+                       "fixing to ensure backwards compatibility.",
170
+                       i, j);
171
+               flags |= SPA_DATA_FLAG_MAPPABLE;
172
+           }
173
 
174
            /* overwrite everything except the chunk */
175
            oldbuf->datasj = *d;
176
+           oldbuf->datasj.flags = flags;
177
            oldbuf->datasj.chunk = oldchunk;
178
 
179
            b->datasj.type = d->type;
180
+           b->datasj.flags = flags;
181
            b->datasj.fd = d->fd;
182
 
183
            spa_log_debug(impl->log, " data %d type:%d fl:%08x fd:%d, offs:%d max:%d",
184
-                   j, d->type, d->flags, (int) d->fd, d->mapoffset,
185
+                   j, d->type, flags, (int) d->fd, d->mapoffset,
186
                    d->maxsize);
187
        }
188
    }
189
@@ -1151,12 +1192,8 @@
190
 
191
 static int
192
 impl_init(struct impl *impl,
193
-     struct spa_dict *info,
194
-     const struct spa_support *support,
195
-     uint32_t n_support)
196
+     struct spa_dict *info)
197
 {
198
-   impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
199
-
200
    impl->node.iface = SPA_INTERFACE_INIT(
201
            SPA_TYPE_INTERFACE_Node,
202
            SPA_VERSION_NODE,
203
@@ -1310,8 +1347,6 @@
204
                      0,
205
                      sizeof(struct pw_node_activation));
206
 
207
-   node_peer_added(impl, node);
208
-
209
    if (impl->bind_node_id) {
210
        pw_global_bind(global, client, PW_PERM_ALL,
211
                impl->bind_node_version, impl->bind_node_id);
212
@@ -1323,12 +1358,12 @@
213
    size_t size;
214
    struct pw_memblock *area;
215
 
216
-   size = sizeof(struct spa_io_buffers) * AREA_SIZE;
217
+   size = AREA_SLOT * AREA_SIZE;
218
 
219
    area = pw_mempool_alloc(impl->context_pool,
220
            PW_MEMBLOCK_FLAG_READWRITE |
221
-           PW_MEMBLOCK_FLAG_MAP |
222
-           PW_MEMBLOCK_FLAG_SEAL,
223
+           PW_MEMBLOCK_FLAG_SEAL |
224
+           PW_MEMBLOCK_FLAG_MAP,
225
            SPA_DATA_MemFd, size);
226
    if (area == NULL)
227
                 return -errno;
228
@@ -1409,6 +1444,7 @@
229
    struct mix *m;
230
    uint32_t idx, pos, len;
231
    struct pw_memblock *area;
232
+   struct spa_io_async_buffers *ab;
233
 
234
    if ((m = create_mix(port, mix->port.port_id)) == NULL)
235
        return -ENOMEM;
236
@@ -1432,19 +1468,23 @@
237
    }
238
    area = *pw_array_get_unchecked(&impl->io_areas, idx, struct pw_memblock*);
239
 
240
-   mix->io = SPA_PTROFF(area->map->ptr,
241
-           pos * sizeof(struct spa_io_buffers), void);
242
-   *mix->io = SPA_IO_BUFFERS_INIT;
243
+   ab = SPA_PTROFF(area->map->ptr, pos * AREA_SLOT, void);
244
+   mix->io_data = ab;
245
+   mix->io0 = &ab->buffers0;
246
+   mix->io1 = &ab->buffers1;
247
+   *mix->io0 = SPA_IO_BUFFERS_INIT;
248
+   *mix->io1 = SPA_IO_BUFFERS_INIT;
249
 
250
    m->peer_id = mix->peer_id;
251
+   m->impl_mix_id = mix->id;
252
 
253
    if (impl->resource && impl->resource->version >= 4)
254
        pw_client_node_resource_port_set_mix_info(impl->resource,
255
                     mix->port.direction, mix->p->port_id,
256
                     mix->port.port_id, mix->peer_id, NULL);
257
 
258
-   pw_log_debug("%p: init mix id:%d io:%p base:%p", impl,
259
-           mix->id, mix->io, area->map->ptr);
260
+   pw_log_debug("%p: init mix id:%d io:%p/%p base:%p", impl,
261
+           mix->id, mix->io0, mix->io1, area->map->ptr);
262
 
263
    return 0;
264
 no_mem:
265
@@ -1462,7 +1502,7 @@
266
    pw_log_debug("%p: remove mix id:%d io:%p",
267
            impl, mix->id, mix->io);
268
 
269
-   if ((m = find_mix(port, mix->port.port_id)) == NULL)
270
+   if (!pw_map_has_item(&impl->io_map, mix->id))
271
        return -EINVAL;
272
 
273
    if (impl->resource && impl->resource->version >= 4)
274
@@ -1471,7 +1511,13 @@
275
                     mix->port.port_id, SPA_ID_INVALID, NULL);
276
 
277
    pw_map_remove(&impl->io_map, mix->id);
278
-   free_mix(port, m);
279
+
280
+   m = find_mix(port, mix->port.port_id);
281
+   if (m && m->impl_mix_id == mix->id)
282
+       free_mix(port, m);
283
+   else
284
+       pw_log_debug("%p: already cleared mix id:%d port-id:%d",
285
+               impl, mix->id, mix->port.port_id);
286
 
287
    return 0;
288
 }
289
@@ -1483,18 +1529,27 @@
290
 };
291
 
292
 static int
293
+impl_mix_add_listener(void *object, struct spa_hook *listener,
294
+       const struct spa_node_events *events, void *data)
295
+{
296
+   struct port *port = object;
297
+   spa_hook_list_append(&port->mix_hooks, listener, events, data);
298
+   return 0;
299
+}
300
+
301
+static int
302
 impl_mix_port_enum_params(void *object, int seq,
303
-              enum spa_direction direction, uint32_t port_id,
304
-              uint32_t id, uint32_t start, uint32_t num,
305
-              const struct spa_pod *filter)
306
+       enum spa_direction direction, uint32_t port_id,
307
+       uint32_t id, uint32_t start, uint32_t num,
308
+       const struct spa_pod *filter)
309
 {
310
    struct port *port = object;
311
 
312
    if (port->direction != direction)
313
        return -ENOTSUP;
314
 
315
-   return impl_node_port_enum_params(&port->impl->node, seq, direction, port->id,
316
-           id, start, num, filter);
317
+   return node_port_enum_params(port->impl, seq, direction, port->id,
318
+           id, start, num, filter, &port->mix_hooks);
319
 }
320
 
321
 static int
322
@@ -1550,11 +1605,24 @@
323
    if (mix == NULL)
324
        return -EINVAL;
325
 
326
-   if (id == SPA_IO_Buffers) {
327
+   switch (id) {
328
+   case SPA_IO_Buffers:
329
        if (data && size >= sizeof(struct spa_io_buffers))
330
-           mix->io = data;
331
+           mix->io0 = mix->io1 = data;
332
        else
333
-           mix->io = NULL;
334
+           mix->io0 = mix->io1 = NULL;
335
+       break;
336
+   case SPA_IO_AsyncBuffers:
337
+       if (data && size >= sizeof(struct spa_io_async_buffers)) {
338
+           struct spa_io_async_buffers *ab = data;
339
+           mix->io0 = &ab->buffers0;
340
+           mix->io1 = &ab->buffers1;
341
+       }
342
+       else
343
+           mix->io0 = mix->io1 = NULL;
344
+       break;
345
+   default:
346
+       break;
347
    }
348
    return do_port_set_io(impl,
349
                  direction, port->port_id, mix->port.port_id,
350
@@ -1565,7 +1633,7 @@
351
 impl_mix_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
352
 {
353
    struct port *p = object;
354
-   return impl_node_port_reuse_buffer(&p->impl->node, p->id, buffer_id);
355
+   return impl_node_port_reuse_buffer(p->impl, p->id, buffer_id);
356
 }
357
 
358
 static int impl_mix_process(void *object)
359
@@ -1575,6 +1643,7 @@
360
 
361
 static const struct spa_node_methods impl_port_mix = {
362
    SPA_VERSION_NODE_METHODS,
363
+   .add_listener = impl_mix_add_listener,
364
    .port_enum_params = impl_mix_port_enum_params,
365
    .port_set_param = impl_mix_port_set_param,
366
    .add_port = impl_mix_add_port,
367
@@ -1599,6 +1668,7 @@
368
    p->id = port->port_id;
369
    p->impl = impl;
370
    pw_map_init(&p->mix, 2, 2);
371
+   spa_hook_list_init(&p->mix_hooks);
372
    p->mix_node.iface = SPA_INTERFACE_INIT(
373
            SPA_TYPE_INTERFACE_Node,
374
            SPA_VERSION_NODE,
375
@@ -1635,16 +1705,6 @@
376
    clear_port(impl, p);
377
 }
378
 
379
-static void node_driver_changed(void *data, struct pw_impl_node *old, struct pw_impl_node *driver)
380
-{
381
-   struct impl *impl = data;
382
-
383
-   pw_log_debug("%p: driver changed %p -> %p", impl, old, driver);
384
-
385
-   node_peer_removed(data, old);
386
-   node_peer_added(data, driver);
387
-}
388
-
389
 static const struct pw_impl_node_events node_events = {
390
    PW_VERSION_IMPL_NODE_EVENTS,
391
    .free = node_free,
392
@@ -1654,7 +1714,6 @@
393
    .port_removed = node_port_removed,
394
    .peer_added = node_peer_added,
395
    .peer_removed = node_peer_removed,
396
-   .driver_changed = node_driver_changed,
397
 };
398
 
399
 static const struct pw_resource_events resource_events = {
400
@@ -1683,8 +1742,6 @@
401
    struct pw_impl_client_node *this;
402
    struct pw_impl_client *client = pw_resource_get_client(resource);
403
    struct pw_context *context = pw_impl_client_get_context(client);
404
-   const struct spa_support *support;
405
-   uint32_t n_support;
406
    int res;
407
 
408
    impl = calloc(1, sizeof(struct impl));
409
@@ -1709,8 +1766,8 @@
410
    impl->data_source.fd = -1;
411
    pw_log_debug("%p: new", &impl->node);
412
 
413
-   support = pw_context_get_support(impl->context, &n_support);
414
-   impl_init(impl, NULL, support, n_support);
415
+   impl_init(impl, NULL);
416
+   impl->log = pw_log_get();
417
    impl->resource = resource;
418
    impl->client = client;
419
    impl->client_pool = pw_impl_client_get_mempool(client);
420
@@ -1742,6 +1799,11 @@
421
 
422
    this->node->remote = true;
423
    this->flags = 0;
424
+   if (resource->version < PW_VERSION_CLIENT_NODE) {
425
+       pw_log_warn("detected old client version %d", resource->version);
426
+       if (resource->version < 6)
427
+           this->node->rt.target.activation->client_version = 0;
428
+   }
429
 
430
    pw_resource_add_listener(this->resource,
431
                &impl->resource_listener,
432
pipewire-1.0.1.tar.bz2/src/modules/module-client-node/remote-node.c -> pipewire-1.2.0.tar.gz/src/modules/module-client-node/remote-node.c Changed
326
 
1
@@ -71,9 +71,6 @@
2
    struct spa_hook proxy_client_node_listener;
3
 
4
    struct spa_list links;
5
-
6
-   struct spa_io_clock *clock;
7
-   struct spa_io_position *position;
8
 };
9
 
10
 struct link {
11
@@ -97,21 +94,10 @@
12
    return NULL;
13
 }
14
 
15
-static int
16
-do_deactivate_link(struct spa_loop *loop,
17
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
18
-{
19
-   struct link *link = user_data;
20
-   pw_log_trace("link %p deactivate", link);
21
-   spa_list_remove(&link->target.link);
22
-   return 0;
23
-}
24
-
25
 static void clear_link(struct node_data *data, struct link *link)
26
 {
27
    pw_log_debug("link %p", link);
28
-   pw_loop_invoke(data->data_loop,
29
-       do_deactivate_link, SPA_ID_INVALID, NULL, 0, true, link);
30
+   pw_impl_node_remove_target(data->node, &link->target);
31
    pw_memmap_free(link->map);
32
    spa_system_close(link->target.system, link->target.fd);
33
    spa_list_remove(&link->link);
34
@@ -122,6 +108,7 @@
35
 {
36
    struct link *l;
37
    uint32_t tag5 = { data->remote_id, };
38
+   struct pw_impl_node *node = data->node;
39
    struct pw_memmap *mm;
40
 
41
    if (!data->have_transport)
42
@@ -132,13 +119,13 @@
43
 
44
    while ((mm = pw_mempool_find_tag(data->pool, tag, sizeof(uint32_t))) != NULL) {
45
        if (mm->tag1 == SPA_ID_INVALID)
46
-           spa_node_set_io(data->node->node, mm->tag2, NULL, 0);
47
+           spa_node_set_io(node->node, mm->tag2, NULL, 0);
48
 
49
        pw_memmap_free(mm);
50
    }
51
 
52
    pw_memmap_free(data->activation);
53
-   data->node->rt.target.activation = data->node->activation->map->ptr;
54
+   node->rt.target.activation = node->activation->map->ptr;
55
 
56
    spa_system_close(data->data_system, data->rtwritefd);
57
    data->have_transport = false;
58
@@ -208,16 +195,22 @@
59
    }
60
 
61
    node->rt.target.activation = data->activation->ptr;
62
-   node->rt.position = &node->rt.target.activation->position;
63
-   node->info.id = node->rt.target.activation->position.clock.id;
64
-   node->rt.target.id = node->info.id;
65
+
66
+   pw_impl_node_set_io(node, SPA_IO_Clock,
67
+           &node->rt.target.activation->position.clock,
68
+           sizeof(struct spa_io_clock));
69
+   pw_impl_node_set_io(node, SPA_IO_Position,
70
+           &node->rt.target.activation->position,
71
+           sizeof(struct spa_io_position));
72
 
73
    pw_log_debug("remote-node %p: fds:%d %d node:%u activation:%p",
74
        proxy, readfd, writefd, data->remote_id, data->activation->ptr);
75
 
76
    data->rtwritefd = writefd;
77
-   spa_system_close(data->data_system, node->source.fd);
78
-   node->source.fd = readfd;
79
+   spa_system_close(node->rt.target.system, node->source.fd);
80
+   node->rt.target.fd = node->source.fd = readfd;
81
+
82
+   node->rt.target.activation->client_version = PW_VERSION_NODE_ACTIVATION;
83
 
84
    data->have_transport = true;
85
 
86
@@ -313,11 +306,25 @@
87
                continue;
88
 
89
            for (idx = 0;;) {
90
+               struct spa_node *qnode;
91
+               uint32_t qport;
92
+
93
                spa_pod_dynamic_builder_init(&b, buf, sizeof(buf), 4096);
94
 
95
-                           res = spa_node_port_enum_params_sync(port->node->node,
96
-                           port->direction, port->port_id,
97
-                           id, &idx, NULL, &param, &b.b);
98
+               switch (id) {
99
+               case SPA_PARAM_IO:
100
+                   qnode = port->mix;
101
+                   qport = SPA_ID_INVALID;
102
+                   break;
103
+               default:
104
+                   qnode = port->node->node;
105
+                   qport = port->port_id;
106
+                   break;
107
+               }
108
+               res = spa_node_port_enum_params_sync(qnode,
109
+                       port->direction, qport,
110
+                       id, &idx, NULL, &param, &b.b);
111
+
112
                if (res == 1) {
113
                    void *p;
114
                    p = pw_reallocarray(params, n_params + 1, sizeof(struct spa_pod*));
115
@@ -397,6 +404,7 @@
116
           uint32_t size)
117
 {
118
    struct node_data *data = _data;
119
+   struct pw_impl_node *node = data->node;
120
    struct pw_proxy *proxy = (struct pw_proxy*)data->client_node;
121
    struct pw_memmap *old, *mm;
122
    void *ptr;
123
@@ -422,18 +430,7 @@
124
    pw_log_debug("node %p: set io %s %p", proxy,
125
            spa_debug_type_find_name(spa_type_io, id), ptr);
126
 
127
-   switch(id) {
128
-   case SPA_IO_Clock:
129
-       data->clock = size >= sizeof(*data->clock) ? ptr : NULL;
130
-       break;
131
-   case SPA_IO_Position:
132
-       data->position = size >= sizeof(*data->position) ? ptr : NULL;
133
-       break;
134
-   }
135
-   data->node->driving = data->clock && data->position &&
136
-       data->position->clock.id == data->clock->id;
137
-
138
-   res =  spa_node_set_io(data->node->node, id, ptr, size);
139
+   res =  pw_impl_node_set_io(node, id, ptr, size);
140
 
141
    pw_memmap_free(old);
142
 exit:
143
@@ -455,6 +452,7 @@
144
 static int client_node_command(void *_data, const struct spa_command *command)
145
 {
146
    struct node_data *data = _data;
147
+   struct pw_impl_node *node = data->node;
148
    struct pw_proxy *proxy = (struct pw_proxy*)data->client_node;
149
    int res;
150
    uint32_t id = SPA_NODE_COMMAND_ID(command);
151
@@ -464,27 +462,27 @@
152
 
153
    switch (id) {
154
    case SPA_NODE_COMMAND_Pause:
155
-       if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_IDLE)) < 0) {
156
+       if ((res = pw_impl_node_set_state(node, PW_NODE_STATE_IDLE)) < 0) {
157
            pw_log_warn("node %p: pause failed", proxy);
158
            pw_proxy_error(proxy, res, "pause failed");
159
        }
160
 
161
        break;
162
    case SPA_NODE_COMMAND_Start:
163
-       if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_RUNNING)) < 0) {
164
+       if ((res = pw_impl_node_set_state(node, PW_NODE_STATE_RUNNING)) < 0) {
165
            pw_log_warn("node %p: start failed", proxy);
166
            pw_proxy_error(proxy, res, "start failed");
167
        }
168
        break;
169
 
170
    case SPA_NODE_COMMAND_Suspend:
171
-       if ((res = pw_impl_node_set_state(data->node, PW_NODE_STATE_SUSPENDED)) < 0) {
172
+       if ((res = pw_impl_node_set_state(node, PW_NODE_STATE_SUSPENDED)) < 0) {
173
            pw_log_warn("node %p: suspend failed", proxy);
174
            pw_proxy_error(proxy, res, "suspend failed");
175
        }
176
        break;
177
    case SPA_NODE_COMMAND_RequestProcess:
178
-       res = pw_impl_node_send_command(data->node, command);
179
+       res = pw_impl_node_send_command(node, command);
180
        break;
181
    default:
182
        pw_log_warn("unhandled node command %d (%s)", id,
183
@@ -524,7 +522,7 @@
184
    int res;
185
 
186
         pw_log_debug("port %p: clear %zd buffers mix:%d", port,
187
-           pw_array_get_len(&mix->buffers, struct buffer *),
188
+           pw_array_get_len(&mix->buffers, struct buffer),
189
            mix->mix.id);
190
 
191
    if ((res = pw_impl_port_use_buffers(port, &mix->mix, 0, NULL, 0)) < 0) {
192
@@ -698,14 +696,14 @@
193
                d->type = bm->type;
194
                d->data = NULL;
195
 
196
-               pw_log_debug(" data %d %u -> fd %d maxsize %d",
197
-                       j, bm->id, bm->fd, d->maxsize);
198
+               pw_log_debug(" data %d %u -> fd %d maxsize %d flags:%08x",
199
+                       j, bm->id, bm->fd, d->maxsize, d->flags);
200
            } else if (d->type == SPA_DATA_MemPtr) {
201
                int offs = SPA_PTR_TO_INT(d->data);
202
                d->data = SPA_PTROFF(mm->ptr, offs, void);
203
                d->fd = -1;
204
-               pw_log_debug(" data %d id:%u -> mem:%p offs:%d maxsize:%d",
205
-                       j, bid->id, d->data, offs, d->maxsize);
206
+               pw_log_debug(" data %d id:%u -> mem:%p offs:%d maxsize:%d flags:%08x",
207
+                       j, bid->id, d->data, offs, d->maxsize, d->flags);
208
            } else {
209
                pw_log_warn("unknown buffer data type %d", d->type);
210
            }
211
@@ -796,17 +794,6 @@
212
 }
213
 
214
 static int
215
-do_activate_link(struct spa_loop *loop,
216
-                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
217
-{
218
-   struct link *link = user_data;
219
-   struct node_data *d = link->data;
220
-   pw_log_trace("link %p activate", link);
221
-   spa_list_append(&d->node->rt.target_list, &link->target.link);
222
-   return 0;
223
-}
224
-
225
-static int
226
 client_node_set_activation(void *_data,
227
                         uint32_t node_id,
228
                         int signalfd,
229
@@ -854,10 +841,11 @@
230
        link->target.activation = ptr;
231
        link->target.system = data->data_system;
232
        link->target.fd = signalfd;
233
+       link->target.trigger = link->target.activation->server_version < 1 ?
234
+           trigger_target_v0 : trigger_target_v1;
235
        spa_list_append(&data->links, &link->link);
236
 
237
-       pw_loop_invoke(data->data_loop,
238
-                       do_activate_link, SPA_ID_INVALID, NULL, 0, false, link);
239
+       pw_impl_node_add_target(node, &link->target);
240
 
241
        pw_log_debug("node %p: add link %p: memid:%u fd:%d id:%u state:%p pending:%d/%d",
242
                node, link, memid, signalfd, node_id,
243
@@ -950,28 +938,29 @@
244
 
245
 static void do_node_init(struct node_data *data)
246
 {
247
+   struct pw_impl_node *node = data->node;
248
    struct pw_impl_port *port;
249
    struct mix *mix;
250
 
251
-   pw_log_debug("%p: node %p init", data, data->node);
252
+   pw_log_debug("%p: node %p init", data, node);
253
    add_node_update(data, PW_CLIENT_NODE_UPDATE_PARAMS |
254
                PW_CLIENT_NODE_UPDATE_INFO,
255
                SPA_NODE_CHANGE_MASK_FLAGS |
256
                SPA_NODE_CHANGE_MASK_PROPS |
257
                SPA_NODE_CHANGE_MASK_PARAMS);
258
 
259
-   spa_list_for_each(port, &data->node->input_ports, link) {
260
+   spa_list_for_each(port, &node->input_ports, link) {
261
        mix = create_mix(data, port, SPA_ID_INVALID, SPA_ID_INVALID);
262
        if (mix == NULL)
263
-           pw_log_error("%p: failed to create port mix: %m", data->node);
264
+           pw_log_error("%p: failed to create port mix: %m", node);
265
        add_port_update(data, port,
266
                PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
267
                PW_CLIENT_NODE_PORT_UPDATE_INFO);
268
    }
269
-   spa_list_for_each(port, &data->node->output_ports, link) {
270
+   spa_list_for_each(port, &node->output_ports, link) {
271
        mix = create_mix(data, port, SPA_ID_INVALID, SPA_ID_INVALID);
272
        if (mix == NULL)
273
-           pw_log_error("%p: failed to create port mix: %m", data->node);
274
+           pw_log_error("%p: failed to create port mix: %m", node);
275
        add_port_update(data, port,
276
                PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
277
                PW_CLIENT_NODE_PORT_UPDATE_INFO);
278
@@ -1125,21 +1114,22 @@
279
 static void client_node_removed(void *_data)
280
 {
281
    struct node_data *data = _data;
282
+   struct pw_impl_node *node = data->node;
283
    pw_log_debug("%p: removed", data);
284
 
285
    spa_hook_remove(&data->proxy_client_node_listener);
286
    spa_hook_remove(&data->client_node_listener);
287
 
288
-   if (data->node) {
289
+   if (node) {
290
        spa_hook_remove(&data->node_listener);
291
-       pw_impl_node_remove_rt_listener(data->node,
292
+       pw_impl_node_remove_rt_listener(node,
293
                &data->node_rt_listener);
294
-       pw_impl_node_set_state(data->node, PW_NODE_STATE_SUSPENDED);
295
+       pw_impl_node_set_state(node, PW_NODE_STATE_SUSPENDED);
296
 
297
        clean_node(data);
298
 
299
        if (data->do_free)
300
-           pw_impl_node_destroy(data->node);
301
+           pw_impl_node_destroy(node);
302
    }
303
    data->client_node = NULL;
304
 }
305
@@ -1197,6 +1187,8 @@
306
    if (node->data_loop == NULL)
307
        goto error;
308
 
309
+   pw_log_debug("%p: export node %p", core, object);
310
+
311
    user_data_size = SPA_ROUND_UP_N(user_data_size, __alignof__(struct node_data));
312
 
313
    client_node = pw_core_create_object(core,
314
@@ -1219,6 +1211,11 @@
315
    data->client_node = (struct pw_client_node *)client_node;
316
    data->remote_id = SPA_ID_INVALID;
317
 
318
+   /* the node might have been registered and added to a driver. When we export,
319
+    * we will be assigned a new driver target from the server and we can forget our
320
+    * local ones. */
321
+   pw_node_peer_unref(spa_steal_ptr(node->from_driver_peer));
322
+   pw_node_peer_unref(spa_steal_ptr(node->to_driver_peer));
323
 
324
    data->allow_mlock = pw_properties_get_bool(node->properties, "mem.allow-mlock",
325
                           data->context->settings.mem_allow_mlock);
326
pipewire-1.0.1.tar.bz2/src/modules/module-client-node/v0/transport.c -> pipewire-1.2.0.tar.gz/src/modules/module-client-node/v0/transport.c Changed
12
 
1
@@ -191,8 +191,8 @@
2
 
3
    impl->mem = pw_mempool_alloc(pw_context_get_mempool(context),
4
            PW_MEMBLOCK_FLAG_READWRITE |
5
-           PW_MEMBLOCK_FLAG_MAP |
6
-           PW_MEMBLOCK_FLAG_SEAL,
7
+           PW_MEMBLOCK_FLAG_SEAL |
8
+           PW_MEMBLOCK_FLAG_MAP,
9
            SPA_DATA_MemFd, area_get_size(&area));
10
    if (impl->mem == NULL) {
11
        free(impl);
12
pipewire-1.0.1.tar.bz2/src/modules/module-combine-stream.c -> pipewire-1.2.0.tar.gz/src/modules/module-combine-stream.c Changed
294
 
1
@@ -21,10 +21,12 @@
2
 #include <spa/utils/json.h>
3
 #include <spa/utils/ringbuffer.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/log.h>
6
 #include <spa/pod/builder.h>
7
 #include <spa/param/audio/format-utils.h>
8
 #include <spa/param/audio/raw.h>
9
 #include <spa/param/latency-utils.h>
10
+#include <spa/param/tag-utils.h>
11
 
12
 #include <pipewire/impl.h>
13
 #include <pipewire/i18n.h>
14
@@ -96,7 +98,7 @@
15
  *             {
16
  *                 matches = 
17
  *                     # any of the items in matches needs to match, if one does,
18
- *                     # actions are emited.
19
+ *                     # actions are emitted.
20
  *                     {
21
  *                         # all keys must match the value. ! negates. ~ starts regex.
22
  *                         #node.name = "~alsa_input.*"
23
@@ -235,7 +237,7 @@
24
 struct impl {
25
    struct pw_context *context;
26
    struct pw_loop *main_loop;
27
-   struct pw_data_loop *data_loop;
28
+   struct pw_loop *data_loop;
29
 
30
    struct pw_properties *props;
31
 
32
@@ -304,13 +306,12 @@
33
 
34
    struct spa_audio_info_raw info;
35
    uint32_t remapSPA_AUDIO_MAX_CHANNELS;
36
-   uint32_t rate;
37
 
38
    void *delaybuf;
39
    struct ringbuffer delaySPA_AUDIO_MAX_CHANNELS;
40
 
41
-   int64_t delay_nsec;     /* for main loop */
42
-   int64_t data_delay_nsec;    /* for data loop */
43
+   int64_t delay_samples;      /* for main loop */
44
+   int64_t data_delay_samples; /* for data loop */
45
 
46
    unsigned int ready:1;
47
    unsigned int added:1;
48
@@ -440,11 +441,10 @@
49
 {
50
    struct pw_time t;
51
 
52
-   if (pw_stream_get_time_n(s->stream, &t, sizeof(t)) < 0 ||
53
-           t.rate.denom == 0)
54
+   if (pw_stream_get_time_n(s->stream, &t, sizeof(t)) < 0)
55
        return INT64_MIN;
56
 
57
-   return t.delay * SPA_NSEC_PER_SEC * t.rate.num / t.rate.denom;
58
+   return t.delay;  /* samples at graph rate */
59
 }
60
 
61
 static void update_latency(struct impl *impl)
62
@@ -541,7 +541,7 @@
63
    for (i = 0; i < channels; ++i)
64
        ringbuffer_init(&info.delayi, SPA_PTROFF(info.buf, i*size, void), size);
65
 
66
-   pw_data_loop_invoke(stream->impl->data_loop, do_replace_delay, 0, NULL, 0, true, &info);
67
+   pw_loop_invoke(stream->impl->data_loop, do_replace_delay, 0, NULL, 0, true, &info);
68
 
69
    free(info.buf);
70
 }
71
@@ -557,20 +557,19 @@
72
    spa_list_for_each(s, &impl->streams, link) {
73
        int64_t delay = get_stream_delay(s);
74
 
75
-       if (delay != s->delay_nsec && delay != INT64_MIN)
76
-           pw_log_debug("stream %d delay:%"PRIi64" ns", s->id, delay);
77
+       if (delay != s->delay_samples && delay != INT64_MIN)
78
+           pw_log_debug("stream %d delay:%"PRIi64" samples", s->id, delay);
79
 
80
        max_delay = SPA_MAX(max_delay, delay);
81
-       s->delay_nsec = delay;
82
+       s->delay_samples = delay;
83
    }
84
 
85
    spa_list_for_each(s, &impl->streams, link) {
86
        uint32_t size = 0;
87
 
88
-       if (s->delay_nsec != INT64_MIN) {
89
-           int64_t delay = max_delay - s->delay_nsec;
90
-           size = delay * s->rate / SPA_NSEC_PER_SEC;
91
-           size *= sizeof(float);
92
+       if (s->delay_samples != INT64_MIN) {
93
+           int64_t delay = max_delay - s->delay_samples;
94
+           size = delay * sizeof(float);
95
        }
96
 
97
        resize_delay(s, size);
98
@@ -605,7 +604,7 @@
99
 
100
 static void clear_delaybuf(struct impl *impl)
101
 {
102
-   pw_data_loop_invoke(impl->data_loop, do_clear_delaybuf, 0, NULL, 0, true, impl);
103
+   pw_loop_invoke(impl->data_loop, do_clear_delaybuf, 0, NULL, 0, true, impl);
104
 }
105
 
106
 static int do_add_stream(struct spa_loop *loop, bool async, uint32_t seq,
107
@@ -621,6 +620,27 @@
108
    return 0;
109
 }
110
 
111
+static void param_tag_changed(struct impl *impl, const struct spa_pod *param)
112
+{
113
+   if (param == NULL)
114
+       return;
115
+
116
+   pw_log_debug("tag update");
117
+   struct stream *s;
118
+   struct spa_tag_info tag;
119
+   const struct spa_pod *params1 = { param };
120
+   void *state = NULL;
121
+
122
+   if (spa_tag_parse(param, &tag, &state) < 0)
123
+       return;
124
+   spa_list_for_each(s, &impl->streams, link) {
125
+       if (s->stream == NULL)
126
+           continue;
127
+       pw_log_debug("updating stream %d", s->id);
128
+       pw_stream_update_params(s->stream, params, 1);
129
+   }
130
+}
131
+
132
 static int do_remove_stream(struct spa_loop *loop, bool async, uint32_t seq,
133
                 const void *data, size_t size, void *user_data)
134
 {
135
@@ -637,7 +657,7 @@
136
 {
137
    pw_log_debug("destroy stream %d", s->id);
138
 
139
-   pw_data_loop_invoke(s->impl->data_loop, do_remove_stream, 0, NULL, 0, true, s);
140
+   pw_loop_invoke(s->impl->data_loop, do_remove_stream, 0, NULL, 0, true, s);
141
 
142
    if (destroy && s->stream) {
143
        spa_hook_remove(&s->stream_listener);
144
@@ -707,22 +727,9 @@
145
 {
146
    struct stream *s = d;
147
    struct spa_latency_info latency;
148
-   struct spa_audio_info format = { 0 };
149
 
150
    switch (id) {
151
    case SPA_PARAM_Format:
152
-       if (!param) {
153
-           s->rate = 0;
154
-       } else {
155
-           if (spa_format_parse(param, &format.media_type, &format.media_subtype) < 0)
156
-               break;
157
-           if (format.media_type != SPA_MEDIA_TYPE_audio ||
158
-                   format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
159
-               break;
160
-           if (spa_format_audio_raw_parse(param, &format.info.raw) < 0)
161
-               break;
162
-           s->rate = format.info.raw.rate;
163
-       }
164
        update_delay(s->impl);
165
        break;
166
    case SPA_PARAM_Latency:
167
@@ -734,6 +741,7 @@
168
            s->latency = latency;
169
        }
170
        update_latency(s->impl);
171
+       update_delay(s->impl);
172
        break;
173
    default:
174
        break;
175
@@ -875,7 +883,7 @@
176
            direction, PW_ID_ANY, flags, params, n_params)) < 0)
177
        goto error;
178
 
179
-   pw_data_loop_invoke(impl->data_loop, do_add_stream, 0, NULL, 0, true, s);
180
+   pw_loop_invoke(impl->data_loop, do_add_stream, 0, NULL, 0, true, s);
181
    update_delay(impl);
182
    return 0;
183
 
184
@@ -1074,10 +1082,10 @@
185
        return false;
186
 
187
    delay = get_stream_delay(s);
188
-   if (delay == INT64_MIN || delay == s->data_delay_nsec)
189
+   if (delay == INT64_MIN || delay == s->data_delay_samples)
190
        return false;
191
 
192
-   s->data_delay_nsec = delay;
193
+   s->data_delay_samples = delay;
194
    return true;
195
 }
196
 
197
@@ -1262,6 +1270,10 @@
198
        update_latency(impl);
199
        break;
200
    }
201
+   case SPA_PARAM_Tag: {
202
+       param_tag_changed(impl, param);
203
+       break;
204
+   }
205
    default:
206
        break;
207
    }
208
@@ -1399,6 +1411,8 @@
209
            pw_core_disconnect(impl->core);
210
        impl->core = NULL;
211
    }
212
+   if (impl->data_loop)
213
+       pw_context_release_loop(impl->context, impl->data_loop);
214
 
215
    pw_properties_free(impl->stream_props);
216
    pw_properties_free(impl->combine_props);
217
@@ -1439,6 +1453,7 @@
218
    struct impl *impl;
219
    const char *str, *prefix;
220
    int res;
221
+   struct spa_error_location loc = {};
222
 
223
    PW_LOG_TOPIC_INIT(mod_topic);
224
 
225
@@ -1447,22 +1462,29 @@
226
        return -errno;
227
 
228
    pw_log_debug("module %p: new %s", impl, args);
229
-   impl->main_loop = pw_context_get_main_loop(context);
230
-   impl->data_loop = pw_context_get_data_loop(context);
231
+   impl->module = module;
232
+   impl->context = context;
233
 
234
    spa_list_init(&impl->streams);
235
 
236
    if (args == NULL)
237
        args = "";
238
 
239
-   props = pw_properties_new_string(args);
240
+   props = pw_properties_new_string_checked(args, strlen(args), &loc);
241
    if (props == NULL) {
242
        res = -errno;
243
-       pw_log_error( "can't create properties: %m");
244
+       if (loc.reason)
245
+           spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_ERROR, &loc,
246
+                   "invalid module arguments: %s", loc.reason);
247
+       else
248
+           pw_log_error("can't create properties: %m");
249
        goto error;
250
    }
251
    impl->props = props;
252
 
253
+   impl->main_loop = pw_context_get_main_loop(context);
254
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
255
+
256
    if ((str = pw_properties_get(props, "combine.mode")) == NULL)
257
        str = "sink";
258
 
259
@@ -1497,8 +1519,7 @@
260
        goto error;
261
    }
262
 
263
-   impl->module = module;
264
-   impl->context = context;
265
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
266
 
267
    if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
268
        pw_properties_setf(props, PW_KEY_NODE_GROUP, "combine-%s-%u-%u",
269
@@ -1532,6 +1553,7 @@
270
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
271
        pw_properties_update_string(impl->stream_props, str, strlen(str));
272
 
273
+   copy_props(props, impl->combine_props, PW_KEY_NODE_LOOP_NAME);
274
    copy_props(props, impl->combine_props, PW_KEY_AUDIO_CHANNELS);
275
    copy_props(props, impl->combine_props, SPA_KEY_AUDIO_POSITION);
276
    copy_props(props, impl->combine_props, PW_KEY_NODE_NAME);
277
@@ -1542,13 +1564,16 @@
278
    copy_props(props, impl->combine_props, PW_KEY_NODE_VIRTUAL);
279
    copy_props(props, impl->combine_props, PW_KEY_MEDIA_CLASS);
280
    copy_props(props, impl->combine_props, "resample.prefill");
281
+   copy_props(props, impl->combine_props, "resample.disable");
282
 
283
    parse_audio_info(impl->combine_props, &impl->info);
284
 
285
+   copy_props(props, impl->stream_props, PW_KEY_NODE_LOOP_NAME);
286
    copy_props(props, impl->stream_props, PW_KEY_NODE_GROUP);
287
    copy_props(props, impl->stream_props, PW_KEY_NODE_VIRTUAL);
288
    copy_props(props, impl->stream_props, PW_KEY_NODE_LINK_GROUP);
289
    copy_props(props, impl->stream_props, "resample.prefill");
290
+   copy_props(props, impl->stream_props, "resample.disable");
291
 
292
    if (pw_properties_get(impl->stream_props, PW_KEY_MEDIA_ROLE) == NULL)
293
        pw_properties_set(props, PW_KEY_MEDIA_ROLE, "filter");
294
pipewire-1.0.1.tar.bz2/src/modules/module-echo-cancel.c -> pipewire-1.2.0.tar.gz/src/modules/module-echo-cancel.c Changed
335
 
1
@@ -249,6 +249,7 @@
2
 {
3
    spa_audio_aec_run(impl->aec, rec, play, out, n_samples);
4
 
5
+#ifdef HAVE_SPA_PLUGINS
6
    if (SPA_UNLIKELY(impl->wav_path0)) {
7
        if (impl->wav_file == NULL) {
8
            struct wav_file_info info;
9
@@ -287,6 +288,7 @@
10
        wav_file_close(impl->wav_file);
11
        impl->wav_file = NULL;
12
    }
13
+#endif
14
 }
15
 
16
 static void process(struct impl *impl)
17
@@ -524,17 +526,38 @@
18
        enum pw_stream_state state, const char *error)
19
 {
20
    struct impl *impl = data;
21
+   int res;
22
+
23
    switch (state) {
24
    case PW_STREAM_STATE_PAUSED:
25
        pw_stream_flush(impl->source, false);
26
        pw_stream_flush(impl->capture, false);
27
+
28
+       if (old == PW_STREAM_STATE_STREAMING) {
29
+           if (pw_stream_get_state(impl->sink, NULL) != PW_STREAM_STATE_STREAMING) {
30
+               pw_log_debug("%p: deactivate %s", impl, impl->aec->name);
31
+               res = spa_audio_aec_deactivate(impl->aec);
32
+               if (res < 0 && res != -EOPNOTSUPP) {
33
+                   pw_log_error("aec plugin %s deactivate failed: %s", impl->aec->name, spa_strerror(res));
34
+               }
35
+           }
36
+       }
37
+       break;
38
+   case PW_STREAM_STATE_STREAMING:
39
+       if (pw_stream_get_state(impl->sink, NULL) == PW_STREAM_STATE_STREAMING) {
40
+           pw_log_debug("%p: activate %s", impl, impl->aec->name);
41
+           res = spa_audio_aec_activate(impl->aec);
42
+           if (res < 0 && res != -EOPNOTSUPP) {
43
+               pw_log_error("aec plugin %s activate failed: %s", impl->aec->name, spa_strerror(res));
44
+           }
45
+       }
46
        break;
47
    case PW_STREAM_STATE_UNCONNECTED:
48
-       pw_log_info("%p: input unconnected", impl);
49
+       pw_log_info("%p: capture unconnected", impl);
50
        pw_impl_module_schedule_destroy(impl->module);
51
        break;
52
    case PW_STREAM_STATE_ERROR:
53
-       pw_log_info("%p: input error: %s", impl, error);
54
+       pw_log_info("%p: capture error: %s", impl, error);
55
        break;
56
    default:
57
        break;
58
@@ -545,34 +568,18 @@
59
        enum pw_stream_state state, const char *error)
60
 {
61
    struct impl *impl = data;
62
-   int res;
63
 
64
    switch (state) {
65
    case PW_STREAM_STATE_PAUSED:
66
        pw_stream_flush(impl->source, false);
67
        pw_stream_flush(impl->capture, false);
68
-
69
-       if (old == PW_STREAM_STATE_STREAMING) {
70
-           pw_log_debug("%p: deactivate %s", impl, impl->aec->name);
71
-           res = spa_audio_aec_deactivate(impl->aec);
72
-           if (res < 0 && res != -EOPNOTSUPP) {
73
-               pw_log_error("aec plugin %s deactivate failed: %s", impl->aec->name, spa_strerror(res));
74
-           }
75
-       }
76
-       break;
77
-   case PW_STREAM_STATE_STREAMING:
78
-       pw_log_debug("%p: activate %s", impl, impl->aec->name);
79
-       res = spa_audio_aec_activate(impl->aec);
80
-       if (res < 0 && res != -EOPNOTSUPP) {
81
-           pw_log_error("aec plugin %s activate failed: %s", impl->aec->name, spa_strerror(res));
82
-       }
83
        break;
84
    case PW_STREAM_STATE_UNCONNECTED:
85
-       pw_log_info("%p: input unconnected", impl);
86
+       pw_log_info("%p: source unconnected", impl);
87
        pw_impl_module_schedule_destroy(impl->module);
88
        break;
89
    case PW_STREAM_STATE_ERROR:
90
-       pw_log_info("%p: input error: %s", impl, error);
91
+       pw_log_info("%p: source error: %s", impl, error);
92
        break;
93
    default:
94
        break;
95
@@ -677,10 +684,34 @@
96
    return 1;
97
 }
98
 
99
-static void input_param_changed(void *data, uint32_t id, const struct spa_pod* param)
100
+static void props_changed(struct impl* impl, const struct spa_pod *param)
101
 {
102
-   struct spa_pod_object* obj = (struct spa_pod_object*)param;
103
+   uint8_t buffer1024;
104
+   struct spa_pod_dynamic_builder b;
105
+   const struct spa_pod* params1;
106
    const struct spa_pod_prop* prop;
107
+   struct spa_pod_object* obj = (struct spa_pod_object*)param;
108
+
109
+   if (param == NULL)
110
+       return;
111
+
112
+   SPA_POD_OBJECT_FOREACH(obj, prop) {
113
+       if (prop->key == SPA_PROP_params)
114
+           set_params(impl, &prop->value);
115
+   }
116
+
117
+   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
118
+   params0 = get_props_param(impl, &b.b);
119
+   if (params0) {
120
+       pw_stream_update_params(impl->capture, params, 1);
121
+       if (impl->playback != NULL)
122
+           pw_stream_update_params(impl->playback, params, 1);
123
+   }
124
+   spa_pod_dynamic_builder_clean(&b);
125
+}
126
+
127
+static void input_param_changed(void *data, uint32_t id, const struct spa_pod* param)
128
+{
129
    struct impl* impl = data;
130
 
131
    switch (id) {
132
@@ -692,27 +723,7 @@
133
        input_param_latency_changed(impl, param);
134
        break;
135
    case SPA_PARAM_Props:
136
-       if (param != NULL) {
137
-           uint8_t buffer1024;
138
-           struct spa_pod_dynamic_builder b;
139
-           const struct spa_pod* params1;
140
-
141
-           SPA_POD_OBJECT_FOREACH(obj, prop) {
142
-               if (prop->key == SPA_PROP_params)
143
-                   set_params(impl, &prop->value);
144
-           }
145
-
146
-           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
147
-           params0 = get_props_param(impl, &b.b);
148
-           if (params0) {
149
-               pw_stream_update_params(impl->capture, params, 1);
150
-               if (impl->playback != NULL)
151
-                   pw_stream_update_params(impl->playback, params, 1);
152
-           }
153
-           spa_pod_dynamic_builder_clean(&b);
154
-       } else {
155
-           pw_log_warn("param is null");
156
-       }
157
+       props_changed(impl, param);
158
        break;
159
    }
160
 }
161
@@ -739,10 +750,37 @@
162
    .param_changed = input_param_changed
163
 };
164
 
165
-static void output_state_changed(void *data, enum pw_stream_state old,
166
+static void playback_state_changed(void *data, enum pw_stream_state old,
167
+       enum pw_stream_state state, const char *error)
168
+{
169
+   struct impl *impl = data;
170
+   switch (state) {
171
+   case PW_STREAM_STATE_PAUSED:
172
+       pw_stream_flush(impl->sink, false);
173
+       if (impl->playback != NULL)
174
+           pw_stream_flush(impl->playback, false);
175
+       if (old == PW_STREAM_STATE_STREAMING) {
176
+           impl->current_delay = 0;
177
+       }
178
+       break;
179
+   case PW_STREAM_STATE_UNCONNECTED:
180
+       pw_log_info("%p: playback unconnected", impl);
181
+       pw_impl_module_schedule_destroy(impl->module);
182
+       break;
183
+   case PW_STREAM_STATE_ERROR:
184
+       pw_log_info("%p: playback error: %s", impl, error);
185
+       break;
186
+   default:
187
+       break;
188
+   }
189
+}
190
+
191
+static void sink_state_changed(void *data, enum pw_stream_state old,
192
        enum pw_stream_state state, const char *error)
193
 {
194
    struct impl *impl = data;
195
+   int res;
196
+
197
    switch (state) {
198
    case PW_STREAM_STATE_PAUSED:
199
        pw_stream_flush(impl->sink, false);
200
@@ -750,14 +788,31 @@
201
            pw_stream_flush(impl->playback, false);
202
        if (old == PW_STREAM_STATE_STREAMING) {
203
            impl->current_delay = 0;
204
+
205
+           if (pw_stream_get_state(impl->capture, NULL) != PW_STREAM_STATE_STREAMING) {
206
+               pw_log_debug("%p: deactivate %s", impl, impl->aec->name);
207
+               res = spa_audio_aec_deactivate(impl->aec);
208
+               if (res < 0 && res != -EOPNOTSUPP) {
209
+                   pw_log_error("aec plugin %s deactivate failed: %s", impl->aec->name, spa_strerror(res));
210
+               }
211
+           }
212
+       }
213
+       break;
214
+   case PW_STREAM_STATE_STREAMING:
215
+       if (pw_stream_get_state(impl->capture, NULL) == PW_STREAM_STATE_STREAMING) {
216
+           pw_log_debug("%p: activate %s", impl, impl->aec->name);
217
+           res = spa_audio_aec_activate(impl->aec);
218
+           if (res < 0 && res != -EOPNOTSUPP) {
219
+               pw_log_error("aec plugin %s activate failed: %s", impl->aec->name, spa_strerror(res));
220
+           }
221
        }
222
        break;
223
    case PW_STREAM_STATE_UNCONNECTED:
224
-       pw_log_info("%p: output unconnected", impl);
225
+       pw_log_info("%p: sink unconnected", impl);
226
        pw_impl_module_schedule_destroy(impl->module);
227
        break;
228
    case PW_STREAM_STATE_ERROR:
229
-       pw_log_info("%p: output error: %s", impl, error);
230
+       pw_log_info("%p: sink error: %s", impl, error);
231
        break;
232
    default:
233
        break;
234
@@ -785,8 +840,6 @@
235
 
236
 static void output_param_changed(void *data, uint32_t id, const struct spa_pod *param)
237
 {
238
-   struct spa_pod_object *obj = (struct spa_pod_object *) param;
239
-   const struct spa_pod_prop *prop;
240
    struct impl *impl = data;
241
 
242
    switch (id) {
243
@@ -798,26 +851,7 @@
244
        output_param_latency_changed(impl, param);
245
        break;
246
    case SPA_PARAM_Props:
247
-       if (param != NULL) {
248
-           uint8_t buffer1024;
249
-           struct spa_pod_dynamic_builder b;
250
-           const struct spa_pod* params1;
251
-
252
-           SPA_POD_OBJECT_FOREACH(obj, prop)
253
-           {
254
-               if (prop->key == SPA_PROP_params) {
255
-                   spa_audio_aec_set_params(impl->aec, &prop->value);
256
-               }
257
-           }
258
-           spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
259
-           params0 = get_props_param(impl, &b.b);
260
-           if (params0 != NULL) {
261
-               pw_stream_update_params(impl->capture, params, 1);
262
-               if (impl->playback != NULL)
263
-                   pw_stream_update_params(impl->playback, params, 1);
264
-           }
265
-           spa_pod_dynamic_builder_clean(&b);
266
-       }
267
+       props_changed(impl, param);
268
        break;
269
    }
270
 }
271
@@ -904,23 +938,25 @@
272
 static const struct pw_stream_events playback_events = {
273
    PW_VERSION_STREAM_EVENTS,
274
    .destroy = playback_destroy,
275
-   .state_changed = output_state_changed,
276
+   .state_changed = playback_state_changed,
277
    .param_changed = output_param_changed
278
 };
279
 static const struct pw_stream_events sink_events = {
280
    PW_VERSION_STREAM_EVENTS,
281
    .destroy = sink_destroy,
282
    .process = sink_process,
283
-   .state_changed = output_state_changed,
284
+   .state_changed = sink_state_changed,
285
    .param_changed = output_param_changed
286
 };
287
 
288
+#define MAX_PARAMS 512u
289
+
290
 static int setup_streams(struct impl *impl)
291
 {
292
    int res;
293
    uint32_t n_params, i;
294
-   uint32_t offsets512;
295
-   const struct spa_pod *params512;
296
+   uint32_t offsetsMAX_PARAMS;
297
+   const struct spa_pod *paramsMAX_PARAMS;
298
    struct spa_pod_dynamic_builder b;
299
 
300
    impl->capture = pw_stream_new(impl->core,
301
@@ -970,15 +1006,28 @@
302
    n_params = 0;
303
    spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
304
 
305
-   offsetsn_params++ = b.b.state.offset;
306
-   spa_format_audio_raw_build(&b.b, SPA_PARAM_EnumFormat, &impl->capture_info);
307
-
308
+   if (n_params < MAX_PARAMS) {
309
+       offsetsn_params++ = b.b.state.offset;
310
+       spa_format_audio_raw_build(&b.b, SPA_PARAM_EnumFormat, &impl->capture_info);
311
+   }
312
    int nbr_of_external_props = spa_audio_aec_enum_props(impl->aec, 0, NULL);
313
-   if (nbr_of_external_props > 0) {
314
-       for (int i = 0; i < nbr_of_external_props; i++) {
315
+   for (int i = 0; i < nbr_of_external_props; i++) {
316
+       if (n_params < MAX_PARAMS) {
317
            offsetsn_params++ = b.b.state.offset;
318
            spa_audio_aec_enum_props(impl->aec, i, &b.b);
319
        }
320
+   }
321
+   if (n_params < MAX_PARAMS) {
322
+       offsetsn_params++ = b.b.state.offset;
323
+       spa_pod_builder_add_object(&b.b,
324
+                                SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
325
+                                SPA_PROP_INFO_name, SPA_POD_String("debug.aec.wav-path"),
326
+                                SPA_PROP_INFO_description, SPA_POD_String("Path to WAV file"),
327
+                                SPA_PROP_INFO_type, SPA_POD_String(impl->wav_path),
328
+                                SPA_PROP_INFO_params, SPA_POD_Bool(true));
329
+   }
330
+   if (n_params < MAX_PARAMS) {
331
+       offsetsn_params++ = b.b.state.offset;
332
        get_props_param(impl, &b.b);
333
    }
334
 
335
pipewire-1.0.1.tar.bz2/src/modules/module-example-filter.c -> pipewire-1.2.0.tar.gz/src/modules/module-example-filter.c Changed
18
 
1
@@ -52,13 +52,13 @@
2
  * - \ref PW_KEY_NODE_GROUP
3
  * - \ref PW_KEY_NODE_LINK_GROUP
4
  * - \ref PW_KEY_NODE_VIRTUAL
5
- * - \ref PW_KEY_NODE_NAME: See notes below. If not specified, defaults to
6
- *     'filter-<pid>-<module-id>'.
7
+ * - \ref PW_KEY_NODE_NAME : See notes below. If not specified, defaults to
8
+ *     'filter-PID-MODULEID'.
9
  *
10
  * Stream only properties:
11
  *
12
  * - \ref PW_KEY_MEDIA_CLASS
13
- * - \ref PW_KEY_NODE_NAME:  if not given per stream, the global node.name will be
14
+ * - \ref PW_KEY_NODE_NAME :  if not given per stream, the global node.name will be
15
  *         prefixed with 'input.' and 'output.' to generate a capture and playback
16
  *         stream node.name respectively.
17
  *
18
pipewire-1.0.1.tar.bz2/src/modules/module-ffado-driver.c -> pipewire-1.2.0.tar.gz/src/modules/module-ffado-driver.c Changed
1274
 
1
@@ -50,10 +50,14 @@
2
  * - `ffado.slave-mode`: slave mode
3
  * - `ffado.snoop-mode`: snoop mode
4
  * - `ffado.verbose`: ffado verbose level
5
+ * - `ffado.rtprio`: ffado realtime priority, this is by default the PipeWire server
6
+ *                   priority + 5
7
+ * - `ffado.realtime`: ffado realtime mode. this requires correctly configured rlimits
8
+ *                     to acquire FIFO scheduling at the ffado.rtprio priority
9
  * - `latency.internal.input`: extra input latency in frames
10
  * - `latency.internal.output`: extra output latency in frames
11
- * - `source.props`: Extra properties for the source filter.
12
- * - `sink.props`: Extra properties for the sink filter.
13
+ * - `source.props`: Extra properties for the source filter
14
+ * - `sink.props`: Extra properties for the sink filter
15
  *
16
  * ## General options
17
  *
18
@@ -82,6 +86,8 @@
19
  *         #ffado.slave-mode  = false
20
  *         #ffado.snoop-mode  = false
21
  *         #ffado.verbose     = 0
22
+ *         #ffado.rtprio      = 65
23
+ *         #ffado.realtime    = true
24
  *         #latency.internal.input  = 0
25
  *         #latency.internal.output = 0
26
  *         #audio.position    =  FL FR 
27
@@ -103,6 +109,7 @@
28
 #define PW_LOG_TOPIC_DEFAULT mod_topic
29
 
30
 #define MAX_PORTS  128
31
+#define FFADO_RT_PRIORITY_PACKETIZER_RELATIVE   5
32
 
33
 #define DEFAULT_DEVICES        " \"hw:0\" "
34
 #define DEFAULT_PERIOD_SIZE    1024
35
@@ -111,21 +118,25 @@
36
 #define DEFAULT_SLAVE_MODE false
37
 #define DEFAULT_SNOOP_MODE false
38
 #define DEFAULT_VERBOSE        0
39
+#define DEFAULT_RTPRIO     (RTPRIO_SERVER + FFADO_RT_PRIORITY_PACKETIZER_RELATIVE)
40
+#define DEFAULT_REALTIME   true
41
 
42
 #define DEFAULT_POSITION   " FL FR "
43
 #define DEFAULT_MIDI_PORTS 1
44
 
45
-#define MODULE_USAGE   "( remote.name=<remote> ) "             \
46
-           "( driver.mode=<sink|source|duplex> ) "         \
47
-           "( ffado.devices=<devices array size, default \"hw:0\"> ) " \
48
-           "( ffado.period-size=<period size, default 1024> ) "    \
49
-           "( ffado.period-num=<period num, default 3> ) "     \
50
-           "( ffado.sample-rate=<sampe rate, default 48000> ) "    \
51
-           "( ffado.slave-mode=<slave mode, default false> ) " \
52
-           "( ffado.snoop-mode=<snoop mode, default false> ) " \
53
-           "( ffado.verbose=<verbose level, default 0> ) "     \
54
-           "( audio.position=<channel map> ) "         \
55
-           "( source.props=<properties> ) "            \
56
+#define MODULE_USAGE   "( remote.name=<remote> ) "                 \
57
+           "( driver.mode=<sink|source|duplex, default duplex> ) "     \
58
+           "( ffado.devices=<devices array, default "DEFAULT_DEVICES"> ) " \
59
+           "( ffado.period-size=<period size, default 1024> ) "        \
60
+           "( ffado.period-num=<period num, default 3> ) "         \
61
+           "( ffado.sample-rate=<sampe rate, default 48000> ) "        \
62
+           "( ffado.slave-mode=<slave mode, default false> ) "     \
63
+           "( ffado.snoop-mode=<snoop mode, default false> ) "     \
64
+           "( ffado.verbose=<verbose level, default 0> ) "         \
65
+           "( ffado.rtprio=<realtime priority, default "SPA_STRINGIFY(DEFAULT_RTPRIO)"> ) "    \
66
+           "( ffado.realtime=<realtime mode, default true> ) "     \
67
+           "( audio.position=<channel map> ) "             \
68
+           "( source.props=<properties> ) "                \
69
            "( sink.props=<properties> ) "
70
 
71
 
72
@@ -136,12 +147,29 @@
73
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
74
 };
75
 
76
+struct port_data {
77
+   struct port *port;
78
+};
79
+
80
 struct port {
81
    enum spa_direction direction;
82
+   ffado_streaming_stream_type stream_type;
83
+   char name280;
84
+
85
    struct spa_latency_info latency2;
86
    bool latency_changed2;
87
    unsigned int is_midi:1;
88
+   unsigned int cleared:1;
89
    void *buffer;
90
+
91
+   uint8_t event_byte;
92
+   uint8_t event_type;
93
+   uint32_t event_time;
94
+   uint8_t event_buffer512;
95
+   uint32_t event_pos;
96
+   int event_pending;
97
+
98
+   struct port_data *data;
99
 };
100
 
101
 struct volume {
102
@@ -162,14 +190,17 @@
103
    struct port *portsMAX_PORTS;
104
    struct volume volume;
105
 
106
+   unsigned int ready:1;
107
    unsigned int running:1;
108
+   unsigned int transfered:1;
109
 };
110
 
111
 struct impl {
112
    struct pw_context *context;
113
    struct pw_loop *main_loop;
114
+   struct pw_loop *data_loop;
115
    struct spa_system *system;
116
-   struct spa_thread_utils *utils;
117
+   struct spa_source *ffado_timer;
118
 
119
    ffado_device_info_t device_info;
120
    ffado_options_t device_options;
121
@@ -189,6 +220,8 @@
122
    struct spa_hook core_proxy_listener;
123
    struct spa_hook core_listener;
124
 
125
+   uint32_t reset_work_id;
126
+
127
    struct spa_io_position *position;
128
 
129
    uint32_t latency2;
130
@@ -204,6 +237,8 @@
131
    bool slave_mode;
132
    bool snoop_mode;
133
    uint32_t verbose;
134
+   int32_t rtprio;
135
+   bool realtime;
136
 
137
    uint32_t input_latency;
138
    uint32_t output_latency;
139
@@ -213,15 +248,21 @@
140
    uint32_t ffado_xrun;
141
    uint32_t frame_time;
142
 
143
+   unsigned int do_disconnect:1;
144
+   unsigned int fix_midi:1;
145
+   unsigned int started:1;
146
+
147
    pthread_t thread;
148
 
149
-   unsigned int do_disconnect:1;
150
    unsigned int done:1;
151
    unsigned int triggered:1;
152
    unsigned int new_xrun:1;
153
-   unsigned int fix_midi:1;
154
 };
155
 
156
+static int stop_ffado_device(struct impl *impl);
157
+static int start_ffado_device(struct impl *impl);
158
+static void schedule_reset_ffado_device(struct impl *impl);
159
+
160
 static void reset_volume(struct volume *vol, uint32_t n_volumes)
161
 {
162
    uint32_t i;
163
@@ -246,6 +287,15 @@
164
    }
165
 }
166
 
167
+static void clear_port_buffer(struct port *p, uint32_t n_samples)
168
+{
169
+   if (!p->cleared) {
170
+       if (p->buffer)
171
+           memset(p->buffer, 0, n_samples * sizeof(uint32_t));
172
+       p->cleared = true;
173
+   }
174
+}
175
+
176
 static inline void fix_midi_event(uint8_t *data, size_t size)
177
 {
178
    /* fixup NoteOn with vel 0 */
179
@@ -255,11 +305,13 @@
180
    }
181
 }
182
 
183
-static void midi_to_ffado(struct impl *impl, float *dst, float *src, uint32_t n_samples)
184
+static void midi_to_ffado(struct port *p, float *src, uint32_t n_samples)
185
 {
186
    struct spa_pod *pod;
187
    struct spa_pod_sequence *seq;
188
    struct spa_pod_control *c;
189
+   uint32_t i, index = 0, unhandled = 0;
190
+   uint32_t *dst = p->buffer;
191
 
192
    if (src == NULL)
193
        return;
194
@@ -271,6 +323,15 @@
195
 
196
    seq = (struct spa_pod_sequence*)pod;
197
 
198
+   clear_port_buffer(p, n_samples);
199
+
200
+   /* first leftovers from previous cycle, always start at offset 0 */
201
+   for (i = 0; i < p->event_pos; i++) {
202
+       dstindex = 0x01000000 | (uint32_t) p->event_bufferi;
203
+       index += 8;
204
+   }
205
+   p->event_pos = 0;
206
+
207
    SPA_POD_SEQUENCE_FOREACH(seq, c) {
208
        switch(c->type) {
209
        case SPA_CONTROL_Midi:
210
@@ -278,37 +339,206 @@
211
            uint8_t *data = SPA_POD_BODY(&c->value);
212
            size_t size = SPA_POD_BODY_SIZE(&c->value);
213
 
214
-           if (impl->fix_midi)
215
-               fix_midi_event(data, size);
216
-
217
+           if (index < c->offset)
218
+               index = SPA_ROUND_UP_N(c->offset, 8);
219
+           for (i = 0; i < size; i++) {
220
+               if (index >= n_samples) {
221
+                   /* keep events that don't fit for the next cycle */
222
+                   if (p->event_pos < sizeof(p->event_buffer))
223
+                       p->event_bufferp->event_pos++ = datai;
224
+                   else
225
+                       unhandled++;
226
+               }
227
+               else
228
+                   dstindex = 0x01000000 | (uint32_t) datai;
229
+               index += 8;
230
+           }
231
            break;
232
        }
233
        default:
234
            break;
235
        }
236
    }
237
+   if (unhandled > 0)
238
+       pw_log_warn("%u MIDI events dropped (index %d)", unhandled, index);
239
+   else if (p->event_pos > 0)
240
+       pw_log_debug("%u MIDI events saved (index %d)", p->event_pos, index);
241
+}
242
+
243
+static int take_bytes(struct port *p, uint32_t *frame, uint8_t **bytes, size_t *size)
244
+{
245
+   if (p->event_pos == 0)
246
+       return 0;
247
+   *frame = p->event_time;
248
+   *bytes = p->event_buffer;
249
+   *size = p->event_pos;
250
+   return 1;
251
 }
252
 
253
-static void ffado_to_midi(float *dst, float *src, uint32_t size)
254
+static const int status_len = {
255
+   2,      /* noteoff */
256
+   2,      /* noteon */
257
+   2,      /* keypress */
258
+   2,      /* controller */
259
+   1,      /* pgmchange */
260
+   1,      /* chanpress */
261
+   2,      /* pitchbend */
262
+   -1,     /* invalid */
263
+   1,      /* sysex 0xf0 */
264
+   1,      /* qframe 0xf1 */
265
+   2,      /* songpos 0xf2 */
266
+   1,      /* songsel 0xf3 */
267
+   -1,     /* none 0xf4 */
268
+   -1,     /* none 0xf5 */
269
+   0,      /* tune request 0xf6 */
270
+   -1,     /* none 0xf7 */
271
+   0,      /* clock 0xf8 */
272
+   -1,     /* none 0xf9 */
273
+   0,      /* start 0xfa */
274
+   0,      /* continue 0xfb */
275
+   0,      /* stop 0xfc */
276
+   -1,     /* none 0xfd */
277
+   0,      /* sensing 0xfe */
278
+   0,      /* reset 0xff */
279
+};
280
+
281
+static int process_byte(struct port *p, uint32_t time, uint8_t byte,
282
+       uint32_t *frame, uint8_t **bytes, size_t *size)
283
+{
284
+   int res = 0;
285
+   if (byte >= 0xf8) {
286
+       if (byte == 0xfd) {
287
+           pw_log_warn("dropping invalid MIDI status bytes %08x", byte);
288
+           return false;
289
+       }
290
+       p->event_byte = byte;
291
+       *frame = time;
292
+       *bytes = &p->event_byte;
293
+       *size = 1;
294
+       return 1;
295
+   }
296
+   if ((byte & 0x80) && (byte != 0xf7 || p->event_type != 8)) {
297
+       if (p->event_pending > 0)
298
+           pw_log_warn("incomplete MIDI message %02x dropped %u time:%u",
299
+                   p->event_type, p->event_pending, time);
300
+       /* new command */
301
+       p->event_buffer0 = byte;
302
+       p->event_time = time;
303
+       if ((byte & 0xf0) == 0xf0) /* system message */
304
+           p->event_type = (byte & 0x0f) + 8;
305
+       else
306
+           p->event_type = (byte >> 4) & 0x07;
307
+       p->event_pos = 1;
308
+       p->event_pending = status_lenp->event_type;
309
+   } else {
310
+        if (p->event_pending > 0) {
311
+           /* rest of command */
312
+           if (p->event_pos < sizeof(p->event_buffer))
313
+               p->event_bufferp->event_pos++ = byte;
314
+           if (p->event_type != 8)
315
+               p->event_pending--;
316
+       } else {
317
+           /* running status */
318
+           p->event_buffer1 = byte;
319
+           p->event_time = time;
320
+           p->event_pending = status_lenp->event_type - 1;
321
+           p->event_pos = 2;
322
+       }
323
+   }
324
+   if (p->event_pending == 0) {
325
+       res = take_bytes(p, frame, bytes, size);
326
+       if (p->event_type >= 8)
327
+           p->event_type = 7;
328
+   } else if (p->event_type == 8) {
329
+       if (byte == 0xf7 || p->event_pos >= sizeof(p->event_buffer)) {
330
+           res = take_bytes(p, frame, bytes, size);
331
+           p->event_pos = 0;
332
+           if (byte == 0xf7) {
333
+               p->event_pending = 0;
334
+               p->event_type = 7;
335
+           }
336
+       }
337
+   }
338
+   return res;
339
+}
340
+
341
+static void ffado_to_midi(struct port *p, float *dst, uint32_t *src, uint32_t size)
342
 {
343
    struct spa_pod_builder b = { 0, };
344
    uint32_t i, count;
345
    struct spa_pod_frame f;
346
 
347
-   count = src ? 0 : 0;
348
+   count = src ? size : 0;
349
 
350
    spa_pod_builder_init(&b, dst, size);
351
    spa_pod_builder_push_sequence(&b, &f, 0);
352
    for (i = 0; i < count; i++) {
353
-   }
354
+       uint32_t data = srci, frame;
355
+       uint8_t *bytes;
356
+       size_t size;
357
+
358
+       if ((data & 0xff000000) == 0)
359
+           continue;
360
+
361
+       if (process_byte(p, i, data & 0xff, &frame, &bytes, &size)) {
362
+           spa_pod_builder_control(&b, frame, SPA_CONTROL_Midi);
363
+                   spa_pod_builder_bytes(&b, bytes, size);
364
+       }
365
+        }
366
    spa_pod_builder_pop(&b, &f);
367
+   if (p->event_pending > 0)
368
+       /* make sure the rest of the MIDI message is sent first in the next cycle */
369
+       p->event_time = 0;
370
+}
371
+
372
+static inline uint64_t get_time_ns(struct impl *impl)
373
+{
374
+   uint64_t nsec;
375
+   if (impl->sink.filter)
376
+       nsec = pw_filter_get_nsec(impl->sink.filter);
377
+   else if (impl->source.filter)
378
+       nsec = pw_filter_get_nsec(impl->source.filter);
379
+   else
380
+       nsec = 0;
381
+   return nsec;
382
+}
383
+
384
+static int set_timeout(struct impl *impl, uint64_t time)
385
+{
386
+   struct timespec timeout, interval;
387
+   timeout.tv_sec = time / SPA_NSEC_PER_SEC;
388
+   timeout.tv_nsec = time % SPA_NSEC_PER_SEC;
389
+   interval.tv_sec = 0;
390
+   interval.tv_nsec = 0;
391
+   pw_loop_update_timer(impl->data_loop,
392
+                                impl->ffado_timer, &timeout, &interval, true);
393
+   return 0;
394
 }
395
 
396
 static void stream_destroy(void *d)
397
 {
398
    struct stream *s = d;
399
+   uint32_t i;
400
+   for (i = 0; i < s->n_ports; i++) {
401
+       struct port *p = s->portsi;
402
+       if (p != NULL) {
403
+           s->portsi = NULL;
404
+           free(p->buffer);
405
+           free(p);
406
+       }
407
+   }
408
+   s->n_ports = 0;
409
    spa_hook_remove(&s->listener);
410
    s->filter = NULL;
411
+   s->ready = false;
412
+   s->running = false;
413
+}
414
+
415
+static void check_start(struct impl *impl)
416
+{
417
+   if ((!(impl->mode & MODE_SINK) || (impl->sink.ready && impl->sink.running)) &&
418
+       (!(impl->mode & MODE_SOURCE) || (impl->source.ready && impl->source.running)))
419
+       start_ffado_device(impl);
420
 }
421
 
422
 static void stream_state_changed(void *d, enum pw_filter_state old,
423
@@ -318,14 +548,19 @@
424
    struct impl *impl = s->impl;
425
    switch (state) {
426
    case PW_FILTER_STATE_ERROR:
427
+       pw_log_error("filter state %d error: %s", state, error);
428
+       SPA_FALLTHROUGH;
429
    case PW_FILTER_STATE_UNCONNECTED:
430
        pw_impl_module_schedule_destroy(impl->module);
431
        break;
432
    case PW_FILTER_STATE_PAUSED:
433
        s->running = false;
434
+       if (!impl->sink.running && !impl->source.running)
435
+           stop_ffado_device(impl);
436
        break;
437
    case PW_FILTER_STATE_STREAMING:
438
        s->running = true;
439
+       check_start(impl);
440
        break;
441
    default:
442
        break;
443
@@ -338,7 +573,8 @@
444
    struct impl *impl = s->impl;
445
    uint32_t i, n_samples = position->clock.duration;
446
 
447
-   if (impl->mode & MODE_SINK && impl->triggered) {
448
+   pw_log_trace_fp("process %d", impl->triggered);
449
+   if (impl->mode == MODE_SINK && impl->triggered) {
450
        impl->triggered = false;
451
        return;
452
    }
453
@@ -346,54 +582,81 @@
454
    for (i = 0; i < s->n_ports; i++) {
455
        struct port *p = s->portsi;
456
        float *src;
457
-       if (p == NULL)
458
+       if (p == NULL || p->data == NULL)
459
            continue;
460
 
461
-       src = pw_filter_get_dsp_buffer(p, n_samples);
462
-       if (src == NULL)
463
+       src = pw_filter_get_dsp_buffer(p->data, n_samples);
464
+       if (src == NULL) {
465
+           clear_port_buffer(p, n_samples);
466
            continue;
467
+       }
468
 
469
        if (SPA_UNLIKELY(p->is_midi))
470
-           midi_to_ffado(impl, p->buffer, src, n_samples);
471
+           midi_to_ffado(p, src, n_samples);
472
        else
473
            do_volume(p->buffer, src, &s->volume, i, n_samples);
474
+
475
+       p->cleared = false;
476
    }
477
    ffado_streaming_transfer_playback_buffers(impl->dev);
478
+   s->transfered = true;
479
 
480
-   pw_log_trace_fp("done %u", impl->frame_time);
481
-   if (impl->mode & MODE_SINK) {
482
+   if (impl->mode == MODE_SINK) {
483
+       pw_log_trace_fp("done %u", impl->frame_time);
484
        impl->done = true;
485
+       set_timeout(impl, position->clock.nsec);
486
    }
487
 }
488
 
489
+static void silence_playback(struct impl *impl)
490
+{
491
+   uint32_t i;
492
+   struct stream *s = &impl->sink;
493
+
494
+   for (i = 0; i < s->n_ports; i++) {
495
+       struct port *p = s->portsi;
496
+       if (p != NULL)
497
+           clear_port_buffer(p, impl->period_size);
498
+   }
499
+   ffado_streaming_transfer_playback_buffers(impl->dev);
500
+   s->transfered = true;
501
+}
502
+
503
 static void source_process(void *d, struct spa_io_position *position)
504
 {
505
    struct stream *s = d;
506
    struct impl *impl = s->impl;
507
    uint32_t i, n_samples = position->clock.duration;
508
 
509
-   if (impl->mode == MODE_SOURCE && !impl->triggered) {
510
+   pw_log_trace_fp("process %d", impl->triggered);
511
+
512
+   if (!impl->triggered) {
513
        pw_log_trace_fp("done %u", impl->frame_time);
514
        impl->done = true;
515
+       if (!impl->sink.transfered)
516
+           silence_playback(impl);
517
+       set_timeout(impl, position->clock.nsec);
518
        return;
519
    }
520
+
521
    impl->triggered = false;
522
 
523
    ffado_streaming_transfer_capture_buffers(impl->dev);
524
+   s->transfered = true;
525
 
526
    for (i = 0; i < s->n_ports; i++) {
527
        struct port *p = s->portsi;
528
        float *dst;
529
 
530
-       if (p == NULL || p->buffer == NULL)
531
+       if (p == NULL || p->data == NULL || p->buffer == NULL)
532
            continue;
533
 
534
-       dst = pw_filter_get_dsp_buffer(p, n_samples);
535
+       dst = pw_filter_get_dsp_buffer(p->data, n_samples);
536
        if (dst == NULL)
537
            continue;
538
 
539
        if (SPA_UNLIKELY(p->is_midi))
540
-           ffado_to_midi(dst, p->buffer, n_samples);
541
+           ffado_to_midi(p, dst, p->buffer, n_samples);
542
        else
543
            do_volume(dst, p->buffer, &s->volume, i, n_samples);
544
    }
545
@@ -415,8 +678,9 @@
546
 }
547
 
548
 static void param_latency_changed(struct stream *s, const struct spa_pod *param,
549
-       struct port *port)
550
+       struct port_data *data)
551
 {
552
+   struct port *port = data->port;
553
    struct spa_latency_info latency;
554
    bool update = false;
555
    enum spa_direction direction = port->direction;
556
@@ -430,60 +694,57 @@
557
    }
558
 }
559
 
560
-static void make_stream_ports(struct stream *s)
561
+static int make_stream_ports(struct stream *s)
562
 {
563
    struct impl *impl = s->impl;
564
    struct pw_properties *props;
565
-   char name512;
566
    uint8_t buffer1024;
567
    struct spa_pod_builder b;
568
    struct spa_latency_info latency;
569
    const struct spa_pod *params2;
570
-   uint32_t i, n_params = 0;
571
+   uint32_t i, n_params = 0, n_channels = 0;
572
    bool is_midi;
573
 
574
    for (i = 0; i < s->n_ports; i++) {
575
        struct port *port = s->portsi;
576
-       ffado_streaming_stream_type stream_type;
577
-       char portname256;
578
-
579
-       if (port != NULL) {
580
-           s->portsi = NULL;
581
+       if (port->data != NULL) {
582
            free(port->buffer);
583
-           pw_filter_remove_port(port);
584
+           pw_filter_remove_port(port->data);
585
+           port->data = NULL;
586
        }
587
+   }
588
+   for (i = 0; i < s->n_ports; i++) {
589
+       struct port *port = s->portsi;
590
+       char channel32;
591
 
592
-       if (s->direction == PW_DIRECTION_INPUT) {
593
-           ffado_streaming_get_playback_stream_name(impl->dev, i, portname, sizeof(portname));
594
-           stream_type = ffado_streaming_get_playback_stream_type(impl->dev, i);
595
-           snprintf(name, sizeof(name), "%s_out", portname);
596
-       } else {
597
-           ffado_streaming_get_capture_stream_name(impl->dev, i, portname, sizeof(portname));
598
-           stream_type = ffado_streaming_get_capture_stream_type(impl->dev, i);
599
-           snprintf(name, sizeof(name), "%s_in", portname);
600
-       }
601
+       snprintf(channel, sizeof(channel), "AUX%u", n_channels % SPA_AUDIO_MAX_CHANNELS);
602
 
603
-       switch (stream_type) {
604
+       switch (port->stream_type) {
605
        case ffado_stream_type_audio:
606
            props = pw_properties_new(
607
                    PW_KEY_FORMAT_DSP, "32 bit float mono audio",
608
                    PW_KEY_PORT_PHYSICAL, "true",
609
-                   PW_KEY_PORT_NAME, name,
610
+                   PW_KEY_PORT_TERMINAL, "true",
611
+                   PW_KEY_PORT_NAME, port->name,
612
+                   PW_KEY_AUDIO_CHANNEL, channel,
613
                    NULL);
614
            is_midi = false;
615
+           n_channels++;
616
            break;
617
        case ffado_stream_type_midi:
618
            props = pw_properties_new(
619
                    PW_KEY_FORMAT_DSP, "8 bit raw midi",
620
-                   PW_KEY_PORT_NAME, name,
621
+                   PW_KEY_PORT_NAME, port->name,
622
                    PW_KEY_PORT_PHYSICAL, "true",
623
+                   PW_KEY_PORT_TERMINAL, "true",
624
+                   PW_KEY_PORT_CONTROL, "true",
625
                    NULL);
626
 
627
            is_midi = true;
628
            break;
629
        default:
630
            pw_log_info("not registering unknown stream %d %s (type %d)", i,
631
-                   name, stream_type);
632
+                   port->name, port->stream_type);
633
            continue;
634
 
635
        }
636
@@ -497,38 +758,46 @@
637
        n_params = 0;
638
        paramsn_params++ = spa_latency_build(&b, SPA_PARAM_Latency, &latency);
639
 
640
-       port = pw_filter_add_port(s->filter,
641
+       port->data = pw_filter_add_port(s->filter,
642
                         s->direction,
643
                         PW_FILTER_PORT_FLAG_MAP_BUFFERS,
644
-                        sizeof(struct port),
645
-           props, params, n_params);
646
-       if (port == NULL) {
647
+                        sizeof(struct port_data), props, params, n_params);
648
+       if (port->data == NULL) {
649
            pw_log_error("Can't create port: %m");
650
-           return;
651
+           return -errno;
652
        }
653
+       port->data->port = port;
654
 
655
        port->latencys->direction = latency;
656
        port->is_midi = is_midi;
657
        port->buffer = calloc(impl->quantum_limit, sizeof(float));
658
        if (port->buffer == NULL) {
659
            pw_log_error("Can't create port buffer: %m");
660
-           return;
661
+           return -errno;
662
        }
663
+   }
664
+   return 0;
665
+}
666
+
667
+static void setup_stream_ports(struct stream *s)
668
+{
669
+   struct impl *impl = s->impl;
670
+   uint32_t i;
671
+   for (i = 0; i < s->n_ports; i++) {
672
+       struct port *port = s->portsi;
673
        if (s->direction == PW_DIRECTION_INPUT) {
674
            if (ffado_streaming_set_playback_stream_buffer(impl->dev, i, port->buffer))
675
-               pw_log_error("cannot configure port buffer for %s", name);
676
+               pw_log_error("cannot configure port buffer for %s", port->name);
677
 
678
            if (ffado_streaming_playback_stream_onoff(impl->dev, i, 1))
679
-               pw_log_error("cannot enable port %s", name);
680
+               pw_log_error("cannot enable port %s", port->name);
681
        } else {
682
            if (ffado_streaming_set_capture_stream_buffer(impl->dev, i, port->buffer))
683
-               pw_log_error("cannot configure port buffer for %s", name);
684
+               pw_log_error("cannot configure port buffer for %s", port->name);
685
 
686
            if (ffado_streaming_capture_stream_onoff(impl->dev, i, 1))
687
-               pw_log_error("cannot enable port %s", name);
688
+               pw_log_error("cannot enable port %s", port->name);
689
        }
690
-
691
-       s->portsi = port;
692
    }
693
 }
694
 
695
@@ -584,6 +853,7 @@
696
        const struct spa_pod *param)
697
 {
698
    struct stream *s = data;
699
+
700
    if (port_data != NULL) {
701
        switch (id) {
702
        case SPA_PARAM_Latency:
703
@@ -594,7 +864,10 @@
704
        switch (id) {
705
        case SPA_PARAM_PortConfig:
706
            pw_log_debug("PortConfig");
707
-           make_stream_ports(s);
708
+           if (make_stream_ports(s) >= 0) {
709
+               s->ready = true;
710
+               check_start(s->impl);
711
+           }
712
            break;
713
        case SPA_PARAM_Props:
714
            pw_log_debug("Props");
715
@@ -635,11 +908,11 @@
716
    n_params = 0;
717
    spa_pod_builder_init(&b, buffer, sizeof(buffer));
718
 
719
-   s->filter = pw_filter_new(impl->core, name, s->props);
720
-   s->props = NULL;
721
+   s->filter = pw_filter_new(impl->core, name, pw_properties_copy(s->props));
722
    if (s->filter == NULL)
723
        return -errno;
724
 
725
+   spa_zero(s->listener);
726
    if (s->direction == PW_DIRECTION_INPUT) {
727
        pw_filter_add_listener(s->filter, &s->listener,
728
                &sink_events, s);
729
@@ -664,105 +937,152 @@
730
            params, n_params);
731
 }
732
 
733
-static int create_filters(struct impl *impl)
734
+static void destroy_stream(struct stream *s)
735
 {
736
-   int res = 0;
737
-
738
-   if (impl->mode & MODE_SINK)
739
-       res = make_stream(&impl->sink, "FFADO Sink");
740
-
741
-   if (impl->mode & MODE_SOURCE)
742
-       res = make_stream(&impl->source, "FFADO Source");
743
-
744
-   return res;
745
+   if (s->filter)
746
+       pw_filter_destroy(s->filter);
747
 }
748
 
749
-static inline uint64_t get_time_ns(void)
750
+static void on_ffado_timeout(void *data, uint64_t expirations)
751
 {
752
-   struct timespec ts;
753
-   clock_gettime(CLOCK_MONOTONIC, &ts);
754
-   return SPA_TIMESPEC_TO_NSEC(&ts);
755
-}
756
-
757
-static void *ffado_process_thread(void *arg)
758
-{
759
-   struct impl *impl = arg;
760
+   struct impl *impl = data;
761
    bool source_running, sink_running;
762
    uint64_t nsec;
763
+   ffado_wait_response response;
764
 
765
-   while (true) {
766
-       ffado_wait_response response;
767
+   pw_log_trace_fp("wakeup %d", impl->done);
768
 
769
-       response = ffado_streaming_wait(impl->dev);
770
-       nsec = get_time_ns();
771
+   if (!impl->done) {
772
+       impl->pw_xrun++;
773
+       impl->new_xrun = true;
774
+       ffado_streaming_reset(impl->dev);
775
+   }
776
+again:
777
+   pw_log_trace_fp("FFADO wait");
778
+   response = ffado_streaming_wait(impl->dev);
779
+   nsec = get_time_ns(impl);
780
 
781
-       switch (response) {
782
-       case ffado_wait_ok:
783
-           break;
784
-       case ffado_wait_xrun:
785
-           pw_log_warn("FFADO xrun");
786
-           break;
787
-       case ffado_wait_shutdown:
788
-           pw_log_info("FFADO shutdown");
789
-           return NULL;
790
-       case ffado_wait_error:
791
-       default:
792
-           pw_log_error("FFADO error");
793
-           return NULL;
794
-       }
795
-       source_running = impl->source.running;
796
-       sink_running = impl->sink.running;
797
+   switch (response) {
798
+   case ffado_wait_ok:
799
+       break;
800
+   case ffado_wait_xrun:
801
+       pw_log_debug("FFADO xrun");
802
+       impl->ffado_xrun++;
803
+       impl->new_xrun = true;
804
+       goto again;
805
+   case ffado_wait_shutdown:
806
+       pw_log_info("FFADO shutdown");
807
+       return;
808
+   case ffado_wait_error:
809
+   default:
810
+       pw_log_error("FFADO error");
811
+       return;
812
+   }
813
+   source_running = impl->source.running && impl->sink.ready;
814
+   sink_running = impl->sink.running && impl->source.ready;
815
 
816
-       pw_log_trace_fp("process %d %u %u %p %d", impl->period_size, source_running,
817
-               sink_running, impl->position, impl->frame_time);
818
+   impl->source.transfered = false;
819
+   impl->sink.transfered = false;
820
 
821
-       if (impl->new_xrun) {
822
-           pw_log_warn("Xrun FFADO:%u PipeWire:%u", impl->ffado_xrun, impl->pw_xrun);
823
-           impl->new_xrun = false;
824
-       }
825
+   if (!source_running) {
826
+       ffado_streaming_transfer_capture_buffers(impl->dev);
827
+       impl->source.transfered = true;
828
+   }
829
+   if (!sink_running)
830
+       silence_playback(impl);
831
+
832
+   pw_log_trace_fp("process %d %u %u %p %d %"PRIu64, impl->period_size, source_running,
833
+           sink_running, impl->position, impl->frame_time, nsec);
834
 
835
-       if (impl->position) {
836
-           struct spa_io_clock *c = &impl->position->clock;
837
+   if (impl->new_xrun) {
838
+       pw_log_warn("Xrun FFADO:%u PipeWire:%u source:%d sink:%d",
839
+               impl->ffado_xrun, impl->pw_xrun, source_running, sink_running);
840
+       impl->new_xrun = false;
841
+   }
842
 
843
-           c->nsec = nsec;
844
-           c->rate = SPA_FRACTION(1, impl->sample_rate);
845
-           c->position += impl->period_size;
846
-           c->duration = impl->period_size;
847
-           c->delay = 0;
848
-           c->rate_diff = 1.0;
849
-           c->next_nsec = nsec;
850
+   if (impl->position) {
851
+       struct spa_io_clock *c = &impl->position->clock;
852
 
853
-           c->target_rate = c->rate;
854
-           c->target_duration = c->duration;
855
-       }
856
-       if (impl->mode & MODE_SINK && sink_running) {
857
-           impl->done = false;
858
-           impl->triggered = true;
859
-           pw_filter_trigger_process(impl->sink.filter);
860
-       } else if (impl->mode == MODE_SOURCE && source_running) {
861
-           impl->done = false;
862
-           impl->triggered = true;
863
-           pw_filter_trigger_process(impl->source.filter);
864
+#if 0
865
+       if (c->target_duration != (uint64_t) impl->period_size) {
866
+           ffado_streaming_transfer_capture_buffers(impl->dev);
867
+           silence_playback(impl);
868
+
869
+           if (ffado_streaming_set_period_size(impl->dev, c->target_duration) != 0) {
870
+               pw_log_warn("can't change period size");
871
+           } else {
872
+               sleep(1);
873
+               impl->period_size = c->target_duration;
874
+           }
875
+           goto again;
876
        }
877
+#endif
878
+
879
+       c->nsec = nsec;
880
+       c->rate = SPA_FRACTION(1, impl->sample_rate);
881
+       c->position += impl->period_size;
882
+       c->duration = impl->period_size;
883
+       c->delay = 0;
884
+       c->rate_diff = 1.0;
885
+       c->next_nsec = nsec + (c->duration * SPA_NSEC_PER_SEC) / impl->sample_rate;
886
+
887
+       c->target_rate = c->rate;
888
+       c->target_duration = c->duration;
889
+   }
890
+   if (impl->mode & MODE_SOURCE && source_running) {
891
+       impl->done = false;
892
+       impl->triggered = true;
893
+       set_timeout(impl, nsec + SPA_NSEC_PER_SEC);
894
+       pw_filter_trigger_process(impl->source.filter);
895
+   } else if (impl->mode == MODE_SINK && sink_running) {
896
+       impl->done = false;
897
+       impl->triggered = true;
898
+       set_timeout(impl, nsec + SPA_NSEC_PER_SEC);
899
+       pw_filter_trigger_process(impl->sink.filter);
900
+   } else {
901
+       impl->done = true;
902
+       set_timeout(impl, nsec);
903
    }
904
-   return NULL;
905
+}
906
+
907
+static void close_ffado_device(struct impl *impl)
908
+{
909
+   if (impl->dev == NULL)
910
+       return;
911
+
912
+   stop_ffado_device(impl);
913
+   ffado_streaming_finish(impl->dev);
914
+   impl->dev = NULL;
915
+
916
+   pw_log_info("closed FFADO device %s", impl->devices0);
917
 }
918
 
919
 static int open_ffado_device(struct impl *impl)
920
 {
921
-   ffado_streaming_stream_type stream_type;
922
-   uint32_t i;
923
+   int32_t target_rate, target_period;
924
+
925
+   if (impl->dev != NULL)
926
+       return 0;
927
+
928
+   if (impl->position) {
929
+       struct spa_io_clock *c = &impl->position->clock;
930
+       target_rate = c->target_rate.denom;
931
+       target_period = c->target_duration;
932
+   } else {
933
+       target_rate = impl->sample_rate;
934
+       target_period = impl->period_size;
935
+   }
936
 
937
    spa_zero(impl->device_info);
938
    impl->device_info.device_spec_strings = impl->devices;
939
    impl->device_info.nb_device_spec_strings = impl->n_devices;
940
 
941
    spa_zero(impl->device_options);
942
-   impl->device_options.sample_rate = impl->sample_rate;
943
-   impl->device_options.period_size = impl->period_size;
944
+   impl->device_options.sample_rate = target_rate;
945
+   impl->device_options.period_size = target_period;
946
    impl->device_options.nb_buffers = impl->n_periods;
947
-   impl->device_options.realtime = 1;
948
-   impl->device_options.packetizer_priority = 88;
949
+   impl->device_options.realtime = impl->realtime;
950
+   impl->device_options.packetizer_priority = impl->rtprio;
951
    impl->device_options.verbose = impl->verbose;
952
    impl->device_options.slave_mode = impl->slave_mode;
953
    impl->device_options.snoop_mode = impl->snoop_mode;
954
@@ -783,70 +1103,170 @@
955
    ffado_streaming_set_audio_datatype(impl->dev, ffado_audio_datatype_float);
956
 
957
    impl->sample_rate = impl->device_options.sample_rate;
958
+   impl->period_size = impl->device_options.period_size;
959
    impl->source.info.rate = impl->sample_rate;
960
    impl->sink.info.rate = impl->sample_rate;
961
 
962
-   impl->source.info.channels = 0;
963
    impl->source.n_ports = ffado_streaming_get_nb_capture_streams(impl->dev);
964
+   impl->sink.n_ports = ffado_streaming_get_nb_playback_streams(impl->dev);
965
+
966
+   if (impl->source.n_ports == 0 && impl->sink.n_ports == 0) {
967
+       close_ffado_device(impl);
968
+       return -EIO;
969
+   }
970
+
971
+   pw_log_info("opened FFADO device %s source:%d sink:%d rate:%d period:%d %p",
972
+           impl->devices0, impl->source.n_ports, impl->sink.n_ports,
973
+           impl->sample_rate, impl->period_size, impl->position);
974
+
975
+   return 0;
976
+}
977
+
978
+static int probe_ffado_device(struct impl *impl)
979
+{
980
+   int res;
981
+   uint32_t i, n_channels;
982
+   struct port *port;
983
+   char name256;
984
+
985
+   if ((res = open_ffado_device(impl)) < 0)
986
+       return res;
987
+
988
+   n_channels = 0;
989
    for (i = 0; i < impl->source.n_ports; i++) {
990
-       stream_type = ffado_streaming_get_capture_stream_type(impl->dev, i);
991
-       switch (stream_type) {
992
+       port = calloc(1, sizeof(struct port));
993
+       if (port == NULL)
994
+           return -errno;
995
+
996
+       port->direction = impl->source.direction;
997
+       port->stream_type = ffado_streaming_get_capture_stream_type(impl->dev, i);
998
+       ffado_streaming_get_capture_stream_name(impl->dev, i, name, sizeof(name));
999
+       snprintf(port->name, sizeof(port->name), "%s_out", name);
1000
+
1001
+       switch (port->stream_type) {
1002
        case ffado_stream_type_audio:
1003
-           impl->source.info.channels++;
1004
+           n_channels++;
1005
            break;
1006
        default:
1007
            break;
1008
        }
1009
+       impl->source.portsi = port;
1010
    }
1011
-   impl->sink.info.channels = 0;
1012
-   impl->sink.n_ports = ffado_streaming_get_nb_playback_streams(impl->dev);
1013
+   if (impl->source.info.channels != n_channels) {
1014
+       impl->source.info.channels = n_channels;
1015
+       for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
1016
+           impl->source.info.positioni = SPA_AUDIO_CHANNEL_AUX0 + i;
1017
+   }
1018
+
1019
+   n_channels = 0;
1020
    for (i = 0; i < impl->sink.n_ports; i++) {
1021
-       stream_type = ffado_streaming_get_playback_stream_type(impl->dev, i);
1022
-       switch (stream_type) {
1023
+       port = calloc(1, sizeof(struct port));
1024
+       if (port == NULL)
1025
+           return -errno;
1026
+
1027
+       port->direction = impl->sink.direction;
1028
+       port->stream_type = ffado_streaming_get_playback_stream_type(impl->dev, i);
1029
+       ffado_streaming_get_playback_stream_name(impl->dev, i, name, sizeof(name));
1030
+       snprintf(port->name, sizeof(port->name), "%s_in", name);
1031
+
1032
+       switch (port->stream_type) {
1033
        case ffado_stream_type_audio:
1034
-           impl->sink.info.channels++;
1035
+           n_channels++;
1036
            break;
1037
        default:
1038
            break;
1039
        }
1040
+       impl->sink.portsi = port;
1041
    }
1042
-   if (ffado_streaming_prepare(impl->dev)) {
1043
-       pw_log_error("Could not prepare streaming");
1044
-       return -EIO;
1045
+   if (impl->sink.info.channels != n_channels) {
1046
+       impl->sink.info.channels = n_channels;
1047
+       for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
1048
+           impl->sink.info.positioni = SPA_AUDIO_CHANNEL_AUX0 + i;
1049
    }
1050
-   return 0;
1051
+
1052
+   if (impl->mode & MODE_SINK) {
1053
+       if ((res = make_stream(&impl->sink, "FFADO Sink")) < 0)
1054
+           goto exit;
1055
+   }
1056
+   if (impl->mode & MODE_SOURCE) {
1057
+       if ((res = make_stream(&impl->source, "FFADO Source")) < 0)
1058
+           goto exit;
1059
+   }
1060
+exit:
1061
+   close_ffado_device(impl);
1062
+
1063
+   return res;
1064
 }
1065
 
1066
+
1067
 static int start_ffado_device(struct impl *impl)
1068
 {
1069
-   struct spa_thread *thr;
1070
+   int res;
1071
 
1072
-   if (ffado_streaming_start(impl->dev)) {
1073
-       pw_log_error("Could not start streaming");
1074
+   if (impl->started)
1075
+       return 0;
1076
+
1077
+   if ((res = open_ffado_device(impl)) < 0)
1078
+       return res;
1079
+
1080
+   setup_stream_ports(&impl->source);
1081
+   setup_stream_ports(&impl->sink);
1082
+
1083
+   if (ffado_streaming_prepare(impl->dev)) {
1084
+       pw_log_error("Could not prepare streaming");
1085
+       schedule_reset_ffado_device(impl);
1086
        return -EIO;
1087
    }
1088
 
1089
-   thr = spa_thread_utils_create(impl->utils, NULL, ffado_process_thread, impl);
1090
-   impl->thread = (pthread_t)thr;
1091
-   if (thr == NULL) {
1092
-       pw_log_error("%p: can't create thread: %m", impl);
1093
-       return -errno;
1094
+   if (ffado_streaming_start(impl->dev)) {
1095
+       pw_log_warn("Could not start FFADO streaming, try reset");
1096
+       schedule_reset_ffado_device(impl);
1097
+       return -EIO;
1098
    }
1099
-   spa_thread_utils_acquire_rt(impl->utils, thr, -1);
1100
+   pw_log_info("FFADO started streaming");
1101
 
1102
+   impl->started = true;
1103
+   impl->done = true;
1104
+   set_timeout(impl, get_time_ns(impl));
1105
    return 0;
1106
 }
1107
 
1108
 static int stop_ffado_device(struct impl *impl)
1109
 {
1110
-   if (ffado_streaming_stop(impl->dev)) {
1111
-       pw_log_error("Could not stop streaming");
1112
-   }
1113
-   spa_thread_utils_join(impl->utils, (struct spa_thread*)impl->thread, NULL);
1114
+   if (!impl->started)
1115
+       return 0;
1116
+
1117
+   impl->started = false;
1118
+   set_timeout(impl, 0);
1119
+   if (ffado_streaming_stop(impl->dev))
1120
+       pw_log_error("Could not stop FFADO streaming");
1121
+   else
1122
+       pw_log_info("FFADO stopped streaming");
1123
+
1124
+   close_ffado_device(impl);
1125
 
1126
    return 0;
1127
 }
1128
 
1129
+
1130
+static void do_reset_ffado(void *obj, void *data, int res, uint32_t id)
1131
+{
1132
+   struct impl *impl = obj;
1133
+
1134
+   impl->reset_work_id = SPA_ID_INVALID;
1135
+   close_ffado_device(impl);
1136
+   open_ffado_device(impl);
1137
+}
1138
+
1139
+static void schedule_reset_ffado_device(struct impl *impl)
1140
+{
1141
+   if (impl->reset_work_id != SPA_ID_INVALID)
1142
+       return;
1143
+
1144
+   impl->reset_work_id = pw_work_queue_add(pw_context_get_work_queue(impl->context),
1145
+                         impl, 0, do_reset_ffado, NULL);
1146
+}
1147
+
1148
 static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
1149
 {
1150
    struct impl *impl = data;
1151
@@ -879,17 +1299,22 @@
1152
 {
1153
    uint32_t i;
1154
 
1155
-   if (impl->dev) {
1156
-       stop_ffado_device(impl);
1157
-       ffado_streaming_finish(impl->dev);
1158
-       impl->dev = NULL;
1159
-   }
1160
-   if (impl->source.filter)
1161
-       pw_filter_destroy(impl->source.filter);
1162
-   if (impl->sink.filter)
1163
-       pw_filter_destroy(impl->sink.filter);
1164
+   if (impl->reset_work_id != SPA_ID_INVALID)
1165
+       pw_work_queue_cancel(pw_context_get_work_queue(impl->context),
1166
+                    impl, SPA_ID_INVALID);
1167
+
1168
+   close_ffado_device(impl);
1169
+
1170
+   destroy_stream(&impl->source);
1171
+   destroy_stream(&impl->sink);
1172
+
1173
    if (impl->core && impl->do_disconnect)
1174
        pw_core_disconnect(impl->core);
1175
+   if (impl->ffado_timer)
1176
+       pw_loop_destroy_source(impl->data_loop, impl->ffado_timer);
1177
+
1178
+   if (impl->data_loop)
1179
+       pw_context_release_loop(impl->context, impl->data_loop);
1180
 
1181
    pw_properties_free(impl->sink.props);
1182
    pw_properties_free(impl->source.props);
1183
@@ -1024,11 +1449,14 @@
1184
            "ffado.snoop-mode", DEFAULT_SNOOP_MODE);
1185
    impl->verbose = pw_properties_get_uint32(props,
1186
            "ffado.verbose", DEFAULT_VERBOSE);
1187
+   impl->rtprio = pw_properties_get_uint32(props,
1188
+           "ffado.rtprio", DEFAULT_RTPRIO);
1189
+   impl->realtime = pw_properties_get_bool(props,
1190
+           "ffado.realtime", DEFAULT_REALTIME);
1191
    impl->input_latency = pw_properties_get_uint32(props,
1192
            "latency.internal.input", 0);
1193
    impl->output_latency = pw_properties_get_uint32(props,
1194
            "latency.internal.output", 0);
1195
-   impl->utils = pw_thread_utils_get();
1196
 
1197
    impl->quantum_limit = pw_properties_get_uint32(
1198
            pw_context_get_properties(context),
1199
@@ -1045,7 +1473,9 @@
1200
    impl->module = module;
1201
    impl->context = context;
1202
    impl->main_loop = pw_context_get_main_loop(context);
1203
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
1204
    impl->system = impl->main_loop->system;
1205
+   impl->reset_work_id = SPA_ID_INVALID;
1206
 
1207
    impl->source.impl = impl;
1208
    impl->source.direction = PW_DIRECTION_OUTPUT;
1209
@@ -1066,21 +1496,32 @@
1210
            goto error;
1211
        }
1212
    }
1213
+   impl->ffado_timer = pw_loop_add_timer(impl->data_loop, on_ffado_timeout, impl);
1214
+   if (impl->ffado_timer == NULL) {
1215
+       pw_log_error("can't create ffado timer: %m");
1216
+       res = -errno;
1217
+       goto error;
1218
+   }
1219
 
1220
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
1221
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
1222
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
1223
    if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
1224
        pw_properties_set(props, PW_KEY_NODE_GROUP, "ffado-group");
1225
-   if (pw_properties_get(props, PW_KEY_NODE_ALWAYS_PROCESS) == NULL)
1226
-       pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
1227
+   if (pw_properties_get(props, PW_KEY_NODE_LINK_GROUP) == NULL)
1228
+       pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, "ffado-group");
1229
+   if (pw_properties_get(props, PW_KEY_NODE_PAUSE_ON_IDLE) == NULL)
1230
+       pw_properties_set(props, PW_KEY_NODE_PAUSE_ON_IDLE, "false");
1231
 
1232
    pw_properties_set(impl->sink.props, PW_KEY_MEDIA_CLASS, "Audio/Sink");
1233
-   pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "35001");
1234
+   pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_DRIVER, "35000");
1235
+   pw_properties_set(impl->sink.props, PW_KEY_PRIORITY_SESSION, "2000");
1236
    pw_properties_set(impl->sink.props, PW_KEY_NODE_NAME, "ffado_sink");
1237
    pw_properties_set(impl->sink.props, PW_KEY_NODE_DESCRIPTION, "FFADO Sink");
1238
 
1239
    pw_properties_set(impl->source.props, PW_KEY_MEDIA_CLASS, "Audio/Source");
1240
-   pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "35000");
1241
+   pw_properties_set(impl->source.props, PW_KEY_PRIORITY_DRIVER, "35001");
1242
+   pw_properties_set(impl->source.props, PW_KEY_PRIORITY_SESSION, "2001");
1243
    pw_properties_set(impl->source.props, PW_KEY_NODE_NAME, "ffado_source");
1244
    pw_properties_set(impl->source.props, PW_KEY_NODE_DESCRIPTION, "FFADO Source");
1245
 
1246
@@ -1089,9 +1530,11 @@
1247
    if ((str = pw_properties_get(props, "source.props")) != NULL)
1248
        pw_properties_update_string(impl->source.props, str, strlen(str));
1249
 
1250
-   copy_props(impl, props, PW_KEY_NODE_ALWAYS_PROCESS);
1251
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
1252
+   copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
1253
    copy_props(impl, props, PW_KEY_NODE_GROUP);
1254
    copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
1255
+   copy_props(impl, props, PW_KEY_NODE_PAUSE_ON_IDLE);
1256
 
1257
    parse_audio_info(impl->source.props, &impl->source.info);
1258
    parse_audio_info(impl->sink.props, &impl->sink.info);
1259
@@ -1119,13 +1562,7 @@
1260
            &impl->core_listener,
1261
            &core_events, impl);
1262
 
1263
-   if ((res = open_ffado_device(impl)) < 0)
1264
-       goto error;
1265
-
1266
-   if ((res = create_filters(impl)) < 0)
1267
-       goto error;
1268
-
1269
-   if ((res = start_ffado_device(impl)) < 0)
1270
+   if ((res = probe_ffado_device(impl)) < 0)
1271
        goto error;
1272
 
1273
    pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
1274
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain.c Changed
219
 
1
@@ -23,6 +23,7 @@
2
 #include <spa/param/tag-utils.h>
3
 #include <spa/pod/dynamic.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include <pipewire/utils.h>
8
 #include <pipewire/impl.h>
9
@@ -128,7 +129,7 @@
10
  * use a mixer if you want the output of multiple filters to go into one
11
  * filter input port.
12
  *
13
- * links can be omited when the graph has just 1 filter.
14
+ * links can be omitted when the graph has just 1 filter.
15
  *
16
  * ### Inputs and Outputs
17
  *
18
@@ -389,7 +390,7 @@
19
  * It has an output port "Out" and also a control output port "notify".
20
  *
21
  * "Freq", "Ampl", "Offset" and "Phase" can be used to control the sine wave
22
- * frequence, amplitude, offset and phase.
23
+ * frequency, amplitude, offset and phase.
24
  *
25
  * ## SOFA filter
26
  *
27
@@ -461,13 +462,13 @@
28
  * - \ref PW_KEY_NODE_GROUP
29
  * - \ref PW_KEY_NODE_LINK_GROUP
30
  * - \ref PW_KEY_NODE_VIRTUAL
31
- * - \ref PW_KEY_NODE_NAME: See notes below. If not specified, defaults to
32
- * 'filter-chain-<pid>-<module-id>'.
33
+ * - \ref PW_KEY_NODE_NAME : See notes below. If not specified, defaults to
34
+ * 'filter-chain-PID-MODULEID'.
35
  *
36
  * Stream only properties:
37
  *
38
  * - \ref PW_KEY_MEDIA_CLASS
39
- * - \ref PW_KEY_NODE_NAME:  if not given per stream, the global node.name will be
40
+ * - \ref PW_KEY_NODE_NAME :  if not given per stream, the global node.name will be
41
  *         prefixed with 'input.' and 'output.' to generate a capture and playback
42
  *         stream node.name respectively.
43
  *
44
@@ -514,8 +515,6 @@
45
  * to a stereo Dolby Surround signal.
46
  *
47
  *\code{.unparsed}
48
- *
49
- *\code{.unparsed}
50
  * context.modules = 
51
  * {   name = libpipewire-module-filter-chain
52
  *     args = {
53
@@ -1023,12 +1022,12 @@
54
        }
55
    } else if (p->hint & FC_HINT_INTEGER) {
56
        if (min == max) {
57
-           spa_pod_builder_int(b, def);
58
+           spa_pod_builder_int(b, (int32_t)def);
59
        } else {
60
            spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_Range, 0);
61
-           spa_pod_builder_int(b, def);
62
-           spa_pod_builder_int(b, min);
63
-           spa_pod_builder_int(b, max);
64
+           spa_pod_builder_int(b, (int32_t)def);
65
+           spa_pod_builder_int(b, (int32_t)min);
66
+           spa_pod_builder_int(b, (int32_t)max);
67
            spa_pod_builder_pop(b, &f1);
68
        }
69
    } else {
70
@@ -1074,7 +1073,7 @@
71
        if (p->hint & FC_HINT_BOOLEAN) {
72
            spa_pod_builder_bool(b, port->control_data0 <= 0.0f ? false : true);
73
        } else if (p->hint & FC_HINT_INTEGER) {
74
-           spa_pod_builder_int(b, port->control_data0);
75
+           spa_pod_builder_int(b, (int32_t)port->control_data0);
76
        } else {
77
            spa_pod_builder_float(b, port->control_data0);
78
        }
79
@@ -1141,7 +1140,7 @@
80
        if (spa_pod_parser_get_float(&prs, &value) >= 0) {
81
            val = &value;
82
        } else if (spa_pod_parser_get_double(&prs, &dbl_val) >= 0) {
83
-           value = dbl_val;
84
+           value = (float)dbl_val;
85
            val = &value;
86
        } else if (spa_pod_parser_get_int(&prs, &int_val) >= 0) {
87
            value = int_val;
88
@@ -1218,7 +1217,7 @@
89
        float v = vol->mute ? 0.0f : vol->volumesi;
90
        switch (vol->scalen_port) {
91
        case SCALE_CUBIC:
92
-           v = cbrt(v);
93
+           v = cbrtf(v);
94
            break;
95
        }
96
        v = v * (vol->maxn_port - vol->minn_port) + vol->minn_port;
97
@@ -1874,24 +1873,35 @@
98
  */
99
 static int parse_config(struct node *node, struct spa_json *config)
100
 {
101
-   const char *val;
102
-   int len;
103
-
104
-   if ((len = spa_json_next(config, &val)) <= 0)
105
-       return len;
106
+   const char *val, *s = config->cur;
107
+   int res = 0, len;
108
+   struct spa_error_location loc;
109
 
110
+   if ((len = spa_json_next(config, &val)) <= 0) {
111
+       res = -EINVAL;
112
+       goto done;
113
+   }
114
    if (spa_json_is_null(val, len))
115
-       return 0;
116
+       goto done;
117
 
118
-   if (spa_json_is_container(val, len))
119
+   if (spa_json_is_container(val, len)) {
120
        len = spa_json_container_len(config, val, len);
121
-
122
-   if ((node->config = malloc(len+1)) == NULL)
123
-       return -errno;
124
+       if (len == 0) {
125
+           res = -EINVAL;
126
+           goto done;
127
+       }
128
+   }
129
+   if ((node->config = malloc(len+1)) == NULL) {
130
+       res = -errno;
131
+       goto done;
132
+   }
133
 
134
    spa_json_parse_stringn(val, len, node->config, len+1);
135
-
136
-   return 0;
137
+done:
138
+   if (spa_json_get_error(config, s, &loc))
139
+       spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN,
140
+               &loc, "error: %s", loc.reason);
141
+   return res;
142
 }
143
 
144
 /**
145
@@ -1955,8 +1965,11 @@
146
                return -EINVAL;
147
            }
148
        }
149
-       else if (spa_json_next(json, &val) < 0)
150
-           break;
151
+       else {
152
+           pw_log_error("unexpected link key '%s'", key);
153
+           if (spa_json_next(json, &val) < 0)
154
+               break;
155
+       }
156
    }
157
    def_out_node = spa_list_first(&graph->node_list, struct node, link);
158
    def_in_node = spa_list_last(&graph->node_list, struct node, link);
159
@@ -2066,8 +2079,11 @@
160
                return -EINVAL;
161
            }
162
        }
163
-       else if (spa_json_next(json, &val) < 0)
164
-           break;
165
+       else {
166
+           pw_log_error("unexpected volume key '%s'", key);
167
+           if (spa_json_next(json, &val) < 0)
168
+               break;
169
+       }
170
    }
171
    if (capture)
172
        def_control = spa_list_first(&graph->node_list, struct node, link);
173
@@ -2162,12 +2178,18 @@
174
            have_config = true;
175
            if (spa_json_next(json, &val) < 0)
176
                break;
177
-       } else if (spa_json_next(json, &val) < 0)
178
-           break;
179
+       } else {
180
+           pw_log_warn("unexpected node key '%s'", key);
181
+           if (spa_json_next(json, &val) < 0)
182
+               break;
183
+       }
184
    }
185
-
186
    if (spa_streq(type, "builtin"))
187
        snprintf(plugin, sizeof(plugin), "%s", "builtin");
188
+   else if (spa_streq(type, "")) {
189
+       pw_log_error("missing plugin type");
190
+       return -EINVAL;
191
+   }
192
 
193
    pw_log_info("loading type:%s plugin:%s label:%s", type, plugin, label);
194
 
195
@@ -2756,8 +2778,11 @@
196
                return -EINVAL;
197
            }
198
            ppvolumes = &pvolumes;
199
-       } else if (spa_json_next(&it1, &val) < 0)
200
-           break;
201
+       } else {
202
+           pw_log_warn("unexpected graph key '%s'", key);
203
+           if (spa_json_next(&it1, &val) < 0)
204
+               break;
205
+       }
206
    }
207
    if (pnodes == NULL) {
208
        pw_log_error("filter.graph is missing a nodes array");
209
@@ -2995,8 +3020,7 @@
210
    }
211
 
212
    cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
213
-   impl->dsp.cpu_flags = cpu_iface ? spa_cpu_get_flags(cpu_iface) : 0;
214
-   dsp_ops_init(&impl->dsp);
215
+   dsp_ops_init(&impl->dsp, cpu_iface ? spa_cpu_get_flags(cpu_iface) : 0);
216
 
217
    if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
218
        pw_properties_setf(props, PW_KEY_NODE_GROUP, "filter-chain-%u-%u", pid, id);
219
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/biquad.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/biquad.c Changed
18
 
1
@@ -19,11 +19,11 @@
2
                double a0, double a1, double a2)
3
 {
4
    double a0_inv = 1 / a0;
5
-   bq->b0 = b0 * a0_inv;
6
-   bq->b1 = b1 * a0_inv;
7
-   bq->b2 = b2 * a0_inv;
8
-   bq->a1 = a1 * a0_inv;
9
-   bq->a2 = a2 * a0_inv;
10
+   bq->b0 = (float)(b0 * a0_inv);
11
+   bq->b1 = (float)(b1 * a0_inv);
12
+   bq->b2 = (float)(b2 * a0_inv);
13
+   bq->a1 = (float)(a1 * a0_inv);
14
+   bq->a2 = (float)(a2 * a0_inv);
15
 }
16
 
17
 static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance)
18
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
108
 
1
@@ -752,10 +752,10 @@
2
         if (samples == NULL)
3
        return NULL;
4
 
5
-   gain *= 2 / M_PI;
6
+   gain *= 2 / (float)M_PI;
7
    h = length / 2;
8
    for (i = 1; i < h; i += 2) {
9
-       v = (gain / i) * (0.43f + 0.57f * cosf(i * M_PI / h));
10
+       v = (gain / i) * (0.43f + 0.57f * cosf(i * (float)M_PI / h));
11
        samplesdelay + h + i = -v;
12
        samplesdelay + h - i =  v;
13
    }
14
@@ -784,6 +784,7 @@
15
 static float *resample_buffer(float *samples, int *n_samples,
16
        unsigned long in_rate, unsigned long out_rate, uint32_t quality)
17
 {
18
+#ifdef HAVE_SPA_PLUGINS
19
    uint32_t in_len, out_len, total_out = 0;
20
    int out_n_samples;
21
    float *out_samples, *out_buf, *in_buf;
22
@@ -849,6 +850,12 @@
23
    free(samples);
24
    free(out_samples);
25
    return NULL;
26
+#else
27
+   pw_log_error("compiled without spa-plugins support, can't resample");
28
+   float *out_samples = calloc(*n_samples, sizeof(float));
29
+   memcpy(out_samples, samples, *n_samples * sizeof(float));
30
+   return out_samples;
31
+#endif
32
 }
33
 
34
 static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
35
@@ -1127,7 +1134,7 @@
36
        return NULL;
37
 
38
    impl->rate = SampleRate;
39
-   impl->buffer_samples = max_delay * impl->rate;
40
+   impl->buffer_samples = (uint32_t)(max_delay * impl->rate);
41
    pw_log_info("max-delay:%f seconds rate:%lu samples:%d", max_delay, impl->rate, impl->buffer_samples);
42
 
43
    impl->buffer = calloc(impl->buffer_samples, sizeof(float));
44
@@ -1156,7 +1163,7 @@
45
    uint32_t r, w;
46
 
47
    if (delay != impl->delay) {
48
-       impl->delay_samples = SPA_CLAMP(delay * impl->rate, 0, impl->buffer_samples-1);
49
+       impl->delay_samples = SPA_CLAMP((uint32_t)(delay * impl->rate), 0u, impl->buffer_samples-1);
50
        impl->delay = delay;
51
    }
52
    r = impl->ptr;
53
@@ -1446,7 +1453,7 @@
54
    { .index = 4,
55
      .name = "Base",
56
      .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
57
-     .def = M_E, .min = -10.0f, .max = 10.0f
58
+     .def = (float)M_E, .min = -10.0f, .max = 10.0f
59
    },
60
 };
61
 
62
@@ -1503,7 +1510,7 @@
63
    { .index = 4,
64
      .name = "Base",
65
      .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
66
-     .def = M_E, .min = 2.0f, .max = 100.0f
67
+     .def = (float)M_E, .min = 2.0f, .max = 100.0f
68
    },
69
    { .index = 5,
70
      .name = "M1",
71
@@ -1604,7 +1611,7 @@
72
    .cleanup = builtin_cleanup,
73
 };
74
 
75
-#define M_PI_M2 ( M_PI + M_PI )
76
+#define M_PI_M2f (float)(M_PI+M_PI)
77
 
78
 /* sine */
79
 static void sine_run(void * Instance, unsigned long SampleCount)
80
@@ -1619,13 +1626,13 @@
81
 
82
    for (n = 0; n < SampleCount; n++) {
83
        if (out != NULL)
84
-           outn = sin(impl->accum) * ampl + offs;
85
+           outn = sinf(impl->accum) * ampl + offs;
86
        if (notify != NULL && n == 0)
87
-           notify0 = sin(impl->accum) * ampl + offs;
88
+           notify0 = sinf(impl->accum) * ampl + offs;
89
 
90
-       impl->accum += M_PI_M2 * freq / impl->rate;
91
-       if (impl->accum >= M_PI_M2)
92
-           impl->accum -= M_PI_M2;
93
+       impl->accum += M_PI_M2f * freq / impl->rate;
94
+       if (impl->accum >= M_PI_M2f)
95
+           impl->accum -= M_PI_M2f;
96
    }
97
 }
98
 
99
@@ -1651,7 +1658,7 @@
100
    { .index = 4,
101
      .name = "Phase",
102
      .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
103
-     .def = 0.0f, .min = -M_PI, .max = M_PI
104
+     .def = 0.0f, .min = (float)-M_PI, .max = (float)M_PI
105
    },
106
    { .index = 5,
107
      .name = "Offset",
108
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/convolver.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/convolver.c Changed
12
 
1
@@ -123,8 +123,8 @@
2
    if (conv->fft_buffer == NULL)
3
        goto error;
4
 
5
-   conv->segments = calloc(sizeof(float*), conv->segCount);
6
-   conv->segmentsIr = calloc(sizeof(float*), conv->segCount);
7
+   conv->segments = calloc(conv->segCount, sizeof(float*));
8
+   conv->segmentsIr = calloc(conv->segCount, sizeof(float*));
9
 
10
    for (i = 0; i < conv->segCount; i++) {
11
        int left = irlen - (i * conv->blockSize);
12
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/dsp-ops.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/dsp-ops.c Changed
19
 
1
@@ -84,14 +84,15 @@
2
    spa_zero(*ops);
3
 }
4
 
5
-int dsp_ops_init(struct dsp_ops *ops)
6
+int dsp_ops_init(struct dsp_ops *ops, uint32_t cpu_flags)
7
 {
8
    const struct dsp_info *info;
9
 
10
-   info = find_dsp_info(ops->cpu_flags);
11
+   info = find_dsp_info(cpu_flags);
12
    if (info == NULL)
13
        return -ENOTSUP;
14
 
15
+   ops->cpu_flags = cpu_flags;
16
    ops->priv = info;
17
    ops->free = impl_dsp_ops_free;
18
    ops->funcs = info->funcs;
19
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/dsp-ops.h -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/dsp-ops.h Changed
10
 
1
@@ -55,7 +55,7 @@
2
    const void *priv;
3
 };
4
 
5
-int dsp_ops_init(struct dsp_ops *ops);
6
+int dsp_ops_init(struct dsp_ops *ops, uint32_t cpu_flags);
7
 
8
 #define dsp_ops_free(ops)      (ops)->free(ops)
9
 
10
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/lv2_plugin.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/lv2_plugin.c Changed
31
 
1
@@ -237,6 +237,7 @@
2
    const LV2_Worker_Interface *work_iface;
3
 
4
    int32_t block_length;
5
+   LV2_Atom empty_atom;
6
 };
7
 
8
 static int
9
@@ -284,7 +285,7 @@
10
    struct plugin *p = d->p;
11
    struct context *c = p->c;
12
    struct instance *i;
13
-   uint32_t n_features = 0;
14
+   uint32_t n, n_features = 0;
15
    static const int32_t min_block_length = 1;
16
    static const int32_t max_block_length = 8192;
17
    static const int32_t seq_size = 32768;
18
@@ -339,6 +340,12 @@
19
                 i->work_iface = (const LV2_Worker_Interface*)
20
            lilv_instance_get_extension_data(i->instance, LV2_WORKER__interface);
21
         }
22
+   for (n = 0; n < desc->n_ports; n++) {
23
+       const LilvPort *port = lilv_plugin_get_port_by_index(p->p, n);
24
+       if (lilv_port_is_a(p->p, port, c->atom_AtomPort)) {
25
+           lilv_instance_connect_port(i->instance, n, &i->empty_atom);
26
+       }
27
+   }
28
 
29
    return i;
30
 }
31
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/pffft.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/pffft.c Changed
89
 
1
@@ -1273,7 +1273,7 @@
2
    int k1, j, ii;
3
 
4
    int nf = decompose(n, ifac, ntryh);
5
-   float argh = (2 * M_PI) / n;
6
+   float argh = (2 * (float)M_PI) / n;
7
    int is = 0;
8
    int nfm1 = nf - 1;
9
    int l1 = 1;
10
@@ -1291,8 +1291,8 @@
11
            for (ii = 3; ii <= ido; ii += 2) {
12
                i += 2;
13
                fi += 1;
14
-               wai - 2 = cos(fi * argld);
15
-               wai - 1 = sin(fi * argld);
16
+               wai - 2 = cosf(fi * argld);
17
+               wai - 1 = sinf(fi * argld);
18
            }
19
            is += ido;
20
        }
21
@@ -1306,7 +1306,7 @@
22
    int k1, j, ii;
23
 
24
    int nf = decompose(n, ifac, ntryh);
25
-   float argh = (2 * M_PI) / (float)n;
26
+   float argh = (2 * (float)M_PI) / (float)n;
27
    int i = 1;
28
    int l1 = 1;
29
    for (k1 = 1; k1 <= nf; k1++) {
30
@@ -1326,8 +1326,8 @@
31
            for (ii = 4; ii <= idot; ii += 2) {
32
                i += 2;
33
                fi += 1;
34
-               wai - 1 = cos(fi * argld);
35
-               wai = sin(fi * argld);
36
+               wai - 1 = cosf(fi * argld);
37
+               wai = sinf(fi * argld);
38
            }
39
            if (ip > 5) {
40
                wai1 - 1 = wai - 1;
41
@@ -1440,11 +1440,11 @@
42
            int i = k / SIMD_SZ;
43
            int j = k % SIMD_SZ;
44
            for (m = 0; m < SIMD_SZ - 1; ++m) {
45
-               float A = -2 * M_PI * (m + 1) * k / N;
46
+               float A = -2 * (float)M_PI * (m + 1) * k / N;
47
                s->e(2 * (i * 3 + m) + 0) * SIMD_SZ + j =
48
-                   cos(A);
49
+                   cosf(A);
50
                s->e(2 * (i * 3 + m) + 1) * SIMD_SZ + j =
51
-                   sin(A);
52
+                   sinf(A);
53
            }
54
        }
55
        rffti1_ps(N / SIMD_SZ, s->twiddle, s->ifac);
56
@@ -1453,11 +1453,11 @@
57
            int i = k / SIMD_SZ;
58
            int j = k % SIMD_SZ;
59
            for (m = 0; m < SIMD_SZ - 1; ++m) {
60
-               float A = -2 * M_PI * (m + 1) * k / N;
61
+               float A = -2 * (float)M_PI * (m + 1) * k / N;
62
                s->e(2 * (i * 3 + m) + 0) * SIMD_SZ + j =
63
-                   cos(A);
64
+                   cosf(A);
65
                s->e(2 * (i * 3 + m) + 1) * SIMD_SZ + j =
66
-                   sin(A);
67
+                   sinf(A);
68
            }
69
        }
70
        cffti1_ps(N / SIMD_SZ, s->twiddle, s->ifac);
71
@@ -1765,7 +1765,7 @@
72
    v4sf_union cr, ci, *uout = (v4sf_union *) out;
73
    v4sf save = in7, zero = VZERO();
74
    float xr0, xi0, xr1, xi1, xr2, xi2, xr3, xi3;
75
-   static const float s = M_SQRT2 / 2;
76
+   static const float s = (float)M_SQRT2 / 2;
77
 
78
    cr.v = in0;
79
    ci.v = inNcvec * 2 - 1;
80
@@ -1871,7 +1871,7 @@
81
 
82
    v4sf_union Xr, Xi, *uout = (v4sf_union *) out;
83
    float cr0, ci0, cr1, ci1, cr2, ci2, cr3, ci3;
84
-   static const float s = M_SQRT2;
85
+   static const float s = (float)M_SQRT2;
86
    assert(in != out);
87
    for (k = 0; k < 4; ++k) {
88
        Xr.fk = ((float *)in)8 * k;
89
pipewire-1.0.1.tar.bz2/src/modules/module-filter-chain/sofa_plugin.c -> pipewire-1.2.0.tar.gz/src/modules/module-filter-chain/sofa_plugin.c Changed
107
 
1
@@ -42,12 +42,16 @@
2
    char filenamePATH_MAX = "";
3
 
4
    errno = EINVAL;
5
-   if (config == NULL)
6
+   if (config == NULL) {
7
+       pw_log_error("spatializer: no config was given");
8
        return NULL;
9
+   }
10
 
11
    spa_json_init(&it0, config, strlen(config));
12
-   if (spa_json_enter_object(&it0, &it1) <= 0)
13
+   if (spa_json_enter_object(&it0, &it1) <= 0) {
14
+       pw_log_error("spatializer: expected object in config");
15
        return NULL;
16
+   }
17
 
18
    impl = calloc(1, sizeof(*impl));
19
    if (impl == NULL) {
20
@@ -91,8 +95,75 @@
21
    impl->sofa = mysofa_open_cached(filename, SampleRate, &impl->n_samples, &ret);
22
 
23
    if (ret != MYSOFA_OK) {
24
-       pw_log_error("Unable to load HRTF from %s: %d", filename, ret);
25
-       errno = ENOENT;
26
+       const char *reason;
27
+       switch (ret) {
28
+       case MYSOFA_INVALID_FORMAT:
29
+           reason = "Invalid format";
30
+           errno = EINVAL;
31
+           break;
32
+       case MYSOFA_UNSUPPORTED_FORMAT:
33
+           reason = "Unsupported format";
34
+           errno = ENOTSUP;
35
+           break;
36
+       case MYSOFA_NO_MEMORY:
37
+           reason = "No memory";
38
+           errno = ENOMEM;
39
+           break;
40
+       case MYSOFA_READ_ERROR:
41
+           reason = "Read error";
42
+           errno = ENOENT;
43
+           break;
44
+       case MYSOFA_INVALID_ATTRIBUTES:
45
+           reason = "Invalid attributes";
46
+           errno = EINVAL;
47
+           break;
48
+       case MYSOFA_INVALID_DIMENSIONS:
49
+           reason = "Invalid dimensions";
50
+           errno = EINVAL;
51
+           break;
52
+       case MYSOFA_INVALID_DIMENSION_LIST:
53
+           reason = "Invalid dimension list";
54
+           errno = EINVAL;
55
+           break;
56
+       case MYSOFA_INVALID_COORDINATE_TYPE:
57
+           reason = "Invalid coordinate type";
58
+           errno = EINVAL;
59
+           break;
60
+       case MYSOFA_ONLY_EMITTER_WITH_ECI_SUPPORTED:
61
+           reason = "Only emitter with ECI supported";
62
+           errno = ENOTSUP;
63
+           break;
64
+       case MYSOFA_ONLY_DELAYS_WITH_IR_OR_MR_SUPPORTED:
65
+           reason = "Only delays with IR or MR supported";
66
+           errno = ENOTSUP;
67
+           break;
68
+       case MYSOFA_ONLY_THE_SAME_SAMPLING_RATE_SUPPORTED:
69
+           reason = "Only the same sampling rate supported";
70
+           errno = ENOTSUP;
71
+           break;
72
+       case MYSOFA_RECEIVERS_WITH_RCI_SUPPORTED:
73
+           reason = "Receivers with RCI supported";
74
+           errno = ENOTSUP;
75
+           break;
76
+       case MYSOFA_RECEIVERS_WITH_CARTESIAN_SUPPORTED:
77
+           reason = "Receivers with cartesian supported";
78
+           errno = ENOTSUP;
79
+           break;
80
+       case MYSOFA_INVALID_RECEIVER_POSITIONS:
81
+           reason = "Invalid receiver positions";
82
+           errno = EINVAL;
83
+           break;
84
+       case MYSOFA_ONLY_SOURCES_WITH_MC_SUPPORTED:
85
+           reason = "Only sources with MC supported";
86
+           errno = ENOTSUP;
87
+           break;
88
+       default:
89
+       case MYSOFA_INTERNAL_ERROR:
90
+           errno = EIO;
91
+           reason = "Internal error";
92
+           break;
93
+       }
94
+       pw_log_error("Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
95
        goto error;
96
    }
97
 
98
@@ -158,7 +229,7 @@
99
    );
100
 
101
    // TODO: make use of delay
102
-   if ((left_delay || right_delay) && (!isnan(left_delay) || !isnan(right_delay))) {
103
+   if ((left_delay != 0.0f || right_delay != 0.0f) && (!isnan(left_delay) || !isnan(right_delay))) {
104
        pw_log_warn("delay dropped l: %f, r: %f", left_delay, right_delay);
105
    }
106
 
107
pipewire-1.0.1.tar.bz2/src/modules/module-jack-tunnel.c -> pipewire-1.2.0.tar.gz/src/modules/module-jack-tunnel.c Changed
43
 
1
@@ -664,6 +664,18 @@
2
    return res;
3
 }
4
 
5
+static inline uint64_t get_time_nsec(struct impl *impl)
6
+{
7
+   uint64_t nsec;
8
+   if (impl->sink.filter)
9
+       nsec = pw_filter_get_nsec(impl->sink.filter);
10
+   else if (impl->source.filter)
11
+       nsec = pw_filter_get_nsec(impl->source.filter);
12
+   else
13
+       nsec = 0;
14
+   return nsec;
15
+}
16
+
17
 static void *jack_process_thread(void *arg)
18
 {
19
    struct impl *impl = arg;
20
@@ -693,11 +705,22 @@
21
            jack_time_t next_usecs;
22
            float period_usecs;
23
            jack_position_t pos;
24
+           uint64_t t1, t2, t3;
25
+           int64_t d1;
26
 
27
            jack.get_cycle_times(impl->client,
28
                    &current_frames, &current_usecs,
29
                    &next_usecs, &period_usecs);
30
 
31
+           /* convert from JACK (likely MONOTONIC_RAW) to MONOTONIC */
32
+           t1 = get_time_nsec(impl) / 1000;
33
+           t2 = jack.get_time();
34
+           t3 = get_time_nsec(impl) / 1000;
35
+           d1 = t1 + (t3 - t1) / 2 - t2;
36
+
37
+           current_usecs += d1;
38
+           next_usecs += d1;
39
+
40
            c->nsec = current_usecs * SPA_NSEC_PER_USEC;
41
            c->rate = SPA_FRACTION(1, impl->samplerate);
42
            c->position = current_frames;
43
pipewire-1.0.1.tar.bz2/src/modules/module-jack-tunnel/weakjack.h -> pipewire-1.2.0.tar.gz/src/modules/module-jack-tunnel/weakjack.h Changed
17
 
1
@@ -21,6 +21,7 @@
2
    jack_nframes_t (*cycle_wait) (jack_client_t* client);
3
    void (*cycle_signal) (jack_client_t* client, int status);
4
 
5
+   jack_time_t (*get_time) (void);
6
    jack_nframes_t (*frame_time) (const jack_client_t *);
7
    int (*get_cycle_times) (const jack_client_t *client,
8
            jack_nframes_t *current_frames,
9
@@ -111,6 +112,7 @@
10
    spa_zero(*jack);
11
    LOAD_SYM(cycle_wait);
12
    LOAD_SYM(cycle_signal);
13
+   LOAD_SYM(get_time);
14
    LOAD_SYM(frame_time);
15
    LOAD_SYM(get_cycle_times);
16
    LOAD_SYM(transport_query);
17
pipewire-1.0.1.tar.bz2/src/modules/module-jackdbus-detect.c -> pipewire-1.2.0.tar.gz/src/modules/module-jackdbus-detect.c Changed
174
 
1
@@ -18,6 +18,7 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/result.h>
4
 #include <spa/support/dbus.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "pipewire/context.h"
8
 #include "pipewire/impl-client.h"
9
@@ -27,7 +28,7 @@
10
 
11
 /** \page page_module_jackdbus_detect JACK DBus detect
12
  *
13
- * Automaticall creates a sink/source when a jackdbus server is started
14
+ * Automatically creates a sink/source when a jackdbus server is started
15
  * and connect to JACK.
16
  *
17
  * ## Module Name
18
@@ -158,6 +159,8 @@
19
 {
20
    set_started(impl, false);
21
 
22
+   cancel_and_unref(&impl->pending_call);
23
+
24
    if (impl->bus)
25
        dbus_connection_unref(impl->bus);
26
    spa_dbus_connection_destroy(impl->conn);
27
@@ -179,28 +182,15 @@
28
    .destroy = module_destroy,
29
 };
30
 
31
-static void set_pending_call(struct impl *impl, DBusPendingCall *pending)
32
-{
33
-   if (impl->pending_call != NULL) {
34
-       dbus_pending_call_cancel(impl->pending_call);
35
-       dbus_pending_call_unref(impl->pending_call);
36
-   }
37
-   impl->pending_call = pending;
38
-}
39
-
40
 static void on_is_started_received(DBusPendingCall *pending,
41
                   void *user_data)
42
 {
43
    struct impl *impl = user_data;
44
-   DBusMessage *m;
45
-   DBusError error;
46
+   spa_auto(DBusError) error = DBUS_ERROR_INIT;
47
    dbus_bool_t started = false;
48
 
49
-   m = dbus_pending_call_steal_reply(pending);
50
-   dbus_pending_call_unref(pending);
51
-   impl->pending_call = NULL;
52
-
53
-   dbus_error_init(&error);
54
+   spa_assert(impl->pending_call == pending);
55
+   spa_autoptr(DBusMessage) m = steal_reply_and_unref(&impl->pending_call);
56
 
57
    if (!m) {
58
        pw_log_error("Failed to receive reply");
59
@@ -221,7 +211,6 @@
60
    dbus_message_get_args(m, &error,
61
            DBUS_TYPE_BOOLEAN, &started,
62
            DBUS_TYPE_INVALID);
63
-   dbus_message_unref(m);
64
 
65
    if (dbus_error_is_set(&error)) {
66
        pw_log_warn("Could not get jackdbus state: %s", error.message);
67
@@ -234,37 +223,33 @@
68
    return;
69
 error:
70
    impl->is_started = false;
71
-   dbus_error_free(&error);
72
 }
73
 
74
 static void check_jack_running(struct impl *impl)
75
 {
76
-   DBusMessage *m;
77
-   DBusPendingCall *pending;
78
-
79
    impl->is_started = false;
80
+   cancel_and_unref(&impl->pending_call);
81
 
82
-   m = dbus_message_new_method_call(JACK_SERVICE_NAME,
83
-           JACK_INTERFACE_PATH,
84
-           JACK_INTERFACE_NAME,
85
-           "IsStarted");
86
+   spa_autoptr(DBusMessage) m = dbus_message_new_method_call(JACK_SERVICE_NAME,
87
+                                 JACK_INTERFACE_PATH,
88
+                                 JACK_INTERFACE_NAME,
89
+                                 "IsStarted");
90
+   if (!m)
91
+       return;
92
 
93
-   dbus_connection_send_with_reply(impl->bus, m, &pending, -1);
94
-   dbus_pending_call_set_notify(pending, on_is_started_received, impl, NULL);
95
+   dbus_message_set_auto_start(m, false);
96
 
97
-   set_pending_call(impl, pending);
98
+   impl->pending_call = send_with_reply(impl->bus, m, on_is_started_received, impl);
99
 }
100
 
101
 static DBusHandlerResult filter_handler(DBusConnection *connection,
102
        DBusMessage *message, void *user_data)
103
 {
104
    struct impl *impl = user_data;
105
-   DBusError error;
106
-
107
-   dbus_error_init(&error);
108
 
109
    if (dbus_message_is_signal(message, "org.freedesktop.DBus",
110
                   "NameOwnerChanged")) {
111
+       spa_auto(DBusError) error = DBUS_ERROR_INIT;
112
        const char *name, *old, *new;
113
        if (!dbus_message_get_args(message, &error,
114
                       DBUS_TYPE_STRING, &name,
115
@@ -273,14 +258,14 @@
116
                       DBUS_TYPE_INVALID)) {
117
            pw_log_error("Failed to get OwnerChanged args: %s",
118
                    error.message);
119
-           goto not_handled;
120
+           return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
121
        }
122
        if (!spa_streq(name, JACK_SERVICE_NAME))
123
-           goto not_handled;
124
+           return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
125
 
126
        pw_log_info("NameOwnerChanged %s -> %s", old, new);
127
        if (spa_streq(new, "")) {
128
-           set_pending_call(impl, NULL);
129
+           cancel_and_unref(&impl->pending_call);
130
            set_started(impl, false);
131
        } else {
132
            check_jack_running(impl);
133
@@ -297,21 +282,15 @@
134
        set_started(impl, false);
135
    }
136
    return DBUS_HANDLER_RESULT_HANDLED;
137
-
138
-not_handled:
139
-   dbus_error_free(&error);
140
-   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
141
 }
142
 
143
 static int init_dbus_connection(struct impl *impl)
144
 {
145
-   DBusError error;
146
-
147
    impl->bus = spa_dbus_connection_get(impl->conn);
148
    if (impl->bus == NULL)
149
        return -EIO;
150
 
151
-   dbus_error_init(&error);
152
+   spa_auto(DBusError) error = DBUS_ERROR_INIT;
153
 
154
    /* XXX: we don't handle dbus reconnection yet, so ref the handle instead */
155
    dbus_connection_ref(impl->bus);
156
@@ -322,7 +301,8 @@
157
            "type='signal',"
158
            "sender='org.freedesktop.DBus',"
159
            "interface='org.freedesktop.DBus',"
160
-           "member='NameOwnerChanged'", &error);
161
+           "member='NameOwnerChanged',"
162
+           "arg0='" JACK_SERVICE_NAME "'", &error);
163
    if (dbus_error_is_set(&error))
164
        goto error;
165
 
166
@@ -347,7 +327,6 @@
167
    return 0;
168
 error:
169
    pw_log_error("Failed to add listener: %s", error.message);
170
-   dbus_error_free(&error);
171
    return -EIO;
172
 }
173
 
174
pipewire-1.0.1.tar.bz2/src/modules/module-loopback.c -> pipewire-1.2.0.tar.gz/src/modules/module-loopback.c Changed
238
 
1
@@ -57,16 +57,26 @@
2
  * - \ref PW_KEY_NODE_GROUP
3
  * - \ref PW_KEY_NODE_LINK_GROUP
4
  * - \ref PW_KEY_NODE_VIRTUAL
5
- * - \ref PW_KEY_NODE_NAME: See notes below. If not specified, defaults to
6
- *     'loopback-<pid>-<module-id>'.
7
+ * - \ref PW_KEY_NODE_NAME : See notes below. If not specified, defaults to
8
+ *     'loopback-PID-MODULEID'.
9
  *
10
  * Stream only properties:
11
  *
12
  * - \ref PW_KEY_MEDIA_CLASS
13
- * - \ref PW_KEY_NODE_NAME:  if not given per stream, the global node.name will be
14
+ * - \ref PW_KEY_NODE_NAME :  if not given per stream, the global node.name will be
15
  *         prefixed with 'input.' and 'output.' to generate a capture and playback
16
  *         stream node.name respectively.
17
  *
18
+ * ## Channel handling
19
+ *
20
+ * Channels from the capture stream are copied, in order, to the channels of the
21
+ * output stream. The remaining streams are ignored (when capture has more channels)
22
+ * or filled with silence (when playback has more channels).
23
+ *
24
+ * When a global channel position is set, both capture and playback will be converted
25
+ * to and from this common channel layout. This can be used to implement up or
26
+ * downmixing loopback sinks/sources.
27
+ *
28
  * ## Example configuration of a virtual sink
29
  *
30
  * This Virtual sink routes stereo input to the rear channels of a 7.1 sink.
31
@@ -104,18 +114,49 @@
32
  * context.modules = 
33
  * {   name = libpipewire-module-loopback
34
  *     args = {
35
- *       node.description = "Scarlett Focusrite Line 1"
36
- *       capture.props = {
37
- *           audio.position =  FL 
38
- *           stream.dont-remix = true
39
- *           node.target = "alsa_input.usb-Focusrite_Scarlett_Solo_USB_Y7ZD17C24495BC-00.analog-stereo"
40
- *           node.passive = true
41
- *       }
42
- *       playback.props = {
43
- *           node.name = "SF_mono_in_1"
44
- *           media.class = "Audio/Source"
45
- *           audio.position =  MONO 
46
- *       }
47
+ *         node.description = "Scarlett Focusrite Line 1"
48
+ *         capture.props = {
49
+ *             audio.position =  FL 
50
+ *             stream.dont-remix = true
51
+ *             node.target = "alsa_input.usb-Focusrite_Scarlett_Solo_USB_Y7ZD17C24495BC-00.analog-stereo"
52
+ *             node.passive = true
53
+ *         }
54
+ *         playback.props = {
55
+ *             node.name = "SF_mono_in_1"
56
+ *             media.class = "Audio/Source"
57
+ *             audio.position =  MONO 
58
+ *         }
59
+ *     }
60
+ * }
61
+ * 
62
+ *\endcode
63
+ *
64
+ * ## Example configuration of an upmix sink
65
+ *
66
+ * This Virtual sink has 2 input channels and 6 output channels. It will perform upmixing
67
+ * using the PSD algorithm on the playback stream.
68
+ *
69
+ *\code{.unparsed}
70
+ * context.modules = 
71
+ * {   name = libpipewire-module-loopback
72
+ *     args = {
73
+ *         node.description = "Upmix Sink"
74
+ *         audio.position =  FL FR 
75
+ *         capture.props = {
76
+ *             node.name = "effect_input.upmix"
77
+ *             media.class = Audio/Sink
78
+ *         }
79
+ *         playback.props = {
80
+ *             node.name = "effect_output.upmix"
81
+ *             audio.position =  FL FR RL RR FC LFE 
82
+ *             node.passive = true
83
+ *             stream.dont-remix = true
84
+ *             channelmix.upmix = true
85
+ *             channelmix.upmix-method = psd
86
+ *             channelmix.lfe-cutoff = 150
87
+ *             channelmix.fc-cutoff = 12000
88
+ *             channelmix.rear-delay = 12.0
89
+ *         }
90
  *     }
91
  * }
92
  * 
93
@@ -171,6 +212,8 @@
94
    struct spa_hook core_proxy_listener;
95
    struct spa_hook core_listener;
96
 
97
+   struct spa_audio_info_raw info;
98
+
99
    struct pw_properties *capture_props;
100
    struct pw_stream *capture;
101
    struct spa_hook capture_listener;
102
@@ -185,8 +228,11 @@
103
    unsigned int recalc_delay:1;
104
 
105
    struct spa_io_position *position;
106
-   struct spa_audio_info_raw info;
107
+
108
+   uint32_t target_rate;
109
    uint32_t rate;
110
+   uint32_t target_channels;
111
+   uint32_t channels;
112
    float target_delay;
113
    struct spa_ringbuffer buffer;
114
    uint8_t *buffer_data;
115
@@ -202,7 +248,7 @@
116
 
117
 static void recalculate_delay(struct impl *impl)
118
 {
119
-   uint32_t target = impl->rate * impl->target_delay, cdelay, pdelay;
120
+   uint32_t target = (uint32_t)(impl->rate * impl->target_delay), cdelay, pdelay;
121
    uint32_t delay, w;
122
    struct pw_time pwt;
123
 
124
@@ -345,14 +391,55 @@
125
    pw_stream_update_params(other, params, 1);
126
 }
127
 
128
+static void param_format_changed(struct impl *impl, const struct spa_pod *param,
129
+       struct pw_stream *stream, bool capture)
130
+{
131
+   struct spa_audio_info_raw info;
132
+
133
+   spa_zero(info);
134
+   if (param != NULL) {
135
+       if (spa_format_audio_raw_parse(param, &info) < 0 ||
136
+           info.channels == 0 ||
137
+           info.channels > SPA_AUDIO_MAX_CHANNELS)
138
+           return;
139
+
140
+       if ((impl->info.format != 0 && impl->info.format != info.format) ||
141
+           (impl->info.rate != 0 && impl->info.rate != info.rate) ||
142
+           (impl->info.channels != 0 &&
143
+            (impl->info.channels != info.channels ||
144
+             memcmp(impl->info.position, info.position,
145
+                 info.channels * sizeof(uint32_t)) != 0))) {
146
+           uint8_t buffer1024;
147
+           struct spa_pod_builder b;
148
+           const struct spa_pod *params1;
149
+
150
+           if (impl->info.format != 0)
151
+               info.format = impl->info.format;
152
+           if (impl->info.rate != 0)
153
+               info.rate = impl->info.rate;
154
+           if (impl->info.channels != 0) {
155
+               info.channels = impl->info.channels;
156
+               memcpy(info.position, impl->info.position, sizeof(info.position));
157
+           }
158
+           spa_pod_builder_init(&b, buffer, sizeof(buffer));
159
+           params0 = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info);
160
+           pw_stream_update_params(stream, params, 1);
161
+       }
162
+   }
163
+   if (capture) {
164
+       impl->target_rate = info.rate;
165
+       impl->target_channels = info.channels;
166
+   }
167
+}
168
+
169
 static void recalculate_buffer(struct impl *impl)
170
 {
171
-   if (impl->target_delay > 0.0f) {
172
-       uint32_t delay = impl->rate * impl->target_delay;
173
+   if (impl->target_delay > 0.0f && impl->channels > 0 && impl->rate > 0) {
174
+       uint32_t delay = (uint32_t)(impl->rate * impl->target_delay);
175
        void *data;
176
 
177
        impl->buffer_size = (delay + (1u<<15)) * 4;
178
-       data = realloc(impl->buffer_data, impl->buffer_size * impl->info.channels);
179
+       data = realloc(impl->buffer_data, impl->buffer_size * impl->channels);
180
        if (data == NULL) {
181
            pw_log_warn("can't allocate delay buffer, delay disabled: %m");
182
            impl->buffer_size = 0;
183
@@ -388,12 +475,13 @@
184
        break;
185
    case PW_STREAM_STATE_STREAMING:
186
    {
187
-       uint32_t target = impl->info.rate;
188
+       uint32_t target = impl->target_rate;
189
        if (target == 0)
190
            target = impl->position ?
191
                impl->position->clock.target_rate.denom : DEFAULT_RATE;
192
-       if (impl->rate != target) {
193
+       if (impl->rate != target || impl->target_channels != impl->channels) {
194
            impl->rate = target;
195
+           impl->channels = impl->target_channels;
196
            recalculate_buffer(impl);
197
        }
198
        break;
199
@@ -409,19 +497,8 @@
200
 
201
    switch (id) {
202
    case SPA_PARAM_Format:
203
-   {
204
-       struct spa_audio_info_raw info;
205
-       spa_zero(info);
206
-       if (param != NULL) {
207
-           if (spa_format_audio_raw_parse(param, &info) < 0 ||
208
-               info.channels == 0 ||
209
-               info.channels > SPA_AUDIO_MAX_CHANNELS)
210
-               return;
211
-       }
212
-       impl->rate = 0;
213
-       impl->info = info;
214
+       param_format_changed(impl, param, impl->capture, true);
215
        break;
216
-   }
217
    case SPA_PARAM_Latency:
218
        param_latency_changed(impl, param, impl->playback);
219
        break;
220
@@ -464,6 +541,9 @@
221
    struct impl *impl = data;
222
 
223
    switch (id) {
224
+   case SPA_PARAM_Format:
225
+       param_format_changed(impl, param, impl->playback, false);
226
+       break;
227
    case SPA_PARAM_Latency:
228
        param_latency_changed(impl, param, impl->capture);
229
        break;
230
@@ -748,6 +828,7 @@
231
    if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL)
232
        pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, str);
233
 
234
+   parse_audio_info(props, &impl->info);
235
    parse_audio_info(impl->capture_props, &impl->capture_info);
236
    parse_audio_info(impl->playback_props, &impl->playback_info);
237
 
238
pipewire-1.0.1.tar.bz2/src/modules/module-metadata/metadata.c -> pipewire-1.2.0.tar.gz/src/modules/module-metadata/metadata.c Changed
81
 
1
@@ -37,7 +37,7 @@
2
    pw_resource_call_res(r,struct pw_metadata_events,m,v,__VA_ARGS__)
3
 
4
 #define pw_metadata_resource_property(r,...)        \
5
-        pw_metadata_resource(r,property,0,__VA_ARGS__)
6
+   pw_metadata_resource(r,property,0,__VA_ARGS__)
7
 
8
 static int metadata_property(void *data,
9
            uint32_t subject,
10
@@ -51,7 +51,9 @@
11
    struct impl *impl = d->impl;
12
 
13
    if (impl->pending == 0 || d->pong_seq != 0) {
14
-       if (pw_impl_client_check_permissions(client, subject, PW_PERM_R) >= 0)
15
+       int res = pw_impl_client_check_permissions(client, subject, PW_PERM_R);
16
+       if (res >= 0 ||
17
+           (res == -ENOENT && key == NULL && type == NULL && value == NULL))
18
            pw_metadata_resource_property(d->resource, subject, key, type, value);
19
    }
20
    return 0;
21
@@ -147,31 +149,31 @@
22
 
23
 static int
24
 global_bind(void *object, struct pw_impl_client *client, uint32_t permissions,
25
-            uint32_t version, uint32_t id)
26
+       uint32_t version, uint32_t id)
27
 {
28
    struct impl *impl = object;
29
    struct pw_resource *resource;
30
    struct resource_data *data;
31
 
32
    resource = pw_resource_new(client, id, permissions, PW_TYPE_INTERFACE_Metadata, version, sizeof(*data));
33
-        if (resource == NULL)
34
-                return -errno;
35
+   if (resource == NULL)
36
+       return -errno;
37
 
38
-        data = pw_resource_get_user_data(resource);
39
-        data->impl = impl;
40
-        data->resource = resource;
41
+   data = pw_resource_get_user_data(resource);
42
+   data->impl = impl;
43
+   data->resource = resource;
44
 
45
    pw_global_add_resource(impl->global, resource);
46
 
47
    /* listen for when the resource goes away */
48
-        pw_resource_add_listener(resource,
49
-                        &data->resource_listener,
50
-                        &resource_events, data);
51
+   pw_resource_add_listener(resource,
52
+           &data->resource_listener,
53
+           &resource_events, data);
54
 
55
    /* resource methods -> implementation */
56
    pw_resource_add_object_listener(resource,
57
            &data->object_listener,
58
-                        &metadata_methods, data);
59
+           &metadata_methods, data);
60
 
61
    pw_impl_client_set_busy(client, true);
62
 
63
@@ -182,7 +184,7 @@
64
 
65
    pw_resource_add_listener(impl->resource,
66
            &data->impl_resource_listener,
67
-                        &impl_resource_events, data);
68
+           &impl_resource_events, data);
69
 
70
    data->pong_seq = pw_resource_ping(impl->resource, data->pong_seq);
71
    impl->pending++;
72
@@ -208,6 +210,8 @@
73
 static void context_global_removed(void *data, struct pw_global *global)
74
 {
75
    struct impl *impl = data;
76
+   pw_log_trace("Clearing properties for global %u in %u",
77
+                pw_global_get_id(global), pw_global_get_id(impl->global));
78
    pw_metadata_set_property(impl->metadata,
79
            pw_global_get_id(global), NULL, NULL, NULL);
80
 }
81
pipewire-1.0.1.tar.bz2/src/modules/module-netjack2-driver.c -> pipewire-1.2.0.tar.gz/src/modules/module-netjack2-driver.c Changed
220
 
1
@@ -34,6 +34,7 @@
2
 
3
 #include "module-netjack2/packets.h"
4
 #include "module-netjack2/peer.c"
5
+#include "network-utils.h"
6
 
7
 #ifndef IPTOS_DSCP
8
 #define IPTOS_DSCP_MASK 0xfc
9
@@ -416,7 +417,7 @@
10
 
11
        if (i < s->info.channels) {
12
            str = spa_debug_type_find_short_name(spa_type_audio_channel,
13
-                   s->info.positioni);
14
+                   s->info.positioni % SPA_AUDIO_MAX_CHANNELS);
15
            if (str)
16
                snprintf(name, sizeof(name), "%s_%s", prefix, str);
17
            else
18
@@ -606,12 +607,16 @@
19
    return res;
20
 }
21
 
22
-
23
-static inline uint64_t get_time_ns(void)
24
+static inline uint64_t get_time_nsec(struct impl *impl)
25
 {
26
-   struct timespec ts;
27
-   clock_gettime(CLOCK_MONOTONIC, &ts);
28
-   return SPA_TIMESPEC_TO_NSEC(&ts);
29
+   uint64_t nsec;
30
+   if (impl->sink.filter)
31
+       nsec = pw_filter_get_nsec(impl->sink.filter);
32
+   else if (impl->source.filter)
33
+       nsec = pw_filter_get_nsec(impl->source.filter);
34
+   else
35
+       nsec = 0;
36
+   return nsec;
37
 }
38
 
39
 static void
40
@@ -633,7 +638,7 @@
41
        if (nframes == 0)
42
            return;
43
 
44
-       nsec = get_time_ns();
45
+       nsec = get_time_nsec(impl);
46
 
47
        if (!impl->done) {
48
            impl->pw_xrun++;
49
@@ -689,26 +694,6 @@
50
    }
51
 }
52
 
53
-static int parse_address(const char *address, uint16_t port,
54
-       struct sockaddr_storage *addr, socklen_t *len)
55
-{
56
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
57
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
58
-
59
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
60
-       sa4->sin_family = AF_INET;
61
-       sa4->sin_port = htons(port);
62
-       *len = sizeof(*sa4);
63
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
64
-       sa6->sin6_family = AF_INET6;
65
-       sa6->sin6_port = htons(port);
66
-       *len = sizeof(*sa6);
67
-   } else
68
-       return -EINVAL;
69
-
70
-   return 0;
71
-}
72
-
73
 static bool is_multicast(struct sockaddr *sa, socklen_t salen)
74
 {
75
    if (sa->sa_family == AF_INET) {
76
@@ -777,19 +762,6 @@
77
    return res;
78
 }
79
 
80
-static const char *get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
81
-{
82
-   if (sa->ss_family == AF_INET) {
83
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
84
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
85
-   } else if (sa->ss_family == AF_INET6) {
86
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
87
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
88
-   } else
89
-       snprintf(ip, len, "invalid ip");
90
-   return ip;
91
-}
92
-
93
 static void update_timer(struct impl *impl, uint64_t timeout)
94
 {
95
    struct timespec value, interval;
96
@@ -819,6 +791,7 @@
97
 {
98
    int res;
99
    struct netjack2_peer *peer = &impl->peer;
100
+   uint32_t i;
101
 
102
    pw_log_info("got follower setup");
103
    nj2_dump_session_params(params);
104
@@ -842,10 +815,18 @@
105
 
106
    impl->source.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels;
107
    impl->source.info.rate =  peer->params.sample_rate;
108
-   impl->source.info.channels =  peer->params.send_audio_channels;
109
+   if ((uint32_t)peer->params.send_audio_channels != impl->source.info.channels) {
110
+       impl->source.info.channels = peer->params.send_audio_channels;
111
+       for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
112
+           impl->source.info.positioni = SPA_AUDIO_CHANNEL_AUX0 + i;
113
+   }
114
    impl->sink.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels;
115
    impl->sink.info.rate =  peer->params.sample_rate;
116
-   impl->sink.info.channels =  peer->params.recv_audio_channels;
117
+   if ((uint32_t)peer->params.recv_audio_channels != impl->sink.info.channels) {
118
+       impl->sink.info.channels =  peer->params.recv_audio_channels;
119
+       for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++)
120
+           impl->sink.info.positioni = SPA_AUDIO_CHANNEL_AUX0 + i;
121
+   }
122
    impl->samplerate = peer->params.sample_rate;
123
    impl->period_size = peer->params.period_size;
124
 
125
@@ -920,7 +901,7 @@
126
        if (len < (int)sizeof(struct nj2_session_params))
127
            goto short_packet;
128
 
129
-       if (strcmp(params.type, "params") != 0)
130
+       if (strncmp(params.type, "params", sizeof(params.type)) != 0)
131
            goto wrong_type;
132
 
133
        switch(ntohl(params.packet_id)) {
134
@@ -950,7 +931,7 @@
135
 
136
    pw_loop_update_io(impl->main_loop, impl->setup_socket, SPA_IO_IN);
137
 
138
-   pw_log_info("sending AVAILABLE to %s", get_ip(&impl->dst_addr, buffer, sizeof(buffer)));
139
+   pw_log_info("sending AVAILABLE to %s", pw_net_get_ip_fmt(&impl->dst_addr, buffer, sizeof(buffer)));
140
 
141
    client_name = pw_properties_get(impl->props, "netjack2.client-name");
142
    if (client_name == NULL)
143
@@ -987,11 +968,11 @@
144
        port = DEFAULT_NET_PORT;
145
    if ((str = pw_properties_get(impl->props, "net.ip")) == NULL)
146
        str = DEFAULT_NET_IP;
147
-   if ((res = parse_address(str, port, &impl->dst_addr, &impl->dst_len)) < 0) {
148
+   if ((res = pw_net_parse_address(str, port, &impl->dst_addr, &impl->dst_len)) < 0) {
149
        pw_log_error("invalid net.ip %s: %s", str, spa_strerror(res));
150
        goto out;
151
    }
152
-   if ((res = parse_address("0.0.0.0", 0, &impl->src_addr, &impl->src_len)) < 0) {
153
+   if ((res = pw_net_parse_address("0.0.0.0", 0, &impl->src_addr, &impl->src_len)) < 0) {
154
        pw_log_error("invalid source.ip: %s", spa_strerror(res));
155
        goto out;
156
    }
157
@@ -1143,6 +1124,9 @@
158
    if (impl->timer)
159
        pw_loop_destroy_source(impl->main_loop, impl->timer);
160
 
161
+   if (impl->data_loop)
162
+       pw_context_release_loop(impl->context, impl->data_loop);
163
+
164
    pw_properties_free(impl->sink.props);
165
    pw_properties_free(impl->source.props);
166
    pw_properties_free(impl->props);
167
@@ -1219,7 +1203,6 @@
168
 {
169
    struct pw_context *context = pw_impl_module_get_context(module);
170
    struct pw_properties *props = NULL;
171
-   struct pw_data_loop *data_loop;
172
    struct impl *impl;
173
    const char *str;
174
    int res;
175
@@ -1230,6 +1213,9 @@
176
    if (impl == NULL)
177
        return -errno;
178
 
179
+   impl->module = module;
180
+   impl->context = context;
181
+
182
    pw_log_debug("module %p: new %s", impl, args);
183
 
184
    if (args == NULL)
185
@@ -1242,8 +1228,7 @@
186
        goto error;
187
    }
188
    impl->props = props;
189
-   data_loop = pw_context_get_data_loop(context);
190
-   impl->data_loop = pw_data_loop_get_loop(data_loop);
191
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
192
    impl->quantum_limit = pw_properties_get_uint32(
193
            pw_context_get_properties(context),
194
            "default.clock.quantum-limit", 8192u);
195
@@ -1256,8 +1241,6 @@
196
        goto error;
197
    }
198
 
199
-   impl->module = module;
200
-   impl->context = context;
201
    impl->main_loop = pw_context_get_main_loop(context);
202
    impl->system = impl->main_loop->system;
203
 
204
@@ -1283,6 +1266,7 @@
205
    impl->latency = pw_properties_get_uint32(impl->props, "netjack2.latency",
206
            DEFAULT_NETWORK_LATENCY);
207
 
208
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
209
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
210
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
211
    if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
212
@@ -1303,6 +1287,7 @@
213
    if ((str = pw_properties_get(props, "source.props")) != NULL)
214
        pw_properties_update_string(impl->source.props, str, strlen(str));
215
 
216
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
217
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
218
    copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
219
    copy_props(impl, props, PW_KEY_NODE_ALWAYS_PROCESS);
220
pipewire-1.0.1.tar.bz2/src/modules/module-netjack2-manager.c -> pipewire-1.2.0.tar.gz/src/modules/module-netjack2-manager.c Changed
171
 
1
@@ -36,6 +36,11 @@
2
 #include "module-netjack2/packets.h"
3
 
4
 #include "module-netjack2/peer.c"
5
+#include "network-utils.h"
6
+
7
+#ifdef __FreeBSD__
8
+#define ifr_ifindex ifr_index
9
+#endif
10
 
11
 #ifndef IPTOS_DSCP
12
 #define IPTOS_DSCP_MASK 0xfc
13
@@ -434,7 +439,7 @@
14
        if (len < (int)sizeof(params))
15
            goto short_packet;
16
 
17
-       if (strcmp(params.type, "params") != 0)
18
+       if (strncmp(params.type, "params", sizeof(params.type)) != 0)
19
            goto wrong_type;
20
 
21
        switch(ntohl(params.packet_id)) {
22
@@ -734,26 +739,6 @@
23
    return res;
24
 }
25
 
26
-static int parse_address(const char *address, uint16_t port,
27
-       struct sockaddr_storage *addr, socklen_t *len)
28
-{
29
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
30
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
31
-
32
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
33
-       sa4->sin_family = AF_INET;
34
-       sa4->sin_port = htons(port);
35
-       *len = sizeof(*sa4);
36
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
37
-       sa6->sin6_family = AF_INET6;
38
-       sa6->sin6_port = htons(port);
39
-       *len = sizeof(*sa6);
40
-   } else
41
-       return -EINVAL;
42
-
43
-   return 0;
44
-}
45
-
46
 static bool is_multicast(struct sockaddr *sa, socklen_t salen)
47
 {
48
    if (sa->sa_family == AF_INET) {
49
@@ -878,20 +863,6 @@
50
    return res;
51
 }
52
 
53
-static const char *get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
54
-{
55
-   if (sa->ss_family == AF_INET) {
56
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
57
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
58
-   } else if (sa->ss_family == AF_INET6) {
59
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
60
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
61
-   } else {
62
-       snprintf(ip, len, "invalid address");
63
-   }
64
-   return ip;
65
-}
66
-
67
 static int handle_follower_available(struct impl *impl, struct nj2_session_params *params,
68
    struct sockaddr_storage *addr, socklen_t addr_len)
69
 {
70
@@ -1035,8 +1006,8 @@
71
 
72
    nj2_session_params_hton(params, &peer->params);
73
    params->packet_id = htonl(NJ2_ID_FOLLOWER_SETUP);
74
-
75
-   pw_log_info("sending follower setup to %s", get_ip(addr, buffer, sizeof(buffer)));
76
+   pw_log_info("sending follower setup to %s",
77
+           pw_net_get_ip_fmt(addr, buffer, sizeof(buffer)));
78
    nj2_dump_session_params(params);
79
    send(follower->socket->fd, params, sizeof(*params), 0);
80
 
81
@@ -1072,7 +1043,7 @@
82
        if (len < (int)sizeof(params))
83
            goto short_packet;
84
 
85
-       if (strcmp(params.type, "params") != 0)
86
+       if (strncmp(params.type, "params", sizeof(params.type)) != 0)
87
            goto wrong_type;
88
 
89
        switch(ntohl(params.packet_id)) {
90
@@ -1109,7 +1080,7 @@
91
    if ((str = pw_properties_get(impl->props, "net.ip")) == NULL)
92
        str = DEFAULT_NET_IP;
93
 
94
-   if ((res = parse_address(str, port, &impl->src_addr, &impl->src_len)) < 0) {
95
+   if ((res = pw_net_parse_address(str, port, &impl->src_addr, &impl->src_len)) < 0) {
96
        pw_log_error("invalid net.ip %s: %s", str, spa_strerror(res));
97
        goto out;
98
    }
99
@@ -1135,7 +1106,7 @@
100
        goto out;
101
    }
102
    pw_log_info("listening for AVAILABLE on %s",
103
-           get_ip(&impl->src_addr, buffer, sizeof(buffer)));
104
+           pw_net_get_ip_fmt(&impl->src_addr, buffer, sizeof(buffer)));
105
    return 0;
106
 out:
107
    return res;
108
@@ -1183,6 +1154,9 @@
109
    if (impl->core && impl->do_disconnect)
110
        pw_core_disconnect(impl->core);
111
 
112
+   if (impl->data_loop)
113
+       pw_context_release_loop(impl->context, impl->data_loop);
114
+
115
    pw_properties_free(impl->sink_props);
116
    pw_properties_free(impl->source_props);
117
    pw_properties_free(impl->props);
118
@@ -1259,7 +1233,6 @@
119
 {
120
    struct pw_context *context = pw_impl_module_get_context(module);
121
    struct pw_properties *props = NULL;
122
-   struct pw_data_loop *data_loop;
123
    struct impl *impl;
124
    const char *str;
125
    int res;
126
@@ -1273,6 +1246,9 @@
127
    pw_log_debug("module %p: new %s", impl, args);
128
    spa_list_init(&impl->follower_list);
129
 
130
+   impl->module = module;
131
+   impl->context = context;
132
+
133
    if (args == NULL)
134
        args = "";
135
 
136
@@ -1283,8 +1259,7 @@
137
        goto error;
138
    }
139
    impl->props = props;
140
-   data_loop = pw_context_get_data_loop(context);
141
-   impl->data_loop = pw_data_loop_get_loop(data_loop);
142
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
143
    impl->quantum_limit = pw_properties_get_uint32(
144
            pw_context_get_properties(context),
145
            "default.clock.quantum-limit", 8192u);
146
@@ -1297,8 +1272,6 @@
147
        goto error;
148
    }
149
 
150
-   impl->module = module;
151
-   impl->context = context;
152
    impl->main_loop = pw_context_get_main_loop(context);
153
    impl->system = impl->main_loop->system;
154
 
155
@@ -1342,6 +1315,7 @@
156
    impl->kbps = pw_properties_get_uint32(impl->props, "netjack2.kbps",
157
            DEFAULT_KBPS);
158
 
159
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
160
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
161
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
162
    if (pw_properties_get(props, PW_KEY_NODE_NETWORK) == NULL)
163
@@ -1366,6 +1340,7 @@
164
    if ((str = pw_properties_get(props, "source.props")) != NULL)
165
        pw_properties_update_string(impl->source_props, str, strlen(str));
166
 
167
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
168
    copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
169
    copy_props(impl, props, PW_KEY_NODE_NETWORK);
170
    copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
171
pipewire-1.0.1.tar.bz2/src/modules/module-netjack2/packets.h -> pipewire-1.2.0.tar.gz/src/modules/module-netjack2/packets.h Changed
10
 
1
@@ -110,7 +110,7 @@
2
 }
3
 
4
 struct nj2_packet_header {
5
-   char type8;         /* packet type ('headr') */
6
+   char type8;         /* packet type ('header') */
7
    uint32_t data_type;     /* 'a' for audio, 'm' for midi and 's' for sync */
8
    uint32_t data_stream;       /* 's' for send, 'r' for return */
9
    uint32_t id;            /* unique ID of the follower */
10
pipewire-1.0.1.tar.bz2/src/modules/module-netjack2/peer.c -> pipewire-1.2.0.tar.gz/src/modules/module-netjack2/peer.c Changed
19
 
1
@@ -663,7 +663,7 @@
2
        if (len >= (ssize_t)sizeof(sync)) {
3
            //nj2_dump_packet_header(&sync);
4
 
5
-           if (strcmp(sync.type, "header") == 0 &&
6
+           if (strncmp(sync.type, "header", sizeof(sync.type)) == 0 &&
7
                ntohl(sync.data_type) == 's' &&
8
                ntohl(sync.data_stream) == peer->other_stream &&
9
                ntohl(sync.id) == peer->params.id)
10
@@ -695,7 +695,7 @@
11
        if (len >= (ssize_t)sizeof(sync)) {
12
            //nj2_dump_packet_header(sync);
13
 
14
-           if (strcmp(sync.type, "header") == 0 &&
15
+           if (strncmp(sync.type, "header", sizeof(sync.type)) == 0 &&
16
                ntohl(sync.data_type) == 's' &&
17
                ntohl(sync.data_stream) == peer->other_stream &&
18
                ntohl(sync.id) == peer->params.id)
19
pipewire-1.2.0.tar.gz/src/modules/module-parametric-equalizer.c Added
465
 
1
@@ -0,0 +1,463 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-FileCopyrightText: Copyright © 2024 Asymptotic Inc. */
5
+/* SPDX-License-Identifier: MIT */
6
+
7
+#include <errno.h>
8
+
9
+#include "config.h"
10
+
11
+#include <spa/utils/result.h>
12
+#include <spa/param/audio/raw.h>
13
+
14
+#include <pipewire/impl.h>
15
+
16
+/** \page page_module_parametric_equalizer Parametric-Equalizer
17
+ *
18
+ * The `parametric-equalizer` module loads parametric equalizer configuration
19
+ * generated from the AutoEQ project or Squiglink. Both the projects allow
20
+ * equalizing headphones or an in-ear monitor to a target curve. While these
21
+ * generate a file for parametric equalization for a given target, but this
22
+ * is not a format that can be directly given to filter chain module.
23
+ *
24
+ * A popular example of the above being EQ'ing to the Harman target curve
25
+ * or EQ'ing one headphone/IEM to another.
26
+ *
27
+ * For AutoEQ, see https://github.com/jaakkopasanen/AutoEq.
28
+ * For SquigLink, see https://squig.link/.
29
+ *
30
+ * Parametric equalizer configuration generated from AutoEQ or Squiglink looks
31
+ * like below.
32
+ *
33
+ * Preamp: -6.8 dB
34
+ * Filter 1: ON PK Fc 21 Hz Gain 6.7 dB Q 1.100
35
+ * Filter 2: ON PK Fc 85 Hz Gain 6.9 dB Q 3.000
36
+ * Filter 3: ON PK Fc 110 Hz Gain -2.6 dB Q 2.700
37
+ * Filter 4: ON PK Fc 210 Hz Gain 5.9 dB Q 2.100
38
+ * Filter 5: ON PK Fc 710 Hz Gain -1.0 dB Q 0.600
39
+ * Filter 6: ON PK Fc 1600 Hz Gain 2.3 dB Q 2.700
40
+ *
41
+ * Fc, Gain and Q specify the frequency, gain and Q factor respectively.
42
+ * The fourth column can be one of PK, LSC or HSC specifying peaking, low
43
+ * shelf and high shelf filter respectively. More often than not only peaking
44
+ * filters are involved.
45
+ *
46
+ * This module parses a configuration like above and loads the filter chain
47
+ * module with the above configuration translated to filter chain arguments.
48
+ *
49
+ * ## Module Name
50
+ *
51
+ * `libpipewire-module-parametric-equalizer`
52
+ *
53
+ * ## Module Options
54
+ *
55
+ * Options specific to the behaviour of this module
56
+ *
57
+ * - `equalizer.filepath = <str>` path of the file with parametric EQ
58
+ * - `equalizer.description = <str>`: Name which will show up in
59
+ * - `audio.channels = <int>`: Number of audio channels, default 2
60
+ * - `audio.position = <str>`: Channel map, default "FL, FR"
61
+ * - `remote.name =<str>`: environment with remote name, default "pipewire-0"
62
+ *
63
+ * ## General options
64
+ *
65
+ * Options with well-known behaviour:
66
+ *
67
+ * - \ref PW_KEY_AUDIO_CHANNELS
68
+ * - \ref SPA_KEY_AUDIO_POSITION
69
+ * - \ref PW_KEY_REMOTE_NAME
70
+ *
71
+ * ## Example configuration
72
+ *\code{.unparsed}
73
+ * context.modules = 
74
+ * {   name = libpipewire-module-parametric-equalizer
75
+ *     args = {
76
+ *         #remote.name = "pipewire-0"
77
+ *         #equalizer.filepath = "/a/b/EQ.txt"
78
+ *         #equalizer.description = "Parametric EQ Sink"
79
+ *         #audio.channels = 2
80
+ *         #audio.position = FL, FR
81
+ *     }
82
+ * }
83
+ * 
84
+ *\endcode
85
+ *
86
+ * \since 1.0.6
87
+ */
88
+
89
+#define NAME "parametric-eq"
90
+
91
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
92
+#define PW_LOG_TOPIC_DEFAULT mod_topic
93
+
94
+#define DEFAULT_DESCRIPTION "Parametric Equalizer Sink";
95
+#define DEFAULT_CHANNELS 2
96
+#define DEFAULT_POSITION " FL FR "
97
+
98
+#define MODULE_USAGE   "( remote.name=<remote> ) "         \
99
+           "( equalizer.filepath=<filepath> )"     \
100
+           "( equalizer.description=<description> )"   \
101
+           "( audio.channels=<number of channels>)"    \
102
+           "( audio.position=<channel map>)"
103
+
104
+static const struct spa_dict_item module_props = {
105
+   { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
106
+   { PW_KEY_MODULE_DESCRIPTION, "Creates a module-filter-chain from Parametric EQ file" },
107
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
108
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
109
+};
110
+
111
+struct impl {
112
+   struct pw_context *context;
113
+   struct pw_properties *props;
114
+
115
+   struct pw_core *core;
116
+   struct pw_impl_module *module;
117
+   struct pw_impl_module *eq_module;
118
+
119
+   struct spa_hook core_proxy_listener;
120
+   struct spa_hook core_listener;
121
+   struct spa_hook module_listener;
122
+   struct spa_hook eq_module_listener;
123
+
124
+   char position64;
125
+   uint32_t channels;
126
+   unsigned int do_disconnect:1;
127
+};
128
+
129
+struct eq_node_param {
130
+   char filter_type4;
131
+   char filter4;
132
+   uint32_t freq;
133
+   float gain;
134
+   float q_fact;
135
+};
136
+
137
+static void filter_chain_module_destroy(void *d)
138
+{
139
+   struct impl *impl = d;
140
+   spa_hook_remove(&impl->eq_module_listener);
141
+   impl->eq_module = NULL;
142
+}
143
+
144
+static const struct pw_impl_module_events filter_chain_module_events = {
145
+   PW_VERSION_IMPL_MODULE_EVENTS,
146
+   .destroy = filter_chain_module_destroy,
147
+};
148
+
149
+void init_eq_node(FILE *f, const char *node_desc) {
150
+   fprintf(f, "{\n");
151
+   fprintf(f, "node.description = \"%s\"\n", node_desc);
152
+   fprintf(f, "media.name = \"%s\"\n", node_desc);
153
+   fprintf(f, "filter.graph = {\n");
154
+   fprintf(f, "nodes = \n");
155
+}
156
+
157
+void add_eq_node(FILE *f, struct eq_node_param *param, uint32_t eq_band_idx) {
158
+   fprintf(f, "{\n");
159
+   fprintf(f, "type = builtin\n");
160
+   fprintf(f, "name = eq_band_%d\n", eq_band_idx);
161
+
162
+   if (strcmp(param->filter_type, "PK") == 0) {
163
+       fprintf(f, "label = bq_peaking\n");
164
+   } else if (strcmp(param->filter_type, "LSC") == 0) {
165
+       fprintf(f, "label = bq_lowshelf\n");
166
+   } else if (strcmp(param->filter_type, "HSC") == 0) {
167
+       fprintf(f, "label = bq_highshelf\n");
168
+   } else {
169
+       fprintf(f, "label = bq_peaking\n");
170
+   }
171
+
172
+   fprintf(f, "control = { \"Freq\" = %d \"Q\" = %f \"Gain\" = %f }\n", param->freq, param->q_fact, param->gain);
173
+
174
+   fprintf(f, "}\n");
175
+}
176
+
177
+void end_eq_node(struct impl *impl, FILE *f, uint32_t number_of_nodes) {
178
+   fprintf(f, "\n");
179
+
180
+   fprintf(f, "links = \n");
181
+   for (uint32_t i = 1; i < number_of_nodes; i++) {
182
+       fprintf(f, "{ output = \"eq_band_%d:Out\" input = \"eq_band_%d:In\" }\n", i, i + 1);
183
+   }
184
+   fprintf(f, "\n");
185
+
186
+   fprintf(f, "}\n");
187
+   fprintf(f, "audio.channels = %d\n", impl->channels);
188
+   fprintf(f, "audio.position = %s\n", impl->position);
189
+
190
+   fprintf(f, "capture.props = {\n");
191
+   fprintf(f, "node.name = \"effect_input.eq%d\"\n", number_of_nodes);
192
+   fprintf(f, "media.class = Audio/Sink\n");
193
+   fprintf(f, "}\n");
194
+
195
+   fprintf(f, "playback.props = {\n");
196
+   fprintf(f, "node.name = \"effect_output.eq%d\"\n", number_of_nodes);
197
+   fprintf(f, "node.passive = true\n");
198
+   fprintf(f, "}\n");
199
+
200
+   fprintf(f, "}\n");
201
+}
202
+
203
+int32_t parse_eq_filter_file(struct impl *impl, FILE *f)
204
+{
205
+   struct eq_node_param eq_param;
206
+   FILE *memstream = NULL;
207
+   const char* str;
208
+
209
+   char *args = NULL;
210
+   char *line = NULL;
211
+   ssize_t nread;
212
+   size_t len, size;
213
+   uint32_t eq_band_idx = 1;
214
+   uint32_t eq_bands = 0;
215
+   int32_t res = 0;
216
+
217
+   if ((memstream = open_memstream(&args, &size)) == NULL) {
218
+       res = -errno;
219
+       pw_log_error("Can't open memstream: %m");
220
+       goto done;
221
+   }
222
+
223
+   if ((str = pw_properties_get(impl->props, "equalizer.description")) == NULL)
224
+       str = DEFAULT_DESCRIPTION;
225
+   init_eq_node(memstream, str);
226
+
227
+   /*
228
+    * Read the Preamp gain line.
229
+    * Example: Preamp: -6.8 dB
230
+    *
231
+    * When a pre-amp gain is required, which is usually the case when
232
+    * applying EQ, we need to modify the first EQ band to apply a
233
+    * bq_highshelf filter at frequency 0 Hz with the provided negative
234
+    * gain.
235
+    *
236
+    * Pre-amp gain is always negative to offset the effect of possible
237
+    * clipping introduced by the amplification resulting from EQ.
238
+    */
239
+   spa_zero(eq_param);
240
+   nread = getline(&line, &len, f);
241
+   if (nread != -1 && sscanf(line, "%*s %6f %*s", &eq_param.gain) == 1) {
242
+       memcpy(eq_param.filter, "ON", 2);
243
+       memcpy(eq_param.filter_type, "HSC", 3);
244
+       eq_param.freq = 0;
245
+       eq_param.q_fact = 1.0;
246
+
247
+       add_eq_node(memstream, &eq_param, eq_band_idx);
248
+
249
+       eq_band_idx++;
250
+       eq_bands++;
251
+   }
252
+
253
+   /* Read the filter bands */
254
+   while ((nread = getline(&line, &len, f)) != -1) {
255
+       spa_zero(eq_param);
256
+
257
+       /*
258
+        * On field widths:
259
+        * - filter can be ON or OFF
260
+        * - filter type can be PK, LSC, HSC
261
+        * - freq can be at most 5 decimal digits
262
+        * - gain can be -xy.z
263
+        * - Q can be x.y00
264
+        *
265
+        * Use a field width of 6 for gain and Q to account for any
266
+        * possible zeros.
267
+        */
268
+       if (sscanf(line, "%*s %*d: %3s %3s %*s %5d %*s %*s %6f %*s %*c %6f", eq_param.filter, eq_param.filter_type, &eq_param.freq, &eq_param.gain, &eq_param.q_fact) == 5) {
269
+           if (strcmp(eq_param.filter, "ON") == 0) {
270
+               add_eq_node(memstream, &eq_param, eq_band_idx);
271
+
272
+               eq_band_idx++;
273
+               eq_bands++;
274
+           }
275
+       }
276
+   }
277
+
278
+   if (eq_bands > 0) {
279
+       end_eq_node(impl, memstream, eq_bands);
280
+   } else {
281
+       pw_log_error("failed to parse equalizer configuration");
282
+       res = -errno;
283
+       goto done;
284
+   }
285
+
286
+   fclose(memstream);
287
+   memstream = NULL;
288
+
289
+   pw_log_info("loading new module-filter-chain with args: %s", args);
290
+   impl->eq_module = pw_context_load_module(impl->context,
291
+               "libpipewire-module-filter-chain",
292
+               args, NULL);
293
+   if (!impl->eq_module) {
294
+       res = -errno;
295
+       pw_log_error("Can't load module: %m");
296
+       goto done;
297
+   }
298
+   pw_log_info("loaded new module-filter-chain");
299
+
300
+   pw_impl_module_add_listener(impl->eq_module,
301
+           &impl->eq_module_listener,
302
+           &filter_chain_module_events, impl);
303
+
304
+   res = 0;
305
+
306
+done:
307
+   if (memstream != NULL)
308
+       fclose(memstream);
309
+   free(args);
310
+
311
+   return res;
312
+}
313
+
314
+static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
315
+{
316
+   struct impl *impl = data;
317
+
318
+   pw_log_error("error id:%u seq:%d res:%d (%s): %s",
319
+           id, seq, res, spa_strerror(res), message);
320
+
321
+   if (id == PW_ID_CORE && res == -EPIPE)
322
+       pw_impl_module_schedule_destroy(impl->module);
323
+}
324
+
325
+static const struct pw_core_events core_events = {
326
+   PW_VERSION_CORE_EVENTS,
327
+   .error = core_error,
328
+};
329
+
330
+static void core_destroy(void *d)
331
+{
332
+   struct impl *impl = d;
333
+   spa_hook_remove(&impl->core_listener);
334
+   impl->core = NULL;
335
+   pw_impl_module_schedule_destroy(impl->module);
336
+}
337
+
338
+static const struct pw_proxy_events core_proxy_events = {
339
+   .destroy = core_destroy,
340
+};
341
+
342
+static void impl_destroy(struct impl *impl)
343
+{
344
+   if (impl->core && impl->do_disconnect)
345
+       pw_core_disconnect(impl->core);
346
+   pw_properties_free(impl->props);
347
+   free(impl);
348
+}
349
+
350
+static void module_destroy(void *data)
351
+{
352
+   struct impl *impl = data;
353
+   spa_hook_remove(&impl->module_listener);
354
+   impl_destroy(impl);
355
+}
356
+
357
+static const struct pw_impl_module_events module_events = {
358
+   PW_VERSION_IMPL_MODULE_EVENTS,
359
+   .destroy = module_destroy,
360
+};
361
+
362
+SPA_EXPORT
363
+int pipewire__module_init(struct pw_impl_module *module, const char *args)
364
+{
365
+   struct pw_context *context = pw_impl_module_get_context(module);
366
+   struct pw_properties *props = NULL;
367
+   struct impl *impl;
368
+   const char *str;
369
+   FILE *f = NULL;
370
+   int res;
371
+
372
+   PW_LOG_TOPIC_INIT(mod_topic);
373
+
374
+   impl = calloc(1, sizeof(struct impl));
375
+   if (impl == NULL)
376
+       return -errno;
377
+
378
+   pw_log_debug("module %p: new %s", impl, args);
379
+
380
+   if (args == NULL)
381
+       args = "";
382
+
383
+   props = pw_properties_new_string(args);
384
+   if (props == NULL) {
385
+       res = -errno;
386
+       pw_log_error( "can't create properties: %m");
387
+       goto error;
388
+   }
389
+   impl->props = props;
390
+
391
+   impl->module = module;
392
+   impl->context = context;
393
+
394
+   impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
395
+   if (impl->core == NULL) {
396
+       str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
397
+       impl->core = pw_context_connect(impl->context,
398
+               pw_properties_new(
399
+                   PW_KEY_REMOTE_NAME, str,
400
+                   NULL),
401
+               0);
402
+       impl->do_disconnect = true;
403
+   }
404
+
405
+   if (impl->core == NULL) {
406
+       res = -errno;
407
+       pw_log_error("can't connect: %m");
408
+       goto error;
409
+   }
410
+
411
+   pw_proxy_add_listener((struct pw_proxy*)impl->core,
412
+           &impl->core_proxy_listener,
413
+           &core_proxy_events, impl);
414
+   pw_core_add_listener(impl->core,
415
+           &impl->core_listener,
416
+           &core_events, impl);
417
+
418
+   impl->channels = pw_properties_get_uint32(impl->props, PW_KEY_AUDIO_CHANNELS, DEFAULT_CHANNELS);
419
+   if (impl->channels == 0) {
420
+       res = -EINVAL;
421
+       pw_log_error("invalid channels '%d'", impl->channels);
422
+       goto error;
423
+   }
424
+
425
+   if ((str = pw_properties_get(impl->props, SPA_KEY_AUDIO_POSITION)) == NULL)
426
+       str = DEFAULT_POSITION;
427
+   strncpy(impl->position, str, strlen(str));
428
+
429
+   if ((str = pw_properties_get(props, "equalizer.filepath")) == NULL) {
430
+       res = -errno;
431
+       pw_log_error( "missing property equalizer.filepath: %m");
432
+       goto error;
433
+   }
434
+
435
+   pw_log_info("Loading equalizer file %s for parsing", str);
436
+
437
+   if ((f = fopen(str, "r")) == NULL) {
438
+       res = -errno;
439
+       pw_log_error("failed to open equalizer file: %m");
440
+       goto error;
441
+   }
442
+
443
+   if (parse_eq_filter_file(impl, f) == -1) {
444
+       res = -EINVAL;
445
+       pw_log_error("failed to parse equalizer file: %m");
446
+       goto error;
447
+   }
448
+
449
+   fclose(f);
450
+
451
+   pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
452
+
453
+   pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
454
+
455
+   return 0;
456
+
457
+error:
458
+   if (f != NULL)
459
+       fclose(f);
460
+
461
+   impl_destroy(impl);
462
+
463
+   return res;
464
+}
465
pipewire-1.0.1.tar.bz2/src/modules/module-pipe-tunnel.c -> pipewire-1.2.0.tar.gz/src/modules/module-pipe-tunnel.c Changed
112
 
1
@@ -199,10 +199,11 @@
2
 
3
    struct spa_dll dll;
4
    float max_error;
5
-   float corr;
6
+   double corr;
7
 
8
    uint64_t next_time;
9
    unsigned int have_sync:1;
10
+   unsigned int underrun:1;
11
 };
12
 
13
 static uint64_t get_time_ns(struct impl *impl)
14
@@ -243,7 +244,7 @@
15
    pw_log_debug("timeout %"PRIu64, duration);
16
 
17
    current_time = impl->next_time;
18
-   impl->next_time += duration / impl->corr * 1e9 / rate;
19
+   impl->next_time += (uint64_t)(duration / impl->corr * 1e9 / rate);
20
    avail = spa_ringbuffer_get_read_index(&impl->ring, &index);
21
 
22
    if (SPA_LIKELY(pos)) {
23
@@ -375,7 +376,7 @@
24
 
25
    if (!impl->driving) {
26
        SPA_FLAG_SET(impl->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE);
27
-       impl->rate_match->rate = 1.0f / impl->corr;
28
+       impl->rate_match->rate = 1.0 / impl->corr;
29
    }
30
 }
31
 
32
@@ -407,11 +408,18 @@
33
    if (avail < (int32_t)size) {
34
        memset(bd->data, 0, size);
35
        if (avail >= 0) {
36
-           pw_log_warn("underrun %d < %u", avail, size);
37
+           if (!impl->underrun) {
38
+               pw_log_warn("underrun %d < %u", avail, size);
39
+               impl->underrun = true;
40
+           }
41
            pause_stream(impl, true);
42
        }
43
        impl->have_sync = false;
44
    }
45
+   if (avail > (int32_t)(impl->target_buffer * 3)) {
46
+       pw_log_warn("resync %d > %u", avail, (int32_t)(impl->target_buffer * 3));
47
+       impl->have_sync = false;
48
+   }
49
    if (avail > (int32_t)RINGBUFFER_SIZE) {
50
        index += avail - impl->target_buffer;
51
        avail = impl->target_buffer;
52
@@ -429,6 +437,7 @@
53
 
54
        index += avail;
55
        spa_ringbuffer_read_update(&impl->ring, index);
56
+       impl->underrun = false;
57
    }
58
    bd->chunk->offset = 0;
59
    bd->chunk->size = size;
60
@@ -722,6 +731,8 @@
61
    if (impl->fd >= 0)
62
        close(impl->fd);
63
 
64
+   pw_context_release_loop(impl->context, impl->data_loop);
65
+
66
    pw_properties_free(impl->stream_props);
67
    pw_properties_free(impl->props);
68
 
69
@@ -848,7 +859,6 @@
70
    struct pw_properties *props = NULL;
71
    struct impl *impl;
72
    const char *str, *media_class = NULL;
73
-   struct pw_data_loop *data_loop;
74
    int res;
75
 
76
    PW_LOG_TOPIC_INIT(mod_topic);
77
@@ -882,8 +892,7 @@
78
    impl->module = module;
79
    impl->context = context;
80
    impl->main_loop = pw_context_get_main_loop(context);
81
-   data_loop = pw_context_get_data_loop(context);
82
-   impl->data_loop = pw_data_loop_get_loop(data_loop);
83
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
84
 
85
    if ((str = pw_properties_get(props, "tunnel.mode")) == NULL)
86
        str = "playback";
87
@@ -914,6 +923,7 @@
88
    if ((str = pw_properties_get(props, "tunnel.may-pause")) != NULL)
89
        impl->may_pause = spa_atob(str);
90
 
91
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
92
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
93
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
94
    if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL)
95
@@ -922,6 +932,7 @@
96
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
97
        pw_properties_update_string(impl->stream_props, str, strlen(str));
98
 
99
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
100
    copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
101
    copy_props(impl, props, PW_KEY_AUDIO_RATE);
102
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
103
@@ -947,7 +958,7 @@
104
    if (impl->info.rate != 0 &&
105
        pw_properties_get(props, PW_KEY_NODE_RATE) == NULL)
106
        pw_properties_setf(props, PW_KEY_NODE_RATE,
107
-               "1/%u", impl->info.rate),
108
+               "1/%u", impl->info.rate);
109
 
110
    copy_props(impl, props, PW_KEY_NODE_RATE);
111
 
112
pipewire-1.0.1.tar.bz2/src/modules/module-portal.c -> pipewire-1.2.0.tar.gz/src/modules/module-portal.c Changed
153
 
1
@@ -18,6 +18,7 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/result.h>
4
 #include <spa/support/dbus.h>
5
+#include <spa-private/dbus-helpers.h>
6
 
7
 #include "pipewire/context.h"
8
 #include "pipewire/impl-client.h"
9
@@ -65,6 +66,8 @@
10
 
11
 #define NAME "portal"
12
 
13
+#define PORTAL_SERVICE_NAME "org.freedesktop.portal.Desktop"
14
+
15
 PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
16
 #define PW_LOG_TOPIC_DEFAULT mod_topic
17
 
18
@@ -127,6 +130,8 @@
19
    spa_hook_remove(&impl->context_listener);
20
    spa_hook_remove(&impl->module_listener);
21
 
22
+   cancel_and_unref(&impl->portal_pid_pending);
23
+
24
    if (impl->bus)
25
        dbus_connection_unref(impl->bus);
26
    spa_dbus_connection_destroy(impl->conn);
27
@@ -145,13 +150,10 @@
28
                   void *user_data)
29
 {
30
    struct impl *impl = user_data;
31
-   DBusMessage *m;
32
-   DBusError error;
33
    uint32_t portal_pid = 0;
34
 
35
-   m = dbus_pending_call_steal_reply(pending);
36
-   dbus_pending_call_unref(pending);
37
-   impl->portal_pid_pending = NULL;
38
+   spa_assert(impl->portal_pid_pending == pending);
39
+   spa_autoptr(DBusMessage) m = steal_reply_and_unref(&impl->portal_pid_pending);
40
 
41
    if (!m) {
42
        pw_log_error("Failed to receive portal pid");
43
@@ -169,15 +171,13 @@
44
        return;
45
    }
46
 
47
-   dbus_error_init(&error);
48
+   spa_auto(DBusError) error = DBUS_ERROR_INIT;
49
    dbus_message_get_args(m, &error, DBUS_TYPE_UINT32, &portal_pid,
50
                  DBUS_TYPE_INVALID);
51
-   dbus_message_unref(m);
52
 
53
    if (dbus_error_is_set(&error)) {
54
        impl->portal_pid = 0;
55
        pw_log_warn("Could not get portal pid: %s", error.message);
56
-       dbus_error_free(&error);
57
    } else {
58
        pw_log_info("Got portal pid %d", portal_pid);
59
        impl->portal_pid = portal_pid;
60
@@ -186,29 +186,22 @@
61
 
62
 static void update_portal_pid(struct impl *impl)
63
 {
64
-   DBusMessage *m;
65
-   const char *name;
66
-   DBusPendingCall *pending;
67
-
68
    impl->portal_pid = 0;
69
+   cancel_and_unref(&impl->portal_pid_pending);
70
 
71
-   m = dbus_message_new_method_call("org.freedesktop.DBus",
72
-                    "/org/freedesktop/DBus",
73
-                    "org.freedesktop.DBus",
74
-                    "GetConnectionUnixProcessID");
75
-
76
-   name = "org.freedesktop.portal.Desktop";
77
-   dbus_message_append_args(m,
78
-                DBUS_TYPE_STRING, &name,
79
-                DBUS_TYPE_INVALID);
80
-
81
-   dbus_connection_send_with_reply(impl->bus, m, &pending, -1);
82
-   dbus_pending_call_set_notify(pending, on_portal_pid_received, impl, NULL);
83
-   if (impl->portal_pid_pending != NULL) {
84
-       dbus_pending_call_cancel(impl->portal_pid_pending);
85
-       dbus_pending_call_unref(impl->portal_pid_pending);
86
-   }
87
-   impl->portal_pid_pending = pending;
88
+   spa_autoptr(DBusMessage) m = dbus_message_new_method_call("org.freedesktop.DBus",
89
+                                 "/org/freedesktop/DBus",
90
+                                 "org.freedesktop.DBus",
91
+                                 "GetConnectionUnixProcessID");
92
+   if (!m)
93
+       return;
94
+
95
+   if (!dbus_message_append_args(m,
96
+                     DBUS_TYPE_STRING, &(const char *){ PORTAL_SERVICE_NAME },
97
+                     DBUS_TYPE_INVALID))
98
+       return;
99
+
100
+   impl->portal_pid_pending = send_with_reply(impl->bus, m, on_portal_pid_received, impl);
101
 }
102
 
103
 static DBusHandlerResult name_owner_changed_handler(DBusConnection *connection,
104
@@ -233,15 +226,12 @@
105
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
106
    }
107
 
108
-   if (!spa_streq(name, "org.freedesktop.portal.Desktop"))
109
+   if (!spa_streq(name, PORTAL_SERVICE_NAME))
110
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
111
 
112
    if (spa_streq(new_owner, "")) {
113
        impl->portal_pid = 0;
114
-       if (impl->portal_pid_pending != NULL) {
115
-           dbus_pending_call_cancel(impl->portal_pid_pending);
116
-           dbus_pending_call_unref(impl->portal_pid_pending);
117
-       }
118
+       cancel_and_unref(&impl->portal_pid_pending);
119
    }
120
    else {
121
        update_portal_pid(impl);
122
@@ -252,8 +242,6 @@
123
 
124
 static int init_dbus_connection(struct impl *impl)
125
 {
126
-   DBusError error;
127
-
128
    impl->bus = spa_dbus_connection_get(impl->conn);
129
    if (impl->bus == NULL)
130
        return -EIO;
131
@@ -261,18 +249,18 @@
132
    /* XXX: we don't handle dbus reconnection yet, so ref the handle instead */
133
    dbus_connection_ref(impl->bus);
134
 
135
-   dbus_error_init(&error);
136
+   spa_auto(DBusError) error = DBUS_ERROR_INIT;
137
 
138
    dbus_bus_add_match(impl->bus,
139
               "type='signal',\
140
               sender='org.freedesktop.DBus',\
141
               interface='org.freedesktop.DBus',\
142
-              member='NameOwnerChanged'",
143
+              member='NameOwnerChanged',\
144
+              arg0='" PORTAL_SERVICE_NAME "'",
145
               &error);
146
    if (dbus_error_is_set(&error)) {
147
        pw_log_error("Failed to add name owner changed listener: %s",
148
                 error.message);
149
-       dbus_error_free(&error);
150
        return -EIO;
151
    }
152
 
153
pipewire-1.0.1.tar.bz2/src/modules/module-profiler.c -> pipewire-1.2.0.tar.gz/src/modules/module-profiler.c Changed
144
 
1
@@ -58,7 +58,7 @@
2
 
3
 #define TMP_BUFFER     (16 * 1024)
4
 #define DATA_BUFFER        (32 * 1024)
5
-#define FLUSH_BUFFER       (8 * 1024 * 1024)
6
+#define FLUSH_BUFFER       (8 * 1024)
7
 
8
 int pw_protocol_native_ext_profiler_init(struct pw_context *context);
9
 
10
@@ -94,7 +94,6 @@
11
    struct pw_properties *properties;
12
 
13
    struct pw_loop *main_loop;
14
-   struct pw_loop *data_loop;
15
 
16
    struct spa_hook context_listener;
17
    struct spa_hook module_listener;
18
@@ -108,12 +107,8 @@
19
    struct spa_source *flush_event;
20
    unsigned int listening:1;
21
 
22
-#ifdef max_align_t
23
-   alignas(max_align_t)
24
-#else
25
-   alignas(64)
26
-#endif
27
-   uint8_t flushFLUSH_BUFFER + sizeof(struct spa_pod_struct);
28
+   uint8_t *flush;
29
+   size_t flush_size;
30
 };
31
 
32
 struct resource_data {
33
@@ -139,16 +134,27 @@
34
 
35
        avail = spa_ringbuffer_get_read_index(&n->buffer, &idx);
36
 
37
-       pw_log_trace("%p avail %d", impl, avail);
38
+       pw_log_trace("%p: avail %d", impl, avail);
39
 
40
        if (avail > 0) {
41
-           if (total + avail < FLUSH_BUFFER) {
42
-               spa_ringbuffer_read_data(&n->buffer, n->data, DATA_BUFFER,
43
-                       idx % DATA_BUFFER,
44
-                       SPA_PTROFF(p, sizeof(struct spa_pod_struct) + total, void),
45
-                       avail);
46
-               total += avail;
47
+           size_t size = total + avail + sizeof(struct spa_pod_struct);
48
+           if (size > impl->flush_size) {
49
+               uint8_t *flush;
50
+               flush = realloc(impl->flush, size);
51
+               if (flush == NULL) {
52
+                   pw_log_warn("%p: failed to realloc flush size %zu", impl, impl->flush_size);
53
+                   continue;
54
+               }
55
+               impl->flush = flush;
56
+               impl->flush_size = size;
57
+               pw_log_debug("%p: new flush buffer size %zu", impl, impl->flush_size);
58
+               p = (struct spa_pod_struct *)impl->flush;
59
            }
60
+           spa_ringbuffer_read_data(&n->buffer, n->data, DATA_BUFFER,
61
+                   idx % DATA_BUFFER,
62
+                   SPA_PTROFF(p, sizeof(struct spa_pod_struct) + total, void),
63
+                   avail);
64
+           total += avail;
65
            spa_ringbuffer_read_update(&n->buffer, idx + avail);
66
        }
67
    }
68
@@ -199,8 +205,8 @@
69
            SPA_POD_Long(pos->clock.duration),
70
            SPA_POD_Long(pos->clock.delay),
71
            SPA_POD_Double(pos->clock.rate_diff),
72
-           SPA_POD_Long(pos->clock.next_nsec));
73
-
74
+           SPA_POD_Long(pos->clock.next_nsec),
75
+           SPA_POD_Int(pos->state));
76
 
77
    spa_pod_builder_prop(&b, SPA_PROFILER_driverBlock, 0);
78
    spa_pod_builder_add_struct(&b,
79
@@ -219,7 +225,7 @@
80
        struct pw_node_activation *na;
81
        struct spa_fraction latency;
82
 
83
-       if (t->id == id || t->flags & PW_NODE_TARGET_PEER)
84
+       if (t->id == id)
85
            continue;
86
 
87
        if (n != NULL) {
88
@@ -412,6 +418,7 @@
89
 
90
    pw_loop_destroy_source(impl->main_loop, impl->flush_event);
91
 
92
+   free(impl->flush);
93
    free(impl);
94
 }
95
 
96
@@ -441,16 +448,18 @@
97
    struct pw_context *context = pw_impl_module_get_context(module);
98
    struct pw_properties *props;
99
    struct impl *impl;
100
-   static const char * const keys = {
101
-       PW_KEY_OBJECT_SERIAL,
102
-       NULL
103
-   };
104
 
105
    PW_LOG_TOPIC_INIT(mod_topic);
106
 
107
    impl = calloc(1, sizeof(struct impl));
108
    if (impl == NULL)
109
        return -errno;
110
+   impl->flush_size = FLUSH_BUFFER + sizeof(struct spa_pod_struct);
111
+   impl->flush = malloc(impl->flush_size);
112
+   if (impl->flush == NULL) {
113
+       free(impl);
114
+       return -errno;
115
+   }
116
 
117
    spa_list_init(&impl->node_list);
118
    pw_protocol_native_ext_profiler_init(context);
119
@@ -465,7 +474,6 @@
120
    impl->context = context;
121
    impl->properties = props;
122
    impl->main_loop = pw_context_get_main_loop(impl->context);
123
-   impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(impl->context));
124
 
125
    impl->global = pw_global_new(context,
126
            PW_TYPE_INTERFACE_Profiler,
127
@@ -474,6 +482,7 @@
128
            pw_properties_copy(props),
129
            global_bind, impl);
130
    if (impl->global == NULL) {
131
+       free(impl->flush);
132
        free(impl);
133
        return -errno;
134
    }
135
@@ -483,8 +492,6 @@
136
 
137
    impl->flush_event = pw_loop_add_event(impl->main_loop, do_flush_event, impl);
138
 
139
-   pw_global_update_keys(impl->global, &impl->properties->dict, keys);
140
-
141
    pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
142
 
143
    pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
144
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-native.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-native.c Changed
483
 
1
@@ -29,9 +29,11 @@
2
 #include <spa/pod/iter.h>
3
 #include <spa/pod/parser.h>
4
 #include <spa/pod/builder.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/utils/json.h>
9
+#include <spa/debug/log.h>
10
 
11
 #ifdef HAVE_SYSTEMD
12
 #include <systemd/sd-daemon.h>
13
@@ -43,7 +45,6 @@
14
 
15
 #include <pipewire/impl.h>
16
 #include <pipewire/extensions/protocol-native.h>
17
-#include <pipewire/cleanup.h>
18
 
19
 #include "pipewire/private.h"
20
 
21
@@ -79,12 +80,15 @@
22
  *
23
  * The module supports the following arguments:
24
  *
25
- * - `sockets`: ` { name = "socket-name", owner = "owner", group = "group", mode = "mode", selinux.context = "context" }, ... `
26
+ * - `sockets`: ` { name = "socket-name", owner = "owner", group = "group", mode = "mode", selinux.context = "context" }, props = { ... }, ... `
27
  *
28
  *   Array of Unix socket names and (optionally) owner/permissions to serve,
29
  *   if the context is a server. If not absolute paths, the sockets are created
30
  *   in the default runtime directory.
31
  *
32
+ *   The props are copied directly to any client that connects through this server
33
+ *   socket and can be used to configure special permissions.
34
+ *
35
  *   Has the default value ` { name = "CORENAME" }, { name = "CORENAME-manager" } `,
36
  *   where `CORENAME` is the name of the PipeWire core, usually `pipewire-0`.
37
  *
38
@@ -137,7 +141,16 @@
39
  *\code{.unparsed}
40
  * context.modules = 
41
  *  { name = libpipewire-module-protocol-native,
42
- *    args = { sockets =  { name = "pipewire-0" }, { name = "pipewire-0-manager" }  } }
43
+ *    args = {
44
+ *        sockets = 
45
+ *            { name = "pipewire-0" }
46
+ *            { name = "pipewire-0-manager" }
47
+ *            { name = "pipewire-1"
48
+ *              props = { my.connection = "the other one" }
49
+ *            }
50
+ *        
51
+ *    }
52
+ *  }
53
  * 
54
  *\endcode
55
  */
56
@@ -157,19 +170,22 @@
57
 #define SO_PEERSEC 31
58
 #endif
59
 
60
-static bool debug_messages = 0;
61
-
62
 #define LOCK_SUFFIX     ".lock"
63
 #define LOCK_SUFFIXLEN  5
64
 
65
 void pw_protocol_native_init(struct pw_protocol *protocol);
66
 void pw_protocol_native0_init(struct pw_protocol *protocol);
67
+void *protocol_native_security_context_init(struct pw_impl_module *module, struct pw_protocol *protocol);
68
+void protocol_native_security_context_free(void *data);
69
 
70
 struct protocol_data {
71
    struct pw_impl_module *module;
72
    struct spa_hook module_listener;
73
    struct pw_protocol *protocol;
74
 
75
+   struct pw_properties *props;
76
+   void *security;
77
+
78
    struct server *local;
79
 };
80
 
81
@@ -214,11 +230,13 @@
82
    int fd_lock;
83
    struct sockaddr_un addr;
84
    char lock_addrUNIX_PATH_MAX + LOCK_SUFFIXLEN;
85
-   struct socket_info socket_info;
86
+
87
+   struct pw_properties *props;
88
 
89
    struct pw_loop *loop;
90
    struct spa_source *source;
91
    struct spa_source *resume;
92
+   struct spa_source *close;
93
    unsigned int activated:1;
94
 };
95
 
96
@@ -244,16 +262,24 @@
97
 static void debug_msg(const char *prefix, const struct pw_protocol_native_message *msg, bool hex)
98
 {
99
    struct spa_pod *pod;
100
+   struct spa_debug_log_ctx c;
101
+
102
+   if (!pw_log_topic_custom_enabled(SPA_LOG_LEVEL_DEBUG, mod_topic_connection))
103
+       return;
104
+
105
+   c = SPA_LOGT_DEBUG_INIT(pw_log_get(),
106
+               SPA_LOG_LEVEL_DEBUG, mod_topic_connection);
107
+
108
    pw_logt_debug(mod_topic_connection,
109
              "%s: id:%d op:%d size:%d seq:%d fds:%d", prefix,
110
              msg->id, msg->opcode, msg->size, msg->seq, msg->n_fds);
111
 
112
    if ((pod = get_first_pod_from_data(msg->data, msg->size, 0)) != NULL)
113
-       spa_debug_pod(0, NULL, pod);
114
+       spa_debugc_pod(&c.ctx, 0, NULL, pod);
115
    else
116
        hex = true;
117
    if (hex)
118
-       spa_debug_mem(0, msg->data, msg->size);
119
+       spa_debugc_mem(&c.ctx, 0, msg->data, msg->size);
120
 
121
    pw_logt_debug(mod_topic_connection, "%s ****", prefix);
122
 
123
@@ -341,7 +367,7 @@
124
        pw_log_trace("%p: got message %d from %u", client->protocol,
125
                 msg->opcode, msg->id);
126
 
127
-       if (debug_messages)
128
+       if (pw_log_topic_custom_enabled(SPA_LOG_LEVEL_DEBUG, mod_topic_connection))
129
            debug_msg("<<<<<< in", msg, false);
130
 
131
        pre_demarshal(conn, msg, client, footer_client_demarshal,
132
@@ -580,12 +606,10 @@
133
    struct protocol_data *d = pw_protocol_get_user_data(protocol);
134
    int i, res;
135
 
136
-   props = pw_properties_new(PW_KEY_PROTOCOL, "protocol-native", NULL);
137
+   props = pw_properties_copy(s->props);
138
    if (props == NULL)
139
        goto exit;
140
 
141
-   pw_properties_set(props, PW_KEY_SEC_SOCKET, s->socket_info.name);
142
-
143
 #if defined(__linux__)
144
    len = sizeof(ucred);
145
    if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
146
@@ -797,6 +821,17 @@
147
    }
148
 }
149
 
150
+static void
151
+close_data(void *data, int fd, uint32_t mask)
152
+{
153
+   struct server *s = data;
154
+
155
+   if (mask & (SPA_IO_HUP | SPA_IO_ERR)) {
156
+       pw_log_info("server %p: closed socket %d %08x", s, fd, mask);
157
+       pw_protocol_server_destroy(&s->this);
158
+   }
159
+}
160
+
161
 static int write_socket_address(struct server *s)
162
 {
163
    long v;
164
@@ -835,9 +870,8 @@
165
    return res;
166
 }
167
 
168
-static int set_socket_permissions(struct server *s)
169
+static int set_socket_permissions(struct server *s, struct socket_info *info)
170
 {
171
-   struct socket_info *info = &s->socket_info;
172
    const char *path = s->addr.sun_path;
173
 
174
    if (info->has_owner)
175
@@ -860,7 +894,7 @@
176
    return 0;
177
 }
178
 
179
-static int add_socket(struct pw_protocol *protocol, struct server *s)
180
+static int add_socket(struct pw_protocol *protocol, struct server *s, struct socket_info *info)
181
 {
182
    socklen_t size;
183
    int fd = -1, res;
184
@@ -907,10 +941,10 @@
185
            goto error_close;
186
        }
187
 
188
-       if ((res = set_socket_permissions(s)) < 0) {
189
+       if ((res = set_socket_permissions(s, info)) < 0) {
190
            errno = -res;
191
            pw_log_error("server %p: failed to set socket %s permissions: %m",
192
-                   s, s->socket_info.name);
193
+                   s, info->name);
194
            goto error_close;
195
        }
196
 
197
@@ -920,9 +954,9 @@
198
            goto error_close;
199
        }
200
    } else {
201
-       if (s->socket_info.has_owner || s->socket_info.has_mode || s->socket_info.selinux_context)
202
+       if (info->has_owner || info->has_mode || info->selinux_context)
203
            pw_log_info("server %p: permissions ignored for socket %s from systemd",
204
-                   s, s->socket_info.name);
205
+                   s, info->name);
206
    }
207
 
208
    res = write_socket_address(s);
209
@@ -995,7 +1029,7 @@
210
 
211
        this->recv_seq = msg->seq;
212
 
213
-       if (debug_messages)
214
+       if (pw_log_topic_custom_enabled(SPA_LOG_LEVEL_DEBUG, mod_topic_connection))
215
            debug_msg("<<<<<< in", msg, false);
216
 
217
        pre_demarshal(conn, msg, this, footer_core_demarshal,
218
@@ -1326,14 +1360,15 @@
219
        pw_loop_destroy_source(s->loop, s->source);
220
    if (s->resume)
221
        pw_loop_destroy_source(s->loop, s->resume);
222
+   if (s->close)
223
+       pw_loop_destroy_source(s->loop, s->close);
224
    if (s->addr.sun_path0 && !s->activated)
225
        unlink(s->addr.sun_path);
226
    if (s->lock_addr0)
227
        unlink(s->lock_addr);
228
    if (s->fd_lock != -1)
229
        close(s->fd_lock);
230
-   free(s->socket_info.name);
231
-   free(s->socket_info.selinux_context);
232
+   pw_properties_free(s->props);
233
    free(s);
234
 }
235
 
236
@@ -1379,6 +1414,12 @@
237
    if ((s = calloc(1, sizeof(struct server))) == NULL)
238
        return NULL;
239
 
240
+   s->props = pw_properties_new(PW_KEY_PROTOCOL, "protocol-native", NULL);
241
+   if (s->props == NULL) {
242
+       free(s);
243
+       return NULL;
244
+   }
245
+   pw_properties_update(s->props, props);
246
    s->fd_lock = -1;
247
 
248
    this = &s->this;
249
@@ -1386,7 +1427,6 @@
250
    this->core = core;
251
    spa_list_init(&this->client_list);
252
    this->destroy = destroy_server;
253
-
254
    spa_list_append(&protocol->server_list, &this->link);
255
 
256
    pw_log_debug("%p: created server %p", protocol, this);
257
@@ -1410,16 +1450,12 @@
258
 
259
    this = &s->this;
260
 
261
-   if (socket_info) {
262
-       s->socket_info = *socket_info;
263
-       s->socket_info.name = strdup(socket_info->name);
264
-       s->socket_info.selinux_context = socket_info->selinux_context ?
265
-           strdup(socket_info->selinux_context) : NULL;
266
+   if (socket_info)
267
        name = socket_info->name;
268
-   } else {
269
+   else
270
        name = get_server_name(props);
271
-       s->socket_info.name = strdup(name);
272
-   }
273
+
274
+   pw_properties_set(s->props, PW_KEY_SEC_SOCKET, name);
275
 
276
    if ((res = init_socket_name(s, name)) < 0)
277
        goto error;
278
@@ -1427,11 +1463,13 @@
279
    if ((res = lock_socket(s)) < 0)
280
        goto error;
281
 
282
-   if ((res = add_socket(protocol, s)) < 0)
283
+   if ((res = add_socket(protocol, s, socket_info)) < 0)
284
        goto error;
285
 
286
-   if ((s->resume = pw_loop_add_event(s->loop, do_resume, s)) == NULL)
287
+   if ((s->resume = pw_loop_add_event(s->loop, do_resume, s)) == NULL) {
288
+       res = -errno;
289
        goto error;
290
+   }
291
 
292
    pw_log_info("%p: Listening on '%s'", protocol, name);
293
 
294
@@ -1451,10 +1489,58 @@
295
    return add_server(protocol, core, props, NULL);
296
 }
297
 
298
+static struct pw_protocol_server *
299
+impl_add_fd_server(struct pw_protocol *protocol,
300
+       struct pw_impl_core *core,
301
+       int listen_fd, int close_fd,
302
+                const struct spa_dict *props)
303
+{
304
+   struct pw_protocol_server *this;
305
+   struct server *s;
306
+   int res;
307
+
308
+   if ((s = create_server(protocol, core, props)) == NULL)
309
+       return NULL;
310
+
311
+   this = &s->this;
312
+
313
+   pw_properties_setf(s->props, PW_KEY_SEC_SOCKET, "pipewire-fd-%d", listen_fd);
314
+
315
+   s->loop = pw_context_get_main_loop(protocol->context);
316
+   if (s->loop == NULL) {
317
+       res = -errno;
318
+       goto error;
319
+   }
320
+   s->source = pw_loop_add_io(s->loop, listen_fd, SPA_IO_IN, true, socket_data, s);
321
+   if (s->source == NULL) {
322
+       res = -errno;
323
+       goto error;
324
+   }
325
+   s->close = pw_loop_add_io(s->loop, close_fd, 0, true, close_data, s);
326
+   if (s->close == NULL) {
327
+       res = -errno;
328
+       goto error;
329
+   }
330
+   if ((s->resume = pw_loop_add_event(s->loop, do_resume, s)) == NULL) {
331
+       res = -errno;
332
+       goto error;
333
+   }
334
+
335
+   pw_log_info("%p: Listening on fd:%d", protocol, listen_fd);
336
+
337
+   return this;
338
+
339
+error:
340
+   destroy_server(this);
341
+   errno = -res;
342
+   return NULL;
343
+}
344
+
345
 static const struct pw_protocol_implementation protocol_impl = {
346
    PW_VERSION_PROTOCOL_IMPLEMENTATION,
347
    .new_client = impl_new_client,
348
    .add_server = impl_add_server,
349
+   .add_fd_server = impl_add_fd_server,
350
 };
351
 
352
 static struct spa_pod_builder *
353
@@ -1544,7 +1630,10 @@
354
 {
355
    struct protocol_data *d = data;
356
 
357
+   if (d->security)
358
+       protocol_native_security_context_free(d->security);
359
    spa_hook_remove(&d->module_listener);
360
+   pw_properties_free(d->props);
361
 
362
    pw_protocol_destroy(d->protocol);
363
 }
364
@@ -1571,13 +1660,14 @@
365
 {
366
    const char *sockets = args ? pw_properties_get(args, "sockets") : NULL;
367
    struct spa_json it3;
368
+   spa_autoptr(pw_properties) p = pw_properties_copy(props);
369
 
370
    if (sockets == NULL) {
371
        struct socket_info info = {0};
372
        spa_autofree char *manager_name = NULL;
373
 
374
-       info.name = (char *)get_server_name(&props->dict);
375
-       if (add_server(this, core, &props->dict, &info) == NULL)
376
+       info.name = (char *)get_server_name(&p->dict);
377
+       if (add_server(this, core, &p->dict, &info) == NULL)
378
            return -errno;
379
 
380
        manager_name = spa_aprintf("%s-manager", info.name);
381
@@ -1585,7 +1675,7 @@
382
            return -ENOMEM;
383
 
384
        info.name = manager_name;
385
-       if (add_server(this, core, &props->dict, &info) == NULL)
386
+       if (add_server(this, core, &p->dict, &info) == NULL)
387
            return -errno;
388
 
389
        return 0;
390
@@ -1605,6 +1695,9 @@
391
        info.uid = getuid();
392
        info.gid = getgid();
393
 
394
+       pw_properties_clear(p);
395
+       pw_properties_update(p, &props->dict);
396
+
397
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
398
            const char *value;
399
            int len;
400
@@ -1667,13 +1760,18 @@
401
                    goto error_invalid;
402
 
403
                info.has_mode = true;
404
+           } else if (spa_streq(key, "props")) {
405
+               if (spa_json_is_container(value, len))
406
+                                   len = spa_json_container_len(&it2, value, len);
407
+
408
+               pw_properties_update_string(p, value, len);
409
            }
410
        }
411
 
412
        if (info.name == NULL)
413
            goto error_invalid;
414
 
415
-       if (add_server(this, core, &props->dict, &info) == NULL)
416
+       if (add_server(this, core, &p->dict, &info) == NULL)
417
            return -errno;
418
    }
419
 
420
@@ -1694,6 +1792,10 @@
421
    const struct pw_properties *props;
422
    spa_autoptr(pw_properties) args = NULL;
423
    int res;
424
+   static const char * const keys = {
425
+       PW_KEY_CORE_NAME,
426
+       NULL
427
+   };
428
 
429
    PW_LOG_TOPIC_INIT(mod_topic);
430
    PW_LOG_TOPIC_INIT(mod_topic_connection);
431
@@ -1709,25 +1811,36 @@
432
    if (this == NULL)
433
        return -errno;
434
 
435
-   debug_messages = mod_topic_connection->level >= SPA_LOG_LEVEL_DEBUG;
436
-
437
    this->implementation = &protocol_impl;
438
    this->extension = &protocol_ext_impl;
439
 
440
    pw_protocol_native_init(this);
441
    pw_protocol_native0_init(this);
442
 
443
-   pw_log_debug("%p: new debug:%d", this, debug_messages);
444
+   pw_log_debug("%p: new", this);
445
 
446
    d = pw_protocol_get_user_data(this);
447
    d->protocol = this;
448
    d->module = module;
449
+   d->props = pw_properties_new(NULL, NULL);
450
+   if (d->props == NULL) {
451
+       res = -ENOMEM;
452
+       goto error_cleanup;
453
+   }
454
+
455
+   d->security = protocol_native_security_context_init(module, this);
456
+   if (d->security == NULL) {
457
+       res = -errno;
458
+       goto error_cleanup;
459
+   }
460
 
461
    props = pw_context_get_properties(context);
462
-   d->local = create_server(this, core, &props->dict);
463
+   pw_properties_update_keys(d->props, &props->dict, keys);
464
+
465
+   d->local = create_server(this, core, &d->props->dict);
466
 
467
    if (need_server(context, &props->dict))
468
-       if ((res = create_servers(this, core, props, args)) < 0)
469
+       if ((res = create_servers(this, core, d->props, args)) < 0)
470
            goto error_cleanup;
471
 
472
    pw_impl_module_add_listener(module, &d->module_listener, &module_events, d);
473
@@ -1737,6 +1850,9 @@
474
    return 0;
475
 
476
 error_cleanup:
477
+   pw_properties_free(d->props);
478
+   if (d->security)
479
+       protocol_native_security_context_free(d->security);
480
    pw_protocol_destroy(this);
481
    return res;
482
 }
483
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-native/connection.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-native/connection.c Changed
10
 
1
@@ -718,7 +718,7 @@
2
    else
3
        buf->n_fds = buf->msg.n_fds;
4
 
5
-   if (mod_topic_connection->level >= SPA_LOG_LEVEL_DEBUG) {
6
+   if (pw_log_topic_custom_enabled(SPA_LOG_LEVEL_DEBUG, mod_topic_connection)) {
7
        pw_logt_debug(mod_topic_connection,
8
            ">>>>>>>>> out: id:%d op:%d size:%d seq:%d fds:%d",
9
                buf->msg.id, buf->msg.opcode, size, buf->msg.seq,
10
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-native/protocol-native.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-native/protocol-native.c Changed
120
 
1
@@ -11,6 +11,7 @@
2
 
3
 #include <pipewire/impl.h>
4
 #include <pipewire/extensions/protocol-native.h>
5
+#include <pipewire/extensions/security-context.h>
6
 
7
 #include "connection.h"
8
 
9
@@ -1879,6 +1880,63 @@
10
    return pw_protocol_native_end_proxy(proxy, b);
11
 }
12
 
13
+static int security_context_method_marshal_add_listener(void *object,
14
+           struct spa_hook *listener,
15
+           const struct pw_security_context_events *events,
16
+           void *data)
17
+{
18
+   struct pw_proxy *proxy = object;
19
+   pw_proxy_add_object_listener(proxy, listener, events, data);
20
+   return 0;
21
+}
22
+
23
+static int security_context_marshal_create(void *object,
24
+       int listen_fd, int close_fd, const struct spa_dict *props)
25
+{
26
+   struct pw_proxy *proxy = object;
27
+   struct spa_pod_builder *b;
28
+   struct spa_pod_frame f;
29
+
30
+   b = pw_protocol_native_begin_proxy(proxy, PW_SECURITY_CONTEXT_METHOD_CREATE, NULL);
31
+
32
+   spa_pod_builder_push_struct(b, &f);
33
+   spa_pod_builder_add(b,
34
+           SPA_POD_Fd(pw_protocol_native_add_proxy_fd(proxy, listen_fd)),
35
+           SPA_POD_Fd(pw_protocol_native_add_proxy_fd(proxy, close_fd)),
36
+           NULL);
37
+   push_dict(b, props);
38
+   spa_pod_builder_pop(b, &f);
39
+
40
+   return pw_protocol_native_end_proxy(proxy, b);
41
+}
42
+
43
+static int security_context_demarshal_create(void *object, const struct pw_protocol_native_message *msg)
44
+{
45
+   struct pw_resource *resource = object;
46
+   struct spa_dict props = SPA_DICT_INIT(NULL, 0);
47
+   struct spa_pod_parser prs;
48
+   struct spa_pod_frame f2;
49
+   int64_t listen_idx, close_idx;
50
+   int listen_fd, close_fd;
51
+
52
+   spa_pod_parser_init(&prs, msg->data, msg->size);
53
+   if (spa_pod_parser_push_struct(&prs, &f0) < 0)
54
+       return -EINVAL;
55
+   if (spa_pod_parser_get(&prs,
56
+               SPA_POD_Fd(&listen_idx),
57
+               SPA_POD_Fd(&close_idx),
58
+               NULL) < 0)
59
+       return -EINVAL;
60
+   parse_dict_struct(&prs, &f1, &props);
61
+
62
+   listen_fd = pw_protocol_native_get_resource_fd(resource, listen_idx);
63
+   close_fd = pw_protocol_native_get_resource_fd(resource, close_idx);
64
+
65
+   return pw_resource_notify(resource, struct pw_security_context_methods, create, 0,
66
+           listen_fd, close_fd, &props);
67
+}
68
+
69
+
70
 static const struct pw_core_methods pw_protocol_native_core_method_marshal = {
71
    PW_VERSION_CORE_METHODS,
72
    .add_listener = &core_method_marshal_add_listener,
73
@@ -2253,6 +2311,40 @@
74
    .client_demarshal = pw_protocol_native_link_event_demarshal,
75
 };
76
 
77
+
78
+static const struct pw_security_context_methods pw_protocol_native_security_context_method_marshal = {
79
+   PW_VERSION_LINK_METHODS,
80
+   .add_listener = &security_context_method_marshal_add_listener,
81
+   .create = &security_context_marshal_create,
82
+};
83
+
84
+static const struct pw_protocol_native_demarshal
85
+pw_protocol_native_security_context_method_demarshalPW_SECURITY_CONTEXT_METHOD_NUM =
86
+{
87
+   PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = { NULL, 0, },
88
+   PW_SECURITY_CONTEXT_METHOD_CREATE = { &security_context_demarshal_create, 0, },
89
+};
90
+
91
+static const struct pw_security_context_events pw_protocol_native_security_context_event_marshal = {
92
+   PW_VERSION_LINK_EVENTS,
93
+};
94
+
95
+static const struct pw_protocol_native_demarshal
96
+pw_protocol_native_security_context_event_demarshalPW_SECURITY_CONTEXT_EVENT_NUM =
97
+{
98
+};
99
+
100
+static const struct pw_protocol_marshal pw_protocol_native_security_context_marshal = {
101
+   PW_TYPE_INTERFACE_SecurityContext,
102
+   PW_VERSION_SECURITY_CONTEXT,
103
+   0,
104
+   PW_SECURITY_CONTEXT_METHOD_NUM,
105
+   PW_SECURITY_CONTEXT_EVENT_NUM,
106
+   .client_marshal = &pw_protocol_native_security_context_method_marshal,
107
+   .server_demarshal = pw_protocol_native_security_context_method_demarshal,
108
+   .server_marshal = &pw_protocol_native_security_context_event_marshal,
109
+   .client_demarshal = pw_protocol_native_security_context_event_demarshal,
110
+};
111
 void pw_protocol_native_init(struct pw_protocol *protocol)
112
 {
113
    pw_protocol_add_marshal(protocol, &pw_protocol_native_core_marshal);
114
@@ -2264,4 +2356,5 @@
115
    pw_protocol_add_marshal(protocol, &pw_protocol_native_factory_marshal);
116
    pw_protocol_add_marshal(protocol, &pw_protocol_native_client_marshal);
117
    pw_protocol_add_marshal(protocol, &pw_protocol_native_link_marshal);
118
+   pw_protocol_add_marshal(protocol, &pw_protocol_native_security_context_marshal);
119
 }
120
pipewire-1.2.0.tar.gz/src/modules/module-protocol-native/security-context.c Added
183
 
1
@@ -0,0 +1,181 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+
7
+#include <pipewire/pipewire.h>
8
+#include <pipewire/impl.h>
9
+#include <pipewire/private.h>
10
+#include <pipewire/extensions/security-context.h>
11
+
12
+PW_LOG_TOPIC_EXTERN(mod_topic);
13
+#define PW_LOG_TOPIC_DEFAULT mod_topic
14
+PW_LOG_TOPIC_EXTERN(mod_topic_connection);
15
+
16
+struct impl {
17
+   struct pw_context *context;
18
+   struct pw_global *global;
19
+   struct spa_hook listener;
20
+
21
+   struct pw_protocol *protocol;
22
+};
23
+
24
+struct resource_data {
25
+   struct impl *impl;
26
+
27
+   struct pw_resource *resource;
28
+   struct spa_hook resource_listener;
29
+   struct spa_hook object_listener;
30
+};
31
+
32
+static int security_context_create(void *object,
33
+           int listen_fd,
34
+           int close_fd,
35
+           const struct spa_dict *props)
36
+{
37
+   struct resource_data *d = object;
38
+   struct impl *impl = d->impl;
39
+   struct pw_impl_client *client;
40
+   const struct pw_properties *p;
41
+   int res = 0;
42
+
43
+   if ((client = impl->context->current_client) == NULL)
44
+       goto invalid_state;
45
+   if (client->protocol != impl->protocol)
46
+       goto invalid_state;
47
+
48
+   /* we can't make a nested security context */
49
+   p = pw_impl_client_get_properties(client);
50
+   if (pw_properties_get(p, PW_KEY_SEC_ENGINE) != NULL)
51
+       goto not_allowed;
52
+
53
+   if (pw_protocol_add_fd_server(impl->protocol, impl->context->core,
54
+           listen_fd, close_fd, props) == NULL) {
55
+       res = -errno;
56
+       pw_resource_errorf(d->resource, res, "can't add fd server: %m");
57
+   }
58
+   return res;
59
+
60
+invalid_state:
61
+   pw_resource_errorf(d->resource, -EIO, "invalid client protocol");
62
+   return -EIO;
63
+not_allowed:
64
+   pw_resource_errorf(d->resource, -EPERM, "Nested security context is not allowed");
65
+   return -EPERM;
66
+}
67
+
68
+static const struct pw_security_context_methods security_context_methods = {
69
+   PW_VERSION_SECURITY_CONTEXT_METHODS,
70
+   .create = security_context_create,
71
+};
72
+
73
+static void global_unbind(void *data)
74
+{
75
+   struct resource_data *d = data;
76
+   if (d->resource) {
77
+           spa_hook_remove(&d->resource_listener);
78
+   }
79
+}
80
+
81
+static const struct pw_resource_events resource_events = {
82
+   PW_VERSION_RESOURCE_EVENTS,
83
+   .destroy = global_unbind,
84
+};
85
+
86
+static int
87
+global_bind(void *object, struct pw_impl_client *client, uint32_t permissions,
88
+            uint32_t version, uint32_t id)
89
+{
90
+   struct impl *impl = object;
91
+   struct pw_resource *resource;
92
+   struct resource_data *data;
93
+
94
+   resource = pw_resource_new(client, id, permissions,
95
+           PW_TYPE_INTERFACE_SecurityContext,
96
+           version, sizeof(*data));
97
+   if (resource == NULL)
98
+       return -errno;
99
+
100
+   data = pw_resource_get_user_data(resource);
101
+   data->impl = impl;
102
+   data->resource = resource;
103
+
104
+   pw_global_add_resource(impl->global, resource);
105
+
106
+   /* listen for when the resource goes away */
107
+   pw_resource_add_listener(resource,
108
+           &data->resource_listener,
109
+           &resource_events, data);
110
+
111
+   /* resource methods -> implementation */
112
+   pw_resource_add_object_listener(resource,
113
+           &data->object_listener,
114
+           &security_context_methods, data);
115
+
116
+   return 0;
117
+}
118
+
119
+
120
+static void global_free(void *data)
121
+{
122
+   struct impl *impl = data;
123
+   if (impl->global) {
124
+       spa_hook_remove(&impl->listener);
125
+       impl->global = NULL;
126
+   }
127
+}
128
+
129
+static const struct pw_global_events global_events = {
130
+   PW_VERSION_GLOBAL_EVENTS,
131
+   .free = global_free
132
+};
133
+
134
+void protocol_native_security_context_free(void *data)
135
+{
136
+   struct impl *impl = data;
137
+   if (impl->global)
138
+       pw_global_destroy(impl->global);
139
+   free(impl);
140
+}
141
+
142
+void *protocol_native_security_context_init(struct pw_impl_module *module, struct pw_protocol *protocol)
143
+{
144
+   struct pw_context *context = pw_impl_module_get_context(module);
145
+   struct impl *impl;
146
+   char serial_str32;
147
+   struct spa_dict_item items1 = {
148
+       SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_SERIAL, serial_str),
149
+   };
150
+   struct spa_dict extra_props = SPA_DICT_INIT_ARRAY(items);
151
+   static const char * const keys = {
152
+       PW_KEY_OBJECT_SERIAL,
153
+       NULL
154
+   };
155
+
156
+   impl = calloc(1, sizeof(struct impl));
157
+   if (impl == NULL)
158
+       return NULL;
159
+
160
+   impl->context = context;
161
+   impl->protocol = protocol;
162
+
163
+   impl->global = pw_global_new(context,
164
+           PW_TYPE_INTERFACE_SecurityContext,
165
+           PW_VERSION_SECURITY_CONTEXT,
166
+           PW_SECURITY_CONTEXT_PERM_MASK,
167
+           NULL,
168
+           global_bind, impl);
169
+   if (impl->global == NULL) {
170
+       free(impl);
171
+       return NULL;
172
+   }
173
+   spa_scnprintf(serial_str, sizeof(serial_str), "%"PRIu64,
174
+           pw_global_get_serial(impl->global));
175
+   pw_global_update_keys(impl->global, &extra_props, keys);
176
+
177
+   pw_global_add_listener(impl->global, &impl->listener, &global_events, impl);
178
+
179
+   pw_global_register(impl->global);
180
+
181
+   return impl;
182
+}
183
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse.c Changed
139
 
1
@@ -63,6 +63,8 @@
2
  *         #  client.access = "restricted"     # permissions for clients
3
  *         #}
4
  *     
5
+ *     #server.dbus-name       = "org.pulseaudio.Server"
6
+ *     #pulse.allow-module-loading = true
7
  *     #pulse.min.req          = 128/48000     # 2.7ms
8
  *     #pulse.default.req      = 960/48000     # 20 milliseconds
9
  *     #pulse.min.frag         = 128/48000     # 2.7ms
10
@@ -71,11 +73,19 @@
11
  *     #pulse.min.quantum      = 128/48000     # 2.7ms
12
  *     #pulse.default.format   = F32
13
  *     #pulse.default.position =  FL FR 
14
- *     # These overrides are only applied when running in a vm.
15
- *     vm.overrides = {
16
- *         pulse.min.quantum = 1024/48000      # 22ms
17
- *     }
18
+ *     #pulse.idle.timeout     = 0
19
  * }
20
+ *
21
+ * pulse.properties.rules = 
22
+ *     {   matches =  { cpu.vm.name = !null } 
23
+ *         actions = {
24
+ *             update-props = {
25
+ *                 # These overrides are only applied when running in a vm.
26
+ *                 pulse.min.quantum = 1024/48000      # 22ms
27
+ *             }
28
+ *         }
29
+ *     }
30
+ * 
31
  *\endcode
32
  *
33
  * ### Connection options
34
@@ -111,6 +121,20 @@
35
  * By default network access is given the "restricted" permissions. The session manager is responsible
36
  * for assigning permission to clients with restricted permissions (usually read-only permissions).
37
  *
38
+ *\code{.unparsed}
39
+ *     server.dbus-name       = "org.pulseaudio.Server"
40
+ *\endcode
41
+ *
42
+ * The DBus name to reserve for the server. If you have multiple servers, you might want
43
+ * to change the name.
44
+ *
45
+ *\code{.unparsed}
46
+ *     pulse.allow-module-loading = true
47
+ *\endcode
48
+ *
49
+ * By default, clients are allowed to load and unload modules. You can disable this
50
+ * feature with this option.
51
+ *
52
  * ### Playback buffering options
53
  *
54
  *\code{.unparsed}
55
@@ -185,19 +209,6 @@
56
  * This is equivalent to the PulseAudio `default-sample-channels` and
57
  * `default-channel-map` options in `/etc/pulse/daemon.conf`.
58
  *
59
- * ### VM options
60
- *
61
- *\code{.unparsed}
62
- *     vm.overrides = {
63
- *         pulse.min.quantum = 1024/48000      # 22ms
64
- *     }
65
- *\endcode
66
- *
67
- * When running in a VM, the `vm.override` section will override the properties
68
- * in pulse.properties with the given values. This might be interesting because
69
- * VMs usually can't support the low latency settings that are possible on real
70
- * hardware.
71
- *
72
  * ### Quirk options
73
  *
74
  *\code{.unparsed}
75
@@ -224,6 +235,16 @@
76
  * Normally the channels would be fixed to the sink/source that the stream connects
77
  * to. When an invalid position (null or "") is set, the FIX_CHANNELS flag is ignored.
78
  *
79
+ *\code{.unparsed}
80
+ *     pulse.idle.timeout = 0
81
+ *\endcode
82
+ *
83
+ * Some clients are not sending data when they should and cause underruns. When
84
+ * setting this option such clients will be set to paused if they underrun
85
+ * for the given amount of seconds. This makes sure that sinks can suspend and
86
+ * save battery power. When the client resumes, it will unpause again.
87
+ * A value of 0 disables this feature.
88
+ *
89
  * ## Command execution
90
  *
91
  * As part of the server startup sequence, a set of commands can be executed.
92
@@ -240,6 +261,30 @@
93
  *     #{ cmd = "load-module" args = "module-gsettings" flags =  "nofail"  }
94
  * 
95
  *\endcode
96
+
97
+ * ## Dynamic properties
98
+ *
99
+ * The pulse.properties can be dynamically updated with rules. It supports
100
+ * an `update-props` action. The matches will be performed on the values in
101
+ * context.properties.
102
+ *
103
+ *\code{.unparsed}
104
+ * pulse.properties.rules = 
105
+ *     {   matches =  { cpu.vm.name = !null } 
106
+ *         actions = {
107
+ *             update-props = {
108
+ *                 # These overrides are only applied when running in a vm.
109
+ *                 pulse.min.quantum = 1024/48000      # 22ms
110
+ *             }
111
+ *         }
112
+ *     }
113
+ * 
114
+ *\endcode
115
+ *
116
+ * In the above example, when running in a VM, the rule will override the properties
117
+ * in pulse.properties with the given values. This might be interesting because
118
+ * VMs usually can't support the low latency settings that are possible on real
119
+ * hardware.
120
  *
121
  * ## Stream settings and rules
122
  *
123
@@ -269,6 +314,7 @@
124
  *             update-props = {
125
  *                 pulse.min.req          = 1024/48000     # 21ms
126
  *                 pulse.min.quantum      = 1024/48000     # 21ms
127
+ *                 pulse.idle.timeout     = 5              # pause after 5 seconds of underrun
128
  *             }
129
  *         }
130
  *     }
131
@@ -324,6 +370,7 @@
132
  *            update-props = {
133
  *                pulse.min.req          = 1024/48000     # 21ms
134
  *                pulse.min.quantum      = 1024/48000     # 21ms
135
+ *                pulse.idle.timeout     = 5              # pause after 5 seconds of underrun
136
  *            }
137
  *        }
138
  *    }
139
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/client.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/client.c Changed
81
 
1
@@ -29,7 +29,10 @@
2
 #include "server.h"
3
 #include "stream.h"
4
 
5
+PW_LOG_TOPIC_EXTERN(pulse_conn);
6
+
7
 #define client_emit_disconnect(c) spa_hook_list_call(&(c)->listener_list, struct client_events, disconnect, 0)
8
+#define client_emit_routes_changed(c) spa_hook_list_call(&(c)->listener_list, struct client_events, routes_changed, 0)
9
 
10
 struct client *client_new(struct server *server)
11
 {
12
@@ -167,6 +170,16 @@
13
    free(client);
14
 }
15
 
16
+void client_update_routes(struct client *client, const char *key, const char *value)
17
+{
18
+   if (key == NULL)
19
+       pw_properties_clear(client->routes);
20
+   else
21
+       pw_properties_set(client->routes, key, value);
22
+
23
+   client_emit_routes_changed(client);
24
+}
25
+
26
 int client_queue_message(struct client *client, struct message *msg)
27
 {
28
    struct impl *impl = client->impl;
29
@@ -232,8 +245,9 @@
30
            data = m->data + idx;
31
            size = m->length - idx;
32
        } else {
33
-           if (debug_messages && m->channel == SPA_ID_INVALID)
34
-               message_dump(SPA_LOG_LEVEL_INFO, m);
35
+           if (m->channel == SPA_ID_INVALID &&
36
+               pw_log_topic_custom_enabled(SPA_LOG_LEVEL_INFO, pulse_conn))
37
+               message_dump(SPA_LOG_LEVEL_INFO, ">>", m);
38
            message_free(m, true, false);
39
            client->out_index = 0;
40
            continue;
41
@@ -296,11 +310,11 @@
42
 
43
    /* NOTE: reverse iteration */
44
    spa_list_for_each_safe_reverse(m, t, &client->out_messages, link) {
45
-       if (m->extra0 != COMMAND_SUBSCRIBE_EVENT)
46
+       if (m->type != MESSAGE_TYPE_SUBSCRIPTION_EVENT)
47
            continue;
48
-       if ((m->extra1 ^ event) & SUBSCRIPTION_EVENT_FACILITY_MASK)
49
+       if ((m->u.subscription_event.event ^ event) & SUBSCRIPTION_EVENT_FACILITY_MASK)
50
            continue;
51
-       if (m->extra2 != index)
52
+       if (m->u.subscription_event.index != index)
53
            continue;
54
 
55
        if ((event & SUBSCRIPTION_EVENT_TYPE_MASK) == SUBSCRIPTION_EVENT_REMOVE) {
56
@@ -308,7 +322,7 @@
57
             * point in keeping the old events regarding
58
             * entry in the queue. */
59
 
60
-           bool is_new = (m->extra1 & SUBSCRIPTION_EVENT_TYPE_MASK) == SUBSCRIPTION_EVENT_NEW;
61
+           bool is_new = (m->u.subscription_event.event & SUBSCRIPTION_EVENT_TYPE_MASK) == SUBSCRIPTION_EVENT_NEW;
62
 
63
            if (drop_from_out_queue(client, m)) {
64
                pw_log_debug("client %p: dropped redundant event due to remove event for object %u",
65
@@ -354,9 +368,12 @@
66
        return 0;
67
 
68
    struct message *reply = message_alloc(client->impl, -1, 0);
69
-   reply->extra0 = COMMAND_SUBSCRIBE_EVENT;
70
-   reply->extra1 = event;
71
-   reply->extra2 = index;
72
+   if (!reply)
73
+       return -errno;
74
+
75
+   reply->type = MESSAGE_TYPE_SUBSCRIPTION_EVENT;
76
+   reply->u.subscription_event.event = event;
77
+   reply->u.subscription_event.index = index;
78
 
79
    message_put(reply,
80
        TAG_U32, COMMAND_SUBSCRIBE_EVENT,
81
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/client.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/client.h Changed
24
 
1
@@ -85,10 +85,12 @@
2
 };
3
 
4
 struct client_events {
5
-#define VERSION_CLIENT_EVENTS  0
6
+#define VERSION_CLIENT_EVENTS  1
7
    uint32_t version;
8
 
9
    void (*disconnect) (void *data);
10
+
11
+   void (*routes_changed) (void *data);
12
 };
13
 
14
 struct client *client_new(struct server *server);
15
@@ -99,6 +101,8 @@
16
 int client_flush_messages(struct client *client);
17
 int client_queue_subscribe_event(struct client *client, uint32_t mask, uint32_t event, uint32_t id);
18
 
19
+void client_update_routes(struct client *client, const char *key, const char *value);
20
+
21
 static inline void client_unref(struct client *client)
22
 {
23
    if (--client->ref == 0)
24
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/cmd.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/cmd.c Changed
12
 
1
@@ -2,9 +2,9 @@
2
 /* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
3
 /* SPDX-License-Identifier: MIT */
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/json.h>
7
 
8
-#include <pipewire/cleanup.h>
9
 #include <pipewire/utils.h>
10
 
11
 #include "module.h"
12
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/defs.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/defs.h Changed
9
 
1
@@ -37,7 +37,6 @@
2
 #define SCACHE_ENTRY_SIZE_MAX  (1024*1024*16)
3
 
4
 #define MODULE_INDEX_MASK  0xfffffffu
5
-#define MODULE_EXTENSION_FLAG  (1u << 28)
6
 #define MODULE_FLAG        (1u << 29)
7
 
8
 #define DEFAULT_SINK       "@DEFAULT_SINK@"
9
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extension.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/extension.c Changed
58
 
1
@@ -5,21 +5,45 @@
2
 #include <spa/utils/defs.h>
3
 #include <spa/utils/string.h>
4
 
5
+#include "client.h"
6
 #include "defs.h"
7
 #include "extension.h"
8
-#include "extensions/registry.h"
9
+#include "message.h"
10
+#include "module.h"
11
 
12
-static const struct extension extensions = {
13
-   { "module-stream-restore", 0 | MODULE_EXTENSION_FLAG, do_extension_stream_restore, },
14
-   { "module-device-restore", 1 | MODULE_EXTENSION_FLAG, do_extension_device_restore, },
15
-   { "module-device-manager", 2 | MODULE_EXTENSION_FLAG, do_extension_device_manager, },
16
-};
17
-
18
-const struct extension *extension_find(uint32_t index, const char *name)
19
+static const struct extension *find_extension_command(struct module *module, uint32_t command)
20
 {
21
-   SPA_FOR_EACH_ELEMENT_VAR(extensions, ext) {
22
-       if (index == ext->index || spa_streq(name, ext->name))
23
-           return ext;
24
+   uint32_t i;
25
+
26
+   if (module->info->extension == NULL)
27
+       return NULL;
28
+
29
+   for (i = 0; module->info->extensioni.name; i++) {
30
+       if (module->info->extensioni.command == command)
31
+           return &module->info->extensioni;
32
    }
33
    return NULL;
34
 }
35
+
36
+int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m)
37
+{
38
+   uint32_t command;
39
+   const struct extension *ext;
40
+   int res;
41
+
42
+   if ((res = message_get(m,
43
+           TAG_U32, &command,
44
+           TAG_INVALID)) < 0)
45
+       return -EPROTO;
46
+
47
+   ext = find_extension_command(module, command);
48
+   if (ext == NULL)
49
+       return -ENOTSUP;
50
+   if (ext->process == NULL)
51
+       return -EPROTO;
52
+
53
+   pw_log_info("client %p %s: %s %s tag:%u",
54
+           client, client->name, module->info->name, ext->name, tag);
55
+
56
+   return ext->process(module, client, command, tag, m);
57
+}
58
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/extension.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/extension.h Changed
26
 
1
@@ -9,19 +9,15 @@
2
 
3
 struct client;
4
 struct message;
5
-
6
-struct extension_sub {
7
-   const char *name;
8
-   uint32_t command;
9
-   int (*process)(struct client *client, uint32_t command, uint32_t tag, struct message *m);
10
-};
11
+struct module;
12
 
13
 struct extension {
14
    const char *name;
15
-   uint32_t index;
16
-   int (*process)(struct client *client, uint32_t tag, struct message *m);
17
+   uint32_t command;
18
+   int (*process)(struct module *module, struct client *client, uint32_t command,
19
+           uint32_t tag, struct message *m);
20
 };
21
 
22
-const struct extension *extension_find(uint32_t index, const char *name);
23
+int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m);
24
 
25
 #endif /* PULSE_SERVER_EXTENSION_H */
26
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/format.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/format.c Changed
20
 
1
@@ -687,6 +687,9 @@
2
    if (val->type != SPA_TYPE_Int)
3
        return -ENOTSUP;
4
 
5
+   if (n_values == 0)
6
+       return -ENOENT;
7
+
8
    values = SPA_POD_BODY(val);
9
 
10
    switch (choice) {
11
@@ -865,7 +868,7 @@
12
    if (spa_json_is_float(val, len)) {
13
        if (spa_json_parse_float(val, len, &f) <= 0)
14
            return -EINVAL;
15
-       ss->channels = f;
16
+       ss->channels = (uint8_t)f;
17
    } else if (spa_json_is_array(val, len)) {
18
        return -ENOTSUP;
19
    } else if (spa_json_is_object(val, len)) {
20
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/internal.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/internal.h Changed
18
 
1
@@ -25,6 +25,7 @@
2
 struct pw_properties;
3
 
4
 struct defs {
5
+   bool allow_module_loading;
6
    struct spa_fraction min_req;
7
    struct spa_fraction default_req;
8
    struct spa_fraction min_frag;
9
@@ -82,8 +83,6 @@
10
        struct spa_hook *listener,
11
        const struct impl_events *events, void *data);
12
 
13
-extern bool debug_messages;
14
-
15
 void broadcast_subscribe_event(struct impl *impl, uint32_t mask, uint32_t event, uint32_t id);
16
 
17
 #endif
18
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/manager.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/manager.c Changed
48
 
1
@@ -168,6 +168,12 @@
2
        }
3
    }
4
 }
5
+static void object_reset_params(struct object *o)
6
+{
7
+   uint32_t i;
8
+   for (i = 0; i < o->this.n_params; i++)
9
+       o->this.paramsi.user = 0;
10
+}
11
 
12
 static void object_data_free(struct object_data *d)
13
 {
14
@@ -311,7 +317,6 @@
15
 
16
            if (info->paramsi.user == 0)
17
                continue;
18
-           info->paramsi.user = 0;
19
 
20
            switch (id) {
21
            case SPA_PARAM_EnumProfile:
22
@@ -373,7 +378,8 @@
23
    if (p == NULL)
24
        return;
25
 
26
-   if (id == SPA_PARAM_Route && !has_param(&o->this.param_list, p)) {
27
+   if ((id == SPA_PARAM_Route || id == SPA_PARAM_EnumRoute) &&
28
+       !has_param(&o->this.param_list, p)) {
29
        uint32_t idx, device;
30
        if (spa_pod_parse_object(param,
31
                SPA_TYPE_OBJECT_ParamRoute, NULL,
32
@@ -438,7 +444,6 @@
33
 
34
            if (info->paramsi.user == 0)
35
                continue;
36
-           info->paramsi.user = 0;
37
 
38
            switch (id) {
39
            case SPA_PARAM_Props:
40
@@ -706,6 +711,7 @@
41
                manager_emit_updated(m, &o->this);
42
                o->changed = 0;
43
            }
44
+           object_reset_params(o);
45
        }
46
    }
47
 }
48
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/message-handler.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/message-handler.c Changed
43
 
1
@@ -21,6 +21,7 @@
2
 #include "collect.h"
3
 #include "log.h"
4
 #include "manager.h"
5
+#include "module.h"
6
 #include "message-handler.h"
7
 
8
 static int bluez_card_object_message_handler(struct client *client, struct pw_manager_object *o, const char *message, const char *params, FILE *response)
9
@@ -114,6 +115,33 @@
10
        int res = malloc_trim(0);
11
        fprintf(response, "%d", res);
12
 #endif
13
+   } else if (spa_streq(message, "pipewire-pulse:log-level")) {
14
+       int res = pw_log_set_level_string(params);
15
+       fprintf(response, "%d", res);
16
+   } else if (spa_streq(message, "pipewire-pulse:describe-module")) {
17
+       const struct module_info *i = module_info_find(client->impl, params);
18
+
19
+       if (i != NULL) {
20
+           fprintf(response, "Name: %s\n", i->name);
21
+               if (i->properties == NULL) {
22
+               fprintf(response, "No module information available\n");
23
+           } else {
24
+               const char *s;
25
+               if ((s = spa_dict_lookup(i->properties, PW_KEY_MODULE_VERSION)))
26
+                   fprintf(response, "Version: %s\n", s);
27
+               if ((s = spa_dict_lookup(i->properties, PW_KEY_MODULE_DESCRIPTION)))
28
+                   fprintf(response, "Description: %s\n", s);
29
+               if ((s = spa_dict_lookup(i->properties, PW_KEY_MODULE_AUTHOR)))
30
+                   fprintf(response, "Author: %s\n", s);
31
+               if ((s = spa_dict_lookup(i->properties, PW_KEY_MODULE_USAGE)))
32
+                   fprintf(response, "Usage: %s\n", s);
33
+               fprintf(response, "Load Once: %s\n", i->load_once ? "Yes": "No");
34
+               if ((s = spa_dict_lookup(i->properties, PW_KEY_MODULE_DEPRECATED)))
35
+                   fprintf(response, "Warning, deprecated: %s\n", s);
36
+           }
37
+       } else {
38
+           fprintf(response, "Failed to open module.\n");
39
+       }
40
    } else {
41
        return -ENOSYS;
42
    }
43
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/message.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/message.c Changed
233
 
1
@@ -8,8 +8,10 @@
2
 #include <spa/debug/buffer.h>
3
 #include <spa/utils/defs.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/debug/log.h>
6
 #include <pipewire/log.h>
7
 
8
+#include "commands.h"
9
 #include "defs.h"
10
 #include "format.h"
11
 #include "internal.h"
12
@@ -120,6 +122,12 @@
13
                TAG_INVALID)) < 0)
14
            return res;
15
 
16
+       if (length != size)
17
+           return -EINVAL;
18
+
19
+       if (strnlen(data, size) != size - 1)
20
+           continue;
21
+
22
        if (remap && (map = str_map_find(props_key_map, NULL, key)) != NULL) {
23
            key = map->pw_str;
24
            if (map->child != NULL &&
25
@@ -413,9 +421,9 @@
26
 {
27
    write_8(m, s ? TAG_STRING : TAG_STRING_NULL);
28
    if (s != NULL) {
29
-       int len = strlen(s) + 1;
30
+       size_t len = strlen(s) + 1;
31
        if (ensure_size(m, len) > 0)
32
-           strcpy(SPA_PTROFF(m->data, m->length, char), s);
33
+           memcpy(SPA_PTROFF(m->data, m->length, char), s, len);
34
        m->length += len;
35
    }
36
 }
37
@@ -628,7 +636,7 @@
38
            write_dict(m, va_arg(va, struct spa_dict*), true);
39
            break;
40
        case TAG_VOLUME:
41
-           write_volume(m, va_arg(va, double));
42
+           write_volume(m, (float)va_arg(va, double));
43
            break;
44
        case TAG_FORMAT_INFO:
45
            write_format_info(m, va_arg(va, struct format_info*));
46
@@ -643,12 +651,14 @@
47
    return 0;
48
 }
49
 
50
-int message_dump(enum spa_log_level level, struct message *m)
51
+int message_dump(enum spa_log_level level, const char *prefix, struct message *m)
52
 {
53
    int res;
54
    uint32_t i, offset = m->offset, o;
55
 
56
-   pw_log(level, "message: len:%d alloc:%u", m->length, m->allocated);
57
+   m->offset = 0;
58
+
59
+   pw_log(level, "%s message: len:%d alloc:%u", prefix, m->length, m->allocated);
60
    while (true) {
61
        uint8_t tag;
62
 
63
@@ -662,18 +672,18 @@
64
            char *val;
65
            if ((res = read_string(m, &val)) < 0)
66
                return res;
67
-           pw_log(level, "%u: string: '%s'", o, val);
68
+           pw_log(level, "%s %u: string: '%s'", prefix, o, val);
69
            break;
70
            }
71
        case TAG_STRING_NULL:
72
-           pw_log(level, "%u: string: NULL", o);
73
+           pw_log(level, "%s %u: string: NULL", prefix, o);
74
            break;
75
        case TAG_U8:
76
        {
77
            uint8_t val;
78
            if ((res = read_u8(m, &val)) < 0)
79
                return res;
80
-           pw_log(level, "%u: u8: %u", o, val);
81
+           pw_log(level, "%s %u: u8: %u", prefix, o, val);
82
            break;
83
        }
84
        case TAG_U32:
85
@@ -681,7 +691,13 @@
86
            uint32_t val;
87
            if ((res = read_u32(m, &val)) < 0)
88
                return res;
89
-           pw_log(level, "%u: u32: %u", o, val);
90
+           if (o == 0) {
91
+               pw_log(level, "%s %u: u32: %u (command %s)",
92
+                       prefix, o, val,
93
+                       val < COMMAND_MAX ? commandsval.name : "INVALID");
94
+           } else {
95
+               pw_log(level, "%s %u: u32: %u", prefix, o, val);
96
+           }
97
            break;
98
        }
99
        case TAG_S64:
100
@@ -689,7 +705,7 @@
101
            uint64_t val;
102
            if ((res = read_u64(m, &val)) < 0)
103
                return res;
104
-           pw_log(level, "%u: s64: %"PRIi64"", o, (int64_t)val);
105
+           pw_log(level, "%s %u: s64: %"PRIi64"", prefix, o, (int64_t)val);
106
            break;
107
        }
108
        case TAG_U64:
109
@@ -697,7 +713,7 @@
110
            uint64_t val;
111
            if ((res = read_u64(m, &val)) < 0)
112
                return res;
113
-           pw_log(level, "%u: u64: %"PRIu64"", o, val);
114
+           pw_log(level, "%s %u: u64: %"PRIu64"", prefix, o, val);
115
            break;
116
        }
117
        case TAG_USEC:
118
@@ -705,7 +721,7 @@
119
            uint64_t val;
120
            if ((res = read_u64(m, &val)) < 0)
121
                return res;
122
-           pw_log(level, "%u: u64: %"PRIu64"", o, val);
123
+           pw_log(level, "%s %u: u64: %"PRIu64"", prefix, o, val);
124
            break;
125
        }
126
        case TAG_SAMPLE_SPEC:
127
@@ -713,7 +729,7 @@
128
            struct sample_spec ss;
129
            if ((res = read_sample_spec(m, &ss)) < 0)
130
                return res;
131
-           pw_log(level, "%u: ss: format:%s rate:%d channels:%u", o,
132
+           pw_log(level, "%s %u: ss: format:%s rate:%d channels:%u", prefix, o,
133
                    format_id2name(ss.format), ss.rate,
134
                    ss.channels);
135
            break;
136
@@ -724,21 +740,22 @@
137
            size_t len;
138
            if ((res = read_arbitrary(m, &mem, &len)) < 0)
139
                return res;
140
-           spa_debug_mem(0, mem, len);
141
+           spa_debug_log_mem(pw_log_get(), level, 0, mem, len);
142
            break;
143
        }
144
        case TAG_BOOLEAN_TRUE:
145
-           pw_log(level, "%u: bool: true", o);
146
+           pw_log(level, "%s %u: bool: true", prefix, o);
147
            break;
148
        case TAG_BOOLEAN_FALSE:
149
-           pw_log(level, "%u: bool: false", o);
150
+           pw_log(level, "%s %u: bool: false", prefix, o);
151
            break;
152
        case TAG_TIMEVAL:
153
        {
154
            struct timeval tv;
155
            if ((res = read_timeval(m, &tv)) < 0)
156
                return res;
157
-           pw_log(level, "%u: timeval: %lu:%lu", o, tv.tv_sec, tv.tv_usec);
158
+           pw_log(level, "%s %u: timeval: %jd:%jd", prefix, o,
159
+               (intmax_t) tv.tv_sec, (intmax_t) tv.tv_usec);
160
            break;
161
        }
162
        case TAG_CHANNEL_MAP:
163
@@ -746,9 +763,9 @@
164
            struct channel_map map;
165
            if ((res = read_channel_map(m, &map)) < 0)
166
                return res;
167
-           pw_log(level, "%u: channelmap: channels:%u", o, map.channels);
168
+           pw_log(level, "%s %u: channelmap: channels:%u", prefix, o, map.channels);
169
            for (i = 0; i < map.channels; i++)
170
-               pw_log(level, "    %d: %s", i, channel_id2name(map.mapi));
171
+               pw_log(level, "%s     %d: %s", prefix, i, channel_id2name(map.mapi));
172
            break;
173
        }
174
        case TAG_CVOLUME:
175
@@ -756,9 +773,9 @@
176
            struct volume vol;
177
            if ((res = read_cvolume(m, &vol)) < 0)
178
                return res;
179
-           pw_log(level, "%u: cvolume: channels:%u", o, vol.channels);
180
+           pw_log(level, "%s %u: cvolume: channels:%u", prefix, o, vol.channels);
181
            for (i = 0; i < vol.channels; i++)
182
-               pw_log(level, "    %d: %f", i, vol.valuesi);
183
+               pw_log(level, "%s     %d: %f", prefix, i, vol.valuesi);
184
            break;
185
        }
186
        case TAG_PROPLIST:
187
@@ -767,9 +784,9 @@
188
            const struct spa_dict_item *it;
189
            res = read_props(m, props, false);
190
            if (res >= 0) {
191
-               pw_log(level, "%u: props: n_items:%u", o, props->dict.n_items);
192
+               pw_log(level, "%s %u: props: n_items:%u", prefix, o, props->dict.n_items);
193
                spa_dict_for_each(it, &props->dict)
194
-                   pw_log(level, "     '%s': '%s'", it->key, it->value);
195
+                   pw_log(level, "%s      '%s': '%s'", prefix, it->key, it->value);
196
            }
197
            pw_properties_free(props);
198
            if (res < 0)
199
@@ -781,7 +798,7 @@
200
            float vol;
201
            if ((res = read_volume(m, &vol)) < 0)
202
                return res;
203
-           pw_log(level, "%u: volume: %f", o, vol);
204
+           pw_log(level, "%s %u: volume: %f", prefix, o, vol);
205
            break;
206
        }
207
        case TAG_FORMAT_INFO:
208
@@ -790,11 +807,12 @@
209
            const struct spa_dict_item *it;
210
            if ((res = read_format_info(m, &info)) < 0)
211
                return res;
212
-           pw_log(level, "%u: format-info: enc:%s n_items:%u",
213
-                   o, format_encoding2name(info.encoding),
214
+           pw_log(level, "%s %u: format-info: enc:%s n_items:%u",
215
+                   prefix, o, format_encoding2name(info.encoding),
216
                    info.props->dict.n_items);
217
            spa_dict_for_each(it, &info.props->dict)
218
-               pw_log(level, "     '%s': '%s'", it->key, it->value);
219
+               pw_log(level, "%s      '%s': '%s'", prefix, it->key, it->value);
220
+           format_info_clear(&info);
221
            break;
222
        }
223
        }
224
@@ -829,7 +847,7 @@
225
        return NULL;
226
    }
227
 
228
-   spa_zero(msg->extra);
229
+   msg->type = MESSAGE_TYPE_UNSPECIFIED;
230
    msg->channel = channel;
231
    msg->offset = 0;
232
    msg->length = size;
233
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/message.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/message.h Changed
38
 
1
@@ -13,15 +13,27 @@
2
 
3
 struct impl;
4
 
5
+enum message_type {
6
+   MESSAGE_TYPE_UNSPECIFIED,
7
+   MESSAGE_TYPE_SUBSCRIPTION_EVENT,
8
+};
9
+
10
 struct message {
11
    struct spa_list link;
12
    struct impl *impl;
13
-   uint32_t extra4;
14
    uint32_t channel;
15
    uint32_t allocated;
16
    uint32_t length;
17
    uint32_t offset;
18
    uint8_t *data;
19
+
20
+   enum message_type type;
21
+   union {
22
+       struct {
23
+           uint32_t event;
24
+           uint32_t index;
25
+       } subscription_event;
26
+   } u;
27
 };
28
 
29
 enum {
30
@@ -50,6 +62,6 @@
31
 void message_free(struct message *msg, bool dequeue, bool destroy);
32
 int message_get(struct message *m, ...);
33
 int message_put(struct message *m, ...);
34
-int message_dump(enum spa_log_level level, struct message *m);
35
+int message_dump(enum spa_log_level level, const char *prefix, struct message *m);
36
 
37
 #endif /* PULSE_SERVER_MESSAGE_H */
38
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/module.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/module.c Changed
52
 
1
@@ -7,11 +7,11 @@
2
 #include <string.h>
3
 #include <ctype.h>
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/defs.h>
7
 #include <spa/utils/list.h>
8
 #include <spa/utils/hook.h>
9
 #include <spa/utils/string.h>
10
-#include <pipewire/cleanup.h>
11
 #include <pipewire/log.h>
12
 #include <pipewire/map.h>
13
 #include <pipewire/properties.h>
14
@@ -293,7 +293,7 @@
15
    }
16
 }
17
 
18
-static const struct module_info *find_module_info(const char *name)
19
+const struct module_info *module_info_find(struct impl *impl, const char *name)
20
 {
21
    extern const struct module_info __start_pw_mod_pulse_modules;
22
    extern const struct module_info __stop_pw_mod_pulse_modules;
23
@@ -323,7 +323,7 @@
24
    struct module *module;
25
    int res;
26
 
27
-   info = find_module_info(name);
28
+   info = module_info_find(impl, name);
29
    if (info == NULL) {
30
        errno = ENOENT;
31
        return NULL;
32
@@ -375,3 +375,19 @@
33
    return NULL;
34
 
35
 }
36
+
37
+struct module *module_lookup(struct impl *impl, uint32_t index, const char *name)
38
+{
39
+   union pw_map_item *item;
40
+
41
+   if (index != SPA_ID_INVALID)
42
+       return pw_map_lookup(&impl->modules, index);
43
+
44
+   pw_array_for_each(item, &impl->modules.items) {
45
+       struct module *m = item->data;
46
+       if (!pw_map_item_is_free(item) &&
47
+           spa_streq(m->info->name, name))
48
+           return m;
49
+   }
50
+   return NULL;
51
+}
52
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/module.h -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/module.h Changed
36
 
1
@@ -13,6 +13,9 @@
2
 
3
 struct module;
4
 struct pw_properties;
5
+struct client;
6
+struct message;
7
+struct extension;
8
 
9
 struct module_info {
10
    const char *name;
11
@@ -23,6 +26,7 @@
12
    int (*load) (struct module *module);
13
    int (*unload) (struct module *module);
14
 
15
+   const struct extension *extension;
16
    const char* const *valid_args;
17
    const struct spa_dict *properties;
18
    size_t data_size;
19
@@ -58,12 +62,16 @@
20
 #define module_emit_loaded(m,r) spa_hook_list_call(&m->listener_list, struct module_events, loaded, 0, r)
21
 #define module_emit_destroy(m) spa_hook_list_call(&(m)->listener_list, struct module_events, destroy, 0)
22
 
23
+const struct module_info *module_info_find(struct impl *impl, const char *name);
24
+
25
 struct module *module_create(struct impl *impl, const char *name, const char *args);
26
 void module_free(struct module *module);
27
 int module_load(struct module *module);
28
 int module_unload(struct module *module);
29
 void module_schedule_unload(struct module *module);
30
 
31
+struct module *module_lookup(struct impl *impl, uint32_t index, const char *name);
32
+
33
 void module_add_listener(struct module *module,
34
             struct spa_hook *listener,
35
             const struct module_events *events, void *data);
36
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-device-manager.c Added
65
 
1
@@ -0,0 +1,63 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <pipewire/pipewire.h>
7
+
8
+#include "../module.h"
9
+
10
+/** \page page_pulse_module_device_manager Device manager extension
11
+ *
12
+ * ## Module Name
13
+ *
14
+ * `module-device-manager`
15
+ *
16
+ * ## Module Options
17
+ *
18
+ * @pulse_module_options@
19
+ */
20
+
21
+static const char *const pulse_module_options =
22
+   "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
23
+   "on_hotplug=<When new device becomes available, recheck streams?> "
24
+   "on_rescue=<When device becomes unavailable, recheck streams?>";
25
+
26
+#define NAME "device-manager"
27
+
28
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
29
+#define PW_LOG_TOPIC_DEFAULT mod_topic
30
+
31
+struct module_device_manager_data {
32
+   struct module *module;
33
+};
34
+
35
+static const struct spa_dict_item module_device_manager_info = {
36
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
37
+   { PW_KEY_MODULE_DESCRIPTION, "Keep track of devices (and their descriptions) both past and present and prioritise by role" },
38
+   { PW_KEY_MODULE_USAGE, pulse_module_options },
39
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
40
+};
41
+
42
+static int module_device_manager_prepare(struct module * const module)
43
+{
44
+   PW_LOG_TOPIC_INIT(mod_topic);
45
+
46
+   struct module_device_manager_data * const data = module->user_data;
47
+   data->module = module;
48
+
49
+   return 0;
50
+}
51
+
52
+static int module_device_manager_load(struct module *module)
53
+{
54
+   return 0;
55
+}
56
+
57
+DEFINE_MODULE_INFO(module_device_manager) = {
58
+   .name = "module-device-manager",
59
+   .load_once = true,
60
+   .prepare = module_device_manager_prepare,
61
+   .load = module_device_manager_load,
62
+   .properties = &SPA_DICT_INIT_ARRAY(module_device_manager_info),
63
+   .data_size = sizeof(struct module_device_manager_data),
64
+};
65
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-device-restore.c Added
495
 
1
@@ -0,0 +1,493 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <pipewire/pipewire.h>
7
+
8
+#include "../module.h"
9
+
10
+/** \page page_pulse_module_device_restore Device restore extension
11
+ *
12
+ * ## Module Name
13
+ *
14
+ * `module-device-restore`
15
+ *
16
+ * ## Module Options
17
+ *
18
+ * @pulse_module_options@
19
+ */
20
+
21
+#define EXT_DEVICE_RESTORE_VERSION 1
22
+
23
+enum {
24
+    SUBCOMMAND_TEST,
25
+    SUBCOMMAND_SUBSCRIBE,
26
+    SUBCOMMAND_EVENT,
27
+    SUBCOMMAND_READ_FORMATS_ALL,
28
+    SUBCOMMAND_READ_FORMATS,
29
+    SUBCOMMAND_SAVE_FORMATS
30
+};
31
+
32
+#include <stdbool.h>
33
+#include <stdint.h>
34
+#include <stdio.h>
35
+#include <stdlib.h>
36
+#include <string.h>
37
+
38
+#include <spa/pod/builder.h>
39
+#include <spa/utils/defs.h>
40
+#include <spa/utils/dict.h>
41
+#include <spa/utils/string.h>
42
+#include <spa/utils/json.h>
43
+#include <spa/param/audio/format.h>
44
+#include <spa/param/props.h>
45
+
46
+#include <pipewire/log.h>
47
+#include <pipewire/properties.h>
48
+
49
+#include "../client.h"
50
+#include "../collect.h"
51
+#include "../commands.h"
52
+#include "../defs.h"
53
+#include "../extension.h"
54
+#include "../format.h"
55
+#include "../manager.h"
56
+#include "../message.h"
57
+#include "../operation.h"
58
+#include "../reply.h"
59
+#include "../volume.h"
60
+
61
+static const char *const pulse_module_options =
62
+   "restore_port=<Save/restore port?> "
63
+   "restore_volume=<Save/restore volumes?> "
64
+   "restore_muted=<Save/restore muted states?> "
65
+   "restore_formats=<Save/restore saved formats?>";
66
+
67
+#define NAME "device-restore"
68
+
69
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
70
+#define PW_LOG_TOPIC_DEFAULT mod_topic
71
+
72
+struct module_device_restore_data {
73
+   struct module *module;
74
+
75
+   struct spa_list subscribed;
76
+};
77
+
78
+static const struct spa_dict_item module_device_restore_info = {
79
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
80
+   { PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute state of devices" },
81
+   { PW_KEY_MODULE_USAGE, pulse_module_options },
82
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
83
+};
84
+
85
+#define DEVICE_TYPE_SINK   0
86
+#define DEVICE_TYPE_SOURCE 1
87
+
88
+static int do_extension_device_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
89
+{
90
+   struct message *reply;
91
+
92
+   reply = reply_new(client, tag);
93
+   message_put(reply,
94
+           TAG_U32, EXT_DEVICE_RESTORE_VERSION,
95
+           TAG_INVALID);
96
+
97
+   return client_queue_message(client, reply);
98
+}
99
+
100
+struct subscribe {
101
+   struct spa_list link;
102
+   struct module_device_restore_data *data;
103
+
104
+   struct client *client;
105
+   struct spa_hook listener;
106
+
107
+   struct spa_hook manager_listener;
108
+};
109
+
110
+static void remove_subscribe(struct subscribe *s)
111
+{
112
+   spa_list_remove(&s->link);
113
+   spa_hook_remove(&s->listener);
114
+   spa_hook_remove(&s->manager_listener);
115
+   free(s);
116
+}
117
+
118
+static void emit_event(struct subscribe *s, uint32_t type, uint32_t idx)
119
+{
120
+   struct client *client = s->client;
121
+   struct message *msg = message_alloc(client->impl, -1, 0);
122
+
123
+   pw_log_info("%s EVENT index:%u name:%s %d/%d", client->name,
124
+           s->data->module->index, s->data->module->info->name, type, idx);
125
+
126
+   message_put(msg,
127
+       TAG_U32, COMMAND_EXTENSION,
128
+       TAG_U32, 0,
129
+       TAG_U32, s->data->module->index,
130
+       TAG_STRING, s->data->module->info->name,
131
+       TAG_U32, SUBCOMMAND_EVENT,
132
+       TAG_U32, type,
133
+       TAG_U32, idx,
134
+       TAG_INVALID);
135
+
136
+   client_queue_message(client, msg);
137
+}
138
+
139
+static void module_client_disconnect(void *data)
140
+{
141
+   struct subscribe *s = data;
142
+   remove_subscribe(s);
143
+}
144
+
145
+static const struct client_events module_client_events = {
146
+   VERSION_CLIENT_EVENTS,
147
+   .disconnect = module_client_disconnect,
148
+};
149
+
150
+static void manager_updated(void *data, struct pw_manager_object *object)
151
+{
152
+   struct subscribe *s = data;
153
+   uint32_t i;
154
+
155
+   if (!pw_manager_object_is_sink(object))
156
+       return;
157
+
158
+   for (i = 0; i < object->n_params; i++) {
159
+       if (object->paramsi.id == SPA_PARAM_EnumFormat &&
160
+           object->paramsi.user != 0)
161
+           emit_event(s, DEVICE_TYPE_SINK, object->index);
162
+   }
163
+}
164
+
165
+struct pw_manager_events manager_events = {
166
+   PW_VERSION_MANAGER_EVENTS,
167
+   .added = manager_updated,
168
+   .updated = manager_updated,
169
+};
170
+
171
+static struct subscribe *add_subscribe(struct module_device_restore_data *data, struct client *c)
172
+{
173
+   struct subscribe *s;
174
+   s = calloc(1, sizeof(*s));
175
+   if (s == NULL)
176
+       return NULL;
177
+   s->data = data;
178
+   s->client = c;
179
+   client_add_listener(c, &s->listener, &module_client_events, s);
180
+   spa_list_append(&data->subscribed, &s->link);
181
+
182
+   pw_manager_add_listener(c->manager, &s->manager_listener,
183
+           &manager_events, s);
184
+   return s;
185
+}
186
+
187
+static struct subscribe *find_subscribe(struct module_device_restore_data *data, struct client *c)
188
+{
189
+   struct subscribe *s;
190
+   spa_list_for_each(s, &data->subscribed, link) {
191
+       if (s->client == c)
192
+           return s;
193
+   }
194
+   return NULL;
195
+}
196
+
197
+static int do_extension_device_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
198
+{
199
+   struct module_device_restore_data * const d = module->user_data;
200
+   int res;
201
+   bool enabled;
202
+   struct subscribe *s;
203
+
204
+   if ((res = message_get(m,
205
+           TAG_BOOLEAN, &enabled,
206
+           TAG_INVALID)) < 0)
207
+       return -EPROTO;
208
+
209
+   s = find_subscribe(d, client);
210
+   if (enabled) {
211
+       if (s == NULL)
212
+           s = add_subscribe(d, client);
213
+       if (s == NULL)
214
+           return -errno;
215
+   } else {
216
+       if (s != NULL)
217
+           remove_subscribe(s);
218
+   }
219
+   return reply_simple_ack(client, tag);
220
+}
221
+
222
+struct format_data {
223
+        struct client *client;
224
+        struct message *reply;
225
+};
226
+
227
+static int do_sink_read_format(void *data, struct pw_manager_object *o)
228
+{
229
+   struct format_data *d = data;
230
+   struct pw_manager_param *p;
231
+   struct format_info info32;
232
+   uint32_t i, n_info = 0;
233
+
234
+   if (!pw_manager_object_is_sink(o))
235
+       return 0;
236
+
237
+   spa_list_for_each(p, &o->param_list, link) {
238
+       uint32_t index = 0;
239
+
240
+       if (p->id != SPA_PARAM_EnumFormat)
241
+           continue;
242
+
243
+       while (n_info < SPA_N_ELEMENTS(info)) {
244
+           spa_zero(infon_info);
245
+           if (format_info_from_param(&infon_info, p->param, index++) < 0)
246
+               break;
247
+           if (infon_info.encoding == ENCODING_ANY) {
248
+               format_info_clear(&infon_info);
249
+               continue;
250
+           }
251
+           n_info++;
252
+       }
253
+   }
254
+   message_put(d->reply,
255
+       TAG_U32, DEVICE_TYPE_SINK,
256
+       TAG_U32, o->index,          /* sink index */
257
+       TAG_U8, n_info,             /* n_formats */
258
+       TAG_INVALID);
259
+   for (i = 0; i < n_info; i++) {
260
+       message_put(d->reply,
261
+           TAG_FORMAT_INFO, &infoi,
262
+           TAG_INVALID);
263
+       format_info_clear(&infoi);
264
+   }
265
+   return 0;
266
+}
267
+
268
+static int do_extension_device_restore_read_formats_all(struct module *module, struct client *client,
269
+       uint32_t command, uint32_t tag, struct message *m)
270
+{
271
+   struct pw_manager *manager = client->manager;
272
+   struct format_data data;
273
+
274
+   spa_zero(data);
275
+   data.client = client;
276
+   data.reply = reply_new(client, tag);
277
+
278
+   pw_manager_for_each_object(manager, do_sink_read_format, &data);
279
+
280
+   return client_queue_message(client, data.reply);
281
+}
282
+
283
+static int do_extension_device_restore_read_formats(struct module *module, struct client *client,
284
+       uint32_t command, uint32_t tag, struct message *m)
285
+{
286
+   struct pw_manager *manager = client->manager;
287
+   struct format_data data;
288
+   uint32_t type, sink_index;
289
+   struct selector sel;
290
+   struct pw_manager_object *o;
291
+   int res;
292
+
293
+   if ((res = message_get(m,
294
+           TAG_U32, &type,
295
+           TAG_U32, &sink_index,
296
+           TAG_INVALID)) < 0)
297
+       return -EPROTO;
298
+
299
+   if (type != DEVICE_TYPE_SINK) {
300
+       pw_log_info("Device format reading is only supported on sinks");
301
+       return -ENOTSUP;
302
+   }
303
+
304
+   spa_zero(sel);
305
+   sel.index = sink_index;
306
+   sel.type = pw_manager_object_is_sink;
307
+
308
+   o = select_object(manager, &sel);
309
+        if (o == NULL)
310
+       return -ENOENT;
311
+
312
+   spa_zero(data);
313
+   data.client = client;
314
+   data.reply = reply_new(client, tag);
315
+
316
+   do_sink_read_format(&data, o);
317
+
318
+   return client_queue_message(client, data.reply);
319
+}
320
+
321
+static int set_card_codecs(struct pw_manager_object *o, uint32_t port_index,
322
+       uint32_t device_id, uint32_t n_codecs, uint32_t *codecs)
323
+{
324
+   char buf1024;
325
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
326
+   struct spa_pod_frame f2;
327
+   struct spa_pod *param;
328
+
329
+   if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
330
+       return -EACCES;
331
+
332
+   if (o->proxy == NULL)
333
+       return -ENOENT;
334
+
335
+   spa_pod_builder_push_object(&b, &f0,
336
+           SPA_TYPE_OBJECT_ParamRoute, SPA_PARAM_Route);
337
+   spa_pod_builder_add(&b,
338
+           SPA_PARAM_ROUTE_index, SPA_POD_Int(port_index),
339
+           SPA_PARAM_ROUTE_device, SPA_POD_Int(device_id),
340
+           0);
341
+   spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_props, 0);
342
+   spa_pod_builder_push_object(&b, &f1,
343
+           SPA_TYPE_OBJECT_Props,  SPA_PARAM_Props);
344
+   spa_pod_builder_add(&b,
345
+           SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t),
346
+               SPA_TYPE_Id, n_codecs, codecs), 0);
347
+   spa_pod_builder_pop(&b, &f1);
348
+   spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_save, 0);
349
+   spa_pod_builder_bool(&b, true);
350
+   param = spa_pod_builder_pop(&b, &f0);
351
+
352
+   pw_device_set_param((struct pw_device*)o->proxy,
353
+           SPA_PARAM_Route, 0, param);
354
+   return 0;
355
+}
356
+
357
+static int set_node_codecs(struct pw_manager_object *o, uint32_t n_codecs, uint32_t *codecs)
358
+{
359
+   char buf1024;
360
+   struct spa_pod_builder b;
361
+   struct spa_pod *param;
362
+
363
+   if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
364
+       return -EACCES;
365
+
366
+   if (o->proxy == NULL)
367
+       return -ENOENT;
368
+
369
+   spa_pod_builder_init(&b, buf, sizeof(buf));
370
+   param = spa_pod_builder_add_object(&b,
371
+           SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
372
+           SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t),
373
+               SPA_TYPE_Id, n_codecs, codecs));
374
+
375
+   pw_node_set_param((struct pw_node*)o->proxy,
376
+           SPA_PARAM_Props, 0, param);
377
+
378
+   return 0;
379
+}
380
+
381
+static int do_extension_device_restore_save_formats(struct module *module, struct client *client,
382
+       uint32_t command, uint32_t tag, struct message *m)
383
+{
384
+   struct pw_manager *manager = client->manager;
385
+   struct selector sel;
386
+   struct pw_manager_object *o, *card = NULL;
387
+   struct pw_node_info *info;
388
+   int res;
389
+   uint32_t type, sink_index;
390
+   uint8_t i, n_formats;
391
+   uint32_t n_codecs = 0, codec, iec958codecs32;
392
+   struct device_info dev_info;
393
+
394
+   if ((res = message_get(m,
395
+           TAG_U32, &type,
396
+           TAG_U32, &sink_index,
397
+           TAG_U8, &n_formats,
398
+           TAG_INVALID)) < 0)
399
+       return -EPROTO;
400
+   if (n_formats < 1)
401
+       return -EPROTO;
402
+
403
+   if (type != DEVICE_TYPE_SINK)
404
+       return -ENOTSUP;
405
+
406
+   for (i = 0; i < n_formats; ++i) {
407
+       struct format_info format;
408
+       spa_zero(format);
409
+       if (message_get(m,
410
+               TAG_FORMAT_INFO, &format,
411
+               TAG_INVALID) < 0)
412
+           return -EPROTO;
413
+
414
+       codec = format_encoding2id(format.encoding);
415
+       if (codec != SPA_ID_INVALID && n_codecs < SPA_N_ELEMENTS(iec958codecs))
416
+           iec958codecsn_codecs++ = codec;
417
+
418
+       format_info_clear(&format);
419
+   }
420
+   if (n_codecs == 0)
421
+       return -ENOTSUP;
422
+
423
+   spa_zero(sel);
424
+   sel.index = sink_index;
425
+   sel.type = pw_manager_object_is_sink;
426
+
427
+   o = select_object(manager, &sel);
428
+   if (o == NULL || (info = o->info) == NULL || info->props == NULL)
429
+       return -ENOENT;
430
+
431
+   get_device_info(o, &dev_info, SPA_DIRECTION_INPUT, false);
432
+
433
+   if (dev_info.card_id != SPA_ID_INVALID) {
434
+       struct selector sel = { .id = dev_info.card_id, .type = pw_manager_object_is_card, };
435
+       card = select_object(manager, &sel);
436
+   }
437
+   if (card != NULL && dev_info.active_port != SPA_ID_INVALID) {
438
+       res = set_card_codecs(card, dev_info.active_port,
439
+               dev_info.device, n_codecs, iec958codecs);
440
+   } else {
441
+       res = set_node_codecs(o, n_codecs, iec958codecs);
442
+   }
443
+   if (res < 0)
444
+       return res;
445
+
446
+   return reply_simple_ack(client, tag);
447
+}
448
+
449
+static const struct extension module_device_restore_extension = {
450
+   { "TEST", SUBCOMMAND_TEST, do_extension_device_restore_test, },
451
+   { "SUBSCRIBE", SUBCOMMAND_SUBSCRIBE, do_extension_device_restore_subscribe, },
452
+   { "EVENT", SUBCOMMAND_EVENT, },
453
+   { "READ_FORMATS_ALL", SUBCOMMAND_READ_FORMATS_ALL, do_extension_device_restore_read_formats_all, },
454
+   { "READ_FORMATS", SUBCOMMAND_READ_FORMATS, do_extension_device_restore_read_formats, },
455
+   { "SAVE_FORMATS", SUBCOMMAND_SAVE_FORMATS, do_extension_device_restore_save_formats, },
456
+   { NULL },
457
+};
458
+
459
+static int module_device_restore_prepare(struct module * const module)
460
+{
461
+   PW_LOG_TOPIC_INIT(mod_topic);
462
+
463
+   struct module_device_restore_data * const data = module->user_data;
464
+   data->module = module;
465
+
466
+   return 0;
467
+}
468
+
469
+static int module_device_restore_load(struct module *module)
470
+{
471
+   struct module_device_restore_data * const data = module->user_data;
472
+   spa_list_init(&data->subscribed);
473
+   return 0;
474
+}
475
+static int module_device_restore_unload(struct module *module)
476
+{
477
+   struct module_device_restore_data * const data = module->user_data;
478
+   struct subscribe *s;
479
+
480
+   spa_list_consume(s, &data->subscribed, link)
481
+       remove_subscribe(s);
482
+   return 0;
483
+}
484
+
485
+DEFINE_MODULE_INFO(module_device_restore) = {
486
+   .name = "module-device-restore",
487
+   .load_once = true,
488
+   .prepare = module_device_restore_prepare,
489
+   .load = module_device_restore_load,
490
+   .unload = module_device_restore_unload,
491
+   .extension = module_device_restore_extension,
492
+   .properties = &SPA_DICT_INIT_ARRAY(module_device_restore_info),
493
+   .data_size = sizeof(struct module_device_restore_data),
494
+};
495
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/modules/module-echo-cancel.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-echo-cancel.c Changed
79
 
1
@@ -176,7 +176,7 @@
2
 static int rename_geometry(struct pw_properties *props, const char *pa_key, const char *pw_key)
3
 {
4
    const char *str;
5
-   int len;
6
+   int i = 0, len;
7
    char *args;
8
    size_t size;
9
    FILE *f;
10
@@ -189,19 +189,26 @@
11
    if ((f = open_memstream(&args, &size)) == NULL)
12
        return -errno;
13
 
14
-   fprintf(f, " ");
15
+   fprintf(f, "");
16
    while (true) {
17
        float p3;
18
+       char ps064, ps164, ps264;
19
        if ((len = parse_point(&str, p)) < 0)
20
            break;
21
 
22
-       fprintf(f, " %f %f %f  ", p0, p1, p2);
23
+       pw_log_info("Got mic #%d position: (%g, %g, %g)", i, p0, p1, p2);
24
+
25
+       fprintf(f, "%s  %s, %s, %s ", i == 0 ?  "" : ",",
26
+               spa_dtoa(ps0, sizeof(ps0), p0),
27
+               spa_dtoa(ps1, sizeof(ps1), p1),
28
+               spa_dtoa(ps2, sizeof(ps2), p2));
29
        str += len;
30
        if (*str != ',')
31
            break;
32
        str++;
33
+       i++;
34
    }
35
-   fprintf(f, "");
36
+   fprintf(f, " ");
37
    fclose(f);
38
 
39
    pw_properties_set(props, pw_key, args);
40
@@ -216,6 +223,7 @@
41
    const char *str;
42
    int res;
43
    float f3;
44
+   char fs064, fs164, fs264;
45
 
46
    if ((str = pw_properties_get(props, pa_key)) == NULL)
47
        return 0;
48
@@ -225,7 +233,12 @@
49
    if ((res = parse_point(&str, f)) < 0)
50
        return res;
51
 
52
-   pw_properties_setf(props, pw_key, " %f %f %f ", f0, f1, f2);
53
+   pw_log_info("Got target direction: (%g, %g, %g)", f0, f1, f2);
54
+
55
+   pw_properties_setf(props, pw_key, " %s, %s, %s ",
56
+               spa_dtoa(fs0, sizeof(fs0), f0),
57
+               spa_dtoa(fs1, sizeof(fs1), f1),
58
+               spa_dtoa(fs2, sizeof(fs2), f2));
59
    pw_properties_set(props, pa_key, NULL);
60
    return 0;
61
 }
62
@@ -319,8 +332,14 @@
63
            rename_bool_prop(aec_props, "extended_filter", "webrtc.extended_filter");
64
            rename_bool_prop(aec_props, "experimental_agc", "webrtc.experimental_agc");
65
            rename_bool_prop(aec_props, "beamforming", "webrtc.beamforming");
66
-           rename_geometry(aec_props, "mic_geometry", "webrtc.mic-geometry");
67
-           rename_direction(aec_props, "target_direction", "webrtc.target-direction");
68
+           if ((res = rename_geometry(aec_props, "mic_geometry", "webrtc.mic-geometry")) < 0) {
69
+               pw_log_warn("failed to parse mic_geometry: %s", spa_strerror(res));
70
+               goto out;
71
+           }
72
+           if ((res = rename_direction(aec_props, "target_direction", "webrtc.target-direction")) < 0) {
73
+               pw_log_warn("failed to parse target_direction: %s", spa_strerror(res));
74
+               goto out;
75
+           }
76
        }
77
        pw_properties_set(props, "aec_args", NULL);
78
    }
79
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/modules/module-null-sink.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-null-sink.c Changed
10
 
1
@@ -196,6 +196,8 @@
2
 
3
    if (pw_properties_get(props, "monitor.channel-volumes") == NULL)
4
        pw_properties_set(props, "monitor.channel-volumes", "true");
5
+   if (pw_properties_get(props, "monitor.passthrough") == NULL)
6
+       pw_properties_set(props, "monitor.passthrough", "true");
7
 
8
    return 0;
9
 }
10
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-stream-restore.c Added
469
 
1
@@ -0,0 +1,467 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <pipewire/pipewire.h>
7
+
8
+#include "../module.h"
9
+#include "../commands.h"
10
+
11
+/** \page page_pulse_module_stream_restore Stream restore extension
12
+ *
13
+ * ## Module Name
14
+ *
15
+ * `module-stream-restore`
16
+ *
17
+ * ## Module Options
18
+ *
19
+ * @pulse_module_options@
20
+ */
21
+
22
+static const char *const pulse_module_options =
23
+   "restore_device=<Save/restore sinks/sources?> "
24
+   "restore_volume=<Save/restore volumes?> "
25
+   "restore_muted=<Save/restore muted states?> "
26
+   "on_hotplug=<This argument is obsolete, please remove it from configuration> "
27
+   "on_rescue=<This argument is obsolete, please remove it from configuration> "
28
+   "fallback_table=<filename>";
29
+
30
+#define NAME "stream-restore"
31
+
32
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
33
+#define PW_LOG_TOPIC_DEFAULT mod_topic
34
+
35
+struct module_stream_restore_data {
36
+   struct module *module;
37
+
38
+   struct spa_list subscribed;
39
+};
40
+
41
+static const struct spa_dict_item module_stream_restore_info = {
42
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
43
+   { PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute/device state of streams" },
44
+   { PW_KEY_MODULE_USAGE, pulse_module_options },
45
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
46
+};
47
+
48
+#define EXT_STREAM_RESTORE_VERSION 1
49
+
50
+enum {
51
+    SUBCOMMAND_TEST,
52
+    SUBCOMMAND_READ,
53
+    SUBCOMMAND_WRITE,
54
+    SUBCOMMAND_DELETE,
55
+    SUBCOMMAND_SUBSCRIBE,
56
+    SUBCOMMAND_EVENT
57
+};
58
+
59
+#include <stdbool.h>
60
+#include <stdint.h>
61
+#include <stdio.h>
62
+#include <stdlib.h>
63
+#include <string.h>
64
+
65
+#include <spa/utils/defs.h>
66
+#include <spa/utils/dict.h>
67
+#include <spa/utils/string.h>
68
+#include <spa/utils/json.h>
69
+#include <pipewire/log.h>
70
+#include <pipewire/properties.h>
71
+
72
+#include "../client.h"
73
+#include "../defs.h"
74
+#include "../extension.h"
75
+#include "../format.h"
76
+#include "../manager.h"
77
+#include "../message.h"
78
+#include "../remap.h"
79
+#include "../reply.h"
80
+#include "../volume.h"
81
+
82
+PW_LOG_TOPIC_EXTERN(pulse_ext_stream_restore);
83
+#undef PW_LOG_TOPIC_DEFAULT
84
+#define PW_LOG_TOPIC_DEFAULT pulse_ext_stream_restore
85
+
86
+static int do_extension_stream_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
87
+{
88
+   struct message *reply;
89
+
90
+   reply = reply_new(client, tag);
91
+   message_put(reply,
92
+           TAG_U32, EXT_STREAM_RESTORE_VERSION,
93
+           TAG_INVALID);
94
+
95
+   return client_queue_message(client, reply);
96
+}
97
+
98
+static int key_from_name(const char *name, char *key, size_t maxlen)
99
+{
100
+   const char *media_class, *select, *str;
101
+
102
+   if (spa_strstartswith(name, "sink-input-"))
103
+       media_class = "Output/Audio";
104
+   else if (spa_strstartswith(name, "source-output-"))
105
+       media_class = "Input/Audio";
106
+   else
107
+       return -1;
108
+
109
+   if ((str = strstr(name, "-by-media-role:")) != NULL) {
110
+       const struct str_map *map;
111
+       str += strlen("-by-media-role:");
112
+       map = str_map_find(media_role_map, NULL, str);
113
+       str = map ? map->pw_str : str;
114
+       select = "media.role";
115
+   }
116
+   else if ((str = strstr(name, "-by-application-id:")) != NULL) {
117
+       str += strlen("-by-application-id:");
118
+       select = "application.id";
119
+   }
120
+   else if ((str = strstr(name, "-by-application-name:")) != NULL) {
121
+       str += strlen("-by-application-name:");
122
+       select = "application.name";
123
+   }
124
+   else if ((str = strstr(name, "-by-media-name:")) != NULL) {
125
+       str += strlen("-by-media-name:");
126
+       select = "media.name";
127
+   } else
128
+       return -1;
129
+
130
+   snprintf(key, maxlen, "restore.stream.%s.%s:%s",
131
+               media_class, select, str);
132
+   return 0;
133
+}
134
+
135
+static int key_to_name(const char *key, char *name, size_t maxlen)
136
+{
137
+   const char *type, *select, *str;
138
+
139
+   if (spa_strstartswith(key, "restore.stream.Output/Audio."))
140
+       type = "sink-input";
141
+   else if (spa_strstartswith(key, "restore.stream.Input/Audio."))
142
+       type = "source-output";
143
+   else
144
+       type = "stream";
145
+
146
+   if ((str = strstr(key, ".media.role:")) != NULL) {
147
+       const struct str_map *map;
148
+       str += strlen(".media.role:");
149
+       map = str_map_find(media_role_map, str, NULL);
150
+       select = "media-role";
151
+       str = map ? map->pa_str : str;
152
+   }
153
+   else if ((str = strstr(key, ".application.id:")) != NULL) {
154
+       str += strlen(".application.id:");
155
+       select = "application-id";
156
+   }
157
+   else if ((str = strstr(key, ".application.name:")) != NULL) {
158
+       str += strlen(".application.name:");
159
+       select = "application-name";
160
+   }
161
+   else if ((str = strstr(key, ".media.name:")) != NULL) {
162
+       str += strlen(".media.name:");
163
+       select = "media-name";
164
+   }
165
+   else
166
+       return -1;
167
+
168
+   snprintf(name, maxlen, "%s-by-%s:%s", type, select, str);
169
+   return 0;
170
+
171
+}
172
+
173
+static int do_extension_stream_restore_read(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
174
+{
175
+   struct message *reply;
176
+   const struct spa_dict_item *item;
177
+
178
+   reply = reply_new(client, tag);
179
+
180
+   spa_dict_for_each(item, &client->routes->dict) {
181
+       struct spa_json it3;
182
+       const char *value;
183
+       char name1024, key128;
184
+       char device_name1024 = "\0";
185
+       bool mute = false;
186
+       struct volume vol = VOLUME_INIT;
187
+       struct channel_map map = CHANNEL_MAP_INIT;
188
+       float volume = 0.0f;
189
+
190
+       if (key_to_name(item->key, name, sizeof(name)) < 0)
191
+           continue;
192
+
193
+       pw_log_debug("%s -> %s: %s", item->key, name, item->value);
194
+
195
+       spa_json_init(&it0, item->value, strlen(item->value));
196
+       if (spa_json_enter_object(&it0, &it1) <= 0)
197
+           continue;
198
+
199
+       while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
200
+           if (spa_streq(key, "volume")) {
201
+               if (spa_json_get_float(&it1, &volume) <= 0)
202
+                   continue;
203
+           }
204
+           else if (spa_streq(key, "mute")) {
205
+               if (spa_json_get_bool(&it1, &mute) <= 0)
206
+                   continue;
207
+           }
208
+           else if (spa_streq(key, "volumes")) {
209
+               vol = VOLUME_INIT;
210
+               if (spa_json_enter_array(&it1, &it2) <= 0)
211
+                   continue;
212
+
213
+               for (vol.channels = 0; vol.channels < CHANNELS_MAX; vol.channels++) {
214
+                   if (spa_json_get_float(&it2, &vol.valuesvol.channels) <= 0)
215
+                       break;
216
+               }
217
+           }
218
+           else if (spa_streq(key, "channels")) {
219
+               if (spa_json_enter_array(&it1, &it2) <= 0)
220
+                   continue;
221
+
222
+               for (map.channels = 0; map.channels < CHANNELS_MAX; map.channels++) {
223
+                   char chname16;
224
+                   if (spa_json_get_string(&it2, chname, sizeof(chname)) <= 0)
225
+                       break;
226
+                   map.mapmap.channels = channel_name2id(chname);
227
+               }
228
+           }
229
+           else if (spa_streq(key, "target-node")) {
230
+               if (spa_json_get_string(&it1, device_name, sizeof(device_name)) <= 0)
231
+                   continue;
232
+           }
233
+           else if (spa_json_next(&it1, &value) <= 0)
234
+               break;
235
+       }
236
+       message_put(reply,
237
+           TAG_STRING, name,
238
+           TAG_CHANNEL_MAP, &map,
239
+           TAG_CVOLUME, &vol,
240
+           TAG_STRING, device_name0 ? device_name : NULL,
241
+           TAG_BOOLEAN, mute,
242
+           TAG_INVALID);
243
+   }
244
+
245
+   return client_queue_message(client, reply);
246
+}
247
+
248
+static int do_extension_stream_restore_write(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
249
+{
250
+   int res;
251
+   uint32_t mode;
252
+   bool apply;
253
+
254
+   if ((res = message_get(m,
255
+           TAG_U32, &mode,
256
+           TAG_BOOLEAN, &apply,
257
+           TAG_INVALID)) < 0)
258
+       return -EPROTO;
259
+
260
+   while (m->offset < m->length) {
261
+       const char *name, *device_name = NULL;
262
+       struct channel_map map;
263
+       struct volume vol;
264
+       bool mute = false;
265
+       uint32_t i;
266
+       FILE *f;
267
+       char *ptr;
268
+       size_t size;
269
+       char key1024, buf128;
270
+
271
+       spa_zero(map);
272
+       spa_zero(vol);
273
+
274
+       if (message_get(m,
275
+               TAG_STRING, &name,
276
+               TAG_CHANNEL_MAP, &map,
277
+               TAG_CVOLUME, &vol,
278
+               TAG_STRING, &device_name,
279
+               TAG_BOOLEAN, &mute,
280
+               TAG_INVALID) < 0)
281
+           return -EPROTO;
282
+
283
+       if (name == NULL || name0 == '\0')
284
+           return -EPROTO;
285
+
286
+       if ((f = open_memstream(&ptr, &size)) == NULL)
287
+           return -errno;
288
+
289
+       fprintf(f, "{");
290
+       fprintf(f, " \"mute\": %s", mute ? "true" : "false");
291
+       if (vol.channels > 0) {
292
+           fprintf(f, ", \"volumes\": ");
293
+           for (i = 0; i < vol.channels; i++)
294
+               fprintf(f, "%s%s", (i == 0 ? " ":", "),
295
+                       spa_json_format_float(buf, sizeof(buf), vol.valuesi));
296
+           fprintf(f, " ");
297
+       }
298
+       if (map.channels > 0) {
299
+           fprintf(f, ", \"channels\": ");
300
+           for (i = 0; i < map.channels; i++)
301
+               fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), channel_id2name(map.mapi));
302
+           fprintf(f, " ");
303
+       }
304
+       if (device_name != NULL && device_name0 &&
305
+           (client->default_source == NULL || !spa_streq(device_name, client->default_source)) &&
306
+           (client->default_sink == NULL || !spa_streq(device_name, client->default_sink)))
307
+           fprintf(f, ", \"target-node\": \"%s\"", device_name);
308
+       fprintf(f, " }");
309
+       fclose(f);
310
+       if (key_from_name(name, key, sizeof(key)) >= 0) {
311
+           pw_log_debug("%s -> %s: %s", name, key, ptr);
312
+           if ((res = pw_manager_set_metadata(client->manager,
313
+                           client->metadata_routes,
314
+                           PW_ID_CORE, key, "Spa:String:JSON", "%s", ptr)) < 0)
315
+               pw_log_warn("failed to set metadata %s = %s, %s", key, ptr, strerror(-res));
316
+       }
317
+       free(ptr);
318
+   }
319
+
320
+   return reply_simple_ack(client, tag);
321
+}
322
+
323
+static int do_extension_stream_restore_delete(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
324
+{
325
+   return reply_simple_ack(client, tag);
326
+}
327
+
328
+struct subscribe {
329
+   struct spa_list link;
330
+   struct module_stream_restore_data *data;
331
+
332
+   struct client *client;
333
+   struct spa_hook listener;
334
+};
335
+
336
+static void remove_subscribe(struct subscribe *s)
337
+{
338
+   spa_list_remove(&s->link);
339
+   spa_hook_remove(&s->listener);
340
+   free(s);
341
+}
342
+
343
+static void module_client_disconnect(void *data)
344
+{
345
+   struct subscribe *s = data;
346
+   remove_subscribe(s);
347
+}
348
+
349
+static void module_client_routes_changed(void *data)
350
+{
351
+   struct subscribe *s = data;
352
+   struct client *client = s->client;
353
+   struct message *msg = message_alloc(client->impl, -1, 0);
354
+
355
+   pw_log_info("%s EVENT index:%u name:%s", client->name,
356
+           s->data->module->index, s->data->module->info->name);
357
+
358
+   message_put(msg,
359
+       TAG_U32, COMMAND_EXTENSION,
360
+       TAG_U32, 0,
361
+       TAG_U32, s->data->module->index,
362
+       TAG_STRING, s->data->module->info->name,
363
+       TAG_U32, SUBCOMMAND_EVENT,
364
+       TAG_INVALID);
365
+
366
+   client_queue_message(client, msg);
367
+}
368
+
369
+static const struct client_events module_client_events = {
370
+   VERSION_CLIENT_EVENTS,
371
+   .disconnect = module_client_disconnect,
372
+   .routes_changed = module_client_routes_changed,
373
+};
374
+
375
+static struct subscribe *add_subscribe(struct module_stream_restore_data *data, struct client *c)
376
+{
377
+   struct subscribe *s;
378
+   s = calloc(1, sizeof(*s));
379
+   if (s == NULL)
380
+       return NULL;
381
+   s->data = data;
382
+   s->client = c;
383
+   client_add_listener(c, &s->listener, &module_client_events, s);
384
+   spa_list_append(&data->subscribed, &s->link);
385
+   return s;
386
+}
387
+
388
+static struct subscribe *find_subscribe(struct module_stream_restore_data *data, struct client *c)
389
+{
390
+   struct subscribe *s;
391
+   spa_list_for_each(s, &data->subscribed, link) {
392
+       if (s->client == c)
393
+           return s;
394
+   }
395
+   return NULL;
396
+}
397
+
398
+static int do_extension_stream_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
399
+{
400
+   struct module_stream_restore_data * const d = module->user_data;
401
+   int res;
402
+   bool enabled;
403
+   struct subscribe *s;
404
+
405
+   if ((res = message_get(m,
406
+           TAG_BOOLEAN, &enabled,
407
+           TAG_INVALID)) < 0)
408
+       return -EPROTO;
409
+
410
+   s = find_subscribe(d, client);
411
+   if (enabled) {
412
+       if (s == NULL)
413
+           s = add_subscribe(d, client);
414
+       if (s == NULL)
415
+           return -errno;
416
+   } else {
417
+       if (s != NULL)
418
+           remove_subscribe(s);
419
+   }
420
+   return reply_simple_ack(client, tag);
421
+}
422
+
423
+static const struct extension module_stream_restore_extension = {
424
+   { "TEST", SUBCOMMAND_TEST, do_extension_stream_restore_test, },
425
+   { "READ", SUBCOMMAND_READ, do_extension_stream_restore_read, },
426
+   { "WRITE", SUBCOMMAND_WRITE, do_extension_stream_restore_write, },
427
+   { "DELETE", SUBCOMMAND_DELETE, do_extension_stream_restore_delete, },
428
+   { "SUBSCRIBE", SUBCOMMAND_SUBSCRIBE, do_extension_stream_restore_subscribe, },
429
+   { "EVENT", SUBCOMMAND_EVENT, },
430
+   { NULL, },
431
+};
432
+
433
+static int module_stream_restore_prepare(struct module * const module)
434
+{
435
+   PW_LOG_TOPIC_INIT(mod_topic);
436
+
437
+   struct module_stream_restore_data * const data = module->user_data;
438
+   data->module = module;
439
+
440
+   return 0;
441
+}
442
+
443
+static int module_stream_restore_load(struct module *module)
444
+{
445
+   struct module_stream_restore_data * const data = module->user_data;
446
+   spa_list_init(&data->subscribed);
447
+   return 0;
448
+}
449
+static int module_stream_restore_unload(struct module *module)
450
+{
451
+   struct module_stream_restore_data * const data = module->user_data;
452
+   struct subscribe *s;
453
+
454
+   spa_list_consume(s, &data->subscribed, link)
455
+       remove_subscribe(s);
456
+   return 0;
457
+}
458
+
459
+DEFINE_MODULE_INFO(module_stream_restore) = {
460
+   .name = "module-stream-restore",
461
+   .load_once = true,
462
+   .prepare = module_stream_restore_prepare,
463
+   .load = module_stream_restore_load,
464
+   .unload = module_stream_restore_unload,
465
+   .extension = module_stream_restore_extension,
466
+   .properties = &SPA_DICT_INIT_ARRAY(module_stream_restore_info),
467
+   .data_size = sizeof(struct module_stream_restore_data),
468
+};
469
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c Changed
20
 
1
@@ -32,6 +32,7 @@
2
    "sink=<name of the remote sink> "
3
    "sink_name=<name for the local sink> "
4
    "sink_properties=<properties for the local sink> "
5
+   "reconnect_interval_ms=<interval to try reconnects, 0 or omitted if disabled> "
6
    "format=<sample format> "
7
    "channels=<number of channels> "
8
    "rate=<sample rate> "
9
@@ -181,6 +182,10 @@
10
    }
11
    audioinfo_to_properties(&info, stream_props);
12
 
13
+   if ((str = pw_properties_get(props, "reconnect_interval_ms")) != NULL) {
14
+       pw_properties_set(props, "reconnect.interval.ms", str);
15
+       pw_properties_set(props, "reconnect_interval_ms", NULL);
16
+   }
17
    if ((str = pw_properties_get(props, "latency_msec")) != NULL) {
18
        pw_properties_set(props, "pulse.latency", str);
19
        pw_properties_set(props, "latency_msec", NULL);
20
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/modules/module-tunnel-source.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/module-tunnel-source.c Changed
20
 
1
@@ -32,6 +32,7 @@
2
    "source=<name of the remote source> "
3
    "source_name=<name for the local source> "
4
    "source_properties=<properties for the local source> "
5
+   "reconnect_interval_ms=<interval to try reconnects, 0 or omitted if disabled> "
6
    "format=<sample format> "
7
    "channels=<number of channels> "
8
    "rate=<sample rate> "
9
@@ -178,6 +179,10 @@
10
    }
11
    audioinfo_to_properties(&info, stream_props);
12
 
13
+   if ((str = pw_properties_get(props, "reconnect_interval_ms")) != NULL) {
14
+       pw_properties_set(props, "reconnect.interval.ms", str);
15
+       pw_properties_set(props, "reconnect_interval_ms", NULL);
16
+   }
17
    if ((str = pw_properties_get(props, "latency_msec")) != NULL) {
18
        pw_properties_set(props, "pulse.latency", str);
19
        pw_properties_set(props, "latency_msec", NULL);
20
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/modules/org.freedesktop.pulseaudio.gschema.xml Added
117
 
1
@@ -0,0 +1,115 @@
2
+<schemalist gettext-domain="pulseaudio">
3
+  <!-- The module-groups object is just an entry point to find the individual
4
+       module-group objects. -->
5
+  <schema id="org.freedesktop.pulseaudio.module-groups" path="/org/freedesktop/pulseaudio/module-groups/">
6
+    <child name="combine" schema="org.freedesktop.pulseaudio.module-group"/>
7
+    <child name="remote-access" schema="org.freedesktop.pulseaudio.module-group"/>
8
+    <child name="zeroconf-discover" schema="org.freedesktop.pulseaudio.module-group"/>
9
+    <child name="raop-discover" schema="org.freedesktop.pulseaudio.module-group"/>
10
+    <child name="rtp-recv" schema="org.freedesktop.pulseaudio.module-group"/>
11
+    <child name="rtp-send" schema="org.freedesktop.pulseaudio.module-group"/>
12
+    <child name="upnp-media-server" schema="org.freedesktop.pulseaudio.module-group"/>
13
+  </schema>
14
+
15
+  <!-- Paprefs puts related modules into groups that are enabled or disabled as
16
+       a whole. One group can contain up to 10 module instances (either of the
17
+       same module or different modules). A module-group object defines up to
18
+       10 modules to load. The name0..name9 keys contain the module names and
19
+       the args0..args9 keys provide the module arguments. -->
20
+  <schema id="org.freedesktop.pulseaudio.module-group">
21
+    <key name="name" type="s">
22
+      <default>''</default>
23
+      <summary>Module group name</summary>
24
+      <description>Module group name</description>
25
+    </key>
26
+
27
+    <key name="enabled" type="b">
28
+      <default>false</default>
29
+    </key>
30
+
31
+    <key name="locked" type="b">
32
+      <default>false</default>
33
+    </key>
34
+
35
+    <key name="args0" type="s">
36
+      <default>''</default>
37
+    </key>
38
+
39
+    <key name="args1" type="s">
40
+      <default>''</default>
41
+    </key>
42
+
43
+    <key name="args2" type="s">
44
+      <default>''</default>
45
+    </key>
46
+
47
+    <key name="args3" type="s">
48
+      <default>''</default>
49
+    </key>
50
+
51
+    <key name="args4" type="s">
52
+      <default>''</default>
53
+    </key>
54
+
55
+    <key name="args5" type="s">
56
+      <default>''</default>
57
+    </key>
58
+
59
+    <key name="args6" type="s">
60
+      <default>''</default>
61
+    </key>
62
+
63
+    <key name="args7" type="s">
64
+      <default>''</default>
65
+    </key>
66
+
67
+    <key name="args8" type="s">
68
+      <default>''</default>
69
+    </key>
70
+
71
+    <key name="args9" type="s">
72
+      <default>''</default>
73
+    </key>
74
+
75
+    <key name="name0" type="s">
76
+      <default>''</default>
77
+    </key>
78
+
79
+    <key name="name1" type="s">
80
+      <default>''</default>
81
+    </key>
82
+
83
+    <key name="name2" type="s">
84
+      <default>''</default>
85
+    </key>
86
+
87
+    <key name="name3" type="s">
88
+      <default>''</default>
89
+    </key>
90
+
91
+    <key name="name4" type="s">
92
+      <default>''</default>
93
+    </key>
94
+
95
+    <key name="name5" type="s">
96
+      <default>''</default>
97
+    </key>
98
+
99
+    <key name="name6" type="s">
100
+      <default>''</default>
101
+    </key>
102
+
103
+    <key name="name7" type="s">
104
+      <default>''</default>
105
+    </key>
106
+
107
+    <key name="name8" type="s">
108
+      <default>''</default>
109
+    </key>
110
+
111
+    <key name="name9" type="s">
112
+      <default>''</default>
113
+    </key>
114
+  </schema>
115
+
116
+</schemalist>
117
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/pulse-server.c Changed
202
 
1
@@ -16,6 +16,7 @@
2
 #include "log.h"
3
 
4
 #include <spa/support/cpu.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/debug/dict.h>
9
@@ -28,7 +29,6 @@
10
 #include <spa/utils/ringbuffer.h>
11
 #include <spa/utils/json.h>
12
 
13
-#include <pipewire/cleanup.h>
14
 #include <pipewire/pipewire.h>
15
 #include <pipewire/extensions/metadata.h>
16
 
17
@@ -56,6 +56,7 @@
18
 #include "utils.h"
19
 #include "volume.h"
20
 
21
+#define DEFAULT_ALLOW_MODULE_LOADING   "true"
22
 #define DEFAULT_MIN_REQ        "128/48000"
23
 #define DEFAULT_DEFAULT_REQ    "960/48000"
24
 #define DEFAULT_MIN_FRAG   "128/48000"
25
@@ -73,10 +74,6 @@
26
 
27
 #define TEMPORARY_MOVE_TIMEOUT (SPA_NSEC_PER_SEC)
28
 
29
-PW_LOG_TOPIC_EXTERN(pulse_conn);
30
-
31
-bool debug_messages = false;
32
-
33
 struct latency_offset_data {
34
    int64_t prev_latency_offset;
35
    uint8_t initialized:1;
36
@@ -208,7 +205,7 @@
37
 {
38
    uint32_t event = 0, mask = 0, res_index = o->index;
39
 
40
-   pw_log_debug("index:%d id:%d %08lx type:%u", o->index, o->id, o->change_mask, type);
41
+   pw_log_debug("index:%d id:%d %08" PRIx64 " type:%u", o->index, o->id, o->change_mask, type);
42
 
43
    if (pw_manager_object_is_sink(o) && o->change_mask & PW_MANAGER_OBJECT_FLAG_SINK) {
44
        client_queue_subscribe_event(client,
45
@@ -597,6 +594,8 @@
46
    if (peer && pw_manager_object_is_sink(peer)) {
47
        peer_index = peer->index;
48
        peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME);
49
+       if (peer_name == NULL)
50
+           peer_name = "unknown";
51
    } else {
52
        peer_index = SPA_ID_INVALID;
53
        peer_name = NULL;
54
@@ -751,6 +750,8 @@
55
        peer = find_linked(manager, peer->id, PW_DIRECTION_OUTPUT);
56
    if (peer && pw_manager_object_is_source_or_monitor(peer)) {
57
        name = pw_properties_get(peer->props, PW_KEY_NODE_NAME);
58
+       if (name == NULL)
59
+           name = "unknown";
60
        peer_index = peer->index;
61
        if (!pw_manager_object_is_source(peer)) {
62
            size_t len = (name ? strlen(name) : 5) + 10;
63
@@ -860,6 +861,8 @@
64
                s->peer_index = peer->index;
65
 
66
                peer_name = pw_properties_get(peer->props, PW_KEY_NODE_NAME);
67
+               if (peer_name == NULL)
68
+                   peer_name = "unknown";
69
                if (peer_name && s->direction == PW_DIRECTION_INPUT &&
70
                    pw_manager_object_is_monitor(peer)) {
71
                    int len = strlen(peer_name) + 10;
72
@@ -993,12 +996,8 @@
73
        if (changed)
74
            send_default_change_subscribe_event(client, true, true);
75
    }
76
-   if (subject == PW_ID_CORE && o == client->metadata_routes) {
77
-       if (key == NULL)
78
-           pw_properties_clear(client->routes);
79
-       else
80
-           pw_properties_set(client->routes, key, value);
81
-   }
82
+   if (subject == PW_ID_CORE && o == client->metadata_routes)
83
+       client_update_routes(client, key, value);
84
 }
85
 
86
 
87
@@ -4200,7 +4199,7 @@
88
 
89
    if (command == COMMAND_GET_MODULE_INFO && (sel.index & MODULE_FLAG) != 0) {
90
        struct module *module;
91
-       module = pw_map_lookup(&impl->modules, sel.index & MODULE_INDEX_MASK);
92
+       module = module_lookup(impl, sel.index & MODULE_INDEX_MASK, NULL);
93
        if (module == NULL)
94
            goto error_noentity;
95
        fill_ext_module_info(client, reply, module);
96
@@ -4560,7 +4559,7 @@
97
 
98
    stream->rate = rate;
99
 
100
-   corr = (double)rate/(double)stream->ss.rate;
101
+   corr = (float)rate/(float)stream->ss.rate;
102
    pw_stream_set_control(stream->stream, SPA_PROP_rate, 1, &corr, NULL);
103
 
104
    return reply_simple_ack(client, tag);
105
@@ -4568,9 +4567,10 @@
106
 
107
 static int do_extension(struct client *client, uint32_t command, uint32_t tag, struct message *m)
108
 {
109
+   struct impl *impl = client->impl;
110
    uint32_t index;
111
    const char *name;
112
-   const struct extension *ext;
113
+   struct module *module;
114
 
115
    if (message_get(m,
116
            TAG_U32, &index,
117
@@ -4585,11 +4585,11 @@
118
        (index != SPA_ID_INVALID && name != NULL))
119
        return -EINVAL;
120
 
121
-   ext = extension_find(index, name);
122
-   if (ext == NULL)
123
+   module = module_lookup(impl, index, name);
124
+   if (module == NULL)
125
        return -ENOENT;
126
 
127
-   return ext->process(client, tag, m);
128
+   return extension_process(module, client, tag, m);
129
 }
130
 
131
 static int do_set_profile(struct client *client, uint32_t command, uint32_t tag, struct message *m)
132
@@ -5015,6 +5015,9 @@
133
    struct pending_module *pm;
134
    int r;
135
 
136
+   if (!impl->defs.allow_module_loading)
137
+       return -EACCES;
138
+
139
    if (message_get(m,
140
            TAG_STRING, &name,
141
            TAG_STRING, &argument,
142
@@ -5060,6 +5063,9 @@
143
    struct module *module;
144
    uint32_t module_index;
145
 
146
+   if (!impl->defs.allow_module_loading)
147
+       return -EACCES;
148
+
149
    if (message_get(m,
150
            TAG_U32, &module_index,
151
            TAG_INVALID) < 0)
152
@@ -5073,7 +5079,7 @@
153
    if ((module_index & MODULE_FLAG) == 0)
154
        return -EPERM;
155
 
156
-   module = pw_map_lookup(&impl->modules, module_index & MODULE_INDEX_MASK);
157
+   module = module_lookup(impl, module_index & MODULE_INDEX_MASK, NULL);
158
    if (module == NULL)
159
        return -ENOENT;
160
 
161
@@ -5450,9 +5456,22 @@
162
    pw_log_info(": defaults: %s = %u", key, *res);
163
    return 0;
164
 }
165
+static int parse_bool(struct pw_properties *props, const char *key, const char *def,
166
+       bool *res)
167
+{
168
+   const char *str;
169
+   if (props == NULL ||
170
+       (str = pw_properties_get(props, key)) == NULL)
171
+       str = def;
172
+   *res = spa_atob(str);
173
+   pw_log_info(": defaults: %s = %s", key, *res ? "true" : "false");
174
+   return 0;
175
+}
176
 
177
 static void load_defaults(struct defs *def, struct pw_properties *props)
178
 {
179
+   parse_bool(props, "pulse.allow-module-loading", DEFAULT_ALLOW_MODULE_LOADING,
180
+           &def->allow_module_loading);
181
    parse_frac(props, "pulse.min.req", DEFAULT_MIN_REQ, &def->min_req);
182
    parse_frac(props, "pulse.default.req", DEFAULT_DEFAULT_REQ, &def->default_req);
183
    parse_frac(props, "pulse.min.frag", DEFAULT_MIN_FRAG, &def->min_frag);
184
@@ -5476,8 +5495,6 @@
185
    const char *str;
186
    int res = 0;
187
 
188
-   debug_messages = pw_log_topic_enabled(SPA_LOG_LEVEL_INFO, pulse_conn);
189
-
190
    impl = calloc(1, sizeof(*impl) + user_data_size);
191
    if (impl == NULL)
192
        goto error_free_props;
193
@@ -5505,6 +5522,8 @@
194
    pw_context_conf_update_props(context, "pulse.properties", props);
195
 
196
    if ((str = pw_properties_get(props, "vm.overrides")) != NULL) {
197
+       pw_log_warn("vm.overrides in pulse.properties are deprecated, "
198
+               "use pulse.properties.rules instead");
199
        if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
200
            pw_properties_update_string(props, str, strlen(str));
201
        pw_properties_set(props, "vm.overrides", NULL);
202
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/server.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/server.c Changed
75
 
1
@@ -25,10 +25,10 @@
2
 #include <systemd/sd-daemon.h>
3
 #endif
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/defs.h>
7
 #include <spa/utils/json.h>
8
 #include <spa/utils/result.h>
9
-#include <pipewire/cleanup.h>
10
 #include <pipewire/pipewire.h>
11
 
12
 #include "client.h"
13
@@ -42,10 +42,15 @@
14
 #include "stream.h"
15
 #include "utils.h"
16
 #include "flatpak-utils.h"
17
+#ifdef HAVE_SNAP
18
+#include "snap-policy.h"
19
+#endif
20
 
21
 #define LISTEN_BACKLOG 32
22
 #define MAX_CLIENTS 64
23
 
24
+PW_LOG_TOPIC_EXTERN(pulse_conn);
25
+
26
 static int handle_packet(struct client *client, struct message *msg)
27
 {
28
    uint32_t command, tag;
29
@@ -67,9 +72,9 @@
30
        goto finish;
31
    }
32
 
33
-   if (debug_messages) {
34
+   if (pw_log_topic_custom_enabled(SPA_LOG_LEVEL_INFO, pulse_conn)) {
35
        pw_log_debug("client %p: command:%s", client, commandscommand.name);
36
-       message_dump(SPA_LOG_LEVEL_INFO, msg);
37
+       message_dump(SPA_LOG_LEVEL_INFO, "<<", msg);
38
    }
39
 
40
    const struct command *cmd = &commandscommand;
41
@@ -405,7 +410,10 @@
42
        client_access = server->client_access;
43
 
44
    if (server->addr.ss_family == AF_UNIX) {
45
-       spa_autofree char *app_id = NULL, *devices = NULL;
46
+       spa_autofree char *app_id = NULL, *snap_app_id = NULL, *devices = NULL;
47
+#ifdef HAVE_SNAP
48
+       pw_sandbox_access_t snap_access;
49
+#endif
50
 
51
 #ifdef SO_PRIORITY
52
        val = 6;
53
@@ -444,6 +452,21 @@
54
            else
55
                pw_properties_set(client->props, PW_KEY_MEDIA_CATEGORY, NULL);
56
        }
57
+       // check SNAP permissions
58
+#ifdef HAVE_SNAP
59
+       snap_access = pw_snap_get_audio_permissions(client, client_fd, &snap_app_id);
60
+       if ((snap_access & PW_SANDBOX_ACCESS_NOT_A_SANDBOX) == 0) {
61
+           pw_properties_set(client->props, PW_KEY_SNAP_ID, snap_app_id);
62
+
63
+           pw_properties_set(client->props,
64
+                     PW_KEY_SNAP_PLAYBACK_ALLOWED,
65
+                     (snap_access & PW_SANDBOX_ACCESS_PLAYBACK) ? "true" : "false");
66
+
67
+           pw_properties_set(client->props,
68
+                     PW_KEY_SNAP_RECORD_ALLOWED,
69
+                     (snap_access & PW_SANDBOX_ACCESS_RECORD) ? "true" : "false");
70
+       }
71
+#endif
72
    }
73
    else if (server->addr.ss_family == AF_INET || server->addr.ss_family == AF_INET6) {
74
 
75
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/snap-policy.c Added
205
 
1
@@ -0,0 +1,203 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifdef HAVE_CONFIG_H
7
+#include <config.h>
8
+#endif
9
+
10
+#include <glib.h>
11
+#include <snapd-glib/snapd-glib.h>
12
+#include <pipewire/pipewire.h>
13
+#include <sys/stat.h>
14
+#include <fcntl.h>
15
+#include "client.h"
16
+#include <sys/apparmor.h>
17
+#include <errno.h>
18
+#include "snap-policy.h"
19
+#include <fcntl.h>
20
+#include <assert.h>
21
+
22
+#define SNAP_LABEL_PREFIX "snap."
23
+
24
+static gboolean check_is_same_snap(gchar *snap1, gchar *snap2)
25
+{
26
+   // Checks if two apparmor labels belong to the same snap
27
+   g_auto(GStrv) strings1 = NULL;
28
+   g_auto(GStrv) strings2 = NULL;
29
+
30
+   if (!g_str_has_prefix(snap1, SNAP_LABEL_PREFIX)) {
31
+       return FALSE;
32
+   }
33
+   if (!g_str_has_prefix(snap2, SNAP_LABEL_PREFIX)) {
34
+       return FALSE;
35
+   }
36
+   strings1 = g_strsplit(snap1, ".", 3);
37
+   strings2 = g_strsplit(snap2, ".", 3);
38
+
39
+   if (g_str_equal(strings11, strings21) && (strings11 != NULL)) {
40
+       return TRUE;
41
+   }
42
+   return FALSE;
43
+}
44
+
45
+pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id)
46
+{
47
+   g_autofree gchar* aa_label = NULL;
48
+   gchar* snap_id = NULL;
49
+   gchar* snap_confinement = NULL;
50
+   gchar *separator = NULL;
51
+   g_autofree gchar *aacon = NULL;
52
+   gchar *aamode = NULL;
53
+   g_autoptr(SnapdClient) snapdclient = NULL;
54
+   g_autoptr(GPtrArray) plugs = NULL;
55
+   gboolean retv;
56
+   pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE;
57
+   SnapdPlug **plug = NULL;
58
+   g_autoptr(GError) error = NULL;
59
+   int exit_code;
60
+
61
+   *app_id = g_strdup("unknown");
62
+   assert(client != NULL);
63
+
64
+   if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) {
65
+       if (errno == EINVAL) {
66
+           // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system
67
+           return PW_SANDBOX_ACCESS_NOT_A_SANDBOX;
68
+       }
69
+       if  (errno == ENOPROTOOPT) {
70
+           static bool warned;
71
+
72
+           // if fine grained unix mediation isn't available, we can't know if this is a snap or
73
+           // not, so we have no choice but give full access
74
+           if (!warned) {
75
+               pw_log_warn("snap_get_audio_permissions: kernel lacks 'fine grained unix mediation'; "
76
+                       "snap audio permissions won't be honored.");
77
+               warned = true;
78
+           }
79
+           return PW_SANDBOX_ACCESS_NOT_A_SANDBOX;
80
+       }
81
+       pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info: %s.", strerror(errno));
82
+       return PW_SANDBOX_ACCESS_NONE;
83
+   }
84
+   if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) {
85
+       // not a SNAP.
86
+       pw_log_info("snap_get_audio_permissions: not an snap.");
87
+       return PW_SANDBOX_ACCESS_NOT_A_SANDBOX;
88
+   }
89
+
90
+   snap_id = g_strdup(aa_label + strlen(SNAP_LABEL_PREFIX));
91
+   separator = strchr(snap_id, '.');
92
+   if (separator == NULL) {
93
+       g_free(snap_id);
94
+       pw_log_info("snap_get_audio_permissions: aa_label has only one dot; not a valid ID.");
95
+       return PW_SANDBOX_ACCESS_NONE;
96
+   }
97
+   *separator = 0;
98
+   g_free(*app_id);
99
+   *app_id = snap_id;
100
+
101
+   // it's a "classic" or a "devmode" confinement snap, so we give it full access
102
+   if (g_str_equal(snap_confinement, "complain")) {
103
+       return PW_SANDBOX_ACCESS_ALL;
104
+   }
105
+
106
+   snapdclient = snapd_client_new();
107
+   if (snapdclient == NULL) {
108
+       pw_log_warn("snap_get_audio_permissions: error creating SnapdClient object.");
109
+       return PW_SANDBOX_ACCESS_NONE;
110
+   }
111
+
112
+   if (aa_getcon(&aacon, &aamode) == -1) {
113
+       pw_log_warn("snap_get_audio_permissions: error checking if pipewire-pulse is inside a snap.");
114
+       return PW_SANDBOX_ACCESS_NONE; // failed to get access to apparmor
115
+   }
116
+
117
+   // If pipewire-pulse is inside a snap, use snapctl API
118
+   if (g_str_has_prefix(aacon, SNAP_LABEL_PREFIX)) {
119
+       // If the snap wanting to get access is the same that contains pipewire,
120
+       // give to it full access.
121
+       if (check_is_same_snap(aacon, aa_label))
122
+           return PW_SANDBOX_ACCESS_ALL;
123
+       snapd_client_set_socket_path(snapdclient, "/run/snapd-snap.socket");
124
+
125
+       /* Take context from the environment if available */
126
+       const char *context = g_getenv("SNAP_COOKIE");
127
+       if (!context)
128
+           context = "";
129
+
130
+       char *snapctl_command = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL };
131
+       if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) {
132
+           pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message);
133
+           return PW_SANDBOX_ACCESS_NONE;
134
+       }
135
+       if (exit_code != 1) {
136
+           // 0  = Connected
137
+           // 10 = Classic environment
138
+           // 11 = Not a snap
139
+           return PW_SANDBOX_ACCESS_ALL;
140
+       }
141
+       char *snapctl_command2 = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL };
142
+       if (!snapd_client_run_snapctl2_sync(snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) {
143
+           pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message);
144
+           return PW_SANDBOX_ACCESS_NONE;
145
+       }
146
+       if (exit_code == 1) {
147
+           return PW_SANDBOX_ACCESS_PLAYBACK;
148
+       }
149
+       return PW_SANDBOX_ACCESS_ALL;
150
+   }
151
+
152
+   retv = snapd_client_get_connections2_sync(snapdclient,
153
+                         SNAPD_GET_CONNECTIONS_FLAGS_NONE,
154
+                         snap_id,
155
+                         NULL,
156
+                         NULL,
157
+                         NULL,
158
+                         &plugs,
159
+                         NULL,
160
+                         NULL,
161
+                         &error);
162
+   if (retv == FALSE) {
163
+       pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message);
164
+       return PW_SANDBOX_ACCESS_NONE;
165
+   }
166
+   if (plugs == NULL) {
167
+       pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message);
168
+       return PW_SANDBOX_ACCESS_NONE;
169
+   }
170
+   if (plugs->pdata == NULL) {
171
+       pw_log_warn("Failed to get Snap connections for snap %s: %s\n", snap_id, error->message);
172
+       return PW_SANDBOX_ACCESS_NONE;
173
+   }
174
+
175
+   plug = (SnapdPlug **)plugs->pdata;
176
+   for (guint p = 0; p < plugs->len; p++, plug++) {
177
+       pw_sandbox_access_t add_permission;
178
+       const gchar *plug_name = snapd_plug_get_name(*plug);
179
+       if (g_str_equal("audio-record", plug_name)) {
180
+           add_permission = PW_SANDBOX_ACCESS_RECORD;
181
+       } else if (g_str_equal("audio-playback", plug_name)) {
182
+           add_permission = PW_SANDBOX_ACCESS_PLAYBACK;
183
+       } else if (g_str_equal("pulseaudio", plug_name)) {
184
+           add_permission = PW_SANDBOX_ACCESS_ALL;
185
+       } else {
186
+           continue;
187
+       }
188
+       GPtrArray *slots = snapd_plug_get_connected_slots(*plug);
189
+       if (slots == NULL)
190
+           continue;
191
+       SnapdSlotRef **slot = (SnapdSlotRef**) slots->pdata;
192
+
193
+       for (guint q = 0; q < slots->len; q++, slot++) {
194
+           const gchar *slot_name = snapd_slot_ref_get_slot(*slot);
195
+           const gchar *snap_name = snapd_slot_ref_get_snap(*slot);
196
+           if ((g_str_equal(snap_name, "snapd") ||
197
+                g_str_equal(snap_name, "core")) &&
198
+                g_str_equal(slot_name, plug_name))
199
+               permissions |= add_permission;
200
+       }
201
+   }
202
+
203
+   return permissions;
204
+}
205
pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/snap-policy.h Added
24
 
1
@@ -0,0 +1,22 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef _SNAP_POLICY_H_
7
+#define _SNAP_POLICY_H_
8
+
9
+typedef enum _pw_sandbox_access {
10
+   PW_SANDBOX_ACCESS_NONE           = 0,
11
+   PW_SANDBOX_ACCESS_NOT_A_SANDBOX  = 1 << 0,
12
+   PW_SANDBOX_ACCESS_RECORD         = 1 << 1,
13
+   PW_SANDBOX_ACCESS_PLAYBACK       = 1 << 2,
14
+   PW_SANDBOX_ACCESS_ALL            = (PW_SANDBOX_ACCESS_PLAYBACK | PW_SANDBOX_ACCESS_RECORD),
15
+} pw_sandbox_access_t;
16
+
17
+#define PW_KEY_SNAP_ID "pipewire.snap.id"
18
+#define PW_KEY_SNAP_PLAYBACK_ALLOWED "pipewire.snap.audio.playback"
19
+#define PW_KEY_SNAP_RECORD_ALLOWED "pipewire.snap.audio.record"
20
+
21
+pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id);
22
+
23
+#endif // _SNAP_POLICY_H_
24
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-pulse/volume.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-pulse/volume.c Changed
10
 
1
@@ -79,7 +79,7 @@
2
        {
3
            float step;
4
            if (spa_pod_get_float(&prop->value, &step) >= 0)
5
-               info->steps = 0x10000u * step;
6
+               info->steps = (uint32_t)(0x10000u * step);
7
            break;
8
        }
9
        case SPA_PROP_channelMap:
10
pipewire-1.0.1.tar.bz2/src/modules/module-protocol-simple.c -> pipewire-1.2.0.tar.gz/src/modules/module-protocol-simple.c Changed
681
 
1
@@ -9,6 +9,7 @@
2
 #include <sys/stat.h>
3
 #include <fcntl.h>
4
 #include <unistd.h>
5
+#include <sys/ioctl.h>
6
 #include <sys/socket.h>
7
 #include <netinet/in.h>
8
 #include <netinet/tcp.h>
9
@@ -29,6 +30,8 @@
10
 
11
 #include <pipewire/impl.h>
12
 
13
+#include "network-utils.h"
14
+
15
 /** \page page_module_protocol_simple Protocol Simple
16
  *
17
  * The simple protocol provides a bidirectional audio stream on a network
18
@@ -40,20 +43,23 @@
19
  * Each client that connects will create a capture and/or playback stream,
20
  * depending on the configuration options.
21
  *
22
+ * You can also use it to feed audio data to other clients such as the snapcast
23
+ * server.
24
+ *
25
  * ## Module Name
26
  *
27
  * `libpipewire-module-protocol-simple`
28
  *
29
  * ## Module Options
30
  *
31
- *  - `capture`: boolean if capture is enabled. This will create a capture stream
32
- *               for each connected client.
33
- *  - `playback`: boolean if playback is enabled. This will create a playback
34
- *               stream for each connected client.
35
- *  - `capture.node`: an optional node serial or name to use for capture.
36
- *  - `playback.node`: an optional node serial or name to use for playback.
37
+ *  - `capture`: boolean if capture is enabled. This will create a capture stream or
38
+ *               sink for each connected client.
39
+ *  - `playback`: boolean if playback is enabled. This will create a playback or
40
+ *               source stream for each connected client.
41
  *  - `server.address = `: an array of server addresses to listen on as
42
  *                            tcp:(<ip>:)<port>.
43
+ *  - `capture.props`: optional properties for the capture stream
44
+ *  - `playback.props`: optional properties for the playback stream
45
  *
46
  * ## General options
47
  *
48
@@ -67,6 +73,8 @@
49
  * - \ref PW_KEY_NODE_LATENCY
50
  * - \ref PW_KEY_NODE_RATE
51
  * - \ref PW_KEY_STREAM_CAPTURE_SINK
52
+ * - \ref PW_KEY_NODE_NAME
53
+ * - \ref PW_KEY_TARGET_OBJECT
54
  *
55
  * By default the server will work with stereo 16 bits samples at 44.1KHz.
56
  *
57
@@ -82,15 +90,6 @@
58
  *         # Provide playback stream, client can send data to PipeWire for playback
59
  *         playback = true
60
  *         #
61
- *         # The node name or id to use for capture.
62
- *         #capture.node = null
63
- *         #
64
- *         # To make the capture stream capture the monitor ports
65
- *         #stream.capture.sink = false
66
- *         #
67
- *         # The node name or id to use for playback.
68
- *         #playback.node = null
69
- *         #
70
  *         #audio.rate = 44100
71
  *         #audio.format = S16
72
  *         #audio.channels = 2
73
@@ -101,10 +100,65 @@
74
  *         server.address = 
75
  *             "tcp:4711"
76
  *         
77
+ *         capture.props = {
78
+ *             # The node name or id to use for capture.
79
+ *             #target.object = null
80
+ *             #
81
+ *             # To make the capture stream capture the monitor ports
82
+ *             #stream.capture.sink = false
83
+ *             #
84
+ *             # Make this a sink instead of a capture stream
85
+ *             #media.class = Audio/Sink
86
+ *         }
87
+ *         playback.props = {
88
+ *             # The node name or id to use for playback.
89
+ *             #target.object = null
90
+ *             #
91
+ *             # Make this a source instead of a playback stream
92
+ *             #media.class = Audio/Source
93
+ *         }
94
  *     }
95
  * }
96
  * 
97
  *\endcode
98
+ *
99
+ * ## Example configuration for a snapcast server
100
+ *
101
+ *\code{.unparsed}
102
+ * context.modules = 
103
+ * {   name = libpipewire-module-protocol-simple
104
+ *     args = {
105
+ *         # Provide sink
106
+ *         capture = true
107
+ *         audio.rate = 48000
108
+ *         audio.format = S16
109
+ *         audio.channels = 2
110
+ *         audio.position =  FL FR 
111
+ *
112
+ *         # The addresses this server listens on for new
113
+ *         # client connections
114
+ *         server.address = 
115
+ *             "tcp:4711"
116
+ *         
117
+ *         capture.props = {
118
+ *             # Make this a sink instead of a capture stream
119
+ *             media.class = Audio/Sink
120
+ *         }
121
+ *     }
122
+ * }
123
+ * 
124
+ *
125
+ * On the snapcast server, add the following to the `snapserver.conf` file:
126
+ *
127
+ *\code{.unparsed}
128
+ * stream
129
+ * sampleformat =  48000:16:2
130
+ * source = tcp://127.0.0.1:4711?name=PipeWireSnapcast&mode=client
131
+ *\endcode
132
+ *
133
+ * Snapcast will try to connect to the protocol-simple server and fetch the
134
+ * samples from it. Snapcast tries to reconnect when the connection is somehow
135
+ * broken.
136
  */
137
 
138
 #define NAME "protocol-simple"
139
@@ -115,26 +169,24 @@
140
 #define DEFAULT_PORT 4711
141
 #define DEFAULT_SERVER " \"tcp:"SPA_STRINGIFY(DEFAULT_PORT)"\" "
142
 
143
-#define DEFAULT_FORMAT "S16"
144
+#define DEFAULT_FORMAT "S16LE"
145
 #define DEFAULT_RATE 44100
146
 #define DEFAULT_CHANNELS 2
147
 #define DEFAULT_POSITION " FL FR "
148
-#define DEFAULT_LATENCY "1024/44100"
149
 
150
 #define MAX_CLIENTS    10
151
 
152
 #define MODULE_USAGE   "( capture=<bool> ) "                       \
153
            "( playback=<bool> ) "                      \
154
            "( remote.name=<remote> ) "                 \
155
-           "( node.latency=<num/denom, default:"DEFAULT_LATENCY"> ) "  \
156
            "( node.rate=<1/rate, default:1/"SPA_STRINGIFY(DEFAULT_RATE)"> ) "  \
157
-           "( capture.node=<source-target> ( stream.capture.sink=true )) " \
158
-           "( playback.node=<sink-target> ) "              \
159
            "( audio.rate=<sample-rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "      \
160
            "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "      \
161
            "( audio.channels=<channels, default: "SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "    \
162
            "( audio.position=<position, default:"DEFAULT_POSITION"> ) "    \
163
-           "( server.address=< tcp:(<ip>:)<port>(,...) , default:"DEFAULT_SERVER"> )"  \
164
+           "( server.address=< tcp:(<ip>:)<port>(,...) , default:"DEFAULT_SERVER"> ) " \
165
+           "( capture.props={ ... } ) "    \
166
+           "( playback.props={ ... } )"    \
167
 
168
 static const struct spa_dict_item module_props = {
169
    { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
170
@@ -153,11 +205,16 @@
171
 
172
    struct pw_work_queue *work_queue;
173
 
174
+   struct pw_properties *capture_props;
175
+   struct pw_properties *playback_props;
176
+
177
    bool capture;
178
    bool playback;
179
 
180
-   struct spa_audio_info_raw info;
181
-   uint32_t frame_size;
182
+   struct spa_audio_info_raw capture_info;
183
+   struct spa_audio_info_raw playback_info;
184
+   uint32_t capture_frame_size;
185
+   uint32_t playback_frame_size;
186
 };
187
 
188
 struct client {
189
@@ -188,9 +245,9 @@
190
 
191
 #define SERVER_TYPE_INVALID    0
192
 #define SERVER_TYPE_UNIX   1
193
-#define SERVER_TYPE_INET   2
194
+#define SERVER_TYPE_TCP        2
195
    uint32_t type;
196
-   struct sockaddr_un addr;
197
+   struct sockaddr_storage addr;
198
    struct spa_source *source;
199
 
200
    struct spa_list client_list;
201
@@ -305,10 +362,11 @@
202
        if (res < 0) {
203
            if (errno == EINTR)
204
                continue;
205
-           if (errno != EAGAIN && errno != EWOULDBLOCK)
206
+           if (errno != EAGAIN && errno != EWOULDBLOCK) {
207
                pw_log_warn("%p: client:%p %s send error %d: %m", impl,
208
                        client, client->name, res);
209
-           client_cleanup(client);
210
+               client_cleanup(client);
211
+           }
212
            break;
213
        }
214
        offset += res;
215
@@ -335,7 +393,7 @@
216
 
217
    size = d->maxsize;
218
    if (buf->requested)
219
-       size = SPA_MIN(size, buf->requested * impl->frame_size);
220
+       size = SPA_MIN(size, buf->requested * impl->playback_frame_size);
221
 
222
    offset = 0;
223
    while (size > 0) {
224
@@ -362,7 +420,7 @@
225
    }
226
    d->chunk->offset = 0;
227
    d->chunk->size = offset;
228
-   d->chunk->stride = impl->frame_size;
229
+   d->chunk->stride = impl->playback_frame_size;
230
 
231
    pw_stream_queue_buffer(client->playback, buf);
232
 }
233
@@ -421,24 +479,12 @@
234
    uint32_t n_params;
235
    const struct spa_pod *params1;
236
    uint8_t buffer1024;
237
-   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
238
+   struct spa_pod_builder b;
239
    struct pw_properties *props;
240
-   const char *latency;
241
    int res;
242
 
243
-   if ((latency = pw_properties_get(impl->props, PW_KEY_NODE_LATENCY)) == NULL)
244
-       latency = DEFAULT_LATENCY;
245
-
246
    if (impl->capture) {
247
-       props = pw_properties_new(
248
-           PW_KEY_NODE_LATENCY, latency,
249
-           PW_KEY_NODE_RATE, pw_properties_get(impl->props, PW_KEY_NODE_RATE),
250
-           PW_KEY_TARGET_OBJECT, pw_properties_get(impl->props, "capture.node"),
251
-           PW_KEY_STREAM_CAPTURE_SINK, pw_properties_get(impl->props,
252
-               PW_KEY_STREAM_CAPTURE_SINK),
253
-           PW_KEY_NODE_NETWORK, "true",
254
-           NULL);
255
-       if (props == NULL)
256
+       if ((props = pw_properties_copy(impl->capture_props)) == NULL)
257
            return -errno;
258
 
259
        pw_properties_setf(props,
260
@@ -453,12 +499,7 @@
261
                &capture_stream_events, client);
262
    }
263
    if (impl->playback) {
264
-       props = pw_properties_new(
265
-           PW_KEY_NODE_LATENCY, latency,
266
-           PW_KEY_NODE_RATE, pw_properties_get(impl->props, PW_KEY_NODE_RATE),
267
-           PW_KEY_TARGET_OBJECT, pw_properties_get(impl->props, "playback.node"),
268
-           PW_KEY_NODE_NETWORK, "true",
269
-           NULL);
270
+       props = pw_properties_copy(impl->playback_props);
271
        if (props == NULL)
272
            return -errno;
273
 
274
@@ -475,11 +516,13 @@
275
                &playback_stream_events, client);
276
    }
277
 
278
-   n_params = 0;
279
-   paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
280
-               &impl->info);
281
 
282
    if (impl->capture) {
283
+       n_params = 0;
284
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
285
+       paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
286
+               &impl->capture_info);
287
+
288
        if ((res = pw_stream_connect(client->capture,
289
                PW_DIRECTION_INPUT,
290
                PW_ID_ANY,
291
@@ -490,6 +533,11 @@
292
            return res;
293
    }
294
    if (impl->playback) {
295
+       n_params = 0;
296
+       spa_pod_builder_init(&b, buffer, sizeof(buffer));
297
+       paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
298
+               &impl->playback_info);
299
+
300
        if ((res = pw_stream_connect(client->playback,
301
                PW_DIRECTION_OUTPUT,
302
                PW_ID_ANY,
303
@@ -568,11 +616,11 @@
304
 
305
    pw_properties_setf(props,
306
            "protocol.server.type", "%s",
307
-           server->type == SERVER_TYPE_INET ? "tcp" : "unix");
308
+           server->type == SERVER_TYPE_TCP ? "tcp" : "unix");
309
 
310
    if (server->type == SERVER_TYPE_UNIX) {
311
        goto error;
312
-   } else if (server->type == SERVER_TYPE_INET) {
313
+   } else if (server->type == SERVER_TYPE_TCP) {
314
        val = 1;
315
        if (setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY,
316
                    (const void *) &val, sizeof(val)) < 0)
317
@@ -606,32 +654,51 @@
318
    return;
319
 }
320
 
321
-static int make_inet_socket(struct server *server, const char *name)
322
+static uint16_t parse_port(const char *str, uint16_t def)
323
 {
324
-   struct sockaddr_in addr;
325
+   uint32_t val;
326
+   if (spa_atou32(str, &val, 0) && val <= 65535u)
327
+       return val;
328
+   return def;
329
+}
330
+
331
+static int make_tcp_socket(struct server *server, const char *name)
332
+{
333
+   struct sockaddr_storage addr;
334
    int res, fd, on;
335
-   uint32_t address = INADDR_ANY;
336
    uint16_t port;
337
-   char *col;
338
+   char *br = NULL, *col, *n;
339
+   socklen_t len = 0;
340
+
341
+   n = strdupa(name);
342
+
343
+   col = strrchr(n, ':');
344
+   if (n0 == '') {
345
+       br = strchr(n, '');
346
+       if (br == NULL)
347
+           return -EINVAL;
348
+       n++;
349
+       *br = 0;
350
+   } else {
351
+   }
352
+   if (br && col && col < br)
353
+       col = NULL;
354
 
355
-   col = strchr(name, ':');
356
    if (col) {
357
-       struct in_addr ipv4;
358
-       char *n;
359
-       port = atoi(col+1);
360
-       n = strndupa(name, col - name);
361
-       if (inet_pton(AF_INET, n, &ipv4) > 0)
362
-           address = ntohl(ipv4.s_addr);
363
-       else
364
-           address = INADDR_ANY;
365
+       *col = '\0';
366
+       port = parse_port(col+1, DEFAULT_PORT);
367
    } else {
368
-       address = INADDR_ANY;
369
-       port = atoi(name);
370
+       port = parse_port(n, DEFAULT_PORT);
371
+       n = strdupa("0.0.0.0");
372
    }
373
-   if (port == 0)
374
-       port = DEFAULT_PORT;
375
 
376
-   if ((fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
377
+   if ((res = pw_net_parse_address(n, port, &addr, &len)) < 0) {
378
+       pw_log_error("%p: can't parse address:%s port:%d: %s", server,
379
+               n, port, spa_strerror(res));
380
+       goto error;
381
+   }
382
+
383
+   if ((fd = socket(addr.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
384
        res = -errno;
385
        pw_log_error("%p: socket() failed: %m", server);
386
        goto error;
387
@@ -641,12 +708,7 @@
388
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0)
389
        pw_log_warn("%p: setsockopt(): %m", server);
390
 
391
-   spa_zero(addr);
392
-   addr.sin_family = AF_INET;
393
-   addr.sin_port = htons(port);
394
-   addr.sin_addr.s_addr = htonl(address);
395
-
396
-   if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
397
+   if (bind(fd, (struct sockaddr *) &addr, len) < 0) {
398
        res = -errno;
399
        pw_log_error("%p: bind() failed: %m", server);
400
        goto error_close;
401
@@ -656,8 +718,14 @@
402
        pw_log_error("%p: listen() failed: %m", server);
403
        goto error_close;
404
    }
405
-   server->type = SERVER_TYPE_INET;
406
-   pw_log_info("listening on tcp:%08x:%u", address, port);
407
+   if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0) {
408
+       res = -errno;
409
+       pw_log_error("%p: getsockname() failed: %m", server);
410
+       goto error_close;
411
+   }
412
+
413
+   server->type = SERVER_TYPE_TCP;
414
+   server->addr = addr;
415
 
416
    return fd;
417
 
418
@@ -696,8 +764,9 @@
419
    spa_list_append(&impl->server_list, &server->link);
420
 
421
    if (spa_strstartswith(address, "tcp:")) {
422
-       fd = make_inet_socket(server, address+4);
423
+       fd = make_tcp_socket(server, address+4);
424
    } else {
425
+       pw_log_error("address %s does not start with tcp:", address);
426
        fd = -EINVAL;
427
    }
428
    if (fd < 0) {
429
@@ -727,6 +796,8 @@
430
    spa_hook_remove(&impl->module_listener);
431
    spa_list_consume(s, &impl->server_list, link)
432
        server_free(s);
433
+   pw_properties_free(impl->capture_props);
434
+   pw_properties_free(impl->playback_props);
435
    pw_properties_free(impl->props);
436
    free(impl);
437
 }
438
@@ -751,21 +822,88 @@
439
    return SPA_AUDIO_CHANNEL_UNKNOWN;
440
 }
441
 
442
-static inline uint32_t parse_position(uint32_t *pos, const char *val, size_t len)
443
+static void parse_position(struct spa_audio_info_raw *info, const char *val, size_t len)
444
 {
445
-   uint32_t channels = 0;
446
    struct spa_json it2;
447
    char v256;
448
 
449
    spa_json_init(&it0, val, len);
450
-   if (spa_json_enter_array(&it0, &it1) <= 0)
451
-       spa_json_init(&it1, val, len);
452
+        if (spa_json_enter_array(&it0, &it1) <= 0)
453
+                spa_json_init(&it1, val, len);
454
 
455
+   info->channels = 0;
456
    while (spa_json_get_string(&it1, v, sizeof(v)) > 0 &&
457
-           channels < SPA_AUDIO_MAX_CHANNELS) {
458
-       poschannels++ = channel_from_name(v);
459
+       info->channels < SPA_AUDIO_MAX_CHANNELS) {
460
+       info->positioninfo->channels++ = channel_from_name(v);
461
+   }
462
+}
463
+
464
+static int calc_frame_size(struct spa_audio_info_raw *info)
465
+{
466
+   int res = info->channels;
467
+   switch (info->format) {
468
+   case SPA_AUDIO_FORMAT_U8:
469
+   case SPA_AUDIO_FORMAT_S8:
470
+   case SPA_AUDIO_FORMAT_ALAW:
471
+   case SPA_AUDIO_FORMAT_ULAW:
472
+       return res;
473
+   case SPA_AUDIO_FORMAT_S16:
474
+   case SPA_AUDIO_FORMAT_S16_OE:
475
+   case SPA_AUDIO_FORMAT_U16:
476
+       return res * 2;
477
+   case SPA_AUDIO_FORMAT_S24:
478
+   case SPA_AUDIO_FORMAT_S24_OE:
479
+   case SPA_AUDIO_FORMAT_U24:
480
+       return res * 3;
481
+   case SPA_AUDIO_FORMAT_S24_32:
482
+   case SPA_AUDIO_FORMAT_S24_32_OE:
483
+   case SPA_AUDIO_FORMAT_S32:
484
+   case SPA_AUDIO_FORMAT_S32_OE:
485
+   case SPA_AUDIO_FORMAT_U32:
486
+   case SPA_AUDIO_FORMAT_U32_OE:
487
+   case SPA_AUDIO_FORMAT_F32:
488
+   case SPA_AUDIO_FORMAT_F32_OE:
489
+       return res * 4;
490
+   case SPA_AUDIO_FORMAT_F64:
491
+   case SPA_AUDIO_FORMAT_F64_OE:
492
+       return res * 8;
493
+   default:
494
+       return 0;
495
+   }
496
+}
497
+
498
+static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info)
499
+{
500
+   const char *str;
501
+
502
+   spa_zero(*info);
503
+   if ((str = pw_properties_get(props, PW_KEY_AUDIO_FORMAT)) == NULL)
504
+       str = DEFAULT_FORMAT;
505
+   info->format = format_from_name(str, strlen(str));
506
+
507
+   info->rate = pw_properties_get_uint32(props, PW_KEY_AUDIO_RATE, info->rate);
508
+   if (info->rate == 0)
509
+       info->rate = DEFAULT_RATE;
510
+
511
+   info->channels = pw_properties_get_uint32(props, PW_KEY_AUDIO_CHANNELS, info->channels);
512
+   info->channels = SPA_MIN(info->channels, SPA_AUDIO_MAX_CHANNELS);
513
+   if ((str = pw_properties_get(props, SPA_KEY_AUDIO_POSITION)) != NULL)
514
+       parse_position(info, str, strlen(str));
515
+   if (info->channels == 0)
516
+       parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION));
517
+
518
+   return calc_frame_size(info);
519
+}
520
+
521
+static void copy_props(struct impl *impl, const char *key)
522
+{
523
+   const char *str;
524
+   if ((str = pw_properties_get(impl->props, key)) != NULL) {
525
+       if (pw_properties_get(impl->capture_props, key) == NULL)
526
+           pw_properties_set(impl->capture_props, key, str);
527
+       if (pw_properties_get(impl->playback_props, key) == NULL)
528
+           pw_properties_set(impl->playback_props, key, str);
529
    }
530
-   return channels;
531
 }
532
 
533
 static int parse_params(struct impl *impl)
534
@@ -781,49 +919,62 @@
535
        return -EINVAL;
536
    }
537
 
538
-   if ((str = pw_properties_get(impl->props, "audio.format")) == NULL)
539
-       str = DEFAULT_FORMAT;
540
-   impl->info.format = format_from_name(str, strlen(str));
541
-   if (impl->info.format == SPA_AUDIO_FORMAT_UNKNOWN) {
542
-       pw_log_error("invalid format '%s'", str);
543
-       return -EINVAL;
544
-   }
545
-   impl->info.rate = pw_properties_get_uint32(impl->props, "audio.rate", DEFAULT_RATE);
546
-   if (impl->info.rate == 0) {
547
-       pw_log_error("invalid rate '%s'", str);
548
-       return -EINVAL;
549
-   }
550
-   impl->info.channels = pw_properties_get_uint32(impl->props, "audio.channels", DEFAULT_CHANNELS);
551
-   if (impl->info.channels == 0) {
552
-       pw_log_error("invalid channels '%s'", str);
553
-       return -EINVAL;
554
+   if (pw_properties_get(impl->props, PW_KEY_NODE_VIRTUAL) == NULL)
555
+       pw_properties_set(impl->props, PW_KEY_NODE_VIRTUAL, "true");
556
+   if (pw_properties_get(impl->props, PW_KEY_NODE_NETWORK) == NULL)
557
+       pw_properties_set(impl->props, PW_KEY_NODE_NETWORK, "true");
558
+
559
+   impl->capture_props = pw_properties_new(
560
+           PW_KEY_TARGET_OBJECT, pw_properties_get(impl->props, "capture.node"),
561
+           PW_KEY_STREAM_CAPTURE_SINK, pw_properties_get(impl->props,
562
+               PW_KEY_STREAM_CAPTURE_SINK),
563
+           NULL);
564
+   impl->playback_props = pw_properties_new(
565
+           PW_KEY_TARGET_OBJECT, pw_properties_get(impl->props, "playback.node"),
566
+           NULL);
567
+   if (impl->capture_props == NULL || impl->playback_props == NULL) {
568
+       pw_log_error("can't create props: %m");
569
+       return -errno;
570
    }
571
-   if ((str = pw_properties_get(impl->props, "audio.position")) == NULL)
572
-       str = DEFAULT_POSITION;
573
-   if (parse_position(impl->info.position, str, strlen(str)) != impl->info.channels) {
574
-       pw_log_error("invalid position '%s'", str);
575
+
576
+   if ((str = pw_properties_get(impl->props, "capture.props")) != NULL)
577
+       pw_properties_update_string(impl->capture_props, str, strlen(str));
578
+   if ((str = pw_properties_get(impl->props, "playback.props")) != NULL)
579
+       pw_properties_update_string(impl->playback_props, str, strlen(str));
580
+
581
+   copy_props(impl, PW_KEY_AUDIO_FORMAT);
582
+   copy_props(impl, PW_KEY_AUDIO_RATE);
583
+   copy_props(impl, PW_KEY_AUDIO_CHANNELS);
584
+   copy_props(impl, SPA_KEY_AUDIO_POSITION);
585
+   copy_props(impl, PW_KEY_NODE_RATE);
586
+   copy_props(impl, PW_KEY_NODE_NAME);
587
+   copy_props(impl, PW_KEY_NODE_DESCRIPTION);
588
+   copy_props(impl, PW_KEY_NODE_GROUP);
589
+   copy_props(impl, PW_KEY_NODE_LATENCY);
590
+   copy_props(impl, PW_KEY_NODE_VIRTUAL);
591
+   copy_props(impl, PW_KEY_NODE_NETWORK);
592
+
593
+   impl->capture_frame_size = parse_audio_info(impl->capture_props, &impl->capture_info);
594
+   if (impl->capture_frame_size == 0) {
595
+       pw_log_error("unsupported capture audio format:%d channels:%d",
596
+               impl->capture_info.format, impl->capture_info.channels);
597
        return -EINVAL;
598
    }
599
 
600
-   switch (impl->info.format) {
601
-   case SPA_AUDIO_FORMAT_U8:
602
-       impl->frame_size = 1;
603
-       break;
604
-   case SPA_AUDIO_FORMAT_S16_LE:
605
-   case SPA_AUDIO_FORMAT_S16_BE:
606
-   case SPA_AUDIO_FORMAT_S16P:
607
-       impl->frame_size = 2;
608
-       break;
609
-   case SPA_AUDIO_FORMAT_S24_LE:
610
-   case SPA_AUDIO_FORMAT_S24_BE:
611
-   case SPA_AUDIO_FORMAT_S24P:
612
-       impl->frame_size = 3;
613
-       break;
614
-   default:
615
-       impl->frame_size = 4;
616
-       break;
617
+   impl->playback_frame_size = parse_audio_info(impl->playback_props, &impl->playback_info);
618
+   if (impl->playback_frame_size == 0) {
619
+       pw_log_error("unsupported playback audio format:%d channels:%d",
620
+               impl->playback_info.format, impl->playback_info.channels);
621
+       return -EINVAL;
622
    }
623
-   impl->frame_size *= impl->info.channels;
624
+   if (impl->capture_info.rate != 0 &&
625
+       pw_properties_get(impl->capture_props, PW_KEY_NODE_RATE) == NULL)
626
+       pw_properties_setf(impl->capture_props, PW_KEY_NODE_RATE,
627
+               "1/%u", impl->capture_info.rate);
628
+   if (impl->playback_info.rate != 0 &&
629
+       pw_properties_get(impl->playback_props, PW_KEY_NODE_RATE) == NULL)
630
+       pw_properties_setf(impl->playback_props, PW_KEY_NODE_RATE,
631
+               "1/%u", impl->playback_info.rate);
632
 
633
    if ((str = pw_properties_get(impl->props, "server.address")) == NULL)
634
        str = DEFAULT_SERVER;
635
@@ -858,7 +1009,12 @@
636
    struct pw_context *context = pw_impl_module_get_context(module);
637
    struct pw_properties *props;
638
    struct impl *impl;
639
+   struct server *s;
640
+   FILE *f;
641
+   char *str;
642
+   size_t size;
643
    int res;
644
+   struct spa_dict_item it1;
645
 
646
    PW_LOG_TOPIC_INIT(mod_topic);
647
 
648
@@ -887,6 +1043,32 @@
649
    if ((res = parse_params(impl)) < 0)
650
        goto error_free;
651
 
652
+   if ((f = open_memstream(&str, &size)) == NULL) {
653
+       res = -errno;
654
+       pw_log_error("Can't open memstream: %m");
655
+       goto error_free;
656
+   }
657
+
658
+   fprintf(f, "");
659
+
660
+   spa_list_for_each(s, &impl->server_list, link) {
661
+       char ip128;
662
+       uint16_t port = 0;
663
+       bool ipv4;
664
+
665
+       if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) >= 0)
666
+           fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "",
667
+                   ip, ipv4 ? "" : "", port);
668
+   }
669
+   fprintf(f, " ");
670
+   fclose(f);
671
+
672
+   pw_log_info("listening on %s", str);
673
+   it0 = SPA_DICT_ITEM_INIT("server.address", str);
674
+   pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(it));
675
+
676
+   free(str);
677
+
678
    return 0;
679
 
680
 error_free:
681
pipewire-1.0.1.tar.bz2/src/modules/module-pulse-tunnel.c -> pipewire-1.2.0.tar.gz/src/modules/module-pulse-tunnel.c Changed
592
 
1
@@ -58,6 +58,9 @@
2
  * - `pulse.server.address`: the address of the PulseAudio server to tunnel to.
3
  * - `pulse.latency`: the latency to end-to-end latency in milliseconds to
4
  *                    maintain (Default 200).
5
+ * - `reconnect.interval.ms`: when the remote connection is broken, retry to connect
6
+ *                  with this interval in millisconds. A value of 0 disables recovery
7
+ *                  and will result in a module unload. (Default 0) (Since 1.1.0)
8
  * - `stream.props`: Extra properties for the local stream.
9
  *
10
  * ## General options
11
@@ -87,6 +90,7 @@
12
  *         # Set the remote address to tunnel to
13
  *         pulse.server.address = "tcp:192.168.1.126"
14
  *         #pulse.latency = 200
15
+ *         #reconnect.interval.ms = 0
16
  *         #audio.rate=<sample rate>
17
  *         #audio.channels=<number of channels>
18
  *         #audio.position=<channel map>
19
@@ -121,6 +125,7 @@
20
            "( audio.position=<channel map>  "          \
21
            "pulse.server.address=<address> "           \
22
            "( pulse.latency=<latency in msec, default 200> ) " \
23
+           "( reconnect.interval.ms=<reconnect interval in msec, default 0> ) "    \
24
            "( tunnel.mode=source|sink, default sink ) "                \
25
            "( stream.props=<properties> ) "
26
 
27
@@ -184,9 +189,16 @@
28
    float max_error;
29
    unsigned resync:1;
30
 
31
-   unsigned int do_disconnect:1;
32
+   bool do_disconnect:1;
33
+   bool stopping;
34
+
35
+   struct spa_source *timer;
36
+   uint32_t reconnect_interval_ms;
37
+   bool recovering;
38
 };
39
 
40
+static int start_pulse_connection(struct impl *impl);
41
+
42
 static void cork_stream(struct impl *impl, bool cork)
43
 {
44
    pa_operation *operation;
45
@@ -327,7 +339,7 @@
46
    error = (float)impl->target_latency - (float)(current_latency);
47
    error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
48
 
49
-   corr = spa_dll_update(&impl->dll, error);
50
+   corr = (float)spa_dll_update(&impl->dll, error);
51
    pw_log_debug("error:%f corr:%f current:%u target:%u",
52
            error, corr,
53
            current_latency, impl->target_latency);
54
@@ -463,8 +475,8 @@
55
    struct spa_pod_builder b;
56
    struct spa_latency_info latency;
57
 
58
-   impl->stream = pw_stream_new(impl->core, "pulse", impl->stream_props);
59
-   impl->stream_props = NULL;
60
+   impl->stream = pw_stream_new(impl->core, "pulse",
61
+           pw_properties_copy(impl->stream_props));
62
 
63
    if (impl->stream == NULL)
64
        return -errno;
65
@@ -503,66 +515,104 @@
66
    return 0;
67
 }
68
 
69
+static void cleanup_streams(struct impl *impl)
70
+{
71
+   if (impl->pa_mainloop) {
72
+       pa_threaded_mainloop_stop(impl->pa_mainloop);
73
+       pa_threaded_mainloop_lock(impl->pa_mainloop);
74
+   }
75
+   if (impl->pa_stream) {
76
+       pa_stream_unref(impl->pa_stream);
77
+       impl->pa_stream = NULL;
78
+   }
79
+   if (impl->pa_context) {
80
+       pa_context_disconnect(impl->pa_context);
81
+       pa_context_unref(impl->pa_context);
82
+       impl->pa_context = NULL;
83
+   }
84
+   if (impl->pa_mainloop) {
85
+       pa_threaded_mainloop_unlock(impl->pa_mainloop);
86
+       pa_threaded_mainloop_free(impl->pa_mainloop);
87
+       impl->pa_mainloop = NULL;
88
+   }
89
+   if (impl->stream)
90
+       pw_stream_destroy(impl->stream);
91
+}
92
+
93
+static void on_timer_event(void *data, uint64_t expirations)
94
+{
95
+   struct impl *impl = data;
96
+   cleanup_streams(impl);
97
+   start_pulse_connection(impl);
98
+}
99
+
100
 static int
101
-do_schedule_destroy(struct spa_loop *loop,
102
+do_schedule_recovery(struct spa_loop *loop,
103
    bool async, uint32_t seq, const void *data, size_t size, void *user_data)
104
 {
105
    struct impl *impl = user_data;
106
-   if (impl->module)
107
-       pw_impl_module_schedule_destroy(impl->module);
108
+   if (impl->reconnect_interval_ms > 0) {
109
+       struct timespec value;
110
+       uint64_t timestamp;
111
+
112
+       timestamp = impl->reconnect_interval_ms * SPA_NSEC_PER_MSEC;
113
+       value.tv_sec = timestamp / SPA_NSEC_PER_SEC;
114
+       value.tv_nsec = timestamp % SPA_NSEC_PER_SEC;
115
+       pw_loop_update_timer(impl->main_loop, impl->timer, &value, NULL, false);
116
+   } else {
117
+       if (impl->module)
118
+           pw_impl_module_schedule_destroy(impl->module);
119
+   }
120
    return 0;
121
 }
122
 
123
-static void module_schedule_destroy(struct impl *impl)
124
+static void schedule_recovery(struct impl *impl)
125
 {
126
-   pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl);
127
+   if (!impl->stopping)
128
+       pw_loop_invoke(impl->main_loop, do_schedule_recovery, 1, NULL, 0, false, impl);
129
 }
130
 
131
-static void context_state_cb(pa_context *c, void *userdata)
132
+static int
133
+do_create_stream(struct spa_loop *loop,
134
+   bool async, uint32_t seq, const void *data, size_t size, void *user_data)
135
 {
136
-   struct impl *impl = userdata;
137
-   bool do_destroy = false;
138
-   switch (pa_context_get_state(c)) {
139
-   case PA_CONTEXT_TERMINATED:
140
-   case PA_CONTEXT_FAILED:
141
-       do_destroy = true;
142
-       SPA_FALLTHROUGH;
143
-   case PA_CONTEXT_READY:
144
-       pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
145
-       break;
146
-   case PA_CONTEXT_UNCONNECTED:
147
-       do_destroy = true;
148
-       break;
149
-   case PA_CONTEXT_CONNECTING:
150
-   case PA_CONTEXT_AUTHORIZING:
151
-   case PA_CONTEXT_SETTING_NAME:
152
-       break;
153
+   struct impl *impl = user_data;
154
+   int res;
155
+   if (impl->stream == NULL) {
156
+       if ((res = create_stream(impl)) < 0) {
157
+           pw_log_error("failed to create stream: %s", spa_strerror(res));
158
+           if (impl->module)
159
+               pw_impl_module_schedule_destroy(impl->module);
160
+       }
161
    }
162
-   if (do_destroy)
163
-       module_schedule_destroy(impl);
164
+   return 0;
165
 }
166
 
167
 static void stream_state_cb(pa_stream *s, void * userdata)
168
 {
169
    struct impl *impl = userdata;
170
    bool do_destroy = false;
171
-   switch (pa_stream_get_state(s)) {
172
-   case PA_STREAM_FAILED:
173
-   case PA_STREAM_TERMINATED:
174
-       do_destroy = true;
175
-       SPA_FALLTHROUGH;
176
+   pa_stream_state_t state = pa_stream_get_state(s);
177
+
178
+   pw_log_debug("stream state %d", state);
179
+
180
+   switch (state) {
181
+   case PA_STREAM_CREATING:
182
+       break;
183
    case PA_STREAM_READY:
184
        impl->pa_index = pa_stream_get_index(impl->pa_stream);
185
-       pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
186
+       pw_loop_invoke(impl->main_loop, do_create_stream, 1, NULL, 0, false, impl);
187
        break;
188
+   case PA_STREAM_FAILED:
189
+   case PA_STREAM_TERMINATED:
190
    case PA_STREAM_UNCONNECTED:
191
        do_destroy = true;
192
        break;
193
-   case PA_STREAM_CREATING:
194
-       break;
195
    }
196
-   if (do_destroy)
197
-       module_schedule_destroy(impl);
198
+   if (do_destroy) {
199
+       pw_log_warn("stream failure: %d", state);
200
+       schedule_recovery(impl);
201
+   }
202
 }
203
 
204
 static void stream_read_request_cb(pa_stream *s, size_t length, void *userdata)
205
@@ -697,14 +747,96 @@
206
 
207
 static void stream_latency_update_cb(pa_stream *s, void *userdata)
208
 {
209
-   struct impl *impl = userdata;
210
    pa_usec_t usec;
211
    int negative;
212
-
213
    pa_stream_get_latency(s, &usec, &negative);
214
-
215
    pw_log_debug("latency %ld negative %d", usec, negative);
216
-   pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
217
+}
218
+
219
+static int create_pulse_stream(struct impl *impl)
220
+{
221
+   pa_sample_spec ss;
222
+   pa_channel_map map;
223
+   uint32_t latency_bytes, i, aux = 0;
224
+   const char *remote_node_target;
225
+   char stream_name1024;
226
+   pa_buffer_attr bufferattr;
227
+   int err = PA_ERR_IO;
228
+
229
+   ss.format = (pa_sample_format_t) format_id2pa(impl->info.format);
230
+   ss.channels = impl->info.channels;
231
+   ss.rate = impl->info.rate;
232
+
233
+   map.channels = impl->info.channels;
234
+   for (i = 0; i < map.channels; i++)
235
+       map.mapi = (pa_channel_position_t)channel_id2pa(impl->info.positioni, &aux);
236
+
237
+   snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"),
238
+           pw_get_user_name(), pw_get_host_name());
239
+
240
+   pw_log_info("create stream %s", stream_name);
241
+
242
+   if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, &map))) {
243
+       err = pa_context_errno(impl->pa_context);
244
+       goto exit;
245
+   }
246
+
247
+   pa_stream_set_state_callback(impl->pa_stream, stream_state_cb, impl);
248
+   pa_stream_set_read_callback(impl->pa_stream, stream_read_request_cb, impl);
249
+   pa_stream_set_write_callback(impl->pa_stream, stream_write_request_cb, impl);
250
+   pa_stream_set_underflow_callback(impl->pa_stream, stream_underflow_cb, impl);
251
+   pa_stream_set_overflow_callback(impl->pa_stream, stream_overflow_cb, impl);
252
+   pa_stream_set_latency_update_callback(impl->pa_stream, stream_latency_update_cb, impl);
253
+
254
+   remote_node_target = pw_properties_get(impl->props, PW_KEY_TARGET_OBJECT);
255
+
256
+   bufferattr.fragsize = (uint32_t) -1;
257
+   bufferattr.minreq = (uint32_t) -1;
258
+   bufferattr.maxlength = (uint32_t) -1;
259
+   bufferattr.prebuf = (uint32_t) -1;
260
+
261
+   latency_bytes = pa_usec_to_bytes(impl->latency_msec * SPA_USEC_PER_MSEC, &ss);
262
+
263
+   impl->target_latency = latency_bytes / impl->frame_size;
264
+
265
+   /* half in our buffer, half in the network + remote */
266
+   impl->target_buffer = latency_bytes / 2;
267
+
268
+   if (impl->mode == MODE_SOURCE) {
269
+       bufferattr.fragsize = latency_bytes / 2;
270
+
271
+       pa_context_subscribe(impl->pa_context,
272
+               PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl);
273
+
274
+       if ((err = pa_stream_connect_record(impl->pa_stream,
275
+               remote_node_target, &bufferattr,
276
+               PA_STREAM_DONT_MOVE |
277
+               PA_STREAM_INTERPOLATE_TIMING |
278
+               PA_STREAM_ADJUST_LATENCY |
279
+               PA_STREAM_AUTO_TIMING_UPDATE)) != 0)
280
+           err = pa_context_errno(impl->pa_context);
281
+   } else {
282
+       bufferattr.tlength = latency_bytes / 2;
283
+       bufferattr.minreq = bufferattr.tlength / 4;
284
+       bufferattr.prebuf = bufferattr.tlength;
285
+
286
+       pa_context_subscribe(impl->pa_context,
287
+               PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl);
288
+
289
+       if ((err = pa_stream_connect_playback(impl->pa_stream,
290
+               remote_node_target, &bufferattr,
291
+               PA_STREAM_DONT_MOVE |
292
+               PA_STREAM_INTERPOLATE_TIMING |
293
+               PA_STREAM_ADJUST_LATENCY |
294
+               PA_STREAM_AUTO_TIMING_UPDATE,
295
+               NULL, NULL)) != 0)
296
+           err = pa_context_errno(impl->pa_context);
297
+   }
298
+
299
+exit:
300
+   if (err != PA_OK)
301
+       pw_log_error("failed to create stream: %s", pa_strerror(err));
302
+   return err_to_res(err);
303
 }
304
 
305
 static int
306
@@ -721,7 +853,7 @@
307
    float soft_volsSPA_AUDIO_MAX_CHANNELS;
308
 
309
    for (i = 0; i < impl->volume.channels; i++) {
310
-       volsi = pa_sw_volume_to_linear(impl->volume.valuesi);
311
+       volsi = (float)pa_sw_volume_to_linear(impl->volume.valuesi);
312
        soft_volsi = 1.0f;
313
    }
314
 
315
@@ -779,6 +911,36 @@
316
                idx, sink_input_info_cb, impl);
317
 }
318
 
319
+static void context_state_cb(pa_context *c, void *userdata)
320
+{
321
+   struct impl *impl = userdata;
322
+   bool do_destroy = false;
323
+   pa_context_state_t state = pa_context_get_state(c);
324
+
325
+   pw_log_debug("state %d", state);
326
+
327
+   switch (state) {
328
+   case PA_CONTEXT_CONNECTING:
329
+   case PA_CONTEXT_AUTHORIZING:
330
+   case PA_CONTEXT_SETTING_NAME:
331
+       break;
332
+   case PA_CONTEXT_READY:
333
+       if (impl->pa_stream == NULL)
334
+           if (create_pulse_stream(impl) < 0)
335
+               do_destroy = true;
336
+       break;
337
+   case PA_CONTEXT_TERMINATED:
338
+   case PA_CONTEXT_UNCONNECTED:
339
+   case PA_CONTEXT_FAILED:
340
+       do_destroy = true;
341
+       break;
342
+   }
343
+   if (do_destroy) {
344
+       pw_log_warn("connection failure: %s", pa_strerror(pa_context_errno(c)));
345
+       schedule_recovery(impl);
346
+   }
347
+}
348
+
349
 static pa_proplist* tunnel_new_proplist(struct impl *impl)
350
 {
351
    pa_proplist *proplist = pa_proplist_new();
352
@@ -788,20 +950,15 @@
353
    return proplist;
354
 }
355
 
356
-static int create_pulse_stream(struct impl *impl)
357
+static int start_pulse_connection(struct impl *impl)
358
 {
359
-   pa_sample_spec ss;
360
-   pa_channel_map map;
361
-   const char *server_address, *remote_node_target;
362
+   const char *server_address;
363
    pa_proplist *props = NULL;
364
    pa_mainloop_api *api;
365
-   char stream_name1024;
366
-   pa_buffer_attr bufferattr;
367
-   int res = -EIO;
368
-   uint32_t latency_bytes, i, aux = 0;
369
+   int err = PA_ERR_IO;
370
 
371
    if ((impl->pa_mainloop = pa_threaded_mainloop_new()) == NULL)
372
-       goto error;
373
+       goto exit;
374
 
375
    api = pa_threaded_mainloop_get_api(impl->pa_mainloop);
376
 
377
@@ -810,15 +967,17 @@
378
    pa_proplist_free(props);
379
 
380
    if (impl->pa_context == NULL)
381
-       goto error;
382
+       goto exit;
383
 
384
    pa_context_set_state_callback(impl->pa_context, context_state_cb, impl);
385
 
386
    server_address = pw_properties_get(impl->props, "pulse.server.address");
387
 
388
+   pw_log_info("connecting to %s...", server_address);
389
+
390
    if (pa_context_connect(impl->pa_context, server_address, 0, NULL) < 0) {
391
-       res = pa_context_errno(impl->pa_context);
392
-       goto error;
393
+       err = pa_context_errno(impl->pa_context);
394
+       goto exit;
395
    }
396
 
397
    pa_threaded_mainloop_lock(impl->pa_mainloop);
398
@@ -826,122 +985,18 @@
399
    pa_context_set_subscribe_callback(impl->pa_context, context_subscribe_cb, impl);
400
 
401
    if (pa_threaded_mainloop_start(impl->pa_mainloop) < 0)
402
-       goto error_unlock;
403
-
404
-   for (;;) {
405
-       pa_context_state_t state;
406
-
407
-       state = pa_context_get_state(impl->pa_context);
408
-       if (state == PA_CONTEXT_READY)
409
-           break;
410
-
411
-       if (!PA_CONTEXT_IS_GOOD(state)) {
412
-           res = pa_context_errno(impl->pa_context);
413
-           goto error_unlock;
414
-       }
415
-       /* Wait until the context is ready */
416
-       pa_threaded_mainloop_wait(impl->pa_mainloop);
417
-   }
418
-
419
-   ss.format = (pa_sample_format_t) format_id2pa(impl->info.format);
420
-   ss.channels = impl->info.channels;
421
-   ss.rate = impl->info.rate;
422
-
423
-   map.channels = impl->info.channels;
424
-   for (i = 0; i < map.channels; i++)
425
-       map.mapi = (pa_channel_position_t)channel_id2pa(impl->info.positioni, &aux);
426
-
427
-   snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"),
428
-           pw_get_user_name(), pw_get_host_name());
429
-
430
-   if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, &map))) {
431
-       res = pa_context_errno(impl->pa_context);
432
-       goto error_unlock;
433
-   }
434
-
435
-   pa_stream_set_state_callback(impl->pa_stream, stream_state_cb, impl);
436
-   pa_stream_set_read_callback(impl->pa_stream, stream_read_request_cb, impl);
437
-   pa_stream_set_write_callback(impl->pa_stream, stream_write_request_cb, impl);
438
-   pa_stream_set_underflow_callback(impl->pa_stream, stream_underflow_cb, impl);
439
-   pa_stream_set_overflow_callback(impl->pa_stream, stream_overflow_cb, impl);
440
-   pa_stream_set_latency_update_callback(impl->pa_stream, stream_latency_update_cb, impl);
441
-
442
-   remote_node_target = pw_properties_get(impl->props, PW_KEY_TARGET_OBJECT);
443
-
444
-   bufferattr.fragsize = (uint32_t) -1;
445
-   bufferattr.minreq = (uint32_t) -1;
446
-   bufferattr.maxlength = (uint32_t) -1;
447
-   bufferattr.prebuf = (uint32_t) -1;
448
-
449
-   latency_bytes = pa_usec_to_bytes(impl->latency_msec * SPA_USEC_PER_MSEC, &ss);
450
-
451
-   impl->target_latency = latency_bytes / impl->frame_size;
452
-
453
-   /* half in our buffer, half in the network + remote */
454
-   impl->target_buffer = latency_bytes / 2;
455
-
456
-   if (impl->mode == MODE_SOURCE) {
457
-       bufferattr.fragsize = latency_bytes / 2;
458
-
459
-       pa_context_subscribe(impl->pa_context,
460
-               PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl);
461
-
462
-       res = pa_stream_connect_record(impl->pa_stream,
463
-               remote_node_target, &bufferattr,
464
-               PA_STREAM_DONT_MOVE |
465
-               PA_STREAM_INTERPOLATE_TIMING |
466
-               PA_STREAM_ADJUST_LATENCY |
467
-               PA_STREAM_AUTO_TIMING_UPDATE);
468
-   } else {
469
-       bufferattr.tlength = latency_bytes / 2;
470
-       bufferattr.minreq = bufferattr.tlength / 4;
471
-       bufferattr.prebuf = bufferattr.tlength;
472
-
473
-       pa_context_subscribe(impl->pa_context,
474
-               PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl);
475
-
476
-       res = pa_stream_connect_playback(impl->pa_stream,
477
-               remote_node_target, &bufferattr,
478
-               PA_STREAM_DONT_MOVE |
479
-               PA_STREAM_INTERPOLATE_TIMING |
480
-               PA_STREAM_ADJUST_LATENCY |
481
-               PA_STREAM_AUTO_TIMING_UPDATE,
482
-               NULL, NULL);
483
-   }
484
+       goto exit_unlock;
485
 
486
-   if (res < 0) {
487
-       res = pa_context_errno(impl->pa_context);
488
-       goto error_unlock;
489
-   }
490
+   err = PA_OK;
491
 
492
-   for (;;) {
493
-       pa_stream_state_t state;
494
-
495
-       state = pa_stream_get_state(impl->pa_stream);
496
-       if (state == PA_STREAM_READY)
497
-           break;
498
-
499
-       if (!PA_STREAM_IS_GOOD(state)) {
500
-           res = pa_context_errno(impl->pa_context);
501
-           goto error_unlock;
502
-       }
503
-
504
-       /* Wait until the stream is ready */
505
-       pa_threaded_mainloop_wait(impl->pa_mainloop);
506
-   }
507
-
508
-   pa_threaded_mainloop_unlock(impl->pa_mainloop);
509
-
510
-   return 0;
511
-
512
-error_unlock:
513
+exit_unlock:
514
    pa_threaded_mainloop_unlock(impl->pa_mainloop);
515
-error:
516
-   pw_log_error("failed to connect: %s", pa_strerror(res));
517
-   return err_to_res(res);
518
+exit:
519
+   if (err != PA_OK)
520
+       pw_log_error("failed to connect: %s", pa_strerror(err));
521
+   return err_to_res(err);
522
 }
523
 
524
-
525
 static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
526
 {
527
    struct impl *impl = data;
528
@@ -975,20 +1030,10 @@
529
 
530
 static void impl_destroy(struct impl *impl)
531
 {
532
+   impl->stopping = true;
533
 
534
-   if (impl->pa_mainloop)
535
-       pa_threaded_mainloop_stop(impl->pa_mainloop);
536
-   if (impl->pa_stream)
537
-       pa_stream_unref(impl->pa_stream);
538
-   if (impl->pa_context) {
539
-       pa_context_disconnect(impl->pa_context);
540
-       pa_context_unref(impl->pa_context);
541
-   }
542
-   if (impl->pa_mainloop)
543
-       pa_threaded_mainloop_free(impl->pa_mainloop);
544
+   cleanup_streams(impl);
545
 
546
-   if (impl->stream)
547
-       pw_stream_destroy(impl->stream);
548
    if (impl->core && impl->do_disconnect)
549
        pw_core_disconnect(impl->core);
550
 
551
@@ -997,6 +1042,9 @@
552
    pw_properties_free(impl->stream_props);
553
    pw_properties_free(impl->props);
554
 
555
+   if (impl->timer)
556
+       pw_loop_destroy_source(impl->main_loop, impl->timer);
557
+
558
    free(impl->buffer);
559
    free(impl);
560
 }
561
@@ -1170,9 +1218,17 @@
562
            goto error;
563
        }
564
    }
565
+   impl->reconnect_interval_ms = pw_properties_get_uint32(props,
566
+           "reconnect.interval.ms", 0);
567
 
568
-   impl->latency_msec = pw_properties_get_uint32(props, "pulse.latency", DEFAULT_LATENCY_MSEC);
569
+   impl->timer = pw_loop_add_timer(impl->main_loop, on_timer_event, impl);
570
+   if (impl->timer == NULL) {
571
+       res = -errno;
572
+       pw_log_error("can't create timer source: %m");
573
+       goto error;
574
+   }
575
 
576
+   impl->latency_msec = pw_properties_get_uint32(props, "pulse.latency", DEFAULT_LATENCY_MSEC);
577
 
578
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
579
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
580
@@ -1234,10 +1290,7 @@
581
            &impl->core_listener,
582
            &core_events, impl);
583
 
584
-   if ((res = create_pulse_stream(impl)) < 0)
585
-       goto error;
586
-
587
-   if ((res = create_stream(impl)) < 0)
588
+   if ((res = start_pulse_connection(impl)) < 0)
589
        goto error;
590
 
591
    pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
592
pipewire-1.0.1.tar.bz2/src/modules/module-raop-discover.c -> pipewire-1.2.0.tar.gz/src/modules/module-raop-discover.c Changed
115
 
1
@@ -45,6 +45,8 @@
2
  *
3
  * Options specific to the behavior of this module
4
  *
5
+ * - `roap.discover-local` = allow discovery of local services as well.
6
+ *    false by default.
7
  * - `raop.latency.ms` = latency for all streams in microseconds. This
8
  *    can be overwritten in the stream rules.
9
  * - `stream.rules` = <rules>: match rules, use create-stream actions. See
10
@@ -56,12 +58,11 @@
11
  * context.modules = 
12
  * {   name = libpipewire-raop-discover
13
  *     args = {
14
+ *         #roap.discover-local = false;
15
  *         #raop.latency.ms = 1000
16
  *         stream.rules = 
17
  *             {   matches = 
18
  *                     {    raop.ip = "~.*"
19
- *                          #raop.ip.version = 4 | 6
20
- *                          #raop.ip.version = 4
21
  *                          #raop.port = 1000
22
  *                          #raop.name = ""
23
  *                          #raop.hostname = ""
24
@@ -119,6 +120,8 @@
25
 struct impl {
26
    struct pw_context *context;
27
 
28
+   bool discover_local;
29
+
30
    struct pw_impl_module *module;
31
    struct spa_hook module_listener;
32
 
33
@@ -247,7 +250,7 @@
34
            value = "none";
35
        pw_properties_set(props, "raop.encryption.type", value);
36
    } else if (spa_streq(key, "cn")) {
37
-       /* Suported audio codecs:
38
+       /* Supported audio codecs:
39
         *  0 = PCM,
40
         *  1 = ALAC,
41
         *  2 = AAC,
42
@@ -365,11 +368,10 @@
43
    struct impl *impl = userdata;
44
    struct tunnel_info tinfo;
45
    struct tunnel *t;
46
-   const char *str;
47
+   const char *str, *link_local_range = "169.254.";
48
    AvahiStringList *l;
49
    struct pw_properties *props = NULL;
50
-   char atAVAHI_ADDRESS_STR_MAX;
51
-   int ipv;
52
+   char atAVAHI_ADDRESS_STR_MAX, if_suffix16 = "";
53
 
54
    if (event != AVAHI_RESOLVER_FOUND) {
55
        pw_log_error("Resolving of '%s' failed: %s", name,
56
@@ -377,6 +379,12 @@
57
        goto done;
58
    }
59
 
60
+   avahi_address_snprint(at, sizeof(at), a);
61
+   if (spa_strstartswith(at, link_local_range)) {
62
+       pw_log_info("found link-local ip address %s - skipping tunnel creation", at);
63
+       goto done;
64
+   }
65
+
66
    tinfo = TUNNEL_INFO(.name = name);
67
 
68
    t = find_tunnel(impl, &tinfo);
69
@@ -387,7 +395,7 @@
70
        goto done;
71
    }
72
    if (t->module != NULL) {
73
-       pw_log_info("found duplicate mdns entry - skipping tunnel creation");
74
+       pw_log_info("found duplicate mdns entry for %s on IP %s - skipping tunnel creation", name, at);
75
        goto done;
76
    }
77
 
78
@@ -397,10 +405,13 @@
79
        goto done;
80
    }
81
 
82
-   avahi_address_snprint(at, sizeof(at), a);
83
-   ipv = protocol == AVAHI_PROTO_INET ? 4 : 6;
84
-   pw_properties_setf(props, "raop.ip", "%s", at);
85
-   pw_properties_setf(props, "raop.ip.version", "%d", ipv);
86
+   if (a->proto == AVAHI_PROTO_INET6 &&
87
+       a->data.ipv6.address0 == 0xfe &&
88
+       (a->data.ipv6.address1 & 0xc0) == 0x80)
89
+       snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
90
+
91
+   pw_properties_setf(props, "raop.ip", "%s%s", at, if_suffix);
92
+   pw_properties_setf(props, "raop.ifindex", "%d", interface);
93
    pw_properties_setf(props, "raop.port", "%u", port);
94
    pw_properties_setf(props, "raop.name", "%s", name);
95
    pw_properties_setf(props, "raop.hostname", "%s", host_name);
96
@@ -449,7 +460,7 @@
97
    struct tunnel_info info;
98
    struct tunnel *t;
99
 
100
-   if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
101
+   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
102
        return;
103
 
104
    info = TUNNEL_INFO(.name = name);
105
@@ -583,6 +594,9 @@
106
    impl->context = context;
107
    impl->properties = props;
108
 
109
+   impl->discover_local =  pw_properties_get_bool(impl->properties,
110
+           "raop.discover-local", false);
111
+
112
    pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
113
 
114
    pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
115
pipewire-1.0.1.tar.bz2/src/modules/module-raop-sink.c -> pipewire-1.2.0.tar.gz/src/modules/module-raop-sink.c Changed
255
 
1
@@ -26,9 +26,11 @@
2
 #include <openssl/engine.h>
3
 #include <openssl/aes.h>
4
 #include <openssl/md5.h>
5
+#include <openssl/evp.h>
6
 
7
 #include "config.h"
8
 
9
+#include <spa/utils/cleanup.h>
10
 #include <spa/utils/result.h>
11
 #include <spa/utils/string.h>
12
 #include <spa/utils/json.h>
13
@@ -39,10 +41,11 @@
14
 #include <spa/param/audio/raw.h>
15
 #include <spa/param/latency-utils.h>
16
 
17
-#include <pipewire/cleanup.h>
18
 #include <pipewire/impl.h>
19
 #include <pipewire/i18n.h>
20
 
21
+#include "network-utils.h"
22
+
23
 #include "module-raop/rtsp-client.h"
24
 #include "module-rtp/rtp.h"
25
 #include "module-rtp/stream.h"
26
@@ -123,7 +126,7 @@
27
 
28
 #define NAME "raop-sink"
29
 
30
-PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
31
+PW_LOG_TOPIC(mod_topic, "mod." NAME);
32
 #define PW_LOG_TOPIC_DEFAULT mod_topic
33
 
34
 #define BUFFER_SIZE        (1u<<22)
35
@@ -252,7 +255,7 @@
36
    struct spa_source *server_source;
37
 
38
    uint32_t psamples;
39
-   uint64_t rate;
40
+   uint32_t rate;
41
    uint32_t mtu;
42
    uint32_t stride;
43
    uint32_t latency;
44
@@ -586,38 +589,25 @@
45
 static int connect_socket(struct impl *impl, int type, int fd, uint16_t port)
46
 {
47
    const char *host;
48
-   struct sockaddr_in sa4;
49
-   struct sockaddr_in6 sa6;
50
-   struct sockaddr *sa;
51
-   size_t salen;
52
-   int res, af;
53
+   struct sockaddr_storage addr;
54
+   socklen_t len = 0;
55
+   int res;
56
 
57
    host = pw_properties_get(impl->props, "raop.ip");
58
    if (host == NULL)
59
        return -EINVAL;
60
 
61
-   if (inet_pton(AF_INET, host, &sa4.sin_addr) > 0) {
62
-       sa4.sin_family = af = AF_INET;
63
-       sa4.sin_port = htons(port);
64
-       sa = (struct sockaddr *) &sa4;
65
-       salen = sizeof(sa4);
66
-   } else if (inet_pton(AF_INET6, host, &sa6.sin6_addr) > 0) {
67
-       sa6.sin6_family = af = AF_INET6;
68
-       sa6.sin6_port = htons(port);
69
-       sa = (struct sockaddr *) &sa6;
70
-       salen = sizeof(sa6);
71
-   } else {
72
-       pw_log_error("Invalid host '%s'", host);
73
+   if ((res = pw_net_parse_address(host, port, &addr, &len)) < 0) {
74
+       pw_log_error("Invalid host '%s' port:%d", host, port);
75
        return -EINVAL;
76
    }
77
-
78
    if (fd < 0 &&
79
-       (fd = socket(af, type | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
80
+       (fd = socket(addr.ss_family, type | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
81
        pw_log_error("socket failed: %m");
82
        return -errno;
83
    }
84
 
85
-   res = connect(fd, sa, salen);
86
+   res = connect(fd, (struct sockaddr*)&addr, len);
87
    if (res < 0 && errno != EINPROGRESS) {
88
        res = -errno;
89
        pw_log_error("connect failed: %m");
90
@@ -864,7 +854,7 @@
91
 
92
 static uint32_t msec_to_samples(struct impl *impl, uint32_t msec)
93
 {
94
-   return msec * impl->rate / 1000;
95
+   return (uint64_t) msec * impl->rate / 1000;
96
 }
97
 
98
 static int rtsp_record_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
99
@@ -880,6 +870,13 @@
100
    struct timespec timeout, interval;
101
 
102
    pw_log_info("record status: %d", status);
103
+   switch (status) {
104
+   case 200:
105
+       break;
106
+   default:
107
+       pw_impl_module_schedule_destroy(impl->module);
108
+       return 0;
109
+   }
110
 
111
    timeout.tv_sec = 2;
112
    timeout.tv_nsec = 0;
113
@@ -971,6 +968,7 @@
114
    return;
115
 error:
116
    pw_loop_update_io(impl->loop, impl->server_source, 0);
117
+   pw_impl_module_schedule_destroy(impl->module);
118
 }
119
 
120
 static int rtsp_setup_reply(void *data, int status, const struct spa_dict *headers, const struct pw_array *content)
121
@@ -982,6 +980,13 @@
122
    uint16_t control_port, timing_port;
123
 
124
    pw_log_info("setup status: %d", status);
125
+   switch (status) {
126
+   case 200:
127
+       break;
128
+   default:
129
+       pw_impl_module_schedule_destroy(impl->module);
130
+       return 0;
131
+   }
132
 
133
    if ((str = spa_dict_lookup(headers, "Session")) == NULL) {
134
        pw_log_error("missing Session header");
135
@@ -1110,6 +1115,13 @@
136
    struct impl *impl = data;
137
 
138
    pw_log_info("announce status: %d", status);
139
+   switch (status) {
140
+   case 200:
141
+       break;
142
+   default:
143
+       pw_impl_module_schedule_destroy(impl->module);
144
+       return 0;
145
+   }
146
 
147
    pw_properties_set(impl->headers, "Apple-Challenge", NULL);
148
 
149
@@ -1232,7 +1244,7 @@
150
                "a=rtpmap:96 AppleLossless\r\n"
151
                "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 %u\r\n",
152
                impl->session_id, ip_version, local_ip,
153
-               ip_version, host, impl->psamples, (uint32_t)impl->rate);
154
+               ip_version, host, impl->psamples, impl->rate);
155
        if (!sdp)
156
            return -errno;
157
        break;
158
@@ -1247,7 +1259,7 @@
159
                "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 %u\r\n"
160
                "a=min-latency:%d",
161
                impl->session_id, ip_version, local_ip,
162
-               ip_version, host, impl->psamples, (uint32_t)impl->rate,
163
+               ip_version, host, impl->psamples, impl->rate,
164
                rtp_latency);
165
        if (!sdp)
166
            return -errno;
167
@@ -1284,7 +1296,7 @@
168
                "a=rsaaeskey:%s\r\n"
169
                "a=aesiv:%s\r\n",
170
                impl->session_id, ip_version, local_ip,
171
-               ip_version, host, impl->psamples, (uint32_t)impl->rate,
172
+               ip_version, host, impl->psamples, impl->rate,
173
                key, iv);
174
        if (!sdp)
175
            return -errno;
176
@@ -1302,6 +1314,13 @@
177
    struct impl *impl = data;
178
 
179
    pw_log_info("auth-setup status: %d", status);
180
+   switch (status) {
181
+   case 200:
182
+       break;
183
+   default:
184
+       pw_impl_module_schedule_destroy(impl->module);
185
+       return 0;
186
+   }
187
 
188
    return rtsp_do_announce(impl);
189
 }
190
@@ -1332,6 +1351,9 @@
191
        else
192
            res = rtsp_do_announce(impl);
193
        break;
194
+   default:
195
+       pw_impl_module_schedule_destroy(impl->module);
196
+       return 0;
197
    }
198
    return res;
199
 }
200
@@ -1405,6 +1427,9 @@
201
        else
202
            res = rtsp_do_announce(impl);
203
        break;
204
+   default:
205
+       pw_impl_module_schedule_destroy(impl->module);
206
+       return 0;
207
    }
208
    return res;
209
 }
210
@@ -1521,8 +1546,6 @@
211
        pw_impl_module_schedule_destroy(impl->module);
212
        return;
213
    }
214
-   if (started)
215
-       rtsp_do_record(impl);
216
 }
217
 
218
 static int rtsp_do_connect(struct impl *impl)
219
@@ -1616,7 +1639,7 @@
220
                    soft_volsi = 1.0f;
221
                }
222
                volume /= n_vols;
223
-               volume = SPA_CLAMPF(cbrt(volume) * 30 - 30, VOLUME_MIN, VOLUME_MAX);
224
+               volume = SPA_CLAMPF(cbrtf(volume) * 30 - 30, VOLUME_MIN, VOLUME_MAX);
225
                impl->volume = volume;
226
 
227
                rtsp_send_volume(impl);
228
@@ -1858,7 +1881,7 @@
229
    if (pw_properties_get(props, PW_KEY_AUDIO_FORMAT) == NULL)
230
        pw_properties_setf(props, PW_KEY_AUDIO_FORMAT, "%s", RAOP_FORMAT);
231
    if (pw_properties_get(props, PW_KEY_AUDIO_RATE) == NULL)
232
-       pw_properties_setf(props, PW_KEY_AUDIO_RATE, "%ld", impl->rate);
233
+       pw_properties_setf(props, PW_KEY_AUDIO_RATE, "%u", impl->rate);
234
    if (pw_properties_get(props, PW_KEY_DEVICE_ICON_NAME) == NULL)
235
        pw_properties_set(props, PW_KEY_DEVICE_ICON_NAME, "audio-speakers");
236
    if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
237
@@ -1869,7 +1892,7 @@
238
    if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
239
        pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "%s", name);
240
    if (pw_properties_get(props, PW_KEY_NODE_LATENCY) == NULL)
241
-       pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%ld",
242
+       pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u",
243
                impl->psamples, impl->rate);
244
    if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
245
        pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
246
@@ -1882,7 +1905,7 @@
247
    if (pw_properties_get(props, "rtp.sender-ts-offset") == NULL)
248
        pw_properties_setf(props, "rtp.sender-ts-offset", "%d", 0);
249
    if (pw_properties_get(props, "sess.ts-direct") == NULL)
250
-       pw_properties_set(props, "sess.ts-direct", 0);
251
+       pw_properties_setf(props, "sess.ts-direct", "%d", 0);
252
    if (pw_properties_get(props, "sess.media") == NULL)
253
        pw_properties_set(props, "sess.media", "raop");
254
    if (pw_properties_get(props, "sess.latency.msec") == NULL)
255
pipewire-1.0.1.tar.bz2/src/modules/module-roc-sink.c -> pipewire-1.2.0.tar.gz/src/modules/module-roc-sink.c Changed
17
 
1
@@ -19,7 +19,6 @@
2
 #include <roc/log.h>
3
 #include <roc/sender.h>
4
 
5
-#include <pipewire/cleanup.h>
6
 #include <pipewire/pipewire.h>
7
 #include <pipewire/impl.h>
8
 
9
@@ -265,6 +264,7 @@
10
    sender_config.frame_encoding.rate = data->rate;
11
    sender_config.frame_encoding.channels = ROC_CHANNEL_LAYOUT_STEREO;
12
    sender_config.frame_encoding.format = ROC_FORMAT_PCM_FLOAT32;
13
+   sender_config.packet_encoding = ROC_PACKET_ENCODING_AVP_L16_STEREO;
14
    sender_config.fec_encoding = data->fec_code;
15
 
16
    info.rate = data->rate;
17
pipewire-1.0.1.tar.bz2/src/modules/module-roc-source.c -> pipewire-1.2.0.tar.gz/src/modules/module-roc-source.c Changed
9
 
1
@@ -19,7 +19,6 @@
2
 #include <roc/log.h>
3
 #include <roc/receiver.h>
4
 
5
-#include <pipewire/cleanup.h>
6
 #include <pipewire/pipewire.h>
7
 #include <pipewire/impl.h>
8
 
9
pipewire-1.0.1.tar.bz2/src/modules/module-rt.c -> pipewire-1.2.0.tar.gz/src/modules/module-rt.c Changed
261
 
1
@@ -54,6 +54,7 @@
2
 
3
 #ifdef HAVE_DBUS
4
 #include <spa/support/dbus.h>
5
+#include <spa-private/dbus-helpers.h>
6
 #include <dbus/dbus.h>
7
 #endif
8
 
9
@@ -135,7 +136,7 @@
10
 
11
 #define DEFAULT_NICE_LEVEL 20  /* invalid value by default, see above */
12
 #define DEFAULT_RT_PRIO_MIN    11
13
-#define DEFAULT_RT_PRIO        88
14
+#define DEFAULT_RT_PRIO        RTPRIO_CLIENT
15
 #define DEFAULT_RT_TIME_SOFT   -1
16
 #define DEFAULT_RT_TIME_HARD   -1
17
 
18
@@ -337,102 +338,73 @@
19
 static long long rtkit_get_int_property(struct impl *impl, const char *propname,
20
                    long long *propval)
21
 {
22
-   DBusMessage *m = NULL, *r = NULL;
23
+   spa_autoptr(DBusMessage) m = NULL, r = NULL;
24
    DBusMessageIter iter, subiter;
25
    dbus_int64_t i64;
26
    dbus_int32_t i32;
27
-   DBusError error;
28
-   int current_type;
29
-   long long ret;
30
    struct pw_rtkit_bus *connection = impl->rtkit_bus;
31
 
32
-   dbus_error_init(&error);
33
-
34
    if (!(m = dbus_message_new_method_call(impl->service_name,
35
                           impl->object_path,
36
                           "org.freedesktop.DBus.Properties", "Get"))) {
37
-       ret = -ENOMEM;
38
-       goto finish;
39
+       return -ENOMEM;
40
    }
41
 
42
    if (!dbus_message_append_args(m,
43
                      DBUS_TYPE_STRING, &impl->interface,
44
                      DBUS_TYPE_STRING, &propname, DBUS_TYPE_INVALID)) {
45
-       ret = -ENOMEM;
46
-       goto finish;
47
-   }
48
-
49
-   if (!(r = dbus_connection_send_with_reply_and_block(connection->bus, m, -1, &error))) {
50
-       ret = translate_error(error.name);
51
-       goto finish;
52
+       return -ENOMEM;
53
    }
54
 
55
-   if (dbus_set_error_from_message(&error, r)) {
56
-       ret = translate_error(error.name);
57
-       goto finish;
58
-   }
59
+   spa_auto(DBusError) error = DBUS_ERROR_INIT;
60
 
61
-   ret = -EBADMSG;
62
-   dbus_message_iter_init(r, &iter);
63
-   while ((current_type = dbus_message_iter_get_arg_type(&iter)) != DBUS_TYPE_INVALID) {
64
+   if (!(r = dbus_connection_send_with_reply_and_block(connection->bus, m, -1, &error)))
65
+       return translate_error(error.name);
66
 
67
-       if (current_type == DBUS_TYPE_VARIANT) {
68
-           dbus_message_iter_recurse(&iter, &subiter);
69
+   if (dbus_set_error_from_message(&error, r))
70
+       return translate_error(error.name);
71
 
72
-           while ((current_type =
73
-               dbus_message_iter_get_arg_type(&subiter)) != DBUS_TYPE_INVALID) {
74
+   if (!dbus_message_has_signature(r, "v"))
75
+       return -EBADMSG;
76
 
77
-               if (current_type == DBUS_TYPE_INT32) {
78
-                   dbus_message_iter_get_basic(&subiter, &i32);
79
-                   *propval = i32;
80
-                   ret = 0;
81
-               }
82
-
83
-               if (current_type == DBUS_TYPE_INT64) {
84
-                   dbus_message_iter_get_basic(&subiter, &i64);
85
-                   *propval = i64;
86
-                   ret = 0;
87
-               }
88
+   dbus_message_iter_init(r, &iter);
89
 
90
-               dbus_message_iter_next(&subiter);
91
-           }
92
-       }
93
-       dbus_message_iter_next(&iter);
94
+   if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
95
+       return -EBADMSG;
96
+
97
+   dbus_message_iter_recurse(&iter, &subiter);
98
+
99
+   switch (dbus_message_iter_get_arg_type(&subiter)) {
100
+   case DBUS_TYPE_INT32:
101
+       dbus_message_iter_get_basic(&subiter, &i32);
102
+       *propval = i32;
103
+       break;
104
+   case DBUS_TYPE_INT64:
105
+       dbus_message_iter_get_basic(&subiter, &i64);
106
+       *propval = i64;
107
+       break;
108
+   default:
109
+       return -EBADMSG;
110
    }
111
 
112
-finish:
113
-
114
-   if (m)
115
-       dbus_message_unref(m);
116
-
117
-   if (r)
118
-       dbus_message_unref(r);
119
-
120
-   dbus_error_free(&error);
121
-
122
-   return ret;
123
+   return 0;
124
 }
125
 
126
 static int pw_rtkit_make_realtime(struct impl *impl, pid_t thread, int priority)
127
 {
128
-   DBusMessage *m = NULL;
129
+   spa_autoptr(DBusMessage) m = NULL;
130
    dbus_uint64_t pid;
131
    dbus_uint64_t u64;
132
-   dbus_uint32_t u32, serial;
133
-   DBusError error;
134
-   int ret;
135
+   dbus_uint32_t u32;
136
    struct pw_rtkit_bus *connection = impl->rtkit_bus;
137
 
138
-   dbus_error_init(&error);
139
-
140
    if (thread == 0)
141
        thread = _gettid();
142
 
143
    if (!(m = dbus_message_new_method_call(impl->service_name,
144
                           impl->object_path, impl->interface,
145
                           "MakeThreadRealtimeWithPID"))) {
146
-       ret = -ENOMEM;
147
-       goto finish;
148
+       return -ENOMEM;
149
    }
150
 
151
    pid = (dbus_uint64_t) getpid();
152
@@ -443,33 +415,21 @@
153
                      DBUS_TYPE_UINT64, &pid,
154
                      DBUS_TYPE_UINT64, &u64,
155
                      DBUS_TYPE_UINT32, &u32, DBUS_TYPE_INVALID)) {
156
-       ret = -ENOMEM;
157
-       goto finish;
158
-   }
159
-
160
-   if (!dbus_connection_send(connection->bus, m, &serial)) {
161
-       ret = translate_error(error.name);
162
-       goto finish;
163
+       return -ENOMEM;
164
    }
165
-   ret = 0;
166
-
167
-finish:
168
 
169
-   if (m)
170
-       dbus_message_unref(m);
171
+   if (!dbus_connection_send(connection->bus, m, NULL))
172
+       return -EIO;
173
 
174
-   return ret;
175
+   return 0;
176
 }
177
 
178
-
179
 static int pw_rtkit_make_high_priority(struct impl *impl, pid_t thread, int nice_level)
180
 {
181
-   DBusMessage *m = NULL;
182
+   spa_autoptr(DBusMessage) m = NULL;
183
    dbus_uint64_t pid;
184
    dbus_uint64_t u64;
185
    dbus_int32_t s32;
186
-   dbus_uint32_t serial;
187
-   int ret;
188
    struct pw_rtkit_bus *connection = impl->rtkit_bus;
189
 
190
    if (thread == 0)
191
@@ -478,8 +438,7 @@
192
    if (!(m = dbus_message_new_method_call(impl->service_name,
193
                           impl->object_path, impl->interface,
194
                           "MakeThreadHighPriorityWithPID"))) {
195
-       ret = -ENOMEM;
196
-       goto finish;
197
+       return -ENOMEM;
198
    }
199
 
200
    pid = (dbus_uint64_t) getpid();
201
@@ -490,21 +449,13 @@
202
                      DBUS_TYPE_UINT64, &pid,
203
                      DBUS_TYPE_UINT64, &u64,
204
                      DBUS_TYPE_INT32, &s32, DBUS_TYPE_INVALID)) {
205
-       ret = -ENOMEM;
206
-       goto finish;
207
-   }
208
-   if (!dbus_connection_send(connection->bus, m, &serial)) {
209
-       ret = -EIO;
210
-       goto finish;
211
+       return -ENOMEM;
212
    }
213
-   ret = 0;
214
-
215
-finish:
216
 
217
-   if (m)
218
-       dbus_message_unref(m);
219
+   if (!dbus_connection_send(connection->bus, m, NULL))
220
+       return -EIO;
221
 
222
-   return ret;
223
+   return 0;
224
 }
225
 #endif /* HAVE_DBUS */
226
 
227
@@ -573,6 +524,7 @@
228
            pw_log_warn("Failed to check RLIMIT_RTPRIO: %s", strerror(err));
229
            break;
230
        }
231
+       min = max = 0;
232
        if ((err = get_rt_priority_range(&min, &max)) < 0) {
233
            pw_log_warn("Failed to get priority range: %s", strerror(err));
234
            break;
235
@@ -696,6 +648,7 @@
236
    struct sched_param sp;
237
    pthread_t pt = (pthread_t)thread;
238
 
239
+   min = max = 0;
240
    if ((err = get_rt_priority_range(&min, &max)) < 0)
241
        return err;
242
 
243
@@ -1003,7 +956,7 @@
244
            impl->interface = RTKIT_INTERFACE;
245
        } else {
246
            res = -errno;
247
-           pw_log_warn("Realtime scheduling disabled: unsufficient realtime privileges, "
248
+           pw_log_warn("Realtime scheduling disabled: insufficient realtime privileges, "
249
                "Portal not found on session bus, and no system bus for RTKit: %m");
250
            return res;
251
        }
252
@@ -1055,7 +1008,7 @@
253
 }
254
 #endif /* HAVE_DBUS */
255
 
256
-int set_uclamp(int uclamp_min, int uclamp_max, pid_t pid) {
257
+static int set_uclamp(int uclamp_min, int uclamp_max, pid_t pid) {
258
 #ifdef __linux__
259
    int ret;
260
    struct sched_attr {
261
pipewire-1.0.1.tar.bz2/src/modules/module-rtp-sap.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp-sap.c Changed
784
 
1
@@ -15,17 +15,22 @@
2
 #include <net/if.h>
3
 #include <ctype.h>
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/hook.h>
7
 #include <spa/utils/result.h>
8
+#include <spa/utils/json.h>
9
 #include <spa/debug/types.h>
10
 
11
 #include <pipewire/pipewire.h>
12
 #include <pipewire/impl.h>
13
 
14
 #include <module-rtp/sap.h>
15
+#include <module-rtp/ptp.h>
16
+#include "network-utils.h"
17
 
18
 #ifdef __FreeBSD__
19
 #define ifr_ifindex ifr_index
20
+#define SO_PASSCRED LOCAL_CREDS_PERSISTENT
21
 #endif
22
 
23
 /** \page page_module_rtp_sap SAP Announce and create RTP streams
24
@@ -56,6 +61,8 @@
25
  * - `net.ttl = <int>`: TTL to use, default 1
26
  * - `net.loop = <bool>`: loopback multicast, default false
27
  * - `stream.rules` = <rules>: match rules, use create-stream and announce-stream actions
28
+ * - `sap.preamble-extra = strings`: extra attributes to add to the atomic SDP preamble
29
+ * - `sap.end-extra = strings`: extra attributes to add to the end of the SDP message
30
  *
31
  * ## General options
32
  *
33
@@ -78,7 +85,7 @@
34
  *         stream.rules = 
35
  *             {   matches = 
36
  *                     # any of the items in matches needs to match, if one does,
37
- *                     # actions are emited.
38
+ *                     # actions are emitted.
39
  *                     {   # all keys must match the value. ! negates. ~ starts regex.
40
  *                         #rtp.origin = "wim 3883629975 0 IN IP4 0.0.0.0"
41
  *                         #rtp.payload = "127"
42
@@ -142,6 +149,7 @@
43
 #define DEFAULT_SAP_PORT   9875
44
 
45
 #define DEFAULT_SOURCE_IP  "0.0.0.0"
46
+#define DEFAULT_SOURCE_IP6 "::"
47
 #define DEFAULT_TTL        1
48
 #define DEFAULT_LOOP       false
49
 
50
@@ -161,9 +169,18 @@
51
    { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
52
 };
53
 
54
+#define PTP_MESSAGE_TYPE_MANAGEMENT 0x0d
55
+#define PTP_VERSION_1588_2008_2_1 0x12
56
+#define PTP_DEFAULT_LOG_MESSAGE_INTERVAL 127
57
+#define PTP_MGMT_ACTION_GET 0
58
+#define PTP_MGMT_ACTION_RESPONSE 2
59
+#define PTP_TLV_TYPE_MGMT 0x0001
60
+#define PTP_MGMT_ID_PARENT_DATA_SET 0x2002
61
+
62
 struct sdp_info {
63
    uint16_t hash;
64
    uint32_t ntp;
65
+   uint32_t t_ntp;
66
 
67
    char *origin;
68
    char *session_name;
69
@@ -183,6 +200,7 @@
70
    uint32_t channels;
71
 
72
    float ptime;
73
+   uint32_t framecount;
74
 
75
    uint32_t ts_offset;
76
    char *ts_refclk;
77
@@ -193,6 +211,7 @@
78
 
79
    bool announce;
80
    uint64_t timestamp;
81
+   bool ts_refclk_ptp;
82
 
83
    struct impl *impl;
84
    struct node *node;
85
@@ -254,6 +273,15 @@
86
 
87
    uint32_t n_sessions;
88
    struct spa_list sessions;
89
+
90
+   char *extra_attrs_preamble;
91
+   char *extra_attrs_end;
92
+
93
+   char *ptp_mgmt_socket;
94
+   int ptp_fd;
95
+   uint32_t ptp_seq;
96
+   uint8_t clock_id8;
97
+   uint8_t gm_id8;
98
 };
99
 
100
 struct format_info {
101
@@ -296,11 +324,16 @@
102
    spa_zero(*info);
103
 }
104
 
105
-static void session_touch(struct session *sess)
106
+static uint64_t get_time_nsec(struct impl *impl)
107
 {
108
    struct timespec ts;
109
    clock_gettime(CLOCK_MONOTONIC, &ts);
110
-   sess->timestamp = SPA_TIMESPEC_TO_NSEC(&ts);
111
+   return SPA_TIMESPEC_TO_NSEC(&ts);
112
+}
113
+
114
+static void session_touch(struct session *sess)
115
+{
116
+   sess->timestamp = get_time_nsec(sess->impl);
117
 }
118
 
119
 static void session_free(struct session *sess)
120
@@ -326,26 +359,6 @@
121
    free(sess);
122
 }
123
 
124
-static int parse_address(const char *address, uint16_t port,
125
-       struct sockaddr_storage *addr, socklen_t *len)
126
-{
127
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
128
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
129
-
130
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
131
-       sa4->sin_family = AF_INET;
132
-       sa4->sin_port = htons(port);
133
-       *len = sizeof(*sa4);
134
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
135
-       sa6->sin6_family = AF_INET6;
136
-       sa6->sin6_port = htons(port);
137
-       *len = sizeof(*sa6);
138
-   } else
139
-       return -EINVAL;
140
-
141
-   return 0;
142
-}
143
-
144
 static bool is_multicast(struct sockaddr *sa, socklen_t salen)
145
 {
146
    if (sa->sa_family == AF_INET) {
147
@@ -359,6 +372,33 @@
148
    return false;
149
 }
150
 
151
+static int make_unix_socket(const char *path) {
152
+   struct sockaddr_un addr;
153
+
154
+   spa_autoclose int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
155
+   if (fd < 0) {
156
+       pw_log_warn("Failed to create PTP management socket");
157
+       return -1;
158
+   }
159
+
160
+   int val = 1;
161
+   if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &val, sizeof(val)) < 0) {
162
+       pw_log_warn("Failed to bind PTP management socket");
163
+       return -1;
164
+   }
165
+
166
+   spa_zero(addr);
167
+   addr.sun_family = AF_UNIX;
168
+   strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
169
+
170
+   if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
171
+       pw_log_warn("Failed to connect PTP management socket");
172
+       return -1;
173
+   }
174
+
175
+   return spa_steal_fd(fd);
176
+}
177
+
178
 static int make_send_socket(
179
        struct sockaddr_storage *src, socklen_t src_len,
180
        struct sockaddr_storage *sa, socklen_t salen,
181
@@ -366,7 +406,7 @@
182
 {
183
    int af, fd, val, res;
184
 
185
-   af = sa->ss_family;
186
+   af = src->ss_family;
187
    if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
188
        pw_log_error("socket failed: %m");
189
        return -errno;
190
@@ -382,13 +422,23 @@
191
        goto error;
192
    }
193
    if (is_multicast((struct sockaddr*)sa, salen)) {
194
-       val = loop;
195
-       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0)
196
-           pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m");
197
+       if (sa->ss_family == AF_INET) {
198
+           val = loop;
199
+           if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0)
200
+               pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m");
201
+
202
+           val = ttl;
203
+           if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0)
204
+               pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m");
205
+       } else {
206
+           val = loop;
207
+           if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) < 0)
208
+               pw_log_warn("setsockopt(IPV6_MULTICAST_LOOP) failed: %m");
209
 
210
-       val = ttl;
211
-       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0)
212
-           pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m");
213
+           val = ttl;
214
+           if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) < 0)
215
+               pw_log_warn("setsockopt(IPV6_MULTICAST_HOPS) failed: %m");
216
+       }
217
    }
218
    return fd;
219
 error:
220
@@ -401,6 +451,7 @@
221
 {
222
    int af, fd, val, res;
223
    struct ifreq req;
224
+   char addr128;
225
 
226
    af = sa->ss_family;
227
    if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
228
@@ -429,6 +480,8 @@
229
            memset(&mr4, 0, sizeof(mr4));
230
            mr4.imr_multiaddr = sa4->sin_addr;
231
            mr4.imr_ifindex = req.ifr_ifindex;
232
+           pw_net_get_ip(sa, addr, sizeof(addr), NULL, NULL);
233
+           pw_log_info("join IPv4 group: %s iface:%d", addr, req.ifr_ifindex);
234
            res = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
235
        } else {
236
            sa4->sin_addr.s_addr = INADDR_ANY;
237
@@ -440,6 +493,8 @@
238
            memset(&mr6, 0, sizeof(mr6));
239
            mr6.ipv6mr_multiaddr = sa6->sin6_addr;
240
            mr6.ipv6mr_interface = req.ifr_ifindex;
241
+           pw_net_get_ip(sa, addr, sizeof(addr), NULL, NULL);
242
+           pw_log_info("join IPv6 group: %s iface:%d", addr, req.ifr_ifindex);
243
            res = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
244
        } else {
245
                sa6->sin6_addr = in6addr_any;
246
@@ -466,20 +521,116 @@
247
    return res;
248
 }
249
 
250
-static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len, bool *ip4)
251
+static void update_ts_refclk(struct impl *impl)
252
 {
253
-   if (sa->ss_family == AF_INET) {
254
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
255
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
256
-   } else if (sa->ss_family == AF_INET6) {
257
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
258
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
259
-       *ip4 = false;
260
-   } else
261
-       return -EIO;
262
-   if (ip4)
263
-       *ip4 = sa->ss_family == AF_INET;
264
-   return 0;
265
+   if (!impl->ptp_mgmt_socket || impl->ptp_fd < 0)
266
+       return;
267
+
268
+   // Read if something is left in the socket
269
+   int avail;
270
+   ioctl(impl->ptp_fd, FIONREAD, &avail);
271
+   uint8_t tmp;
272
+   while (avail--) read(impl->ptp_fd, &tmp, 1);
273
+
274
+   struct ptp_management_msg req;
275
+   spa_zero(req);
276
+
277
+   req.major_sdo_id_message_type = PTP_MESSAGE_TYPE_MANAGEMENT;
278
+   req.ver = PTP_VERSION_1588_2008_2_1;
279
+   req.message_length_be = htobe16(sizeof(struct ptp_management_msg));
280
+   spa_zero(req.clock_identity);
281
+   req.source_port_id_be = htobe16(getpid());
282
+   req.log_message_interval = 127;
283
+   req.sequence_id_be = htobe16(impl->ptp_seq++);
284
+   memset(req.target_port_identity, 0xff, 8);
285
+   req.target_port_id_be = htobe16(0xffff);
286
+   req.starting_boundary_hops = 1;
287
+   req.boundary_hops = 1;
288
+   req.action = PTP_MGMT_ACTION_GET;
289
+   req.tlv_type_be = htobe16(PTP_TLV_TYPE_MGMT);
290
+   // sent empty TLV, only sending management_id
291
+   req.management_message_length_be = htobe16(2);
292
+   req.management_id_be = htobe16(PTP_MGMT_ID_PARENT_DATA_SET);
293
+
294
+   if (write(impl->ptp_fd, &req, sizeof(req)) == -1) {
295
+       pw_log_warn("Failed to send PTP management request: %m");
296
+       return;
297
+   }
298
+
299
+   uint8_t bufsizeof(struct ptp_management_msg) + sizeof(struct ptp_parent_data_set);
300
+   if (read(impl->ptp_fd, &buf, sizeof(buf)) == -1) {
301
+       pw_log_warn("Failed to receive PTP management response: %m");
302
+       return;
303
+   }
304
+
305
+   struct ptp_management_msg res = *(struct ptp_management_msg *)buf;
306
+   struct ptp_parent_data_set parent =
307
+       *(struct ptp_parent_data_set *)(buf + sizeof(struct ptp_management_msg));
308
+
309
+   if ((res.ver & 0x0f) != 2) {
310
+       pw_log_warn("PTP major version is %d, expected 2", res.ver);
311
+       return;
312
+   }
313
+
314
+   if ((res.major_sdo_id_message_type & 0x0f) != PTP_MESSAGE_TYPE_MANAGEMENT) {
315
+       pw_log_warn("PTP management returned type %x, expected management", res.major_sdo_id_message_type);
316
+       return;
317
+   }
318
+
319
+   if (res.action != PTP_MGMT_ACTION_RESPONSE) {
320
+       pw_log_warn("PTP management returned action %d, expected response", res.action);
321
+       return;
322
+   }
323
+
324
+   if (be16toh(res.tlv_type_be) != PTP_TLV_TYPE_MGMT) {
325
+       pw_log_warn("PTP management returned tlv type %d, expected management", be16toh(res.tlv_type_be));
326
+       return;
327
+   }
328
+
329
+   if (be16toh(res.management_id_be) != PTP_MGMT_ID_PARENT_DATA_SET) {
330
+       pw_log_warn("PTP management returned ID %d, expected PARENT_DATA_SET", be16toh(res.management_id_be));
331
+       return;
332
+   }
333
+
334
+   uint16_t data_len = be16toh(res.management_message_length_be) - 2;
335
+   if (data_len != sizeof(struct ptp_parent_data_set))
336
+       pw_log_warn("Unexpected PTP GET PARENT_DATA_SET response length %u, expected %zu", data_len, sizeof(struct ptp_parent_data_set));
337
+
338
+   uint8_t *cid = res.clock_identity;
339
+   if (memcmp(cid, impl->clock_id, 8) != 0)
340
+       pw_log_info(
341
+           "Local clock ID: IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%d",
342
+           cid0,
343
+           cid1,
344
+           cid2,
345
+           cid3,
346
+           cid4,
347
+           cid5,
348
+           cid6,
349
+           cid7,
350
+           0 /* domain */
351
+     );
352
+
353
+   uint8_t *gmid = parent.gm_clock_id;
354
+   if (memcmp(gmid, impl->gm_id, 8) != 0)
355
+       pw_log_info(
356
+           "GM ID: IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%d",
357
+           gmid0,
358
+           gmid1,
359
+           gmid2,
360
+           gmid3,
361
+           gmid4,
362
+           gmid5,
363
+           gmid6,
364
+           gmid7,
365
+           0 /* domain */
366
+     );
367
+
368
+   // When GM is not equal to own clock we are clocked by external master
369
+   pw_log_debug("Synced to GM: %s", (memcmp(cid, gmid, 8) != 0) ? "true" : "false");
370
+
371
+   memcpy(impl->clock_id, cid, 8);
372
+   memcpy(impl->gm_id, gmid, 8);
373
 }
374
 
375
 static int send_sap(struct impl *impl, struct session *sess, bool bye)
376
@@ -506,7 +657,7 @@
377
    iov0.iov_base = &header;
378
    iov0.iov_len = sizeof(header);
379
 
380
-   if ((res = get_ip(&impl->src_addr, src_addr, sizeof(src_addr), &src_ip4)) < 0)
381
+   if ((res = pw_net_get_ip(&impl->src_addr, src_addr, sizeof(src_addr), &src_ip4, NULL)) < 0)
382
        return res;
383
 
384
    if (src_ip4) {
385
@@ -520,7 +671,7 @@
386
    iov2.iov_base = SAP_MIME_TYPE;
387
    iov2.iov_len = sizeof(SAP_MIME_TYPE);
388
 
389
-   if ((res = get_ip(&sdp->dst_addr, dst_addr, sizeof(dst_addr), &dst_ip4)) < 0)
390
+   if ((res = pw_net_get_ip(&sdp->dst_addr, dst_addr, sizeof(dst_addr), &dst_ip4, NULL)) < 0)
391
        return res;
392
 
393
    if ((user_name = pw_get_user_name()) == NULL)
394
@@ -544,14 +695,25 @@
395
            user_name, sdp->ntp, src_ip4 ? "IP4" : "IP6", src_addr,
396
            sdp->session_name,
397
            dst_ip4 ? "IP4" : "IP6", dst_addr, dst_ttl,
398
-           sdp->ntp,
399
+           sdp->t_ntp,
400
            sdp->media_type, sdp->dst_port, sdp->payload);
401
 
402
+   if (impl->extra_attrs_preamble)
403
+       spa_strbuf_append(&buf, "%s", impl->extra_attrs_preamble);
404
+
405
    if (sdp->channels) {
406
        if (sdp->channelmap0 != 0) {
407
+           // Produce Audinate format channel record. It's recognized by RAVENNA
408
            spa_strbuf_append(&buf,
409
                "i=%d channels: %s\n", sdp->channels,
410
                sdp->channelmap);
411
+       } else {
412
+           spa_strbuf_append(&buf, "i=%d channels:", sdp->channels);
413
+           for (uint i = 1; i <= sdp->channels; i++) {
414
+               if (i > 1) spa_strbuf_append(&buf, ",");
415
+               spa_strbuf_append(&buf, " AUX%u", i);
416
+           }
417
+           spa_strbuf_append(&buf, "\n");
418
        }
419
        spa_strbuf_append(&buf,
420
            "a=recvonly\n"
421
@@ -564,16 +726,37 @@
422
                sdp->payload, sdp->mime_type, sdp->rate);
423
    }
424
 
425
+   if (is_multicast((struct sockaddr*)&sdp->dst_addr, sdp->dst_len))
426
+       spa_strbuf_append(&buf,
427
+           "a=source-filter: incl IN %s %s %s\n", dst_ip4 ? "IP4" : "IP6",
428
+               dst_addr, src_addr);
429
+
430
    if (sdp->ptime > 0)
431
        spa_strbuf_append(&buf,
432
            "a=ptime:%.6g\n", sdp->ptime);
433
 
434
-   if (sdp->ts_refclk != NULL) {
435
+   if (sdp->framecount > 0)
436
        spa_strbuf_append(&buf,
437
-               "a=ts-refclk:%s\n"
438
-               "a=mediaclk:direct=%u\n",
439
-               sdp->ts_refclk,
440
-               sdp->ts_offset);
441
+           "a=framecount:%u\n", sdp->framecount);
442
+
443
+   if (sdp->ts_refclk != NULL || sess->ts_refclk_ptp) {
444
+       // Only broadcast the GM ID when we are synced to external time source
445
+       if (sess->ts_refclk_ptp && memcmp(impl->clock_id, impl->gm_id, 8) != 0) {
446
+           spa_strbuf_append(&buf,
447
+                   "a=ts-refclk:ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%d\n",
448
+                   impl->gm_id0,
449
+                   impl->gm_id1,
450
+                   impl->gm_id2,
451
+                   impl->gm_id3,
452
+                   impl->gm_id4,
453
+                   impl->gm_id5,
454
+                   impl->gm_id6,
455
+                   impl->gm_id7,
456
+                   0/* domain */);
457
+       } else {
458
+           spa_strbuf_append(&buf, "a=ts-refclk:%s\n", sdp->ts_refclk);
459
+       }
460
+       spa_strbuf_append(&buf, "a=mediaclk:direct=%u\n",   sdp->ts_offset);
461
    } else {
462
        spa_strbuf_append(&buf, "a=mediaclk:sender\n");
463
    }
464
@@ -583,6 +766,9 @@
465
        "a=type:broadcast\n",
466
        pw_get_library_version());
467
 
468
+   if (impl->extra_attrs_end)
469
+       spa_strbuf_append(&buf, "%s", impl->extra_attrs_end);
470
+
471
    pw_log_debug("sending SAP for %u %s", sess->node->id, buffer);
472
 
473
    iov3.iov_base = buffer;
474
@@ -609,12 +795,11 @@
475
 {
476
    struct impl *impl = data;
477
    struct session *sess, *tmp;
478
-   struct timespec ts;
479
    uint64_t timestamp, interval;
480
 
481
-   clock_gettime(CLOCK_MONOTONIC, &ts);
482
-   timestamp = SPA_TIMESPEC_TO_NSEC(&ts);
483
+   timestamp = get_time_nsec(impl);
484
    interval = impl->cleanup_interval * SPA_NSEC_PER_SEC;
485
+   update_ts_refclk(impl);
486
 
487
    spa_list_for_each_safe(sess, tmp, &impl->sessions, link) {
488
        if (sess->announce) {
489
@@ -665,7 +850,8 @@
490
    sess->announce = true;
491
 
492
    sdp->hash = pw_rand32();
493
-   sdp->ntp = (uint32_t) time(NULL) + 2208988800U;
494
+   sdp->ntp = (uint32_t) time(NULL) + 2208988800U + impl->n_sessions;
495
+   sdp->t_ntp = pw_properties_get_uint32(props, "rtp.ntp", sdp->ntp);
496
    sess->props = props;
497
 
498
    if ((str = pw_properties_get(props, "sess.name")) == NULL)
499
@@ -680,7 +866,7 @@
500
 
501
    if ((str = pw_properties_get(props, "rtp.destination.ip")) == NULL)
502
        goto error_free;
503
-   if ((res = parse_address(str, sdp->dst_port, &sdp->dst_addr, &sdp->dst_len)) < 0) {
504
+   if ((res = pw_net_parse_address(str, sdp->dst_port, &sdp->dst_addr, &sdp->dst_len)) < 0) {
505
        pw_log_error("invalid destination.ip %s: %s", str, spa_strerror(res));
506
        goto error_free;
507
    }
508
@@ -691,6 +877,10 @@
509
        if (!spa_atof(str, &sdp->ptime))
510
            sdp->ptime = 0.0;
511
 
512
+   if ((str = pw_properties_get(props, "rtp.framecount")) != NULL)
513
+       if (!spa_atou32(str, &sdp->framecount, 0))
514
+           sdp->framecount = 0;
515
+
516
    if ((str = pw_properties_get(props, "rtp.media")) != NULL)
517
        sdp->media_type = strdup(str);
518
    if ((str = pw_properties_get(props, "rtp.mime")) != NULL)
519
@@ -703,8 +893,23 @@
520
        sdp->ts_offset = atoi(str);
521
    if ((str = pw_properties_get(props, "rtp.ts-refclk")) != NULL)
522
        sdp->ts_refclk = strdup(str);
523
-   if ((str = pw_properties_get(props, PW_KEY_NODE_CHANNELNAMES)) != NULL)
524
-       snprintf(sdp->channelmap, sizeof(sdp->channelmap), "%s", str);
525
+   sess->ts_refclk_ptp = pw_properties_get_bool(props, "rtp.fetch-ts-refclk", false);
526
+   if ((str = pw_properties_get(props, PW_KEY_NODE_CHANNELNAMES)) != NULL) {
527
+       struct spa_strbuf buf;
528
+       spa_strbuf_init(&buf, sdp->channelmap, sizeof(sdp->channelmap));
529
+
530
+       struct spa_json it2;
531
+       char v256;
532
+
533
+       spa_json_init(&it0, str, strlen(str));
534
+       if (spa_json_enter_array(&it0, &it1) <= 0)
535
+           spa_json_init(&it1, str, strlen(str));
536
+
537
+       if (spa_json_get_string(&it1, v, sizeof(v)) > 0)
538
+           spa_strbuf_append(&buf, "%s", v);
539
+       while (spa_json_get_string(&it1, v, sizeof(v)) > 0)
540
+           spa_strbuf_append(&buf, ", %s", v);
541
+   }
542
 
543
    pw_log_info("created new session for node:%u", node->id);
544
    node->session = sess;
545
@@ -769,6 +974,10 @@
546
    if ((media = pw_properties_get(props, "sess.media")) == NULL)
547
        media = "audio";
548
 
549
+   if ((str = pw_properties_get(props, "cleanup.sec")) != NULL) {
550
+       fprintf(f, "\"cleanup.sec\" = \"%s\", ", str);
551
+   }
552
+
553
    if (spa_streq(media, "audio")) {
554
        const char *mime;
555
        const struct format_info *format_info;
556
@@ -868,7 +1077,7 @@
557
    struct session *session;
558
    struct pw_properties *props;
559
    const char *str;
560
-   char dst_addr64;
561
+   char dst_addr64, tmp64;
562
 
563
    if (impl->n_sessions >= MAX_SESSIONS) {
564
        pw_log_warn("too many sessions (%u >= %u)", impl->n_sessions, MAX_SESSIONS);
565
@@ -904,11 +1113,12 @@
566
        pw_properties_set(props, PW_KEY_MEDIA_NAME, "RTP Stream");
567
    }
568
 
569
-   get_ip(&info->dst_addr, dst_addr, sizeof(dst_addr), NULL);
570
+   pw_net_get_ip(&info->dst_addr, dst_addr, sizeof(dst_addr), NULL, NULL);
571
    pw_properties_setf(props, "rtp.destination.ip", "%s", dst_addr);
572
    pw_properties_setf(props, "rtp.destination.port", "%u", info->dst_port);
573
    pw_properties_setf(props, "rtp.payload", "%u", info->payload);
574
-   pw_properties_setf(props, "rtp.ptime", "%f", info->ptime);
575
+   pw_properties_set(props, "rtp.ptime", spa_dtoa(tmp, sizeof(tmp), info->ptime));
576
+   pw_properties_setf(props, "rtp.framecount", "%u", info->framecount);
577
    pw_properties_setf(props, "rtp.media", "%s", info->media_type);
578
    pw_properties_setf(props, "rtp.mime", "%s", info->mime_type);
579
    pw_properties_setf(props, "rtp.rate", "%u", info->rate);
580
@@ -1006,7 +1216,8 @@
581
 /* some AES67 devices have channelmap encoded in i=*
582
  * if `i` record is found, it matches the template
583
  * and channel count matches, name the channels respectively
584
- * `i=2 channels: 01, 08` is the format */
585
+ * `i=2 channels: 01, 08` is the format
586
+ * This is Audinate format. TODO: parse RAVENNA `i=CH1,CH2,CH3` format */
587
 static int parse_sdp_i(struct impl *impl, char *c, struct sdp_info *info)
588
 {
589
    if (!strstr(c, " channels: ")) {
590
@@ -1060,8 +1271,18 @@
591
    } else
592
        return -EINVAL;
593
 
594
-   pw_log_debug("rate: %d, ch: %d", info->rate, info->channels);
595
+   pw_log_debug("a=rtpmap: rate: %d, ch: %d", info->rate, info->channels);
596
+
597
+   return 0;
598
+}
599
+
600
+static int parse_sdp_a_ptime(struct impl *impl, char *c, struct sdp_info *info)
601
+{
602
+   if (!spa_strstartswith(c, "a=ptime:"))
603
+       return 0;
604
 
605
+   c += strlen("a=ptime:");
606
+   spa_atof(c, &info->ptime);
607
    return 0;
608
 }
609
 
610
@@ -1105,7 +1326,7 @@
611
            goto too_short;
612
 
613
        sl = 0;
614
-       pw_log_debug("%d: %s", count, s);
615
+       pw_log_debug("SDP line: %d: %s", count, s);
616
 
617
        if (count++ == 0 && strcmp(s, "v=0") != 0)
618
            goto invalid_version;
619
@@ -1120,6 +1341,8 @@
620
            res = parse_sdp_m(impl, s, info);
621
        else if (spa_strstartswith(s, "a=rtpmap:"))
622
            res = parse_sdp_a_rtpmap(impl, s, info);
623
+       else if (spa_strstartswith(s, "a=ptime:"))
624
+           res = parse_sdp_a_ptime(impl, s, info);
625
        else if (spa_strstartswith(s, "a=mediaclk:"))
626
            res = parse_sdp_a_mediaclk(impl, s, info);
627
        else if (spa_strstartswith(s, "a=ts-refclk:"))
628
@@ -1172,7 +1395,7 @@
629
    if (header->c)
630
        return -ENOTSUP;
631
 
632
-   offs = header->a ? 12 : 8;
633
+   offs = header->a ? 20 : 8;
634
    offs += header->auth_len * 4;
635
    if (len <= offs)
636
        return -EINVAL;
637
@@ -1212,6 +1435,7 @@
638
 on_sap_io(void *data, int fd, uint32_t mask)
639
 {
640
    struct impl *impl = data;
641
+   int res;
642
 
643
    if (mask & SPA_IO_IN) {
644
        uint8_t buffer2048;
645
@@ -1225,7 +1449,8 @@
646
            return;
647
 
648
        bufferlen = 0;
649
-       parse_sap(impl, buffer, len);
650
+       if ((res = parse_sap(impl, buffer, len)) < 0)
651
+           pw_log_warn("error parsing SAP: %s", spa_strerror(res));
652
    }
653
 }
654
 
655
@@ -1233,6 +1458,7 @@
656
 {
657
    int fd, res;
658
    struct timespec value, interval;
659
+   char addr128 = "invalid";
660
 
661
    if ((fd = make_send_socket(&impl->src_addr, impl->src_len,
662
                    &impl->sap_addr, impl->sap_len,
663
@@ -1257,7 +1483,8 @@
664
    if ((fd = make_recv_socket(&impl->sap_addr, impl->sap_len, impl->ifname)) < 0)
665
        return fd;
666
 
667
-   pw_log_info("starting SAP listener");
668
+   pw_net_get_ip(&impl->sap_addr, addr, sizeof(addr), NULL, NULL);
669
+   pw_log_info("starting SAP listener on %s", addr);
670
    impl->sap_source = pw_loop_add_io(impl->loop, fd,
671
                SPA_IO_IN, true, on_sap_io, impl);
672
    if (impl->sap_source == NULL) {
673
@@ -1400,9 +1627,15 @@
674
 
675
    if (impl->sap_fd != -1)
676
        close(impl->sap_fd);
677
+   if (impl->ptp_fd != -1)
678
+       close(impl->ptp_fd);
679
 
680
    pw_properties_free(impl->props);
681
 
682
+   free(impl->extra_attrs_preamble);
683
+   free(impl->extra_attrs_end);
684
+
685
+   free(impl->ptp_mgmt_socket);
686
    free(impl->ifname);
687
    free(impl);
688
 }
689
@@ -1472,10 +1705,17 @@
690
    str = pw_properties_get(props, "local.ifname");
691
    impl->ifname = str ? strdup(str) : NULL;
692
 
693
+   str = pw_properties_get(props, "ptp.management-socket");
694
+   impl->ptp_mgmt_socket = str ? strdup(str) : NULL;
695
+
696
+   // TODO: support UDP management access as well
697
+   if (impl->ptp_mgmt_socket)
698
+       impl->ptp_fd = make_unix_socket(impl->ptp_mgmt_socket);
699
+
700
    if ((str = pw_properties_get(props, "sap.ip")) == NULL)
701
        str = DEFAULT_SAP_IP;
702
    port = pw_properties_get_uint32(props, "sap.port", DEFAULT_SAP_PORT);
703
-   if ((res = parse_address(str, port, &impl->sap_addr, &impl->sap_len)) < 0) {
704
+   if ((res = pw_net_parse_address(str, port, &impl->sap_addr, &impl->sap_len)) < 0) {
705
        pw_log_error("invalid sap.ip %s: %s", str, spa_strerror(res));
706
        goto out;
707
    }
708
@@ -1483,13 +1723,12 @@
709
            "sap.cleanup.sec", DEFAULT_CLEANUP_SEC);
710
 
711
    if ((str = pw_properties_get(props, "source.ip")) == NULL) {
712
-       str = DEFAULT_SOURCE_IP;
713
        if (impl->ifname) {
714
-           int fd = socket(AF_INET, SOCK_DGRAM, 0);
715
+           int fd = socket(impl->sap_addr.ss_family, SOCK_DGRAM, 0);
716
            if (fd >= 0) {
717
                struct ifreq req;
718
                spa_zero(req);
719
-               req.ifr_addr.sa_family = AF_INET;
720
+               req.ifr_addr.sa_family = impl->sap_addr.ss_family;
721
                snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", impl->ifname);
722
                res = ioctl(fd, SIOCGIFADDR, &req);
723
                if (res < 0)
724
@@ -1499,15 +1738,17 @@
725
                        addr, sizeof(addr));
726
                if (str == NULL) {
727
                    pw_log_warn("can't parse interface ip: %m");
728
-                   str = DEFAULT_SOURCE_IP;
729
                } else {
730
                    pw_log_info("interface %s IP: %s", impl->ifname, str);
731
                }
732
                close(fd);
733
            }
734
        }
735
+       if (str == NULL)
736
+           str = impl->sap_addr.ss_family == AF_INET ?
737
+               DEFAULT_SOURCE_IP : DEFAULT_SOURCE_IP6;
738
    }
739
-   if ((res = parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
740
+   if ((res = pw_net_parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
741
        pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
742
        goto out;
743
    }
744
@@ -1515,6 +1756,39 @@
745
    impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL);
746
    impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
747
 
748
+   impl->extra_attrs_preamble = NULL;
749
+   impl->extra_attrs_end = NULL;
750
+   char buffer2048;
751
+   struct spa_strbuf buf;
752
+   if ((str = pw_properties_get(props, "sap.preamble-extra")) != NULL) {
753
+       spa_strbuf_init(&buf, buffer, sizeof(buffer));
754
+       struct spa_json it2;
755
+       char line256;
756
+
757
+       spa_json_init(&it0, str, strlen(str));
758
+       if (spa_json_enter_array(&it0, &it1) <= 0)
759
+           spa_json_init(&it1, str, strlen(str));
760
+
761
+       while (spa_json_get_string(&it1, line, sizeof(line)) > 0)
762
+           spa_strbuf_append(&buf, "%s\n", line);
763
+
764
+       impl->extra_attrs_preamble = strdup(buffer);
765
+   }
766
+   if ((str = pw_properties_get(props, "sap.end-extra")) != NULL) {
767
+       spa_strbuf_init(&buf, buffer, sizeof(buffer));
768
+       struct spa_json it2;
769
+       char line256;
770
+
771
+       spa_json_init(&it0, str, strlen(str));
772
+       if (spa_json_enter_array(&it0, &it1) <= 0)
773
+           spa_json_init(&it1, str, strlen(str));
774
+
775
+       while (spa_json_get_string(&it1, line, sizeof(line)) > 0)
776
+           spa_strbuf_append(&buf, "%s\n", line);
777
+
778
+       impl->extra_attrs_end = strdup(buffer);
779
+   }
780
+
781
    impl->core = pw_context_get_object(context, PW_TYPE_INTERFACE_Core);
782
    if (impl->core == NULL) {
783
        str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
784
pipewire-1.0.1.tar.bz2/src/modules/module-rtp-session.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp-session.c Changed
304
 
1
@@ -22,6 +22,7 @@
2
 #include <spa/param/audio/format-utils.h>
3
 #include <spa/debug/types.h>
4
 #include <spa/debug/mem.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include <pipewire/pipewire.h>
8
 #include <pipewire/impl.h>
9
@@ -36,6 +37,7 @@
10
 #include <module-rtp/rtp.h>
11
 #include <module-rtp/apple-midi.h>
12
 #include <module-rtp/stream.h>
13
+#include "network-utils.h"
14
 
15
 #ifdef __FreeBSD__
16
 #define ifr_ifindex ifr_index
17
@@ -47,7 +49,7 @@
18
  * with avahi/mDNS/Bonjour.
19
  *
20
  * Other machines on the network that run a compatible session will see
21
- * eachother and will be able to send audio/midi between eachother.
22
+ * each other and will be able to send audio/midi between each other.
23
  *
24
  * The session setup is based on apple-midi and is compatible with
25
  * apple-midi when the session is using midi.
26
@@ -66,6 +68,7 @@
27
  * - `net.mtu = <int>`: MTU to use, default 1280
28
  * - `net.ttl = <int>`: TTL to use, default 1
29
  * - `net.loop = <bool>`: loopback multicast, default false
30
+ *   `sess.discover-local`: discover local services as well, default false
31
  * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2
32
  * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20
33
  * - `sess.latency.msec = <int>`: receiver latency in milliseconds, default 100
34
@@ -103,6 +106,7 @@
35
  *         #net.mtu = 1280
36
  *         #net.ttl = 1
37
  *         #net.loop = false
38
+ *         #sess.discover-local = false
39
  *         #sess.min-ptime = 2
40
  *         #sess.max-ptime = 20
41
  *         #sess.name = "PipeWire RTP stream"
42
@@ -124,7 +128,7 @@
43
 
44
 #define NAME "rtp-session"
45
 
46
-PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
47
+PW_LOG_TOPIC(mod_topic, "mod." NAME);
48
 #define PW_LOG_TOPIC_DEFAULT mod_topic
49
 
50
 #define DEFAULT_CONTROL_IP "0.0.0.0"
51
@@ -224,6 +228,7 @@
52
    struct spa_hook module_listener;
53
    struct pw_properties *props;
54
 
55
+   bool discover_local;
56
    AvahiPoll *avahi_poll;
57
    AvahiClient *client;
58
    AvahiServiceBrowser *browser;
59
@@ -269,7 +274,7 @@
60
    ssize_t n;
61
    n = sendmsg(fd, msg, MSG_NOSIGNAL);
62
    if (n < 0)
63
-       pw_log_debug("sendmsg() failed: %m");
64
+       pw_log_warn("sendmsg() failed: %m");
65
    return n;
66
 }
67
 
68
@@ -602,26 +607,11 @@
69
    } else if (sa->ss_family == AF_INET6 && sb->ss_family == AF_INET6) {
70
        struct sockaddr_in6 *ia = (struct sockaddr_in6*)sa;
71
        struct sockaddr_in6 *ib = (struct sockaddr_in6*)sb;
72
-       return ia->sin6_addr.s6_addr == ib->sin6_addr.s6_addr;
73
+       return ia->sin6_addr.s6_addr == ib->sin6_addr.s6_addr && ia->sin6_scope_id == ib->sin6_scope_id;
74
    }
75
    return false;
76
 }
77
 
78
-static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len, uint16_t *port)
79
-{
80
-   if (sa->ss_family == AF_INET) {
81
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
82
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
83
-       *port = ntohs(in->sin_port);
84
-   } else if (sa->ss_family == AF_INET6) {
85
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
86
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
87
-       *port = ntohs(in->sin6_port);
88
-   } else
89
-       return -EIO;
90
-   return 0;
91
-}
92
-
93
 static struct session *make_session(struct impl *impl, struct pw_properties *props)
94
 {
95
    struct session *sess;
96
@@ -728,7 +718,7 @@
97
    initiator = ntohl(hdr->initiator);
98
    ssrc = ntohl(hdr->ssrc);
99
 
100
-   get_ip(sa, addr, sizeof(addr), &port);
101
+   pw_net_get_ip(sa, addr, sizeof(addr), NULL, &port);
102
    pw_log_info("IN from %s:%d %s ssrc:%08x initiator:%08x",
103
            addr, port, hdr->name, ssrc, initiator);
104
 
105
@@ -791,7 +781,7 @@
106
    msg.msg_iov = iov;
107
    msg.msg_iovlen = 2;
108
 
109
-   pw_log_debug("send %p %u", msg.msg_name, msg.msg_namelen);
110
+   pw_log_trace("send %p %u", msg.msg_name, msg.msg_namelen);
111
 
112
    send_packet(ctrl ? impl->ctrl_source->fd : impl->data_source->fd, &msg);
113
 }
114
@@ -866,7 +856,7 @@
115
        return;
116
    }
117
 
118
-   pw_log_debug("got CK count %d", hdr->count);
119
+   pw_log_trace("got CK count %d", hdr->count);
120
 
121
    now = current_time_ns() / 10000;
122
    reply = *hdr;
123
@@ -896,7 +886,7 @@
124
        latency = t3 - t1;
125
        offset = ((t3 + t1) / 2) - t2;
126
 
127
-       pw_log_debug("latency:%f offset:%f", latency / 1e5, offset / 1e5);
128
+       pw_log_trace("latency:%f offset:%f", latency / 1e5, offset / 1e5);
129
        if (hdr->count >= 2)
130
            return;
131
    }
132
@@ -912,7 +902,7 @@
133
    msg.msg_iov = iov;
134
    msg.msg_iovlen = 1;
135
 
136
-   pw_log_debug("send %p %u", msg.msg_name, msg.msg_namelen);
137
+   pw_log_trace("send %p %u", msg.msg_name, msg.msg_namelen);
138
 
139
    send_packet(ctrl ? impl->ctrl_source->fd : impl->data_source->fd, &msg);
140
 }
141
@@ -1010,7 +1000,7 @@
142
        if (buffer0 == 0xff && buffer1 == 0xff) {
143
            parse_apple_midi_cmd(impl, true, buffer, len, &sa, salen);
144
        } else {
145
-           spa_debug_mem(0, buffer, len);
146
+           spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_DEBUG, 0, buffer, len);
147
        }
148
    }
149
    return;
150
@@ -1020,7 +1010,7 @@
151
    return;
152
 short_packet:
153
    pw_log_warn("short packet received");
154
-   spa_debug_mem(0, buffer, len);
155
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_DEBUG, 0, buffer, len);
156
    return;
157
 }
158
 
159
@@ -1065,7 +1055,7 @@
160
    return;
161
 short_packet:
162
    pw_log_warn("short packet received");
163
-   spa_debug_mem(0, buffer, len);
164
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_DEBUG, 0, buffer, len);
165
    return;
166
 unknown_ssrc:
167
    pw_log_debug("unknown SSRC %08x", ssrc);
168
@@ -1158,7 +1148,6 @@
169
        pw_log_error("bind() failed: %m");
170
        goto error;
171
    }
172
-   /* FIXME AES67 wants IPTOS_DSCP_AF41 */
173
    val = IPTOS_LOWDELAY;
174
    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
175
        pw_log_warn("setsockopt(IP_TOS) failed: %m");
176
@@ -1232,6 +1221,9 @@
177
    if (impl->client)
178
        avahi_client_free(impl->client);
179
 
180
+   if (impl->data_loop)
181
+       pw_context_release_loop(impl->context, impl->data_loop);
182
+
183
    pw_properties_free(impl->stream_props);
184
    pw_properties_free(impl->props);
185
 
186
@@ -1269,26 +1261,6 @@
187
    .error = on_core_error,
188
 };
189
 
190
-static int parse_address(const char *address, uint16_t port,
191
-       struct sockaddr_storage *addr, socklen_t *len)
192
-{
193
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
194
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
195
-
196
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
197
-       sa4->sin_family = AF_INET;
198
-       sa4->sin_port = htons(port);
199
-       *len = sizeof(*sa4);
200
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
201
-       sa6->sin6_family = AF_INET6;
202
-       sa6->sin6_port = htons(port);
203
-       *len = sizeof(*sa6);
204
-   } else
205
-       return -EINVAL;
206
-
207
-   return 0;
208
-}
209
-
210
 static void free_service(struct service *s)
211
 {
212
    spa_list_remove(&s->link);
213
@@ -1318,7 +1290,7 @@
214
        AvahiStringList *txt)
215
 {
216
    struct service *s = NULL;
217
-   char atAVAHI_ADDRESS_STR_MAX;
218
+   char atAVAHI_ADDRESS_STR_MAX, if_suffix16 = "";
219
    struct session *sess;
220
    int res, ipv;
221
    struct pw_properties *props = NULL;
222
@@ -1415,9 +1387,15 @@
223
    avahi_address_snprint(at, sizeof(at), &s->info.address);
224
    pw_log_info("create session: %s %s:%u %s", s->info.name, at, s->info.port, s->info.type);
225
 
226
+   if (s->info.protocol == AVAHI_PROTO_INET6 &&
227
+       s->info.address.data.ipv6.address0 == 0xfe &&
228
+       (s->info.address.data.ipv6.address1 & 0xc0) == 0x80)
229
+       snprintf(if_suffix, sizeof(if_suffix), "%%%d", s->info.interface);
230
+
231
    ipv = s->info.protocol == AVAHI_PROTO_INET ? 4 : 6;
232
    pw_properties_set(props, "sess.name", s->info.name);
233
-   pw_properties_setf(props, "destination.ip", "%s", at);
234
+   pw_properties_setf(props, "destination.ip", "%s%s", at, if_suffix);
235
+   pw_properties_setf(props, "destination.ifindex", "%u", s->info.interface);
236
    pw_properties_setf(props, "destination.port", "%u", s->info.port);
237
 
238
    if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
239
@@ -1439,10 +1417,10 @@
240
    }
241
    s->sess = sess;
242
 
243
-   if ((res = parse_address(at, s->info.port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
244
+   if ((res = pw_net_parse_address(at, s->info.port, &sess->ctrl_addr, &sess->ctrl_len)) < 0) {
245
        pw_log_error("invalid address %s: %s", at, spa_strerror(res));
246
    }
247
-   if ((res = parse_address(at, s->info.port+1, &sess->data_addr, &sess->data_len)) < 0) {
248
+   if ((res = pw_net_parse_address(at, s->info.port+1, &sess->data_addr, &sess->data_len)) < 0) {
249
        pw_log_error("invalid address %s: %s", at, spa_strerror(res));
250
    }
251
    return s;
252
@@ -1504,7 +1482,7 @@
253
    struct service_info info;
254
    struct service *s;
255
 
256
-   if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
257
+   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
258
        return;
259
 
260
    info = SERVICE_INFO(.interface = interface,
261
@@ -1714,6 +1692,9 @@
262
    }
263
    impl->props = props;
264
 
265
+   impl->discover_local =  pw_properties_get_bool(impl->props,
266
+           "sess.discover-local", false);
267
+
268
    stream_props = pw_properties_new(NULL, NULL);
269
    if (stream_props == NULL) {
270
        res = -errno;
271
@@ -1725,7 +1706,9 @@
272
    impl->module = module;
273
    impl->context = context;
274
    impl->loop = pw_context_get_main_loop(context);
275
-   impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context));
276
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
277
+
278
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
279
 
280
    if (pw_properties_get(props, "sess.media") == NULL)
281
        pw_properties_set(props, "sess.media", "midi");
282
@@ -1733,6 +1716,7 @@
283
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
284
        pw_properties_update_string(stream_props, str, strlen(str));
285
 
286
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
287
    copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
288
    copy_props(impl, props, PW_KEY_AUDIO_RATE);
289
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
290
@@ -1782,11 +1766,11 @@
291
 
292
    impl->ctrl_port = port;
293
 
294
-   if ((res = parse_address(str, port, &impl->ctrl_addr, &impl->ctrl_len)) < 0) {
295
+   if ((res = pw_net_parse_address(str, port, &impl->ctrl_addr, &impl->ctrl_len)) < 0) {
296
        pw_log_error("invalid control.ip %s: %s", str, spa_strerror(res));
297
        goto out;
298
    }
299
-   if ((res = parse_address(str, port ? port+1 : 0, &impl->data_addr, &impl->data_len)) < 0) {
300
+   if ((res = pw_net_parse_address(str, port ? port+1 : 0, &impl->data_addr, &impl->data_len)) < 0) {
301
        pw_log_error("invalid data.ip %s: %s", str, spa_strerror(res));
302
        goto out;
303
    }
304
pipewire-1.0.1.tar.bz2/src/modules/module-rtp-sink.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp-sink.c Changed
359
 
1
@@ -26,6 +26,7 @@
2
 #include <pipewire/impl.h>
3
 
4
 #include <module-rtp/stream.h>
5
+#include "network-utils.h"
6
 
7
 #ifndef IPTOS_DSCP
8
 #define IPTOS_DSCP_MASK 0xfc
9
@@ -47,14 +48,19 @@
10
  *
11
  * - `source.ip =<str>`: source IP address, default "0.0.0.0"
12
  * - `destination.ip =<str>`: destination IP address, default "224.0.0.56"
13
- * - `destination.port =<int>`: destination port, default random beteen 46000 and 47024
14
+ * - `destination.port =<int>`: destination port, default random between 46000 and 47024
15
  * - `local.ifname = <str>`: interface name to use
16
  * - `net.mtu = <int>`: MTU to use, default 1280
17
  * - `net.ttl = <int>`: TTL to use, default 1
18
  * - `net.loop = <bool>`: loopback multicast, default false
19
- * - `sess.min-ptime = <int>`: minimum packet time in milliseconds, default 2
20
- * - `sess.max-ptime = <int>`: maximum packet time in milliseconds, default 20
21
+ * - `sess.min-ptime = <float>`: minimum packet time in milliseconds, default 2
22
+ * - `sess.max-ptime = <float>`: maximum packet time in milliseconds, default 20
23
  * - `sess.name = <str>`: a session name
24
+ * - `rtp.ptime = <float>`: size of the packets in milliseconds, default up to MTU but
25
+ *       between sess.min-ptime and sess.max-ptime
26
+ * - `rtp.framecount = <int>`: number of samples per packet, default up to MTU but
27
+ *       between sess.min-ptime and sess.max-ptime
28
+ * - `sess.latency.msec = <float>`: target node latency in milliseconds, default as rtp.ptime
29
  * - `sess.ts-offset = <int>`: an offset to apply to the timestamp, default -1 = random offset
30
  * - `sess.ts-refclk = <string>`: the name of a reference clock
31
  * - `sess.media = <string>`: the media type audio|midi|opus, default audio
32
@@ -110,11 +116,12 @@
33
 
34
 #define NAME "rtp-sink"
35
 
36
-PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
37
+PW_LOG_TOPIC(mod_topic, "mod." NAME);
38
 #define PW_LOG_TOPIC_DEFAULT mod_topic
39
 
40
 #define DEFAULT_PORT       46000
41
 #define DEFAULT_SOURCE_IP  "0.0.0.0"
42
+#define DEFAULT_SOURCE_IP6 "::"
43
 #define DEFAULT_DESTINATION_IP "224.0.0.56"
44
 #define DEFAULT_TTL        1
45
 #define DEFAULT_LOOP       false
46
@@ -124,7 +131,7 @@
47
 
48
 #define USAGE  "( source.ip=<source IP address, default:"DEFAULT_SOURCE_IP"> ) "           \
49
        "( destination.ip=<destination IP address, default:"DEFAULT_DESTINATION_IP"> ) "    \
50
-       "( destination.port=<int, default random beteen 46000 and 47024> ) "            \
51
+       "( destination.port=<int, default random between 46000 and 47024> ) "           \
52
        "( local.ifname=<local interface name to use> ) "                   \
53
        "( net.mtu=<desired MTU, default:"SPA_STRINGIFY(DEFAULT_MTU)"> ) "          \
54
        "( net.ttl=<desired TTL, default:"SPA_STRINGIFY(DEFAULT_TTL)"> ) "          \
55
@@ -133,7 +140,7 @@
56
        "( sess.name=<a name for the session> ) "                       \
57
        "( sess.min-ptime=<minimum packet time in milliseconds, default:2> ) "          \
58
        "( sess.max-ptime=<maximum packet time in milliseconds, default:20> ) "         \
59
-       "( sess.media=<string, the media type audio|midi|opus, default audio> ) "       \
60
+       "( sess.media=<string, the media type audio|midi|opus, default audio> ) "       \
61
        "( audio.format=<format, default:"DEFAULT_FORMAT"> ) "                  \
62
        "( audio.rate=<sample rate, default:"SPA_STRINGIFY(DEFAULT_RATE)"> ) "          \
63
        "( audio.channels=<number of channels, default:"SPA_STRINGIFY(DEFAULT_CHANNELS)"> ) "   \
64
@@ -181,67 +188,6 @@
65
    int rtp_fd;
66
 };
67
 
68
-static void stream_destroy(void *d)
69
-{
70
-   struct impl *impl = d;
71
-   impl->stream = NULL;
72
-}
73
-
74
-static void stream_send_packet(void *data, struct iovec *iov, size_t iovlen)
75
-{
76
-   struct impl *impl = data;
77
-   struct msghdr msg;
78
-   ssize_t n;
79
-
80
-   spa_zero(msg);
81
-   msg.msg_iov = iov;
82
-   msg.msg_iovlen = iovlen;
83
-   msg.msg_control = NULL;
84
-   msg.msg_controllen = 0;
85
-   msg.msg_flags = 0;
86
-
87
-   n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL);
88
-   if (n < 0)
89
-       pw_log_debug("sendmsg() failed: %m");
90
-}
91
-
92
-static void stream_state_changed(void *data, bool started, const char *error)
93
-{
94
-   struct impl *impl = data;
95
-
96
-   if (error) {
97
-       pw_log_error("stream error: %s", error);
98
-       pw_impl_module_schedule_destroy(impl->module);
99
-   }
100
-}
101
-
102
-static const struct rtp_stream_events stream_events = {
103
-   RTP_VERSION_STREAM_EVENTS,
104
-   .destroy = stream_destroy,
105
-   .state_changed = stream_state_changed,
106
-   .send_packet = stream_send_packet,
107
-};
108
-
109
-static int parse_address(const char *address, uint16_t port,
110
-       struct sockaddr_storage *addr, socklen_t *len)
111
-{
112
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
113
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
114
-
115
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
116
-       sa4->sin_family = AF_INET;
117
-       sa4->sin_port = htons(port);
118
-       *len = sizeof(*sa4);
119
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
120
-       sa6->sin6_family = AF_INET6;
121
-       sa6->sin6_port = htons(port);
122
-       *len = sizeof(*sa6);
123
-   } else
124
-       return -EINVAL;
125
-
126
-   return 0;
127
-}
128
-
129
 static bool is_multicast(struct sockaddr *sa, socklen_t salen)
130
 {
131
    if (sa->sa_family == AF_INET) {
132
@@ -284,14 +230,26 @@
133
        goto error;
134
    }
135
    if (is_multicast((struct sockaddr*)dst, dst_len)) {
136
-       val = loop;
137
-       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0)
138
-           pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m");
139
-
140
-       val = ttl;
141
-       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0)
142
-           pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m");
143
+       if (dst->ss_family == AF_INET) {
144
+           val = loop;
145
+           if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val)) < 0)
146
+               pw_log_warn("setsockopt(IP_MULTICAST_LOOP) failed: %m");
147
+
148
+           val = ttl;
149
+           if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)) < 0)
150
+               pw_log_warn("setsockopt(IP_MULTICAST_TTL) failed: %m");
151
+       } else {
152
+           val = loop;
153
+           if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) < 0)
154
+               pw_log_warn("setsockopt(IPV6_MULTICAST_LOOP) failed: %m");
155
+
156
+           val = ttl;
157
+           if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) < 0)
158
+               pw_log_warn("setsockopt(IPV6_MULTICAST_HOPS) failed: %m");
159
+       }
160
    }
161
+
162
+
163
 #ifdef SO_PRIORITY
164
    val = 6;
165
    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0)
166
@@ -310,19 +268,124 @@
167
    return res;
168
 }
169
 
170
-static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
171
+static void stream_destroy(void *d)
172
 {
173
-   if (sa->ss_family == AF_INET) {
174
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
175
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
176
-   } else if (sa->ss_family == AF_INET6) {
177
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
178
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
179
-   } else
180
-       return -EIO;
181
-   return 0;
182
+   struct impl *impl = d;
183
+   impl->stream = NULL;
184
+}
185
+
186
+static void stream_send_packet(void *data, struct iovec *iov, size_t iovlen)
187
+{
188
+   struct impl *impl = data;
189
+   struct msghdr msg;
190
+   ssize_t n;
191
+
192
+   spa_zero(msg);
193
+   msg.msg_iov = iov;
194
+   msg.msg_iovlen = iovlen;
195
+   msg.msg_control = NULL;
196
+   msg.msg_controllen = 0;
197
+   msg.msg_flags = 0;
198
+
199
+   n = sendmsg(impl->rtp_fd, &msg, MSG_NOSIGNAL);
200
+   if (n < 0)
201
+       pw_log_warn("sendmsg() failed: %m");
202
+}
203
+
204
+static void stream_state_changed(void *data, bool started, const char *error)
205
+{
206
+   struct impl *impl = data;
207
+
208
+   if (error) {
209
+       pw_log_error("stream error: %s", error);
210
+       pw_impl_module_schedule_destroy(impl->module);
211
+   } else if (started) {
212
+       int res;
213
+
214
+       if ((res = make_socket(&impl->src_addr, impl->src_len,
215
+                   &impl->dst_addr, impl->dst_len,
216
+                   impl->mcast_loop, impl->ttl, impl->dscp,
217
+                   impl->ifname)) < 0) {
218
+           pw_log_error("can't make socket: %s", spa_strerror(res));
219
+           rtp_stream_set_error(impl->stream, res, "Can't make socket");
220
+           return;
221
+       }
222
+       impl->rtp_fd = res;
223
+   } else {
224
+       close(impl->rtp_fd);
225
+       impl->rtp_fd = -1;
226
+   }
227
+}
228
+
229
+static void stream_props_changed(struct impl *impl, uint32_t id, const struct spa_pod *param)
230
+{
231
+   struct spa_pod_object *obj = (struct spa_pod_object *)param;
232
+   struct spa_pod_prop *prop;
233
+
234
+   if (param == NULL)
235
+       return;
236
+
237
+   SPA_POD_OBJECT_FOREACH(obj, prop) {
238
+       if (prop->key == SPA_PROP_params) {
239
+           struct spa_pod *params = NULL;
240
+           struct spa_pod_parser prs;
241
+           struct spa_pod_frame f;
242
+           const char *key;
243
+           struct spa_pod *pod;
244
+           const char *value;
245
+
246
+           if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_params,
247
+                   SPA_POD_OPT_Pod(&params)) < 0)
248
+               return;
249
+           spa_pod_parser_pod(&prs, params);
250
+           if (spa_pod_parser_push_struct(&prs, &f) < 0)
251
+               return;
252
+
253
+           while (true) {
254
+               if (spa_pod_parser_get_string(&prs, &key) < 0)
255
+                   break;
256
+               if (spa_pod_parser_get_pod(&prs, &pod) < 0)
257
+                   break;
258
+               if (spa_pod_get_string(pod, &value) < 0)
259
+                   continue;
260
+               pw_log_info("key '%s', value '%s'", key, value);
261
+               if (!spa_streq(key, "destination.ip"))
262
+                   continue;
263
+               if (pw_net_parse_address(value, impl->dst_port, &impl->dst_addr,
264
+                       &impl->dst_len) < 0) {
265
+                   pw_log_error("invalid destination.ip: '%s'", value);
266
+                   break;
267
+               }
268
+               pw_properties_set(impl->stream_props, "rtp.destination.ip", value);
269
+               struct spa_dict_item item1;
270
+               item0 = SPA_DICT_ITEM_INIT("rtp.destination.ip", value);
271
+               rtp_stream_update_properties(impl->stream, &SPA_DICT_INIT(item, 1));
272
+               break;
273
+           }
274
+       }
275
+   }
276
+}
277
+
278
+static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param)
279
+{
280
+   struct impl *impl = data;
281
+
282
+   switch (id) {
283
+   case SPA_PARAM_Props:
284
+       if (param != NULL)
285
+           stream_props_changed(impl, id, param);
286
+       break;
287
+   }
288
 }
289
 
290
+static const struct rtp_stream_events stream_events = {
291
+   RTP_VERSION_STREAM_EVENTS,
292
+   .destroy = stream_destroy,
293
+   .state_changed = stream_state_changed,
294
+   .param_changed = stream_param_changed,
295
+   .send_packet = stream_send_packet,
296
+};
297
+
298
 static void core_destroy(void *d)
299
 {
300
    struct impl *impl = d;
301
@@ -470,21 +533,21 @@
302
    str = pw_properties_get(props, "local.ifname");
303
    impl->ifname = str ? strdup(str) : NULL;
304
 
305
-   if ((str = pw_properties_get(props, "source.ip")) == NULL)
306
-       str = DEFAULT_SOURCE_IP;
307
-   if ((res = parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
308
-       pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
309
-       goto out;
310
-   }
311
-
312
    impl->dst_port = DEFAULT_PORT + ((uint32_t) (pw_rand32() % 512) << 1);
313
    impl->dst_port = pw_properties_get_uint32(props, "destination.port", impl->dst_port);
314
    if ((str = pw_properties_get(props, "destination.ip")) == NULL)
315
        str = DEFAULT_DESTINATION_IP;
316
-   if ((res = parse_address(str, impl->dst_port, &impl->dst_addr, &impl->dst_len)) < 0) {
317
+   if ((res = pw_net_parse_address(str, impl->dst_port, &impl->dst_addr, &impl->dst_len)) < 0) {
318
        pw_log_error("invalid destination.ip %s: %s", str, spa_strerror(res));
319
        goto out;
320
    }
321
+   if ((str = pw_properties_get(props, "source.ip")) == NULL)
322
+       str = impl->dst_addr.ss_family == AF_INET ?
323
+           DEFAULT_SOURCE_IP : DEFAULT_SOURCE_IP6;
324
+   if ((res = pw_net_parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
325
+       pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
326
+       goto out;
327
+   }
328
 
329
    impl->ttl = pw_properties_get_uint32(props, "net.ttl", DEFAULT_TTL);
330
    impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
331
@@ -495,9 +558,9 @@
332
        ts_offset = pw_rand32();
333
    pw_properties_setf(stream_props, "rtp.sender-ts-offset", "%u", (uint32_t)ts_offset);
334
 
335
-   get_ip(&impl->src_addr, addr, sizeof(addr));
336
+   pw_net_get_ip(&impl->src_addr, addr, sizeof(addr), NULL, NULL);
337
    pw_properties_set(stream_props, "rtp.source.ip", addr);
338
-   get_ip(&impl->dst_addr, addr, sizeof(addr));
339
+   pw_net_get_ip(&impl->dst_addr, addr, sizeof(addr), NULL, NULL);
340
    pw_properties_set(stream_props, "rtp.destination.ip", addr);
341
    pw_properties_setf(stream_props, "rtp.destination.port", "%u", impl->dst_port);
342
    pw_properties_setf(stream_props, "rtp.ttl", "%u", impl->ttl);
343
@@ -526,15 +589,6 @@
344
            &impl->core_listener,
345
            &core_events, impl);
346
 
347
-   if ((res = make_socket(&impl->src_addr, impl->src_len,
348
-                   &impl->dst_addr, impl->dst_len,
349
-                   impl->mcast_loop, impl->ttl, impl->dscp,
350
-                   impl->ifname)) < 0) {
351
-       pw_log_error("can't make socket: %s", spa_strerror(res));
352
-       goto out;
353
-   }
354
-   impl->rtp_fd = res;
355
-
356
    impl->stream = rtp_stream_new(impl->core,
357
            PW_DIRECTION_INPUT, pw_properties_copy(stream_props),
358
            &stream_events, impl);
359
pipewire-1.0.1.tar.bz2/src/modules/module-rtp-source.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp-source.c Changed
321
 
1
@@ -30,6 +30,7 @@
2
 #include <pipewire/impl.h>
3
 
4
 #include <module-rtp/stream.h>
5
+#include "network-utils.h"
6
 
7
 #ifdef __FreeBSD__
8
 #define ifr_ifindex ifr_index
9
@@ -52,10 +53,11 @@
10
  * Options specific to the behavior of this module
11
  *
12
  * - `local.ifname = <str>`: interface name to use
13
- * - `source.ip = <str>`: the source ip address, default 224.0.0.56
14
+ * - `source.ip = <str>`: the source ip address, default 224.0.0.56. Set this to the IP address
15
+ *                you want to receive packets from or 0.0.0.0 to receive from any source address.
16
  * - `source.port = <int>`: the source port
17
  * - `node.always-process = <bool>`: true to receive even when not running
18
- * - `sess.latency.msec = <str>`: target network latency in milliseconds, default 100
19
+ * - `sess.latency.msec = <float>`: target network latency in milliseconds, default 100
20
  * - `sess.ignore-ssrc = <bool>`: ignore SSRC, default false
21
  * - `sess.media = <string>`: the media type audio|midi|opus, default audio
22
  * - `stream.props = {}`: properties to be passed to the stream
23
@@ -107,7 +109,7 @@
24
 
25
 #define NAME "rtp-source"
26
 
27
-PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
28
+PW_LOG_TOPIC(mod_topic, "mod." NAME);
29
 #define PW_LOG_TOPIC_DEFAULT mod_topic
30
 
31
 #define DEFAULT_CLEANUP_SEC        60
32
@@ -163,6 +165,7 @@
33
    struct spa_source *source;
34
 
35
    unsigned receiving:1;
36
+   unsigned last_receiving:1;
37
 };
38
 
39
 static void
40
@@ -179,8 +182,10 @@
41
        if (len < 12)
42
            goto short_packet;
43
 
44
-       if (SPA_LIKELY(impl->stream))
45
-           rtp_stream_receive_packet(impl->stream, buffer, len);
46
+       if (SPA_LIKELY(impl->stream)) {
47
+           if (rtp_stream_receive_packet(impl->stream, buffer, len) < 0)
48
+               goto receive_error;
49
+       }
50
 
51
        impl->receiving = true;
52
    }
53
@@ -194,35 +199,19 @@
54
    return;
55
 }
56
 
57
-static int parse_address(const char *address, uint16_t port,
58
-       struct sockaddr_storage *addr, socklen_t *len)
59
-{
60
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
61
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
62
-
63
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
64
-       sa4->sin_family = AF_INET;
65
-       sa4->sin_port = htons(port);
66
-       *len = sizeof(*sa4);
67
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
68
-       sa6->sin6_family = AF_INET6;
69
-       sa6->sin6_port = htons(port);
70
-       *len = sizeof(*sa6);
71
-   } else
72
-       return -EINVAL;
73
-
74
-   return 0;
75
-}
76
-
77
 static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname)
78
 {
79
    int af, fd, val, res;
80
    struct ifreq req;
81
+   struct sockaddr_storage ba = *(struct sockaddr_storage *)sa;
82
+   bool do_connect = false;
83
+   char addr128;
84
 
85
    af = sa->sa_family;
86
    if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
87
+       res = -errno;
88
        pw_log_error("socket failed: %m");
89
-       return -errno;
90
+       return res;
91
    }
92
 #ifdef SO_TIMESTAMP
93
    val = 1;
94
@@ -255,9 +244,15 @@
95
            memset(&mr4, 0, sizeof(mr4));
96
            mr4.imr_multiaddr = sa4->sin_addr;
97
            mr4.imr_ifindex = req.ifr_ifindex;
98
+           pw_net_get_ip((struct sockaddr_storage*)sa, addr, sizeof(addr), NULL, NULL);
99
+           pw_log_info("join IPv4 group: %s", addr);
100
            res = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
101
        } else {
102
-           sa4->sin_addr.s_addr = INADDR_ANY;
103
+           struct sockaddr_in *ba4 = (struct sockaddr_in*)&ba;
104
+           if (ba4->sin_addr.s_addr != INADDR_ANY) {
105
+               ba4->sin_addr.s_addr = INADDR_ANY;
106
+               do_connect = true;
107
+           }
108
        }
109
    } else if (af == AF_INET6) {
110
        struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
111
@@ -266,9 +261,12 @@
112
            memset(&mr6, 0, sizeof(mr6));
113
            mr6.ipv6mr_multiaddr = sa6->sin6_addr;
114
            mr6.ipv6mr_interface = req.ifr_ifindex;
115
+           pw_net_get_ip((struct sockaddr_storage*)sa, addr, sizeof(addr), NULL, NULL);
116
+           pw_log_info("join IPv6 group: %s", addr);
117
            res = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
118
        } else {
119
-               sa6->sin6_addr = in6addr_any;
120
+           struct sockaddr_in6 *ba6 = (struct sockaddr_in6*)&ba;
121
+           ba6->sin6_addr = in6addr_any;
122
        }
123
    } else {
124
        res = -EINVAL;
125
@@ -281,11 +279,18 @@
126
        goto error;
127
    }
128
 
129
-   if (bind(fd, sa, salen) < 0) {
130
+   if (bind(fd, (struct sockaddr*)&ba, salen) < 0) {
131
        res = -errno;
132
        pw_log_error("bind() failed: %m");
133
        goto error;
134
    }
135
+   if (do_connect) {
136
+       if (connect(fd, sa, salen) < 0) {
137
+           res = -errno;
138
+           pw_log_error("connect() failed: %m");
139
+           goto error;
140
+       }
141
+   }
142
    return fd;
143
 error:
144
    close(fd);
145
@@ -304,7 +309,7 @@
146
    if ((fd = make_socket((const struct sockaddr *)&impl->src_addr,
147
                    impl->src_len, impl->ifname)) < 0) {
148
        pw_log_error("failed to create socket: %m");
149
-       return fd;
150
+       return -errno;
151
    }
152
 
153
    impl->source = pw_loop_add_io(impl->data_loop, fd,
154
@@ -337,29 +342,103 @@
155
 static void stream_state_changed(void *data, bool started, const char *error)
156
 {
157
    struct impl *impl = data;
158
+   int res;
159
 
160
    if (error) {
161
        pw_log_error("stream error: %s", error);
162
        pw_impl_module_schedule_destroy(impl->module);
163
    } else if (started) {
164
-       if ((errno = -stream_start(impl)) < 0)
165
-           pw_log_error("failed to start RTP stream: %m");
166
+       if ((res = stream_start(impl)) < 0) {
167
+           pw_log_error("failed to start RTP stream: %s", spa_strerror(res));
168
+           rtp_stream_set_error(impl->stream, res, "Can't start RTP stream");
169
+       }
170
    } else {
171
        if (!impl->always_process)
172
            stream_stop(impl);
173
    }
174
 }
175
 
176
+static void stream_props_changed(struct impl *impl, uint32_t id, const struct spa_pod *param)
177
+{
178
+   struct spa_pod_object *obj = (struct spa_pod_object *)param;
179
+   struct spa_pod_prop *prop;
180
+
181
+   if (param == NULL)
182
+       return;
183
+
184
+   SPA_POD_OBJECT_FOREACH(obj, prop) {
185
+       if (prop->key == SPA_PROP_params) {
186
+           struct spa_pod *params = NULL;
187
+           struct spa_pod_parser prs;
188
+           struct spa_pod_frame f;
189
+           const char *key;
190
+           struct spa_pod *pod;
191
+           const char *value;
192
+
193
+           if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_params,
194
+                   SPA_POD_OPT_Pod(&params)) < 0)
195
+               return;
196
+           spa_pod_parser_pod(&prs, params);
197
+           if (spa_pod_parser_push_struct(&prs, &f) < 0)
198
+               return;
199
+
200
+           while (true) {
201
+               if (spa_pod_parser_get_string(&prs, &key) < 0)
202
+                   break;
203
+               if (spa_pod_parser_get_pod(&prs, &pod) < 0)
204
+                   break;
205
+               if (spa_pod_get_string(pod, &value) < 0)
206
+                   continue;
207
+               pw_log_info("key '%s', value '%s'", key, value);
208
+               if (!spa_streq(key, "source.ip"))
209
+                   continue;
210
+               if (pw_net_parse_address(value, impl->src_port, &impl->src_addr,
211
+                       &impl->src_len) < 0) {
212
+                   pw_log_error("invalid source.ip: '%s'", value);
213
+                   break;
214
+               }
215
+               pw_properties_set(impl->stream_props, "rtp.source.ip", value);
216
+               struct spa_dict_item item1;
217
+               item0 = SPA_DICT_ITEM_INIT("rtp.source.ip", value);
218
+               rtp_stream_update_properties(impl->stream, &SPA_DICT_INIT(item, 1));
219
+               break;
220
+           }
221
+       }
222
+   }
223
+}
224
+
225
+static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param)
226
+{
227
+   struct impl *impl = data;
228
+
229
+   switch (id) {
230
+   case SPA_PARAM_Props:
231
+       if (param != NULL)
232
+           stream_props_changed(impl, id, param);
233
+       break;
234
+   }
235
+}
236
+
237
 static const struct rtp_stream_events stream_events = {
238
    RTP_VERSION_STREAM_EVENTS,
239
    .destroy = stream_destroy,
240
    .state_changed = stream_state_changed,
241
+   .param_changed = stream_param_changed,
242
 };
243
 
244
 static void on_timer_event(void *data, uint64_t expirations)
245
 {
246
    struct impl *impl = data;
247
 
248
+   if (impl->receiving != impl->last_receiving) {
249
+       struct spa_dict_item item1;
250
+
251
+       impl->last_receiving = impl->receiving;
252
+
253
+       item0 = SPA_DICT_ITEM_INIT("rtp.receiving", impl->receiving ? "true" : "false");
254
+       rtp_stream_update_properties(impl->stream, &SPA_DICT_INIT(item, 1));
255
+   }
256
+
257
    if (!impl->receiving) {
258
        pw_log_info("timeout, inactive RTP source");
259
        //pw_impl_module_schedule_destroy(impl->module);
260
@@ -394,6 +473,9 @@
261
    if (impl->timer)
262
        pw_loop_destroy_source(impl->loop, impl->timer);
263
 
264
+   if (impl->data_loop)
265
+       pw_context_release_loop(impl->context, impl->data_loop);
266
+
267
    pw_properties_free(impl->stream_props);
268
    pw_properties_free(impl->props);
269
 
270
@@ -447,6 +529,7 @@
271
    struct timespec value, interval;
272
    struct pw_properties *props, *stream_props;
273
    int64_t ts_offset;
274
+   char addr128;
275
    int res = 0;
276
 
277
    PW_LOG_TOPIC_INIT(mod_topic);
278
@@ -469,11 +552,12 @@
279
    impl->module = module;
280
    impl->context = context;
281
    impl->loop = pw_context_get_main_loop(context);
282
-   impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context));
283
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
284
 
285
    if ((sess_name = pw_properties_get(props, "sess.name")) == NULL)
286
        sess_name = pw_get_host_name();
287
 
288
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
289
    if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
290
        pw_properties_setf(props, PW_KEY_NODE_NAME, "rtp_session.%s", sess_name);
291
    if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
292
@@ -485,6 +569,7 @@
293
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
294
        pw_properties_update_string(stream_props, str, strlen(str));
295
 
296
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
297
    copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
298
    copy_props(impl, props, PW_KEY_AUDIO_RATE);
299
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
300
@@ -511,15 +596,19 @@
301
 
302
    impl->src_port = pw_properties_get_uint32(props, "source.port", 0);
303
    if (impl->src_port == 0) {
304
+       res = -EINVAL;
305
        pw_log_error("invalid source.port");
306
        goto out;
307
    }
308
    if ((str = pw_properties_get(props, "source.ip")) == NULL)
309
        str = DEFAULT_SOURCE_IP;
310
-   if ((res = parse_address(str, impl->src_port, &impl->src_addr, &impl->src_len)) < 0) {
311
+   if ((res = pw_net_parse_address(str, impl->src_port, &impl->src_addr, &impl->src_len)) < 0) {
312
        pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
313
        goto out;
314
    }
315
+   pw_net_get_ip(&impl->src_addr, addr, sizeof(addr), NULL, NULL);
316
+   pw_properties_set(stream_props, "rtp.source.ip", addr);
317
+   pw_properties_setf(stream_props, "rtp.source.port", "%u", impl->src_port);
318
 
319
    ts_offset = pw_properties_get_int64(props, "sess.ts-offset", DEFAULT_TS_OFFSET);
320
    if (ts_offset == -1)
321
pipewire-1.0.1.tar.bz2/src/modules/module-rtp/audio.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp/audio.c Changed
220
 
1
@@ -11,7 +11,7 @@
2
    int32_t avail;
3
 
4
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
5
-       pw_log_debug("Out of stream buffers: %m");
6
+       pw_log_info("Out of stream buffers: %m");
7
        return;
8
    }
9
    d = buf->buffer->datas;
10
@@ -66,9 +66,9 @@
11
            error = (float)target_buffer - (float)avail;
12
            error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
13
 
14
-           corr = spa_dll_update(&impl->dll, error);
15
+           corr = (float)spa_dll_update(&impl->dll, error);
16
 
17
-           pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
18
+           pw_log_trace("avail:%u target:%u error:%f corr:%f", avail,
19
                    target_buffer, error, corr);
20
 
21
            if (impl->io_rate_match) {
22
@@ -165,7 +165,7 @@
23
                BUFFER_SIZE / stride);
24
        impl->have_sync = false;
25
    } else {
26
-       pw_log_debug("got samples:%u", samples);
27
+       pw_log_trace("got samples:%u", samples);
28
        spa_ringbuffer_write_data(&impl->ring,
29
                impl->buffer,
30
                BUFFER_SIZE,
31
@@ -181,7 +181,7 @@
32
    return -EINVAL;
33
 invalid_version:
34
    pw_log_warn("invalid RTP version");
35
-   spa_debug_mem(0, buffer, len);
36
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_INFO, 0, buffer, len);
37
    return -EPROTO;
38
 invalid_len:
39
    pw_log_warn("invalid RTP length");
40
@@ -192,6 +192,18 @@
41
    return -EINVAL;
42
 }
43
 
44
+static void set_timer(struct impl *impl, uint64_t time, uint64_t itime)
45
+{
46
+   struct itimerspec ts;
47
+   ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
48
+   ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
49
+   ts.it_interval.tv_sec = itime / SPA_NSEC_PER_SEC;
50
+   ts.it_interval.tv_nsec = itime % SPA_NSEC_PER_SEC;
51
+   spa_system_timerfd_settime(impl->data_loop->system,
52
+           impl->timer->fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
53
+   impl->timer_running = time != 0 && itime != 0;
54
+}
55
+
56
 static inline void
57
 set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size,
58
        uint32_t offset, struct iovec *iov, uint32_t len)
59
@@ -202,7 +214,7 @@
60
    iov1.iov_base = buffer;
61
 }
62
 
63
-static void rtp_audio_flush_packets(struct impl *impl)
64
+static void rtp_audio_flush_packets(struct impl *impl, uint32_t num_packets)
65
 {
66
    int32_t avail, tosend;
67
    uint32_t stride, timestamp;
68
@@ -211,9 +223,16 @@
69
 
70
    avail = spa_ringbuffer_get_read_index(&impl->ring, &timestamp);
71
    tosend = impl->psamples;
72
-
73
    if (avail < tosend)
74
-       return;
75
+       if (impl->started)
76
+           goto done;
77
+       else {
78
+           /* send last packet before emitting state_changed */
79
+           tosend = avail;
80
+           num_packets = 1;
81
+       }
82
+   else
83
+       num_packets = SPA_MIN(num_packets, (uint32_t)(avail / tosend));
84
 
85
    stride = impl->stride;
86
 
87
@@ -225,7 +244,7 @@
88
    iov0.iov_base = &header;
89
    iov0.iov_len = sizeof(header);
90
 
91
-   while (avail >= tosend) {
92
+   while (num_packets > 0) {
93
        if (impl->marker_on_first && impl->first)
94
            header.m = 1;
95
        else
96
@@ -238,7 +257,8 @@
97
            (timestamp * stride) & BUFFER_MASK,
98
            &iov1, tosend * stride);
99
 
100
-       pw_log_trace("sending %d avail:%d ts_offset:%d timestamp:%d", tosend, avail, impl->ts_offset, timestamp);
101
+       pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%d",
102
+               tosend, num_packets, impl->ts_offset, timestamp);
103
 
104
        rtp_stream_emit_send_packet(impl, iov, 3);
105
 
106
@@ -246,8 +266,30 @@
107
        impl->first = false;
108
        timestamp += tosend;
109
        avail -= tosend;
110
+       num_packets--;
111
    }
112
    spa_ringbuffer_read_update(&impl->ring, timestamp);
113
+done:
114
+   if (impl->timer_running) {
115
+       if (impl->started) {
116
+           if (avail < tosend) {
117
+               set_timer(impl, 0, 0);
118
+           }
119
+       } else if (avail <= 0) {
120
+           bool started = false;
121
+
122
+           /* the stream has been stopped and all packets have been sent */
123
+           set_timer(impl, 0, 0);
124
+           pw_loop_invoke(impl->main_loop, do_emit_state_changed, SPA_ID_INVALID, &started, sizeof started, false, impl);
125
+       }
126
+   }
127
+}
128
+
129
+static void rtp_audio_flush_timeout(struct impl *impl, uint64_t expirations)
130
+{
131
+   if (expirations > 1)
132
+       pw_log_warn("missing timeout %"PRIu64, expirations);
133
+   rtp_audio_flush_packets(impl, expirations);
134
 }
135
 
136
 static void rtp_audio_process_capture(void *data)
137
@@ -257,9 +299,12 @@
138
    struct spa_data *d;
139
    uint32_t offs, size, timestamp, expected_timestamp, stride;
140
    int32_t filled, wanted;
141
+   uint32_t pending, num_queued;
142
+   struct spa_io_position *pos;
143
+   uint64_t next_nsec, quantum;
144
 
145
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
146
-       pw_log_debug("Out of stream buffers: %m");
147
+       pw_log_info("Out of stream buffers: %m");
148
        return;
149
    }
150
    d = buf->buffer->datas;
151
@@ -271,11 +316,17 @@
152
 
153
    filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
154
 
155
-   if (SPA_LIKELY(impl->io_position)) {
156
-       uint32_t rate = impl->io_position->clock.rate.denom;
157
-       timestamp = impl->io_position->clock.position * impl->rate / rate;
158
-   } else
159
+   pos = impl->io_position;
160
+   if (SPA_LIKELY(pos)) {
161
+       uint32_t rate = pos->clock.rate.denom;
162
+       timestamp = pos->clock.position * impl->rate / rate;
163
+       next_nsec = pos->clock.next_nsec;
164
+       quantum = (uint64_t)(pos->clock.duration * SPA_NSEC_PER_SEC / (rate * pos->clock.rate_diff));
165
+   } else {
166
        timestamp = expected_timestamp;
167
+       next_nsec = 0;
168
+       quantum = 0;
169
+   }
170
 
171
    if (!impl->have_sync) {
172
        pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u",
173
@@ -284,6 +335,7 @@
174
        memset(impl->buffer, 0, BUFFER_SIZE);
175
        impl->have_sync = true;
176
        expected_timestamp = timestamp;
177
+       filled = 0;
178
    } else {
179
        if (SPA_ABS((int32_t)expected_timestamp - (int32_t)timestamp) > 32) {
180
            pw_log_warn("expected %u != timestamp %u", expected_timestamp, timestamp);
181
@@ -291,6 +343,7 @@
182
        } else if (filled + wanted > (int32_t)(BUFFER_SIZE / stride)) {
183
            pw_log_warn("overrun %u + %u > %u", filled, wanted, BUFFER_SIZE / stride);
184
            impl->have_sync = false;
185
+           filled = 0;
186
        }
187
    }
188
 
189
@@ -304,7 +357,22 @@
190
 
191
    pw_stream_queue_buffer(impl->stream, buf);
192
 
193
-   rtp_audio_flush_packets(impl);
194
+   pending = filled / impl->psamples;
195
+   num_queued = (filled + wanted) / impl->psamples;
196
+
197
+   if (num_queued > 0) {
198
+       /* flush all previous packets plus new one right away */
199
+       rtp_audio_flush_packets(impl, pending + 1);
200
+       num_queued -= SPA_MIN(num_queued, pending + 1);
201
+
202
+       if (num_queued > 0) {
203
+           /* schedule timer for remaining */
204
+           int64_t interval = quantum / (num_queued + 1);
205
+           uint64_t time = next_nsec - num_queued * interval;
206
+           pw_log_trace("%u %u %"PRIu64" %"PRIu64, pending, num_queued, time, interval);
207
+           set_timer(impl, time, interval);
208
+       }
209
+   }
210
 }
211
 
212
 static int rtp_audio_init(struct impl *impl, enum spa_direction direction)
213
@@ -314,5 +382,6 @@
214
    else
215
        impl->stream_events.process = rtp_audio_process_playback;
216
    impl->receive_rtp = rtp_audio_receive;
217
+   impl->flush_timeout = rtp_audio_flush_timeout;
218
    return 0;
219
 }
220
pipewire-1.0.1.tar.bz2/src/modules/module-rtp/midi.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp/midi.c Changed
93
 
1
@@ -15,7 +15,7 @@
2
    struct spa_pod_control *c;
3
 
4
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
5
-       pw_log_debug("Out of stream buffers: %m");
6
+       pw_log_info("Out of stream buffers: %m");
7
        return;
8
    }
9
    d = buf->buffer->datas;
10
@@ -137,16 +137,16 @@
11
 
12
 static double get_time(struct impl *impl)
13
 {
14
-   struct timespec ts;
15
+   uint64_t now;
16
    struct spa_io_position *pos;
17
    double t;
18
 
19
-   clock_gettime(CLOCK_MONOTONIC, &ts);
20
+   now = pw_stream_get_nsec(impl->stream);
21
    if ((pos = impl->io_position) != NULL) {
22
        t = pos->clock.position / (double) pos->clock.rate.denom;
23
-       t += (SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec) / (double)SPA_NSEC_PER_SEC;
24
+       t += (now - pos->clock.nsec) / (double)SPA_NSEC_PER_SEC;
25
    } else {
26
-       t = SPA_TIMESPEC_TO_NSEC(&ts);
27
+       t = now;
28
    }
29
    return t;
30
 }
31
@@ -204,12 +204,12 @@
32
            /* our current time is now the estimated time */
33
            t = estimated;
34
        }
35
-       pw_log_debug("%f %f %f %f", t, estimated, diff, impl->corr);
36
+       pw_log_trace("%f %f %f %f", t, estimated, diff, impl->corr);
37
 
38
-       timestamp = t * impl->rate;
39
+       timestamp = (uint32_t)(t * impl->rate);
40
 
41
-       impl->last_timestamp = ts;
42
-       impl->last_time = t;
43
+       impl->last_timestamp = (float)ts;
44
+       impl->last_time = (float)t;
45
    }
46
 
47
    filled = spa_ringbuffer_get_write_index(&impl->ring, &write);
48
@@ -248,7 +248,7 @@
49
        else
50
            offs += parse_varlen(&packetoffs, end - offs, &delta);
51
 
52
-       timestamp += delta * impl->corr;
53
+       timestamp += (uint32_t)(delta * impl->corr);
54
        spa_pod_builder_control(&b, timestamp, SPA_CONTROL_Midi);
55
 
56
        size = get_midi_size(&packetoffs, end - offs);
57
@@ -315,7 +315,7 @@
58
    return -EINVAL;
59
 invalid_version:
60
    pw_log_warn("invalid RTP version");
61
-   spa_debug_mem(0, buffer, len);
62
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_INFO, 0, buffer, len);
63
    return -EPROTO;
64
 invalid_len:
65
    pw_log_warn("invalid RTP length");
66
@@ -400,7 +400,7 @@
67
            }
68
            iov2.iov_len = len;
69
 
70
-           pw_log_debug("sending %d timestamp:%d %u %u",
71
+           pw_log_trace("sending %d timestamp:%d %u %u",
72
                    len, timestamp + base,
73
                    offset, impl->psamples);
74
            rtp_stream_emit_send_packet(impl, iov, 3);
75
@@ -436,7 +436,7 @@
76
        }
77
        iov2.iov_len = len;
78
 
79
-       pw_log_debug("sending %d timestamp:%d", len, base);
80
+       pw_log_trace("sending %d timestamp:%d", len, base);
81
        rtp_stream_emit_send_packet(impl, iov, 3);
82
        impl->seq++;
83
    }
84
@@ -452,7 +452,7 @@
85
    void *ptr;
86
 
87
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
88
-       pw_log_debug("Out of stream buffers: %m");
89
+       pw_log_info("Out of stream buffers: %m");
90
        return;
91
    }
92
    d = buf->buffer->datas;
93
pipewire-1.0.1.tar.bz2/src/modules/module-rtp/opus.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp/opus.c Changed
67
 
1
@@ -16,7 +16,7 @@
2
    int32_t avail;
3
 
4
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
5
-       pw_log_debug("Out of stream buffers: %m");
6
+       pw_log_info("Out of stream buffers: %m");
7
        return;
8
    }
9
    d = buf->buffer->datas;
10
@@ -71,9 +71,9 @@
11
            error = (float)target_buffer - (float)avail;
12
            error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
13
 
14
-           corr = spa_dll_update(&impl->dll, error);
15
+           corr = (float)spa_dll_update(&impl->dll, error);
16
 
17
-           pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
18
+           pw_log_trace("avail:%u target:%u error:%f corr:%f", avail,
19
                    target_buffer, error, corr);
20
 
21
            if (impl->io_rate_match) {
22
@@ -183,7 +183,7 @@
23
        if (end > BUFFER_SIZE2)
24
            memmove(impl->buffer, &impl->bufferBUFFER_SIZE2, end - BUFFER_SIZE2);
25
 
26
-       pw_log_debug("receiving %zd len:%d timestamp:%d %u", plen, res, timestamp, index);
27
+       pw_log_info("receiving %zd len:%d timestamp:%d %u", plen, res, timestamp, index);
28
        samples = res;
29
 
30
        write += samples;
31
@@ -196,7 +196,7 @@
32
    return -EINVAL;
33
 invalid_version:
34
    pw_log_warn("invalid RTP version");
35
-   spa_debug_mem(0, buffer, len);
36
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_INFO, 0, buffer, len);
37
    return -EPROTO;
38
 invalid_len:
39
    pw_log_warn("invalid RTP length");
40
@@ -244,7 +244,7 @@
41
                (const float*)&impl->bufferoffset * stride, tosend,
42
                out, sizeof(out));
43
 
44
-       pw_log_debug("sending %d len:%d timestamp:%d", tosend, res, timestamp);
45
+       pw_log_trace("sending %d len:%d timestamp:%d", tosend, res, timestamp);
46
        iov1.iov_len = res;
47
 
48
        rtp_stream_emit_send_packet(impl, iov, 2);
49
@@ -255,7 +255,7 @@
50
        avail -= tosend;
51
    }
52
 
53
-   pw_log_debug("move %d offset:%d", avail, offset);
54
+   pw_log_trace("move %d offset:%d", avail, offset);
55
    memmove(impl->buffer, &impl->bufferoffset * stride, avail * stride);
56
 
57
    spa_ringbuffer_read_update(&impl->ring, timestamp);
58
@@ -270,7 +270,7 @@
59
    int32_t filled, wanted;
60
 
61
    if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) {
62
-       pw_log_debug("Out of stream buffers: %m");
63
+       pw_log_info("Out of stream buffers: %m");
64
        return;
65
    }
66
    d = buf->buffer->datas;
67
pipewire-1.2.0.tar.gz/src/modules/module-rtp/ptp.h Added
63
 
1
@@ -0,0 +1,61 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Dmitry Sharshakov <d3dx12.xx@gmail.com> */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_PTP_H
7
+#define PIPEWIRE_PTP_H
8
+
9
+#include <stdint.h>
10
+
11
+#ifdef __cplusplus
12
+extern "C" {
13
+#endif
14
+
15
+struct ptp_management_msg {
16
+   // 4 for major_sdo, 4 for msg_type
17
+   uint8_t  major_sdo_id_message_type;
18
+   // 4 for minor, 4 for major
19
+   uint8_t  ver;
20
+   uint16_t message_length_be;
21
+   uint8_t  domain_number;
22
+   uint8_t  minor_sdo_id;
23
+   uint16_t flags_be;
24
+   uint8_t  correction_field8;
25
+   uint32_t message_type_specific;
26
+   uint8_t  clock_identity8;
27
+   uint16_t source_port_id_be;
28
+   uint16_t sequence_id_be;
29
+   uint8_t  control_field;
30
+   uint8_t  log_message_interval;
31
+   uint8_t  target_port_identity8;
32
+   uint16_t target_port_id_be;
33
+   uint8_t  starting_boundary_hops;
34
+   uint8_t  boundary_hops;
35
+   uint8_t  action;
36
+   uint8_t  reserved;
37
+   uint16_t tlv_type_be;
38
+   // length of data after this + 2 for management_id
39
+   uint16_t management_message_length_be;
40
+   uint16_t management_id_be;
41
+} __attribute__((packed));
42
+
43
+struct ptp_parent_data_set {
44
+   uint8_t  parent_clock_id8;
45
+   uint16_t parent_port_id_be;
46
+   uint8_t  parent_stats;
47
+   uint8_t  reserved;
48
+   uint16_t log_variance_be;
49
+   int32_t  phase_change_rate_be;
50
+   uint8_t  gm_prio1;
51
+   uint8_t  gm_clock_class;
52
+   uint8_t  gm_clock_accuracy;
53
+   uint16_t gm_clock_variance_be;
54
+   uint8_t  gm_prio2;
55
+   uint8_t  gm_clock_id8;
56
+} __attribute__((packed));
57
+
58
+#ifdef __cplusplus
59
+}
60
+#endif
61
+
62
+#endif /* PIPEWIRE_PTP_H */
63
pipewire-1.0.1.tar.bz2/src/modules/module-rtp/stream.c -> pipewire-1.2.0.tar.gz/src/modules/module-rtp/stream.c Changed
287
 
1
@@ -13,6 +13,7 @@
2
 #include <spa/control/control.h>
3
 #include <spa/debug/types.h>
4
 #include <spa/debug/mem.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include "config.h"
8
 
9
@@ -23,6 +24,9 @@
10
 #include <module-rtp/stream.h>
11
 #include <module-rtp/apple-midi.h>
12
 
13
+PW_LOG_TOPIC_EXTERN(mod_topic);
14
+#define PW_LOG_TOPIC_DEFAULT mod_topic
15
+
16
 #define BUFFER_SIZE            (1u<<22)
17
 #define BUFFER_MASK            (BUFFER_SIZE-1)
18
 #define BUFFER_SIZE2           (BUFFER_SIZE>>1)
19
@@ -40,6 +44,8 @@
20
    struct spa_audio_info info;
21
    struct spa_audio_info stream_info;
22
 
23
+   struct pw_context *context;
24
+
25
    struct pw_stream *stream;
26
    struct spa_hook stream_listener;
27
    struct pw_stream_events stream_events;
28
@@ -84,9 +90,24 @@
29
    unsigned receiving:1;
30
    unsigned first:1;
31
 
32
+   struct pw_loop *main_loop;
33
+   struct pw_loop *data_loop;
34
+   struct spa_source *timer;
35
+   bool timer_running;
36
+
37
    int (*receive_rtp)(struct impl *impl, uint8_t *buffer, ssize_t len);
38
+   void (*flush_timeout)(struct impl *impl, uint64_t expirations);
39
 };
40
 
41
+static int do_emit_state_changed(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
42
+{
43
+   struct impl *impl = user_data;
44
+   bool *started = (bool *)data;
45
+
46
+   rtp_stream_emit_state_changed(impl, *started, NULL);
47
+   return 0;
48
+}
49
+
50
 #include "module-rtp/audio.c"
51
 #include "module-rtp/midi.c"
52
 #include "module-rtp/opus.c"
53
@@ -148,7 +169,9 @@
54
    if (!impl->started)
55
        return 0;
56
 
57
-   rtp_stream_emit_state_changed(impl, false, NULL);
58
+   /* if timer is running, the state changed event must be emitted by the timer after all packets have been sent */
59
+   if (!impl->timer_running)
60
+       rtp_stream_emit_state_changed(impl, false, NULL);
61
 
62
    impl->started = false;
63
    return 0;
64
@@ -165,7 +188,6 @@
65
            break;
66
        case PW_STREAM_STATE_ERROR:
67
            pw_log_error("stream error: %s", error);
68
-           rtp_stream_emit_state_changed(impl, false, error);
69
            break;
70
        case PW_STREAM_STATE_STREAMING:
71
            if ((errno = -stream_start(impl)) < 0)
72
@@ -261,9 +283,19 @@
73
        parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION));
74
 }
75
 
76
-static uint32_t msec_to_samples(struct impl *impl, uint32_t msec)
77
+static uint32_t msec_to_samples(struct impl *impl, float msec)
78
+{
79
+   return (uint32_t)(msec * impl->rate / 1000);
80
+}
81
+static float samples_to_msec(struct impl *impl, uint32_t samples)
82
+{
83
+   return samples * 1000.0f / impl->rate;
84
+}
85
+
86
+static void on_flush_timeout(void *d, uint64_t expirations)
87
 {
88
-   return msec * impl->rate / 1000;
89
+   struct impl *impl = d;
90
+   impl->flush_timeout(d, expirations);
91
 }
92
 
93
 struct rtp_stream *rtp_stream_new(struct pw_core *core,
94
@@ -272,24 +304,33 @@
95
 {
96
    struct impl *impl;
97
    const char *str;
98
+   char tmp64;
99
    uint8_t buffer1024;
100
    struct spa_pod_builder b;
101
    uint32_t n_params, min_samples, max_samples;
102
    float min_ptime, max_ptime;
103
    const struct spa_pod *params1;
104
    enum pw_stream_flags flags;
105
-   int latency_msec;
106
+   float latency_msec;
107
    int res;
108
 
109
    impl = calloc(1, sizeof(*impl));
110
    if (impl == NULL) {
111
        res = -errno;
112
        goto out;
113
-       return NULL;
114
    }
115
    impl->first = true;
116
    spa_hook_list_init(&impl->listener_list);
117
    impl->stream_events = stream_events;
118
+   impl->context = pw_core_get_context(core);
119
+   impl->main_loop = pw_context_get_main_loop(impl->context);
120
+   impl->data_loop = pw_context_acquire_loop(impl->context, &props->dict);
121
+   impl->timer = pw_loop_add_timer(impl->data_loop, on_flush_timeout, impl);
122
+   if (impl->timer == NULL) {
123
+       res = -errno;
124
+       pw_log_error("can't create timer");
125
+       goto out;
126
+   }
127
 
128
    if ((str = pw_properties_get(props, "sess.media")) == NULL)
129
        str = "audio";
130
@@ -303,7 +344,6 @@
131
        impl->info.media_type = SPA_MEDIA_TYPE_audio;
132
        impl->info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
133
        impl->payload = 0x60;
134
-       impl->marker_on_first = 1;
135
    }
136
    else if (spa_streq(str, "midi")) {
137
        impl->info.media_type = SPA_MEDIA_TYPE_application;
138
@@ -383,6 +423,8 @@
139
        pw_properties_set(props, PW_KEY_NODE_NETWORK, "true");
140
 
141
    impl->marker_on_first = pw_properties_get_bool(props, "sess.marker-on-first", false);
142
+   if (spa_streq(str, "raop"))
143
+       impl->marker_on_first = 1;
144
    impl->ignore_ssrc = pw_properties_get_bool(props, "sess.ignore-ssrc", false);
145
    impl->direct_timestamp = pw_properties_get_bool(props, "sess.ts-direct", false);
146
 
147
@@ -407,32 +449,74 @@
148
    if (!spa_atof(str, &max_ptime))
149
        max_ptime = DEFAULT_MAX_PTIME;
150
 
151
-   min_samples = min_ptime * impl->rate / 1000;
152
-   max_samples = max_ptime * impl->rate / 1000;
153
+   min_samples = msec_to_samples(impl, min_ptime);
154
+   max_samples = msec_to_samples(impl, max_ptime);
155
 
156
    float ptime = 0;
157
    if ((str = pw_properties_get(props, "rtp.ptime")) != NULL)
158
        if (!spa_atof(str, &ptime))
159
-           ptime = 0.0;
160
-
161
-   if (ptime) {
162
-       impl->psamples = ptime * impl->rate / 1000;
163
+           ptime = 0.0f;
164
+
165
+   uint32_t framecount = 0;
166
+   if ((str = pw_properties_get(props, "rtp.framecount")) != NULL)
167
+       if (!spa_atou32(str, &framecount, 0))
168
+           framecount = 0;
169
+
170
+   if (ptime > 0.0f || framecount > 0) {
171
+       if (!framecount) {
172
+           impl->psamples = msec_to_samples(impl, ptime);
173
+           pw_properties_setf(props, "rtp.framecount", "%u", impl->psamples);
174
+       } else if (ptime == 0.0f) {
175
+           impl->psamples = framecount;
176
+           pw_properties_set(props, "rtp.ptime",
177
+                   spa_dtoa(tmp, sizeof(tmp),
178
+                       samples_to_msec(impl, impl->psamples)));
179
+       } else if (fabsf((samples_to_msec(impl, framecount)) - ptime) > 0.1f) {
180
+           impl->psamples = msec_to_samples(impl, ptime);
181
+           pw_log_warn("rtp.ptime doesn't match rtp.framecount. Choosing rtp.ptime");
182
+       }
183
    } else {
184
        impl->psamples = impl->mtu / impl->stride;
185
        impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
186
-       if (direction == PW_DIRECTION_INPUT)
187
-           pw_properties_setf(props, "rtp.ptime", "%f",
188
-                   impl->psamples * 1000.0 / impl->rate);
189
+       if (direction == PW_DIRECTION_INPUT) {
190
+           pw_properties_set(props, "rtp.ptime",
191
+                   spa_dtoa(tmp, sizeof(tmp),
192
+                       samples_to_msec(impl, impl->psamples)));
193
+
194
+           pw_properties_setf(props, "rtp.framecount", "%u", impl->psamples);
195
+       }
196
+   }
197
+
198
+   ptime = samples_to_msec(impl, impl->psamples);
199
+
200
+   /* For senders, the default latency is ptime and for a receiver it is
201
+    * DEFAULT_SESS_LATENCY. Setting the sess.latency.msec for a sender to
202
+    * something smaller/bigger will influence the quantum and the amount
203
+    * of packets we send in one cycle */
204
+   str = pw_properties_get(props, "sess.latency.msec");
205
+   if (!spa_atof(str, &latency_msec)) {
206
+       latency_msec = direction == PW_DIRECTION_INPUT ?
207
+           ptime :
208
+           DEFAULT_SESS_LATENCY;
209
    }
210
-   latency_msec = pw_properties_get_uint32(props,
211
-           "sess.latency.msec", DEFAULT_SESS_LATENCY);
212
    impl->target_buffer = msec_to_samples(impl, latency_msec);
213
    impl->max_error = msec_to_samples(impl, ERROR_MSEC);
214
 
215
+   if (impl->target_buffer < ptime) {
216
+       pw_log_warn("sess.latency.msec cannot be lower than rtp.ptime");
217
+       impl->target_buffer = impl->psamples;
218
+   }
219
+
220
+   /* We're not expecting odd ptimes, so this modulo should be 0 */
221
+   if (fmodf(impl->target_buffer, ptime) != 0) {
222
+       pw_log_warn("sess.latency.msec should be an integer multiple of rtp.ptime");
223
+       impl->target_buffer = (uint32_t)((impl->target_buffer / ptime) * impl->psamples);
224
+   }
225
+
226
    pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", impl->rate);
227
    if (direction == PW_DIRECTION_INPUT) {
228
        pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
229
-               impl->psamples, impl->rate);
230
+               impl->target_buffer, impl->rate);
231
    } else {
232
        pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%d/%d",
233
                impl->target_buffer / 2, impl->rate);
234
@@ -527,17 +611,29 @@
235
    if (impl->stream)
236
        pw_stream_destroy(impl->stream);
237
 
238
+   if (impl->timer)
239
+       pw_loop_destroy_source(impl->data_loop, impl->timer);
240
+
241
+   if (impl->data_loop)
242
+       pw_context_release_loop(impl->context, impl->data_loop);
243
+
244
    spa_hook_list_clean(&impl->listener_list);
245
    free(impl);
246
 }
247
 
248
+int rtp_stream_update_properties(struct rtp_stream *s, const struct spa_dict *dict)
249
+{
250
+   struct impl *impl = (struct impl*)s;
251
+   return pw_stream_update_properties(impl->stream, dict);
252
+}
253
+
254
 int rtp_stream_receive_packet(struct rtp_stream *s, uint8_t *buffer, size_t len)
255
 {
256
    struct impl *impl = (struct impl*)s;
257
    return impl->receive_rtp(impl, buffer, len);
258
 }
259
 
260
-uint64_t rtp_stream_get_time(struct rtp_stream *s, uint64_t *rate)
261
+uint64_t rtp_stream_get_time(struct rtp_stream *s, uint32_t *rate)
262
 {
263
    struct impl *impl = (struct impl*)s;
264
    struct spa_io_position *pos = impl->io_position;
265
@@ -564,6 +660,12 @@
266
    impl->first = true;
267
 }
268
 
269
+void rtp_stream_set_error(struct rtp_stream *s, int res, const char *error)
270
+{
271
+   struct impl *impl = (struct impl*)s;
272
+   pw_stream_set_error(impl->stream, res, "%s: %s", error, spa_strerror(res));
273
+}
274
+
275
 enum pw_stream_state rtp_stream_get_state(struct rtp_stream *s, const char **error)
276
 {
277
    struct impl *impl = (struct impl*)s;
278
@@ -583,6 +685,5 @@
279
            uint32_t n_params)
280
 {
281
    struct impl *impl = (struct impl*)s;
282
-   
283
    return pw_stream_update_params(impl->stream, params, n_params);
284
-}
285
\ No newline at end of file
286
+}
287
pipewire-1.0.1.tar.bz2/src/modules/module-rtp/stream.h -> pipewire-1.2.0.tar.gz/src/modules/module-rtp/stream.h Changed
37
 
1
@@ -16,12 +16,12 @@
2
 #define DEFAULT_CHANNELS   2
3
 #define DEFAULT_POSITION   " FL FR "
4
 
5
-#define ERROR_MSEC         2
6
-#define DEFAULT_SESS_LATENCY       100
7
+#define ERROR_MSEC     2.0f
8
+#define DEFAULT_SESS_LATENCY   100.0f
9
 
10
 #define DEFAULT_MTU        1280
11
-#define DEFAULT_MIN_PTIME  2
12
-#define DEFAULT_MAX_PTIME  20
13
+#define DEFAULT_MIN_PTIME  2.0f
14
+#define DEFAULT_MAX_PTIME  20.0f
15
 
16
 struct rtp_stream_events {
17
 #define RTP_VERSION_STREAM_EVENTS        0
18
@@ -44,14 +44,17 @@
19
 
20
 void rtp_stream_destroy(struct rtp_stream *s);
21
 
22
+int rtp_stream_update_properties(struct rtp_stream *s, const struct spa_dict *dict);
23
+
24
 int rtp_stream_receive_packet(struct rtp_stream *s, uint8_t *buffer, size_t len);
25
 
26
-uint64_t rtp_stream_get_time(struct rtp_stream *s, uint64_t *rate);
27
+uint64_t rtp_stream_get_time(struct rtp_stream *s, uint32_t *rate);
28
 
29
 uint16_t rtp_stream_get_seq(struct rtp_stream *s);
30
 
31
 void rtp_stream_set_first(struct rtp_stream *s);
32
 
33
+void rtp_stream_set_error(struct rtp_stream *s, int res, const char *error);
34
 enum pw_stream_state rtp_stream_get_state(struct rtp_stream *s, const char **error);
35
 
36
 int rtp_stream_set_param(struct rtp_stream *s, uint32_t id, const struct spa_pod *param);
37
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager.c Changed
10
 
1
@@ -9,7 +9,7 @@
2
 
3
 /** \page page_module_session_manager Session Manager
4
  *
5
- * This module implements some usefull objects for implementing a session
6
+ * This module implements some useful objects for implementing a session
7
  * manager. It is not yet actively used.
8
  *
9
  * ## Module Name
10
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/client-endpoint/endpoint.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/client-endpoint/endpoint.c Changed
9
 
1
@@ -299,7 +299,6 @@
2
        struct pw_properties *properties)
3
 {
4
    static const char * const keys = {
5
-       PW_KEY_OBJECT_SERIAL,
6
        PW_KEY_FACTORY_ID,
7
        PW_KEY_CLIENT_ID,
8
        PW_KEY_DEVICE_ID,
9
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/client-session/session.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/client-session/session.c Changed
9
 
1
@@ -269,7 +269,6 @@
2
        struct pw_properties *properties)
3
 {
4
    static const char * const keys = {
5
-       PW_KEY_OBJECT_SERIAL,
6
        PW_KEY_FACTORY_ID,
7
        PW_KEY_CLIENT_ID,
8
        NULL
9
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/endpoint-link.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/endpoint-link.c Changed
28
 
1
@@ -371,15 +371,6 @@
2
              struct pw_properties *properties)
3
 {
4
    struct impl *impl;
5
-   char serial_str32;
6
-   struct spa_dict_item items1 = {
7
-       SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_SERIAL, serial_str),
8
-   };
9
-   struct spa_dict extra_props = SPA_DICT_INIT_ARRAY(items);
10
-   static const char * const keys = {
11
-       PW_KEY_OBJECT_SERIAL,
12
-       NULL
13
-   };
14
 
15
    impl = calloc(1, sizeof(*impl));
16
    if (impl == NULL) {
17
@@ -399,10 +390,6 @@
18
    }
19
    impl->resource = resource;
20
 
21
-   spa_scnprintf(serial_str, sizeof(serial_str), "%"PRIu64,
22
-           pw_global_get_serial(impl->global));
23
-   pw_global_update_keys(impl->global, &extra_props, keys);
24
-
25
    spa_list_init(&impl->cached_params);
26
 
27
    /* handle destroy events */
28
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/endpoint-stream.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/endpoint-stream.c Changed
28
 
1
@@ -362,15 +362,6 @@
2
              struct pw_properties *properties)
3
 {
4
    struct impl *impl;
5
-   char serial_str32;
6
-   struct spa_dict_item items1 = {
7
-       SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_SERIAL, serial_str),
8
-   };
9
-   struct spa_dict extra_props = SPA_DICT_INIT_ARRAY(items);
10
-   static const char * const keys = {
11
-       PW_KEY_OBJECT_SERIAL,
12
-       NULL
13
-   };
14
 
15
    impl = calloc(1, sizeof(*impl));
16
    if (impl == NULL) {
17
@@ -390,10 +381,6 @@
18
    }
19
    impl->resource = resource;
20
 
21
-   spa_scnprintf(serial_str, sizeof(serial_str), "%"PRIu64,
22
-           pw_global_get_serial(impl->global));
23
-   pw_global_update_keys(impl->global, &extra_props, keys);
24
-
25
    spa_list_init(&impl->cached_params);
26
 
27
    /* handle destroy events */
28
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/endpoint.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/endpoint.c Changed
28
 
1
@@ -371,15 +371,6 @@
2
              struct pw_properties *properties)
3
 {
4
    struct impl *impl;
5
-   char serial_str32;
6
-   struct spa_dict_item items1 = {
7
-       SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_SERIAL, serial_str),
8
-   };
9
-   struct spa_dict extra_props = SPA_DICT_INIT_ARRAY(items);
10
-   static const char * const keys = {
11
-       PW_KEY_OBJECT_SERIAL,
12
-       NULL
13
-   };
14
 
15
    impl = calloc(1, sizeof(*impl));
16
    if (impl == NULL) {
17
@@ -399,10 +390,6 @@
18
    }
19
    impl->resource = resource;
20
 
21
-   spa_scnprintf(serial_str, sizeof(serial_str), "%"PRIu64,
22
-           pw_global_get_serial(impl->global));
23
-   pw_global_update_keys(impl->global, &extra_props, keys);
24
-
25
    spa_list_init(&impl->cached_params);
26
 
27
    /* handle destroy events */
28
pipewire-1.0.1.tar.bz2/src/modules/module-session-manager/session.c -> pipewire-1.2.0.tar.gz/src/modules/module-session-manager/session.c Changed
28
 
1
@@ -360,15 +360,6 @@
2
              struct pw_properties *properties)
3
 {
4
    struct impl *impl;
5
-   char serial_str32;
6
-   struct spa_dict_item items1 = {
7
-       SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_SERIAL, serial_str),
8
-   };
9
-   struct spa_dict extra_props = SPA_DICT_INIT_ARRAY(items);
10
-   static const char * const keys = {
11
-       PW_KEY_OBJECT_SERIAL,
12
-       NULL
13
-   };
14
 
15
    impl = calloc(1, sizeof(*impl));
16
    if (impl == NULL) {
17
@@ -388,10 +379,6 @@
18
    }
19
    impl->resource = resource;
20
 
21
-   spa_scnprintf(serial_str, sizeof(serial_str), "%"PRIu64,
22
-           pw_global_get_serial(impl->global));
23
-   pw_global_update_keys(impl->global, &extra_props, keys);
24
-
25
    spa_list_init(&impl->cached_params);
26
 
27
    /* handle destroy events */
28
pipewire-1.2.0.tar.gz/src/modules/module-snapcast-discover.c Added
918
 
1
@@ -0,0 +1,916 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <string.h>
7
+#include <stdio.h>
8
+#include <errno.h>
9
+#include <sys/types.h>
10
+#include <sys/stat.h>
11
+#include <sys/ioctl.h>
12
+#include <sys/socket.h>
13
+#include <fcntl.h>
14
+#include <unistd.h>
15
+#include <netinet/in.h>
16
+#include <arpa/inet.h>
17
+#include <netdb.h>
18
+#include <net/if.h>
19
+#include <ifaddrs.h>
20
+
21
+#include "config.h"
22
+
23
+#include <spa/utils/result.h>
24
+#include <spa/utils/string.h>
25
+#include <spa/utils/json.h>
26
+#include <spa/param/audio/format.h>
27
+#include <spa/debug/types.h>
28
+
29
+#include <pipewire/impl.h>
30
+#include <pipewire/i18n.h>
31
+
32
+#include <avahi-client/lookup.h>
33
+#include <avahi-common/error.h>
34
+#include <avahi-common/malloc.h>
35
+
36
+#include "module-protocol-pulse/format.h"
37
+#include "module-zeroconf-discover/avahi-poll.h"
38
+
39
+/** \page page_module_snapcast_discover Snapcast Discover
40
+ *
41
+ * Automatically creates a Snapcast sink device based on zeroconf
42
+ * information.
43
+ *
44
+ * This module will load module-protocol-simple for each announced stream that matches
45
+ * the rule with the create-stream action and passes the properties to the module.
46
+ *
47
+ * If no stream.rules are given, it will create a sink for all announced
48
+ * snapcast servers.
49
+ *
50
+ * A new stream will be created on the snapcast server with the given
51
+ * `snapcast.stream-name` or `PipeWire-<hostname>`. You will need to route this new
52
+ * stream to clients with the snapcast control application.
53
+ *
54
+ * ## Module Name
55
+ *
56
+ * `libpipewire-module-snapcast-discover`
57
+ *
58
+ * ## Module Options
59
+ *
60
+ * Options specific to the behavior of this module
61
+ *
62
+ * - `snapcast.discover-local` = allow discovery of local services as well.
63
+ *    false by default.
64
+ * - `stream.rules` = <rules>: match rules, use create-stream actions. See
65
+ *   \ref page_module_protocol_simple for module properties.
66
+ *
67
+ * ## Example configuration
68
+ *
69
+ *\code{.unparsed}
70
+ * context.modules = 
71
+ * {   name = libpipewire-snapcast-discover
72
+ *     args = {
73
+ *         stream.rules = 
74
+ *             {   matches = 
75
+ *                     {    snapcast.ip = "~.*"
76
+ *                          #snapcast.ifindex = 1
77
+ *                          #snapcast.ifname = eth0
78
+ *                          #snapcast.port = 1000
79
+ *                          #snapcast.name = ""
80
+ *                          #snapcast.hostname = ""
81
+ *                          #snapcast.domain = ""
82
+ *                     }
83
+ *                 
84
+ *                 actions = {
85
+ *                     create-stream = {
86
+ *                         #audio.rate = 44100
87
+ *                         #audio.format = S16LE   # S16LE, S24_32LE, S32LE
88
+ *                         #audio.channels = 2
89
+ *                         #audio.position =  FL FR 
90
+ *                         #
91
+ *                         #snapcast.stream-name = "PipeWire"
92
+ *                         #
93
+ *                         #capture = true
94
+ *                         #capture.props = {
95
+ *                             #target.object = ""
96
+ *                             #media.class = "Audio/Sink"
97
+ *                         #}
98
+ *                     }
99
+ *                 }
100
+ *             }
101
+ *         
102
+ *     }
103
+ * }
104
+ * 
105
+ *\endcode
106
+ *
107
+ * ## See also
108
+ *
109
+ * \ref page_module_protocol_simple
110
+ */
111
+
112
+#define NAME "snapcast-discover"
113
+
114
+PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
115
+#define PW_LOG_TOPIC_DEFAULT mod_topic
116
+
117
+#define MODULE_USAGE "( stream.rules=<rules>, use create-stream actions )"
118
+
119
+#define DEFAULT_FORMAT "S16LE"
120
+#define DEFAULT_RATE 48000
121
+#define DEFAULT_CHANNELS 2
122
+#define DEFAULT_POSITION " FL FR "
123
+
124
+#define DEFAULT_CREATE_RULES   \
125
+        " { matches =  { snapcast.ip = \"~.*\" }  actions = { create-stream = { } } }  "
126
+
127
+static const struct spa_dict_item module_props = {
128
+   { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
129
+   { PW_KEY_MODULE_DESCRIPTION, "Discover remote Snapcast streams" },
130
+   { PW_KEY_MODULE_USAGE, MODULE_USAGE },
131
+   { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
132
+};
133
+
134
+#define SERVICE_TYPE_CONTROL "_snapcast-jsonrpc._tcp"
135
+
136
+struct impl {
137
+   struct pw_context *context;
138
+   struct pw_loop *loop;
139
+
140
+   struct pw_impl_module *module;
141
+   struct spa_hook module_listener;
142
+
143
+   struct pw_properties *properties;
144
+   bool discover_local;
145
+
146
+   AvahiPoll *avahi_poll;
147
+   AvahiClient *client;
148
+   AvahiServiceBrowser *sink_browser;
149
+
150
+   struct spa_list tunnel_list;
151
+};
152
+
153
+struct tunnel_info {
154
+   const char *name;
155
+   const char *host;
156
+   uint16_t port;
157
+};
158
+
159
+#define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ })
160
+
161
+struct tunnel {
162
+   struct impl *impl;
163
+   struct spa_list link;
164
+   struct tunnel_info info;
165
+   struct pw_impl_module *module;
166
+   struct spa_hook module_listener;
167
+   char *server_address;
168
+   char *stream_name;
169
+   struct spa_audio_info_raw audio_info;
170
+   struct spa_source *source;
171
+   bool connecting;
172
+   bool need_flush;
173
+};
174
+
175
+static int start_client(struct impl *impl);
176
+
177
+static struct tunnel *make_tunnel(struct impl *impl, const struct tunnel_info *info)
178
+{
179
+   struct tunnel *t;
180
+
181
+   t = calloc(1, sizeof(*t));
182
+   if (t == NULL)
183
+       return NULL;
184
+
185
+   t->info.name = info->name ? strdup(info->name) : NULL;
186
+   t->info.host = info->host ? strdup(info->host) : NULL;
187
+   t->info.port = info->port;
188
+   t->impl = impl;
189
+   spa_list_append(&impl->tunnel_list, &t->link);
190
+
191
+   return t;
192
+}
193
+
194
+static struct tunnel *find_tunnel(struct impl *impl, const struct tunnel_info *info)
195
+{
196
+   struct tunnel *t;
197
+   spa_list_for_each(t, &impl->tunnel_list, link) {
198
+       if (spa_streq(t->info.name, info->name))
199
+           return t;
200
+   }
201
+   return NULL;
202
+}
203
+
204
+static void free_tunnel(struct tunnel *t)
205
+{
206
+   spa_list_remove(&t->link);
207
+   if (t->module)
208
+       pw_impl_module_destroy(t->module);
209
+   free((char *) t->info.name);
210
+   free((char *) t->info.host);
211
+   free(t->server_address);
212
+   free(t->stream_name);
213
+   free(t);
214
+}
215
+
216
+static void impl_free(struct impl *impl)
217
+{
218
+   struct tunnel *t;
219
+
220
+   spa_list_consume(t, &impl->tunnel_list, link)
221
+       free_tunnel(t);
222
+
223
+   if (impl->sink_browser)
224
+       avahi_service_browser_free(impl->sink_browser);
225
+   if (impl->client)
226
+       avahi_client_free(impl->client);
227
+   if (impl->avahi_poll)
228
+       pw_avahi_poll_free(impl->avahi_poll);
229
+   pw_properties_free(impl->properties);
230
+   free(impl);
231
+}
232
+
233
+static void module_destroy(void *data)
234
+{
235
+   struct impl *impl = data;
236
+   spa_hook_remove(&impl->module_listener);
237
+   impl_free(impl);
238
+}
239
+
240
+static const struct pw_impl_module_events module_events = {
241
+   PW_VERSION_IMPL_MODULE_EVENTS,
242
+   .destroy = module_destroy,
243
+};
244
+
245
+static void pw_properties_from_avahi_string(const char *key, const char *value,
246
+       struct pw_properties *props)
247
+{
248
+}
249
+
250
+static void submodule_destroy(void *data)
251
+{
252
+   struct tunnel *t = data;
253
+   spa_hook_remove(&t->module_listener);
254
+   t->module = NULL;
255
+}
256
+
257
+static const struct pw_impl_module_events submodule_events = {
258
+   PW_VERSION_IMPL_MODULE_EVENTS,
259
+   .destroy = submodule_destroy,
260
+};
261
+
262
+static int snapcast_disconnect(struct tunnel *t)
263
+{
264
+   if (t->source)
265
+       pw_loop_destroy_source(t->impl->loop, t->source);
266
+   t->source = NULL;
267
+   return 0;
268
+}
269
+
270
+static int get_bps(uint32_t format)
271
+{
272
+   switch (format) {
273
+   case SPA_AUDIO_FORMAT_S16_LE:
274
+       return 16;
275
+   case SPA_AUDIO_FORMAT_S24_32_LE:
276
+       return 24;
277
+   case SPA_AUDIO_FORMAT_S32_LE:
278
+       return 32;
279
+   default:
280
+       return 0;
281
+   }
282
+}
283
+
284
+static int handle_connect(struct tunnel *t, int fd)
285
+{
286
+   int res;
287
+   socklen_t len;
288
+   char *str;
289
+
290
+   len = sizeof(res);
291
+   if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &res, &len) < 0) {
292
+       pw_log_error("getsockopt: %m");
293
+       return -errno;
294
+   }
295
+   if (res != 0)
296
+       return -res;
297
+
298
+   t->connecting = false;
299
+   pw_log_info("connected");
300
+
301
+   str = "{\"id\":8,\"jsonrpc\": \"2.0\",\"method\":\"Server.GetRPCVersion\"}\r\n";
302
+   res = write(t->source->fd, str, strlen(str));
303
+   pw_log_info("wrote %s: %d", str, res);
304
+
305
+   str = spa_aprintf("{\"id\":4,\"jsonrpc\":\"2.0\",\"method\":\"Stream.RemoveStream\","
306
+           "\"params\":{\"id\":\"%s\"}}\r\n", t->stream_name);
307
+   res = write(t->source->fd, str, strlen(str));
308
+   pw_log_info("wrote %s: %d", str, res);
309
+   free(str);
310
+
311
+   str = spa_aprintf("{\"id\":4,\"jsonrpc\":\"2.0\",\"method\":\"Stream.AddStream\""
312
+       ",\"params\":{\"streamUri\":\"tcp://%s?name=%s&mode=client&"
313
+       "sampleformat=%d:%d:%d&codec=pcm&chunk_ms=20\"}}\r\n",
314
+       t->server_address, t->stream_name, t->audio_info.rate,
315
+       get_bps(t->audio_info.format), t->audio_info.channels);
316
+
317
+   res = write(t->source->fd, str, strlen(str));
318
+   pw_log_info("wrote %s: %d", str, res);
319
+   free(str);
320
+
321
+   return 0;
322
+}
323
+
324
+static int process_input(struct tunnel *t)
325
+{
326
+   char buffer1024 = "";
327
+   int res = 0;
328
+
329
+   while (true) {
330
+       res = read(t->source->fd, buffer, sizeof(buffer));
331
+       pw_log_info("%d", res);
332
+       if (res == 0)
333
+           return -EPIPE;
334
+       if (res < 0) {
335
+           res = -errno;
336
+           if (res == -EINTR)
337
+               continue;
338
+           if (res != -EAGAIN && res != -EWOULDBLOCK)
339
+               return res;
340
+           break;
341
+       }
342
+   }
343
+
344
+   pw_log_info("%s", buffer);
345
+   return 0;
346
+}
347
+
348
+static int flush_output(struct tunnel *t)
349
+{
350
+   t->need_flush = false;
351
+   return 0;
352
+}
353
+
354
+static void
355
+on_source_io(void *data, int fd, uint32_t mask)
356
+{
357
+   struct tunnel *t = data;
358
+   int res;
359
+
360
+   if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
361
+       res = -EPIPE;
362
+       goto error;
363
+   }
364
+   if (mask & SPA_IO_IN) {
365
+       if ((res = process_input(t)) < 0)
366
+           goto error;
367
+   }
368
+   if (mask & SPA_IO_OUT || t->need_flush) {
369
+       if (t->connecting) {
370
+           if ((res = handle_connect(t, fd)) < 0)
371
+               goto error;
372
+       }
373
+       res = flush_output(t);
374
+       if (res >= 0) {
375
+           pw_loop_update_io(t->impl->loop, t->source,
376
+               t->source->mask & ~SPA_IO_OUT);
377
+       } else if (res != -EAGAIN)
378
+           goto error;
379
+   }
380
+done:
381
+   return;
382
+error:
383
+   pw_log_error("%p: got connection error %d (%s)", t, res, spa_strerror(res));
384
+   snapcast_disconnect(t);
385
+   goto done;
386
+}
387
+
388
+
389
+static int snapcast_connect(struct tunnel *t)
390
+{
391
+   struct addrinfo hints;
392
+   struct addrinfo *result, *rp;
393
+   int res, fd;
394
+   char port_str12;
395
+
396
+   if (t->server_address == NULL)
397
+       return 0;
398
+
399
+   if (t->source != NULL)
400
+       snapcast_disconnect(t);
401
+
402
+   pw_log_info("%p: connect %s:%u", t, t->info.host, t->info.port);
403
+
404
+   memset(&hints, 0, sizeof(hints));
405
+   hints.ai_family = AF_UNSPEC;
406
+   hints.ai_socktype = SOCK_STREAM;
407
+   hints.ai_flags = 0;
408
+   hints.ai_protocol = 0;
409
+
410
+   spa_scnprintf(port_str, sizeof(port_str), "%u", t->info.port);
411
+
412
+   if ((res = getaddrinfo(t->info.host, port_str, &hints, &result)) != 0) {
413
+       pw_log_error("getaddrinfo: %s", gai_strerror(res));
414
+       return -EINVAL;
415
+   }
416
+   res = -ENOENT;
417
+   for (rp = result; rp != NULL; rp = rp->ai_next) {
418
+       fd = socket(rp->ai_family,
419
+               rp->ai_socktype | SOCK_CLOEXEC | SOCK_NONBLOCK,
420
+               rp->ai_protocol);
421
+       if (fd == -1)
422
+           continue;
423
+
424
+       res = connect(fd, rp->ai_addr, rp->ai_addrlen);
425
+       if (res == 0 || (res < 0 && errno == EINPROGRESS))
426
+           break;
427
+
428
+       res = -errno;
429
+       close(fd);
430
+   }
431
+   freeaddrinfo(result);
432
+
433
+   if (rp == NULL) {
434
+       pw_log_error("Could not connect to %s:%u: %s", t->info.host, t->info.port,
435
+               spa_strerror(res));
436
+       return -EINVAL;
437
+   }
438
+
439
+   t->source = pw_loop_add_io(t->impl->loop, fd,
440
+           SPA_IO_IN | SPA_IO_OUT | SPA_IO_HUP | SPA_IO_ERR,
441
+           true, on_source_io, t);
442
+
443
+   if (t->source == NULL) {
444
+       res = -errno;
445
+       pw_log_error("%p: source create failed: %m", t);
446
+       close(fd);
447
+       return res;
448
+   }
449
+   t->connecting = true;
450
+   pw_log_info("%p: connecting", t);
451
+
452
+   return 0;
453
+
454
+}
455
+
456
+static int add_snapcast_stream(struct impl *impl, struct tunnel *t,
457
+       struct pw_properties *props, const char *servers)
458
+{
459
+   struct spa_json it2;
460
+   char v256;
461
+
462
+   spa_json_init(&it0, servers, strlen(servers));
463
+        if (spa_json_enter_array(&it0, &it1) <= 0)
464
+                spa_json_init(&it1, servers, strlen(servers));
465
+
466
+   while (spa_json_get_string(&it1, v, sizeof(v)) > 0) {
467
+       t->server_address = strdup(v);
468
+       snapcast_connect(t);
469
+       break;
470
+   }
471
+   return 0;
472
+}
473
+
474
+static inline uint32_t format_from_name(const char *name, size_t len)
475
+{
476
+   int i;
477
+   for (i = 0; spa_type_audio_formati.name; i++) {
478
+       if (strncmp(name, spa_debug_type_short_name(spa_type_audio_formati.name), len) == 0)
479
+           return spa_type_audio_formati.type;
480
+   }
481
+   return SPA_AUDIO_FORMAT_UNKNOWN;
482
+}
483
+
484
+static inline uint32_t channel_from_name(const char *name)
485
+{
486
+   int i;
487
+   for (i = 0; spa_type_audio_channeli.name; i++) {
488
+       if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channeli.name)))
489
+           return spa_type_audio_channeli.type;
490
+   }
491
+   return SPA_AUDIO_CHANNEL_UNKNOWN;
492
+}
493
+
494
+static void parse_position(struct spa_audio_info_raw *info, const char *val, size_t len)
495
+{
496
+   struct spa_json it2;
497
+   char v256;
498
+
499
+   spa_json_init(&it0, val, len);
500
+        if (spa_json_enter_array(&it0, &it1) <= 0)
501
+                spa_json_init(&it1, val, len);
502
+
503
+   info->channels = 0;
504
+   while (spa_json_get_string(&it1, v, sizeof(v)) > 0 &&
505
+       info->channels < SPA_AUDIO_MAX_CHANNELS) {
506
+       info->positioninfo->channels++ = channel_from_name(v);
507
+   }
508
+}
509
+
510
+static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info)
511
+{
512
+   const char *str;
513
+
514
+   spa_zero(*info);
515
+   if ((str = pw_properties_get(props, PW_KEY_AUDIO_FORMAT)) == NULL)
516
+       str = DEFAULT_FORMAT;
517
+   info->format = format_from_name(str, strlen(str));
518
+   if (info->format == 0) {
519
+       str = DEFAULT_FORMAT;
520
+       info->format = format_from_name(str, strlen(str));
521
+   }
522
+   pw_properties_set(props, PW_KEY_AUDIO_FORMAT, str);
523
+
524
+   info->rate = pw_properties_get_uint32(props, PW_KEY_AUDIO_RATE, info->rate);
525
+   if (info->rate == 0)
526
+       info->rate = DEFAULT_RATE;
527
+   pw_properties_setf(props, PW_KEY_AUDIO_RATE, "%d", info->rate);
528
+
529
+   info->channels = pw_properties_get_uint32(props, PW_KEY_AUDIO_CHANNELS, info->channels);
530
+   info->channels = SPA_MIN(info->channels, SPA_AUDIO_MAX_CHANNELS);
531
+   if ((str = pw_properties_get(props, SPA_KEY_AUDIO_POSITION)) != NULL)
532
+       parse_position(info, str, strlen(str));
533
+   if (info->channels == 0)
534
+       parse_position(info, DEFAULT_POSITION, strlen(DEFAULT_POSITION));
535
+   pw_properties_setf(props, PW_KEY_AUDIO_CHANNELS, "%d", info->channels);
536
+}
537
+
538
+static int create_stream(struct impl *impl, struct pw_properties *props,
539
+       struct tunnel *t)
540
+{
541
+   FILE *f;
542
+   char *args;
543
+   size_t size;
544
+   int res = 0;
545
+   struct pw_impl_module *mod;
546
+   const struct pw_properties *mod_props;
547
+   const char *str;
548
+
549
+   if ((str = pw_properties_get(props, "snapcast.stream-name")) == NULL)
550
+       pw_properties_setf(props, "snapcast.stream-name",
551
+               "PipeWire-%s", pw_get_host_name());
552
+   if ((str = pw_properties_get(props, "snapcast.stream-name")) == NULL)
553
+       str = "PipeWire";
554
+   t->stream_name = strdup(str);
555
+
556
+   if ((str = pw_properties_get(props, "capture")) == NULL)
557
+       pw_properties_set(props, "capture", "true");
558
+   if ((str = pw_properties_get(props, "capture.props")) == NULL)
559
+       pw_properties_set(props, "capture.props", "{ media.class = Audio/Sink }");
560
+
561
+   parse_audio_info(props, &t->audio_info);
562
+
563
+   if ((f = open_memstream(&args, &size)) == NULL) {
564
+       res = -errno;
565
+       pw_log_error("Can't open memstream: %m");
566
+       goto done;
567
+   }
568
+
569
+   fprintf(f, "{");
570
+   pw_properties_serialize_dict(f, &props->dict, 0);
571
+   fprintf(f, "}");
572
+        fclose(f);
573
+
574
+   pw_log_info("loading module args:'%s'", args);
575
+   mod = pw_context_load_module(impl->context,
576
+           "libpipewire-module-protocol-simple",
577
+           args, NULL);
578
+   free(args);
579
+
580
+   if (mod == NULL) {
581
+       res = -errno;
582
+       pw_log_error("Can't load module: %m");
583
+                goto done;
584
+   }
585
+
586
+   pw_impl_module_add_listener(mod, &t->module_listener, &submodule_events, t);
587
+   t->module = mod;
588
+
589
+   if ((mod_props = pw_impl_module_get_properties(mod)) != NULL) {
590
+       const char *addr;
591
+       if ((addr = pw_properties_get(mod_props, "server.address"))) {
592
+           add_snapcast_stream(impl, t, props, addr);
593
+       }
594
+   }
595
+done:
596
+   return res;
597
+}
598
+
599
+struct match_info {
600
+   struct impl *impl;
601
+   struct pw_properties *props;
602
+   struct tunnel *tunnel;
603
+   bool matched;
604
+};
605
+
606
+static int rule_matched(void *data, const char *location, const char *action,
607
+                        const char *str, size_t len)
608
+{
609
+   struct match_info *i = data;
610
+   int res = 0;
611
+
612
+   i->matched = true;
613
+   if (spa_streq(action, "create-stream")) {
614
+       pw_properties_update_string(i->props, str, len);
615
+       create_stream(i->impl, i->props, i->tunnel);
616
+   }
617
+   return res;
618
+}
619
+
620
+static void resolver_cb(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol,
621
+   AvahiResolverEvent event, const char *name, const char *type, const char *domain,
622
+   const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt,
623
+   AvahiLookupResultFlags flags, void *userdata)
624
+{
625
+   struct impl *impl = userdata;
626
+   struct tunnel_info tinfo;
627
+   struct tunnel *t;
628
+   const char *str, *link_local_range = "169.254.";
629
+   AvahiStringList *l;
630
+   struct pw_properties *props = NULL;
631
+   char atAVAHI_ADDRESS_STR_MAX;
632
+   char hbufNI_MAXHOST;
633
+   char if_suffix16 = "";
634
+   struct ifreq ifreq;
635
+   int fd, res, family;
636
+
637
+   if (event != AVAHI_RESOLVER_FOUND) {
638
+       pw_log_error("Resolving of '%s' failed: %s", name,
639
+               avahi_strerror(avahi_client_errno(impl->client)));
640
+       goto done;
641
+   }
642
+
643
+   avahi_address_snprint(at, sizeof(at), a);
644
+   if (spa_strstartswith(at, link_local_range)) {
645
+       pw_log_info("found link-local ip address %s - skipping tunnel creation", at);
646
+       goto done;
647
+   }
648
+   pw_log_info("%s %s", name, at);
649
+
650
+   tinfo = TUNNEL_INFO(.name = name, .port = port);
651
+
652
+   t = find_tunnel(impl, &tinfo);
653
+   if (t == NULL)
654
+       t = make_tunnel(impl, &tinfo);
655
+   if (t == NULL) {
656
+       pw_log_error("Can't make tunnel: %m");
657
+       goto done;
658
+   }
659
+   if (t->module != NULL) {
660
+       pw_log_info("found duplicate mdns entry for %s on IP %s - skipping tunnel creation", name, at);
661
+       goto done;
662
+   }
663
+
664
+   props = pw_properties_new(NULL, NULL);
665
+   if (props == NULL) {
666
+       pw_log_error("Can't allocate properties: %m");
667
+       goto done;
668
+   }
669
+
670
+   if (a->proto == AVAHI_PROTO_INET6 &&
671
+       a->data.ipv6.address0 == 0xfe &&
672
+       (a->data.ipv6.address1 & 0xc0) == 0x80)
673
+       snprintf(if_suffix, sizeof(if_suffix), "%%%d", interface);
674
+
675
+   pw_properties_setf(props, "snapcast.ip", "%s%s", at, if_suffix);
676
+   pw_properties_setf(props, "snapcast.ifindex", "%d", interface);
677
+   pw_properties_setf(props, "snapcast.port", "%u", port);
678
+   pw_properties_setf(props, "snapcast.name", "%s", name);
679
+   pw_properties_setf(props, "snapcast.hostname", "%s", host_name);
680
+   pw_properties_setf(props, "snapcast.domain", "%s", domain);
681
+
682
+   free((char*)t->info.host);
683
+   t->info.host = strdup(pw_properties_get(props, "snapcast.ip"));
684
+
685
+   family = protocol == AVAHI_PROTO_INET ? AF_INET : AF_INET6;
686
+
687
+   spa_zero(ifreq);
688
+   fd = socket(family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
689
+   ifreq.ifr_ifindex = interface;
690
+   ioctl(fd, SIOCGIFNAME, &ifreq, sizeof(ifreq));
691
+   pw_properties_setf(props, "snapcast.ifname", "%s", ifreq.ifr_name);
692
+
693
+   struct ifaddrs *if_addr, *ifp;
694
+   if (getifaddrs(&if_addr) < 0)
695
+       pw_log_error("error: %m");
696
+
697
+   for (ifp = if_addr; ifp != NULL; ifp = ifp->ifa_next) {
698
+       if (ifp->ifa_addr == NULL)
699
+           continue;
700
+
701
+       if (spa_streq(ifp->ifa_name, ifreq.ifr_name) &&
702
+           ifp->ifa_addr->sa_family == family) {
703
+           break;
704
+       }
705
+   }
706
+   if (ifp != NULL) {
707
+       if ((res = getnameinfo((struct sockaddr *)ifp->ifa_addr,
708
+               (family == AF_INET) ? sizeof(struct sockaddr_in) :
709
+                       sizeof(struct sockaddr_in6),
710
+               hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) == 0) {
711
+           pw_properties_setf(props, "server.address", " \"tcp:%s%s%s:0\" ",
712
+                   family == AF_INET ? "" : "",
713
+                   hbuf,
714
+                   family == AF_INET ? "" : "");
715
+       } else {
716
+           pw_log_warn("error: %m %d %s", res, gai_strerror(res));
717
+       }
718
+   }
719
+   freeifaddrs(if_addr);
720
+   close(fd);
721
+
722
+   for (l = txt; l; l = l->next) {
723
+       char *key, *value;
724
+
725
+       if (avahi_string_list_get_pair(l, &key, &value, NULL) != 0)
726
+           break;
727
+
728
+       pw_properties_from_avahi_string(key, value, props);
729
+       avahi_free(key);
730
+       avahi_free(value);
731
+   }
732
+
733
+   if ((str = pw_properties_get(impl->properties, "stream.rules")) == NULL)
734
+       str = DEFAULT_CREATE_RULES;
735
+   if (str != NULL) {
736
+       struct match_info minfo = {
737
+           .impl = impl,
738
+           .props = props,
739
+           .tunnel = t,
740
+       };
741
+       pw_conf_match_rules(str, strlen(str), NAME, &props->dict,
742
+               rule_matched, &minfo);
743
+
744
+       if (!minfo.matched)
745
+           pw_log_info("unmatched service found %s", str);
746
+   }
747
+
748
+done:
749
+   avahi_service_resolver_free(r);
750
+   pw_properties_free(props);
751
+}
752
+
753
+
754
+static void browser_cb(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
755
+   AvahiBrowserEvent event, const char *name, const char *type, const char *domain,
756
+   AvahiLookupResultFlags flags, void *userdata)
757
+{
758
+   struct impl *impl = userdata;
759
+   struct tunnel_info info;
760
+   struct tunnel *t;
761
+
762
+   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
763
+       return;
764
+
765
+   /* snapcast does not seem to work well with IPv6 */
766
+   if (protocol == AVAHI_PROTO_INET6)
767
+       return;
768
+
769
+   info = TUNNEL_INFO(.name = name);
770
+
771
+   t = find_tunnel(impl, &info);
772
+
773
+   switch (event) {
774
+   case AVAHI_BROWSER_NEW:
775
+       if (t != NULL) {
776
+           pw_log_info("found duplicate mdns entry - skipping tunnel creation");
777
+           return;
778
+       }
779
+       if (!(avahi_service_resolver_new(impl->client,
780
+                       interface, protocol,
781
+                       name, type, domain,
782
+                       AVAHI_PROTO_UNSPEC, 0,
783
+                       resolver_cb, impl)))
784
+           pw_log_error("can't make service resolver: %s",
785
+                   avahi_strerror(avahi_client_errno(impl->client)));
786
+       break;
787
+   case AVAHI_BROWSER_REMOVE:
788
+       if (t == NULL)
789
+           return;
790
+       free_tunnel(t);
791
+       break;
792
+   default:
793
+       break;
794
+   }
795
+}
796
+
797
+
798
+static struct AvahiServiceBrowser *make_browser(struct impl *impl, const char *service_type)
799
+{
800
+   struct AvahiServiceBrowser *s;
801
+
802
+   s = avahi_service_browser_new(impl->client,
803
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
804
+                              service_type, NULL, 0,
805
+                              browser_cb, impl);
806
+   if (s == NULL) {
807
+       pw_log_error("can't make browser for %s: %s", service_type,
808
+               avahi_strerror(avahi_client_errno(impl->client)));
809
+   }
810
+   return s;
811
+}
812
+
813
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata)
814
+{
815
+   struct impl *impl = userdata;
816
+
817
+   impl->client = c;
818
+
819
+   switch (state) {
820
+   case AVAHI_CLIENT_S_REGISTERING:
821
+   case AVAHI_CLIENT_S_RUNNING:
822
+   case AVAHI_CLIENT_S_COLLISION:
823
+       if (impl->sink_browser == NULL)
824
+           impl->sink_browser = make_browser(impl, SERVICE_TYPE_CONTROL);
825
+       if (impl->sink_browser == NULL)
826
+           goto error;
827
+       break;
828
+   case AVAHI_CLIENT_FAILURE:
829
+       if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
830
+           start_client(impl);
831
+
832
+       SPA_FALLTHROUGH;
833
+   case AVAHI_CLIENT_CONNECTING:
834
+       if (impl->sink_browser) {
835
+           avahi_service_browser_free(impl->sink_browser);
836
+           impl->sink_browser = NULL;
837
+       }
838
+       break;
839
+   default:
840
+       break;
841
+   }
842
+   return;
843
+error:
844
+   pw_impl_module_schedule_destroy(impl->module);
845
+}
846
+
847
+static int start_client(struct impl *impl)
848
+{
849
+   int res;
850
+   if ((impl->client = avahi_client_new(impl->avahi_poll,
851
+                   AVAHI_CLIENT_NO_FAIL,
852
+                   client_callback, impl,
853
+                   &res)) == NULL) {
854
+       pw_log_error("can't create client: %s", avahi_strerror(res));
855
+       pw_impl_module_schedule_destroy(impl->module);
856
+       return -EIO;
857
+   }
858
+   return 0;
859
+}
860
+
861
+static int start_avahi(struct impl *impl)
862
+{
863
+   struct pw_loop *loop;
864
+
865
+   loop = pw_context_get_main_loop(impl->context);
866
+   impl->avahi_poll = pw_avahi_poll_new(loop);
867
+
868
+   return start_client(impl);
869
+}
870
+
871
+SPA_EXPORT
872
+int pipewire__module_init(struct pw_impl_module *module, const char *args)
873
+{
874
+   struct pw_context *context = pw_impl_module_get_context(module);
875
+   struct pw_properties *props;
876
+   struct impl *impl;
877
+   int res;
878
+
879
+   PW_LOG_TOPIC_INIT(mod_topic);
880
+
881
+   impl = calloc(1, sizeof(struct impl));
882
+   if (impl == NULL)
883
+       goto error_errno;
884
+
885
+   pw_log_debug("module %p: new %s", impl, args);
886
+
887
+   if (args == NULL)
888
+       args = "";
889
+
890
+   props = pw_properties_new_string(args);
891
+   if (props == NULL)
892
+       goto error_errno;
893
+
894
+   spa_list_init(&impl->tunnel_list);
895
+
896
+   impl->loop = pw_context_get_main_loop(context);
897
+   impl->module = module;
898
+   impl->context = context;
899
+   impl->properties = props;
900
+
901
+   impl->discover_local =  pw_properties_get_bool(impl->properties,
902
+           "snapcast.discover-local", false);
903
+
904
+   pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
905
+
906
+   pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
907
+
908
+   start_avahi(impl);
909
+
910
+   return 0;
911
+
912
+error_errno:
913
+   res = -errno;
914
+   if (impl)
915
+       impl_free(impl);
916
+   return res;
917
+}
918
pipewire-1.0.1.tar.bz2/src/modules/module-vban-recv.c -> pipewire-1.2.0.tar.gz/src/modules/module-vban-recv.c Changed
77
 
1
@@ -30,6 +30,7 @@
2
 #include <pipewire/impl.h>
3
 
4
 #include <module-vban/stream.h>
5
+#include "network-utils.h"
6
 
7
 #ifdef __FreeBSD__
8
 #define ifr_ifindex ifr_index
9
@@ -189,26 +190,6 @@
10
    return;
11
 }
12
 
13
-static int parse_address(const char *address, uint16_t port,
14
-       struct sockaddr_storage *addr, socklen_t *len)
15
-{
16
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
17
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
18
-
19
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
20
-       sa4->sin_family = AF_INET;
21
-       sa4->sin_port = htons(port);
22
-       *len = sizeof(*sa4);
23
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
24
-       sa6->sin6_family = AF_INET6;
25
-       sa6->sin6_port = htons(port);
26
-       *len = sizeof(*sa6);
27
-   } else
28
-       return -EINVAL;
29
-
30
-   return 0;
31
-}
32
-
33
 static int make_socket(const struct sockaddr* sa, socklen_t salen, char *ifname)
34
 {
35
    int af, fd, val, res;
36
@@ -389,6 +370,9 @@
37
    if (impl->timer)
38
        pw_loop_destroy_source(impl->loop, impl->timer);
39
 
40
+   if (impl->data_loop)
41
+       pw_context_release_loop(impl->context, impl->data_loop);
42
+
43
    pw_properties_free(impl->stream_props);
44
    pw_properties_free(impl->props);
45
 
46
@@ -463,11 +447,12 @@
47
    impl->module = module;
48
    impl->context = context;
49
    impl->loop = pw_context_get_main_loop(context);
50
-   impl->data_loop = pw_data_loop_get_loop(pw_context_get_data_loop(context));
51
+   impl->data_loop = pw_context_acquire_loop(context, &props->dict);
52
 
53
    if ((sess_name = pw_properties_get(props, "sess.name")) == NULL)
54
        sess_name = pw_get_host_name();
55
 
56
+   pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name);
57
    if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL)
58
        pw_properties_setf(props, PW_KEY_NODE_NAME, "vban_session.%s", sess_name);
59
    if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL)
60
@@ -479,6 +464,7 @@
61
    if ((str = pw_properties_get(props, "stream.props")) != NULL)
62
        pw_properties_update_string(stream_props, str, strlen(str));
63
 
64
+   copy_props(impl, props, PW_KEY_NODE_LOOP_NAME);
65
    copy_props(impl, props, PW_KEY_AUDIO_FORMAT);
66
    copy_props(impl, props, PW_KEY_AUDIO_RATE);
67
    copy_props(impl, props, PW_KEY_AUDIO_CHANNELS);
68
@@ -508,7 +494,7 @@
69
    }
70
    if ((str = pw_properties_get(props, "source.ip")) == NULL)
71
        str = DEFAULT_SOURCE_IP;
72
-   if ((res = parse_address(str, impl->src_port, &impl->src_addr, &impl->src_len)) < 0) {
73
+   if ((res = pw_net_parse_address(str, impl->src_port, &impl->src_addr, &impl->src_len)) < 0) {
74
        pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
75
        goto out;
76
    }
77
pipewire-1.0.1.tar.bz2/src/modules/module-vban-send.c -> pipewire-1.2.0.tar.gz/src/modules/module-vban-send.c Changed
86
 
1
@@ -26,6 +26,7 @@
2
 #include <pipewire/impl.h>
3
 
4
 #include <module-vban/stream.h>
5
+#include "network-utils.h"
6
 
7
 #ifndef IPTOS_DSCP
8
 #define IPTOS_DSCP_MASK 0xfc
9
@@ -218,26 +219,6 @@
10
    .send_packet = stream_send_packet,
11
 };
12
 
13
-static int parse_address(const char *address, uint16_t port,
14
-       struct sockaddr_storage *addr, socklen_t *len)
15
-{
16
-   struct sockaddr_in *sa4 = (struct sockaddr_in*)addr;
17
-   struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)addr;
18
-
19
-   if (inet_pton(AF_INET, address, &sa4->sin_addr) > 0) {
20
-       sa4->sin_family = AF_INET;
21
-       sa4->sin_port = htons(port);
22
-       *len = sizeof(*sa4);
23
-   } else if (inet_pton(AF_INET6, address, &sa6->sin6_addr) > 0) {
24
-       sa6->sin6_family = AF_INET6;
25
-       sa6->sin6_port = htons(port);
26
-       *len = sizeof(*sa6);
27
-   } else
28
-       return -EINVAL;
29
-
30
-   return 0;
31
-}
32
-
33
 static bool is_multicast(struct sockaddr *sa, socklen_t salen)
34
 {
35
    if (sa->sa_family == AF_INET) {
36
@@ -299,19 +280,6 @@
37
    return res;
38
 }
39
 
40
-static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
41
-{
42
-   if (sa->ss_family == AF_INET) {
43
-       struct sockaddr_in *in = (struct sockaddr_in*)sa;
44
-       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
45
-   } else if (sa->ss_family == AF_INET6) {
46
-       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
47
-       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
48
-   } else
49
-       return -EIO;
50
-   return 0;
51
-}
52
-
53
 static void core_destroy(void *d)
54
 {
55
    struct impl *impl = d;
56
@@ -460,7 +428,7 @@
57
 
58
    if ((str = pw_properties_get(props, "source.ip")) == NULL)
59
        str = DEFAULT_SOURCE_IP;
60
-   if ((res = parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
61
+   if ((res = pw_net_parse_address(str, 0, &impl->src_addr, &impl->src_len)) < 0) {
62
        pw_log_error("invalid source.ip %s: %s", str, spa_strerror(res));
63
        goto out;
64
    }
65
@@ -468,7 +436,7 @@
66
    impl->dst_port = pw_properties_get_uint32(props, "destination.port", DEFAULT_PORT);
67
    if ((str = pw_properties_get(props, "destination.ip")) == NULL)
68
        str = DEFAULT_DESTINATION_IP;
69
-   if ((res = parse_address(str, impl->dst_port, &impl->dst_addr, &impl->dst_len)) < 0) {
70
+   if ((res = pw_net_parse_address(str, impl->dst_port, &impl->dst_addr, &impl->dst_len)) < 0) {
71
        pw_log_error("invalid destination.ip %s: %s", str, spa_strerror(res));
72
        goto out;
73
    }
74
@@ -477,9 +445,9 @@
75
    impl->mcast_loop = pw_properties_get_bool(props, "net.loop", DEFAULT_LOOP);
76
    impl->dscp = pw_properties_get_uint32(props, "net.dscp", DEFAULT_DSCP);
77
 
78
-   get_ip(&impl->src_addr, addr, sizeof(addr));
79
+   pw_net_get_ip(&impl->src_addr, addr, sizeof(addr), NULL, NULL);
80
    pw_properties_set(stream_props, "vban.source.ip", addr);
81
-   get_ip(&impl->dst_addr, addr, sizeof(addr));
82
+   pw_net_get_ip(&impl->dst_addr, addr, sizeof(addr), NULL, NULL);
83
    pw_properties_set(stream_props, "vban.destination.ip", addr);
84
    pw_properties_setf(stream_props, "vban.destination.port", "%u", impl->dst_port);
85
    pw_properties_setf(stream_props, "vban.ttl", "%u", impl->ttl);
86
pipewire-1.0.1.tar.bz2/src/modules/module-vban/audio.c -> pipewire-1.2.0.tar.gz/src/modules/module-vban/audio.c Changed
19
 
1
@@ -57,7 +57,7 @@
2
        error = (float)target_buffer - (float)avail;
3
        error = SPA_CLAMP(error, -impl->max_error, impl->max_error);
4
 
5
-       corr = spa_dll_update(&impl->dll, error);
6
+       corr = (float)spa_dll_update(&impl->dll, error);
7
 
8
        pw_log_debug("avail:%u target:%u error:%f corr:%f", avail,
9
                target_buffer, error, corr);
10
@@ -161,7 +161,7 @@
11
    return -EINVAL;
12
 invalid_version:
13
    pw_log_warn("invalid VBAN version");
14
-   spa_debug_mem(0, buffer, len);
15
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_INFO, 0, buffer, len);
16
    return -EPROTO;
17
 }
18
 
19
pipewire-1.0.1.tar.bz2/src/modules/module-vban/midi.c -> pipewire-1.2.0.tar.gz/src/modules/module-vban/midi.c Changed
10
 
1
@@ -217,7 +217,7 @@
2
    return -EINVAL;
3
 invalid_version:
4
    pw_log_warn("invalid RTP version");
5
-   spa_debug_mem(0, buffer, len);
6
+   spa_debug_log_mem(pw_log_get(), SPA_LOG_LEVEL_INFO, 0, buffer, len);
7
    return -EPROTO;
8
 }
9
 
10
pipewire-1.0.1.tar.bz2/src/modules/module-vban/stream.c -> pipewire-1.2.0.tar.gz/src/modules/module-vban/stream.c Changed
49
 
1
@@ -13,6 +13,7 @@
2
 #include <spa/control/control.h>
3
 #include <spa/debug/types.h>
4
 #include <spa/debug/mem.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include "config.h"
8
 
9
@@ -253,6 +254,7 @@
10
 {
11
    struct impl *impl;
12
    const char *str;
13
+   char tmp64;
14
    uint8_t buffer1024;
15
    struct spa_pod_builder b;
16
    uint32_t n_params, min_samples, max_samples;
17
@@ -356,22 +358,23 @@
18
    if (!spa_atof(str, &max_ptime))
19
        max_ptime = DEFAULT_MAX_PTIME;
20
 
21
-   min_samples = min_ptime * impl->rate / 1000;
22
-   max_samples = SPA_MIN(256, max_ptime * impl->rate / 1000);
23
+   min_samples = (uint32_t)(min_ptime * impl->rate / 1000);
24
+   max_samples = SPA_MIN(256u, (uint32_t)(max_ptime * impl->rate / 1000));
25
 
26
-   float ptime = 0;
27
+   float ptime = 0.f;
28
    if ((str = pw_properties_get(props, "vban.ptime")) != NULL)
29
        if (!spa_atof(str, &ptime))
30
-           ptime = 0.0;
31
+           ptime = 0.0f;
32
 
33
-   if (ptime) {
34
-       impl->psamples = ptime * impl->rate / 1000;
35
+   if (ptime != 0.f) {
36
+       impl->psamples = (uint32_t)(ptime * impl->rate / 1000);
37
    } else {
38
        impl->psamples = impl->mtu / impl->stride;
39
        impl->psamples = SPA_CLAMP(impl->psamples, min_samples, max_samples);
40
        if (direction == PW_DIRECTION_OUTPUT)
41
-           pw_properties_setf(props, "vban.ptime", "%f",
42
-                   impl->psamples * 1000.0 / impl->rate);
43
+           pw_properties_set(props, "vban.ptime",
44
+                   spa_dtoa(tmp, sizeof(tmp),
45
+                       impl->psamples * 1000.0 / impl->rate));
46
    }
47
    latency_msec = pw_properties_get_uint32(props,
48
            "sess.latency.msec", DEFAULT_SESS_LATENCY);
49
pipewire-1.0.1.tar.bz2/src/modules/module-zeroconf-discover.c -> pipewire-1.2.0.tar.gz/src/modules/module-zeroconf-discover.c Changed
37
 
1
@@ -40,6 +40,8 @@
2
  *
3
  * ## Module Options
4
  *
5
+ * - `pulse.discover-local` = allow discovery of local services as well.
6
+ *    false by default.
7
  * - `pulse.latency`: the latency to end-to-end latency in milliseconds to
8
  *                    maintain (Default 200ms).
9
  *
10
@@ -80,6 +82,7 @@
11
 
12
    struct pw_properties *properties;
13
 
14
+   bool discover_local;
15
    AvahiPoll *avahi_poll;
16
    AvahiClient *client;
17
    AvahiServiceBrowser *sink_browser;
18
@@ -375,7 +378,7 @@
19
    struct tunnel_info info;
20
    struct tunnel *t;
21
 
22
-   if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
23
+   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) && !impl->discover_local)
24
        return;
25
 
26
    info = TUNNEL_INFO(.name = name);
27
@@ -519,6 +522,9 @@
28
    impl->context = context;
29
    impl->properties = props;
30
 
31
+   impl->discover_local =  pw_properties_get_bool(impl->properties,
32
+           "pulse.discover-local", false);
33
+
34
    pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
35
 
36
    pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
37
pipewire-1.2.0.tar.gz/src/modules/network-utils.h Added
80
 
1
@@ -0,0 +1,78 @@
2
+#ifndef NETWORK_UTILS_H
3
+#define NETWORK_UTILS_H
4
+
5
+#include <stdio.h>
6
+#include <netdb.h>
7
+#include <string.h>
8
+#include <arpa/inet.h>
9
+#include <net/if.h>
10
+#include <errno.h>
11
+
12
+static inline int pw_net_parse_address(const char *address, uint16_t port,
13
+       struct sockaddr_storage *addr, socklen_t *len)
14
+{
15
+   struct addrinfo hints;
16
+   struct addrinfo *result, *rp;
17
+   int res;
18
+   char port_str6;
19
+
20
+   snprintf(port_str, sizeof(port_str), "%u", port);
21
+
22
+   memset(&hints, 0, sizeof(hints));
23
+   hints.ai_family = AF_UNSPEC;
24
+   hints.ai_socktype = SOCK_DGRAM;
25
+   hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
26
+
27
+   res = getaddrinfo(address, port_str, &hints, &result);
28
+
29
+   if (res != 0)
30
+       return -EINVAL;
31
+
32
+   for (rp = result; rp != NULL; rp = rp->ai_next) {
33
+       memcpy(addr, rp->ai_addr, rp->ai_addrlen);
34
+       *len = rp->ai_addrlen;
35
+       break;
36
+   }
37
+   freeaddrinfo(result);
38
+
39
+   return 0;
40
+}
41
+
42
+static inline int pw_net_get_ip(const struct sockaddr_storage *sa, char *ip, size_t len, bool *ip4, uint16_t *port)
43
+{
44
+   if (sa->ss_family == AF_INET) {
45
+       struct sockaddr_in *in = (struct sockaddr_in*)sa;
46
+       inet_ntop(sa->ss_family, &in->sin_addr, ip, len);
47
+       if (port)
48
+           *port = ntohs(in->sin_port);
49
+   } else if (sa->ss_family == AF_INET6) {
50
+       struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
51
+       inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
52
+       if (port)
53
+           *port = ntohs(in->sin6_port);
54
+       if (in->sin6_scope_id == 0 || len <= 1)
55
+           goto finish;
56
+
57
+       size_t curlen = strlen(ip);
58
+       if (len-(curlen+1) >= IFNAMSIZ) {
59
+           ip += curlen+1;
60
+           ip-1 = '%';
61
+           if (if_indextoname(in->sin6_scope_id, ip) == NULL)
62
+               ip-1 = 0;
63
+       }
64
+   } else
65
+       return -EINVAL;
66
+finish:
67
+   if (ip4)
68
+       *ip4 = sa->ss_family == AF_INET;
69
+   return 0;
70
+}
71
+
72
+static inline char *pw_net_get_ip_fmt(const struct sockaddr_storage *sa, char *ip, size_t len)
73
+{
74
+   if (pw_net_get_ip(sa, ip, len, NULL, NULL) != 0)
75
+       snprintf(ip, len, "invalid ip");
76
+   return ip;
77
+}
78
+
79
+#endif /* NETWORK_UTILS_H */
80
pipewire-1.0.1.tar.bz2/src/modules/spa/module-device.c -> pipewire-1.2.0.tar.gz/src/modules/spa/module-device.c Changed
9
 
1
@@ -8,7 +8,6 @@
2
 #include <getopt.h>
3
 #include <limits.h>
4
 
5
-#include <pipewire/cleanup.h>
6
 #include <pipewire/impl.h>
7
 
8
 #include "spa-device.h"
9
pipewire-1.0.1.tar.bz2/src/modules/spa/module-node-factory.c -> pipewire-1.2.0.tar.gz/src/modules/spa/module-node-factory.c Changed
35
 
1
@@ -196,6 +196,7 @@
2
    int res;
3
    struct pw_impl_client *client;
4
    bool linger;
5
+   spa_autoptr(pw_properties) copy = NULL;
6
 
7
    if (properties == NULL)
8
        goto error_properties;
9
@@ -214,10 +215,13 @@
10
        pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d",
11
            pw_global_get_id(pw_impl_client_get_global(client)));
12
    }
13
+
14
+   copy = pw_properties_copy(properties);
15
+
16
    node = pw_spa_node_load(context,
17
                factory_name,
18
                PW_SPA_NODE_FLAG_ACTIVATE,
19
-               properties,
20
+               spa_steal_ptr(properties),
21
                sizeof(struct node_data));
22
    if (node == NULL)
23
        goto error_create_node;
24
@@ -241,8 +245,8 @@
25
 
26
        pw_resource_add_listener(nd->resource, &nd->resource_listener, &resource_events, nd);
27
    }
28
-   if (pw_properties_get_bool(properties, PW_KEY_OBJECT_EXPORT, false)) {
29
-       res = export_node(nd, properties);
30
+   if (pw_properties_get_bool(copy, PW_KEY_OBJECT_EXPORT, false)) {
31
+       res = export_node(nd, copy);
32
        if (res < 0)
33
            goto error_export;
34
    }
35
pipewire-1.0.1.tar.bz2/src/modules/spa/module-node.c -> pipewire-1.2.0.tar.gz/src/modules/spa/module-node.c Changed
9
 
1
@@ -10,7 +10,6 @@
2
 #include <getopt.h>
3
 #include <limits.h>
4
 
5
-#include <pipewire/cleanup.h>
6
 #include <pipewire/impl.h>
7
 
8
 #include "spa-node.h"
9
pipewire-1.0.1.tar.bz2/src/modules/spa/spa-device.c -> pipewire-1.2.0.tar.gz/src/modules/spa/spa-device.c Changed
38
 
1
@@ -92,6 +92,22 @@
2
    return impl->user_data;
3
 }
4
 
5
+struct match {
6
+   struct pw_properties *props;
7
+   int count;
8
+};
9
+#define MATCH_INIT(p) ((struct match){ .props = (p) })
10
+
11
+static int execute_match(void *data, const char *location, const char *action,
12
+       const char *val, size_t len)
13
+{
14
+   struct match *match = data;
15
+   if (spa_streq(action, "update-props")) {
16
+       match->count += pw_properties_update_string(match->props, val, len);
17
+   }
18
+   return 1;
19
+}
20
+
21
 struct pw_impl_device *pw_spa_device_load(struct pw_context *context,
22
                 const char *factory_name,
23
                 enum pw_spa_device_flags flags,
24
@@ -102,7 +118,13 @@
25
    struct spa_handle *handle;
26
    void *iface;
27
    int res;
28
+   struct match match;
29
 
30
+   if (properties) {
31
+       match = MATCH_INIT(properties);
32
+       pw_context_conf_section_match_rules(context, "device.rules",
33
+               &properties->dict, execute_match, &match);
34
+   }
35
    handle = pw_context_load_spa_handle(context, factory_name,
36
            properties ? &properties->dict : NULL);
37
    if (handle == NULL)
38
pipewire-1.0.1.tar.bz2/src/modules/spa/spa-node.c -> pipewire-1.2.0.tar.gz/src/modules/spa/spa-node.c Changed
93
 
1
@@ -10,6 +10,7 @@
2
 
3
 #include <spa/node/node.h>
4
 #include <spa/node/utils.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/param/props.h>
8
 #include <spa/pod/iter.h>
9
@@ -210,6 +211,22 @@
10
    return 0;
11
 }
12
 
13
+struct match {
14
+   struct pw_properties *props;
15
+   int count;
16
+};
17
+#define MATCH_INIT(p) ((struct match){ .props = (p) })
18
+
19
+static int execute_match(void *data, const char *location, const char *action,
20
+       const char *val, size_t len)
21
+{
22
+   struct match *match = data;
23
+   if (spa_streq(action, "update-props")) {
24
+       match->count += pw_properties_update_string(match->props, val, len);
25
+   }
26
+   return 1;
27
+}
28
+
29
 
30
 struct pw_impl_node *pw_spa_node_load(struct pw_context *context,
31
                 const char *factory_name,
32
@@ -223,16 +240,33 @@
33
    struct spa_handle *handle;
34
    void *iface;
35
    const struct pw_properties *p;
36
+   struct pw_loop *loop;
37
+   struct match match;
38
 
39
    if (properties) {
40
        p = pw_context_get_properties(context);
41
        pw_properties_set(properties, "clock.quantum-limit",
42
                pw_properties_get(p, "default.clock.quantum-limit"));
43
+   } else {
44
+       properties = pw_properties_new(NULL, NULL);
45
+       if (properties == NULL)
46
+           return NULL;
47
+   }
48
+
49
+   match = MATCH_INIT(properties);
50
+   pw_context_conf_section_match_rules(context, "node.rules",
51
+           &properties->dict, execute_match, &match);
52
+
53
+   loop =  pw_context_acquire_loop(context, &properties->dict);
54
+   if (loop == NULL) {
55
+       res = -errno;
56
+       goto error_exit;
57
    }
58
 
59
-   handle = pw_context_load_spa_handle(context,
60
-           factory_name,
61
-           properties ? &properties->dict : NULL);
62
+   pw_properties_set(properties, PW_KEY_NODE_LOOP_NAME, loop->name);
63
+   pw_context_release_loop(context, loop);
64
+
65
+   handle = pw_context_load_spa_handle(context, factory_name, &properties->dict);
66
    if (handle == NULL) {
67
        res = -errno;
68
        goto error_exit;
69
@@ -247,20 +281,15 @@
70
 
71
    spa_node = iface;
72
 
73
-   if (properties != NULL) {
74
-       if ((res = setup_props(context, spa_node, properties)) < 0) {
75
-           pw_log_warn("can't setup properties: %s", spa_strerror(res));
76
-       }
77
-   }
78
+   if ((res = setup_props(context, spa_node, properties)) < 0)
79
+       pw_log_warn("can't setup properties: %s", spa_strerror(res));
80
 
81
    this = pw_spa_node_new(context, flags,
82
-                  spa_node, handle, properties, user_data_size);
83
+                  spa_node, handle, spa_steal_ptr(properties), user_data_size);
84
    if (this == NULL) {
85
        res = -errno;
86
-       properties = NULL;
87
        goto error_exit_unload;
88
    }
89
-
90
    return this;
91
 
92
 error_exit_unload:
93
pipewire-1.0.1.tar.bz2/src/pipewire/array.h -> pipewire-1.2.0.tar.gz/src/pipewire/array.h Changed
120
 
1
@@ -29,18 +29,21 @@
2
    void *data;     /**< pointer to array data */
3
    size_t size;        /**< length of array in bytes */
4
    size_t alloc;       /**< number of allocated memory in \a data */
5
-   size_t extend;      /**< number of bytes to extend with */
6
+   size_t extend;      /**< number of bytes to extend with, 0 when the
7
+                 *  data should not expand */
8
 };
9
 
10
+/** Initialize an array. The new array is empty. */
11
 #define PW_ARRAY_INIT(extend) ((struct pw_array) { NULL, 0, 0, (extend) })
12
 
13
+/** Return the length of an array. */
14
 #define pw_array_get_len_s(a,s)            ((a)->size / (s))
15
 #define pw_array_get_unchecked_s(a,idx,s,t)    SPA_PTROFF((a)->data,(idx)*(s),t)
16
 #define pw_array_check_index_s(a,idx,s)        ((idx) < pw_array_get_len_s(a,s))
17
 
18
 /** Get the number of items of type \a t in array */
19
 #define pw_array_get_len(a,t)          pw_array_get_len_s(a,sizeof(t))
20
-/** Get the item with index \a idx and type \a t from array */
21
+/** Get the item with index \a idx and type \a t from array. No bounds check is done. */
22
 #define pw_array_get_unchecked(a,idx,t)        pw_array_get_unchecked_s(a,idx,sizeof(t),t)
23
 /** Check if an item with index \a idx and type \a t exist in array */
24
 #define pw_array_check_index(a,idx,t)      pw_array_check_index_s(a,idx,sizeof(t))
25
@@ -66,7 +69,8 @@
26
                 SPA_PTRDIFF(pw_array_end(a),(p)));         \
27
 })
28
 
29
-/** Initialize the array with given extend */
30
+/** Initialize the array with given extend. Extend needs to be > 0 or else
31
+ * the array will not be able to expand. */
32
 static inline void pw_array_init(struct pw_array *arr, size_t extend)
33
 {
34
    arr->data = NULL;
35
@@ -74,13 +78,22 @@
36
    arr->extend = extend;
37
 }
38
 
39
-/** Clear the array */
40
+/** Clear the array. This should be called when pw_array_init() was called.  */
41
 static inline void pw_array_clear(struct pw_array *arr)
42
 {
43
-   free(arr->data);
44
+   if (arr->extend > 0)
45
+       free(arr->data);
46
    pw_array_init(arr, arr->extend);
47
 }
48
 
49
+/** Initialize a static array. */
50
+static inline void pw_array_init_static(struct pw_array *arr, void *data, size_t size)
51
+{
52
+   arr->data = data;
53
+   arr->alloc = size;
54
+   arr->size = arr->extend = 0;
55
+}
56
+
57
 /** Reset the array */
58
 static inline void pw_array_reset(struct pw_array *arr)
59
 {
60
@@ -97,10 +110,9 @@
61
 
62
    if (SPA_UNLIKELY(alloc < need)) {
63
        void *data;
64
-       alloc = SPA_MAX(alloc, arr->extend);
65
-       spa_assert(alloc != 0); /* forgot pw_array_init */
66
-       while (alloc < need)
67
-           alloc *= 2;
68
+       if (arr->extend == 0)
69
+           return -ENOSPC;
70
+       alloc = SPA_ROUND_UP(need, arr->extend);
71
        if (SPA_UNLIKELY((data = realloc(arr->data, alloc)) == NULL))
72
            return -errno;
73
        arr->data = data;
74
@@ -110,7 +122,8 @@
75
 }
76
 
77
 /** Add \a ref size bytes to \a arr. A pointer to memory that can
78
- * hold at least \a size bytes is returned */
79
+ * hold at least \a size bytes is returned or NULL when an error occurred
80
+ * and errno will be set.*/
81
 static inline void *pw_array_add(struct pw_array *arr, size_t size)
82
 {
83
    void *p;
84
@@ -124,27 +137,17 @@
85
    return p;
86
 }
87
 
88
-/** Add \a ref size bytes to \a arr. When there is not enough memory to
89
- * hold \a size bytes, NULL is returned */
90
-static inline void *pw_array_add_fixed(struct pw_array *arr, size_t size)
91
+/** Add a pointer to array. Returns 0 on success and a negative errno style
92
+ * error on failure. */
93
+static inline int pw_array_add_ptr(struct pw_array *arr, void *ptr)
94
 {
95
-   void *p;
96
-
97
-   if (SPA_UNLIKELY(arr->alloc < arr->size + size)) {
98
-       errno = ENOSPC;
99
-       return NULL;
100
-   }
101
-
102
-   p = SPA_PTROFF(arr->data, arr->size, void);
103
-   arr->size += size;
104
-
105
-   return p;
106
+   void **p = (void **)pw_array_add(arr, sizeof(void*));
107
+   if (p == NULL)
108
+       return -errno;
109
+   *p = ptr;
110
+   return 0;
111
 }
112
 
113
-/** Add a pointer to array */
114
-#define pw_array_add_ptr(a,p)                  \
115
-   *((void**) pw_array_add(a, sizeof(void*))) = (p)
116
-
117
 /**
118
  * \}
119
  */
120
pipewire-1.0.1.tar.bz2/src/pipewire/buffers.c -> pipewire-1.2.0.tar.gz/src/pipewire/buffers.c Changed
232
 
1
@@ -6,6 +6,7 @@
2
 #include <spa/pod/parser.h>
3
 #include <spa/param/param.h>
4
 #include <spa/buffer/alloc.h>
5
+#include <spa/debug/types.h>
6
 
7
 #include "pipewire/keys.h"
8
 #include "pipewire/private.h"
9
@@ -27,8 +28,8 @@
10
 /* Allocate an array of buffers that can be shared */
11
 static int alloc_buffers(struct pw_mempool *pool,
12
             uint32_t n_buffers,
13
-            uint32_t n_params,
14
-            struct spa_pod **params,
15
+            uint32_t n_metas,
16
+            struct spa_meta *metas,
17
             uint32_t n_datas,
18
             uint32_t *data_sizes,
19
             int32_t *data_strides,
20
@@ -40,8 +41,6 @@
21
    struct spa_buffer **buffers;
22
    void *skel, *data;
23
    uint32_t i;
24
-   uint32_t n_metas;
25
-   struct spa_meta *metas;
26
    struct spa_data *datas;
27
    struct pw_memblock *m;
28
    struct spa_buffer_alloc_info info = { 0, };
29
@@ -49,30 +48,8 @@
30
    if (!SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_SHARED))
31
        SPA_FLAG_SET(info.flags, SPA_BUFFER_ALLOC_FLAG_INLINE_ALL);
32
 
33
-   n_metas = 0;
34
-
35
-   metas = alloca(sizeof(struct spa_meta) * n_params);
36
    datas = alloca(sizeof(struct spa_data) * n_datas);
37
 
38
-   /* collect metadata */
39
-   for (i = 0; i < n_params; i++) {
40
-       if (spa_pod_is_object_type (paramsi, SPA_TYPE_OBJECT_ParamMeta)) {
41
-           uint32_t type, size;
42
-
43
-           if (spa_pod_parse_object(paramsi,
44
-               SPA_TYPE_OBJECT_ParamMeta, NULL,
45
-               SPA_PARAM_META_type, SPA_POD_Id(&type),
46
-               SPA_PARAM_META_size, SPA_POD_Int(&size)) < 0)
47
-               continue;
48
-
49
-           pw_log_debug("%p: enable meta %d %d", allocation, type, size);
50
-
51
-           metasn_metas.type = type;
52
-           metasn_metas.size = size;
53
-           n_metas++;
54
-       }
55
-   }
56
-
57
    for (i = 0; i < n_datas; i++) {
58
        struct spa_data *d = &datasi;
59
 
60
@@ -120,8 +97,8 @@
61
        data = NULL;
62
    }
63
 
64
-   pw_log_debug("%p: layout buffers skel:%p data:%p buffers:%p",
65
-           allocation, skel, data, buffers);
66
+   pw_log_debug("%p: layout buffers skel:%p data:%p n_buffers:%d buffers:%p",
67
+           allocation, skel, data, n_buffers, buffers);
68
    spa_buffer_alloc_layout_array(&info, n_buffers, buffers, skel, data);
69
 
70
    allocation->mem = m;
71
@@ -200,27 +177,17 @@
72
    return num;
73
 }
74
 
75
-static struct spa_pod *find_param(struct spa_pod **params, uint32_t n_params, uint32_t type)
76
-{
77
-   uint32_t i;
78
-
79
-   for (i = 0; i < n_params; i++) {
80
-       if (spa_pod_is_object_type(paramsi, type))
81
-           return paramsi;
82
-   }
83
-   return NULL;
84
-}
85
-
86
 SPA_EXPORT
87
 int pw_buffers_negotiate(struct pw_context *context, uint32_t flags,
88
        struct spa_node *outnode, uint32_t out_port_id,
89
        struct spa_node *innode, uint32_t in_port_id,
90
        struct pw_buffers *result)
91
 {
92
-   struct spa_pod **params, *param;
93
+   struct spa_pod **params;
94
    uint8_t buffer4096;
95
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
96
-   uint32_t i, offset, n_params;
97
+   uint32_t i, j, offset, n_params, n_metas;
98
+   struct spa_meta *metas;
99
    uint32_t max_buffers, blocks;
100
    size_t minsize, stride, align;
101
    uint32_t *data_sizes;
102
@@ -237,6 +204,7 @@
103
        input = tmp;
104
    }
105
 
106
+   /* collect buffers */
107
    res = param_filter(result, &input, &output, SPA_PARAM_Buffers, &b);
108
    if (res < 0) {
109
        pw_context_debug_port_params(context, input.node, input.direction,
110
@@ -247,17 +215,41 @@
111
                "output param");
112
        return res;
113
    }
114
+
115
+   /* collect metadata */
116
    n_params = res;
117
    if ((res = param_filter(result, &input, &output, SPA_PARAM_Meta, &b)) > 0)
118
        n_params += res;
119
 
120
+   metas = alloca(sizeof(struct spa_meta) * n_params);
121
+
122
+   n_metas = 0;
123
    params = alloca(n_params * sizeof(struct spa_pod *));
124
    for (i = 0, offset = 0; i < n_params; i++) {
125
+       uint32_t type, size;
126
+
127
        paramsi = SPA_PTROFF(buffer, offset, struct spa_pod);
128
        spa_pod_fixate(paramsi);
129
        pw_log_debug("%p: fixated param %d:", result, i);
130
        pw_log_pod(SPA_LOG_LEVEL_DEBUG, paramsi);
131
        offset += SPA_ROUND_UP_N(SPA_POD_SIZE(paramsi), 8);
132
+
133
+       if (!spa_pod_is_object_type (paramsi, SPA_TYPE_OBJECT_ParamMeta))
134
+           continue;
135
+       if (spa_pod_parse_object(paramsi,
136
+                   SPA_TYPE_OBJECT_ParamMeta, NULL,
137
+                   SPA_PARAM_META_type, SPA_POD_Id(&type),
138
+                   SPA_PARAM_META_size, SPA_POD_Int(&size)) < 0) {
139
+           pw_log_warn("%p: invalid Meta param", result);
140
+           continue;
141
+       }
142
+
143
+       pw_log_debug("%p: enable meta %s size:%d", result,
144
+               spa_debug_type_find_name(spa_type_meta_type, type), size);
145
+
146
+       metasn_metas.type = type;
147
+       metasn_metas.size = size;
148
+       n_metas++;
149
    }
150
 
151
    max_buffers = context->settings.link_max_buffers;
152
@@ -268,20 +260,30 @@
153
    types = SPA_ID_INVALID; /* bitmask of allowed types */
154
    blocks = 1;
155
 
156
-   param = find_param(params, n_params, SPA_TYPE_OBJECT_ParamBuffers);
157
-   if (param) {
158
+   for (i = 0; i < n_params; i++) {
159
        uint32_t qmax_buffers = max_buffers,
160
            qminsize = minsize, qstride = stride, qalign = align;
161
-       uint32_t qtypes = types, qblocks = blocks;
162
-
163
-       spa_pod_parse_object(param,
164
-           SPA_TYPE_OBJECT_ParamBuffers, NULL,
165
-           SPA_PARAM_BUFFERS_buffers,  SPA_POD_OPT_Int(&qmax_buffers),
166
-           SPA_PARAM_BUFFERS_blocks,   SPA_POD_OPT_Int(&qblocks),
167
-           SPA_PARAM_BUFFERS_size,     SPA_POD_OPT_Int(&qminsize),
168
-           SPA_PARAM_BUFFERS_stride,   SPA_POD_OPT_Int(&qstride),
169
-           SPA_PARAM_BUFFERS_align,    SPA_POD_OPT_Int(&qalign),
170
-           SPA_PARAM_BUFFERS_dataType, SPA_POD_OPT_Int(&qtypes));
171
+       uint32_t qtypes = types, qblocks = blocks, qmetas = 0;
172
+
173
+       if (!spa_pod_is_object_type (paramsi, SPA_TYPE_OBJECT_ParamBuffers))
174
+           continue;
175
+
176
+       if (spa_pod_parse_object(paramsi,
177
+               SPA_TYPE_OBJECT_ParamBuffers, NULL,
178
+               SPA_PARAM_BUFFERS_buffers,  SPA_POD_OPT_Int(&qmax_buffers),
179
+               SPA_PARAM_BUFFERS_blocks,   SPA_POD_OPT_Int(&qblocks),
180
+               SPA_PARAM_BUFFERS_size,     SPA_POD_OPT_Int(&qminsize),
181
+               SPA_PARAM_BUFFERS_stride,   SPA_POD_OPT_Int(&qstride),
182
+               SPA_PARAM_BUFFERS_align,    SPA_POD_OPT_Int(&qalign),
183
+               SPA_PARAM_BUFFERS_dataType, SPA_POD_OPT_Int(&qtypes),
184
+               SPA_PARAM_BUFFERS_metaType, SPA_POD_OPT_Int(&qmetas)) < 0) {
185
+           pw_log_warn("%p: invalid Buffers param", result);
186
+           continue;
187
+       }
188
+       for (j = 0; qmetas != 0 && j < n_metas; j++)
189
+           SPA_FLAG_CLEAR(qmetas, 1<<metasj.type);
190
+       if (qmetas != 0)
191
+           continue;
192
 
193
        max_buffers =
194
            qmax_buffers == 0 ? max_buffers : SPA_MIN(qmax_buffers,
195
@@ -292,18 +294,20 @@
196
        align = SPA_MAX(align, qalign);
197
        types = qtypes;
198
 
199
-       if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_ASYNC))
200
-           max_buffers = SPA_MAX(2u, max_buffers);
201
-
202
        pw_log_debug("%p: %d %d %d %d %d %d -> %d %zd %zd %d %zd %d", result,
203
                qblocks, qminsize, qstride, qmax_buffers, qalign, qtypes,
204
                blocks, minsize, stride, max_buffers, align, types);
205
-   } else {
206
+       break;
207
+   }
208
+   if (i == n_params) {
209
        pw_log_warn("%p: no buffers param", result);
210
        minsize = context->settings.clock_quantum_limit;
211
-       max_buffers = 2;
212
+       max_buffers = 2u;
213
    }
214
 
215
+   if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_ASYNC))
216
+       max_buffers = SPA_MAX(2u, max_buffers);
217
+
218
    if (SPA_FLAG_IS_SET(flags, PW_BUFFERS_FLAG_SHARED_MEM)) {
219
        if (types != SPA_ID_INVALID)
220
            SPA_FLAG_CLEAR(types, 1<<SPA_DATA_MemPtr);
221
@@ -328,8 +332,8 @@
222
 
223
    if ((res = alloc_buffers(context->pool,
224
                 max_buffers,
225
-                n_params,
226
-                params,
227
+                n_metas,
228
+                metas,
229
                 blocks,
230
                 data_sizes, data_strides,
231
                 data_aligns, data_types,
232
pipewire-1.0.1.tar.bz2/src/pipewire/conf.c -> pipewire-1.2.0.tar.gz/src/pipewire/conf.c Changed
774
 
1
@@ -24,11 +24,12 @@
2
 #endif
3
 #endif
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/utils/json.h>
9
+#include <spa/debug/log.h>
10
 
11
-#include <pipewire/cleanup.h>
12
 #include <pipewire/impl.h>
13
 #include <pipewire/private.h>
14
 
15
@@ -162,6 +163,7 @@
16
 static int get_config_dir(char *path, size_t size, const char *prefix, const char *name, int *level)
17
 {
18
    int res;
19
+   bool no_config;
20
 
21
    if (prefix == NULL) {
22
        prefix = name;
23
@@ -173,7 +175,8 @@
24
        return -ENOENT;
25
    }
26
 
27
-   if (pw_check_option("no-config", "true"))
28
+   no_config = pw_check_option("no-config", "true");
29
+   if (no_config)
30
        goto no_config;
31
 
32
    if ((res = get_envconf_path(path, size, prefix, name)) != 0) {
33
@@ -183,9 +186,12 @@
34
    }
35
 
36
    if (*level == 0) {
37
+no_config:
38
        (*level)++;
39
-       if ((res = get_homeconf_path(path, size, prefix, name)) != 0)
40
+       if ((res = get_confdata_path(path, size, prefix, name)) != 0)
41
            return res;
42
+       if (no_config)
43
+           return 0;
44
    }
45
    if (*level == 1) {
46
        (*level)++;
47
@@ -193,9 +199,8 @@
48
            return res;
49
    }
50
    if (*level == 2) {
51
-no_config:
52
        (*level)++;
53
-       if ((res = get_confdata_path(path, size, prefix, name)) != 0)
54
+       if ((res = get_homeconf_path(path, size, prefix, name)) != 0)
55
            return res;
56
    }
57
    return 0;
58
@@ -380,9 +385,11 @@
59
 
60
 static int conf_load(const char *path, struct pw_properties *conf)
61
 {
62
-   char *data;
63
+   char *data = MAP_FAILED;
64
    struct stat sbuf;
65
    int count;
66
+   struct spa_error_location loc = { 0 };
67
+   int res;
68
 
69
    spa_autoclose int fd = open(path,  O_CLOEXEC | O_RDONLY);
70
    if (fd < 0)
71
@@ -395,7 +402,11 @@
72
        if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
73
            goto error;
74
 
75
-       count = pw_properties_update_string(conf, data, sbuf.st_size);
76
+       count = pw_properties_update_string_checked(conf, data, sbuf.st_size, &loc);
77
+       if (count < 0) {
78
+           errno = EINVAL;
79
+           goto error;
80
+       }
81
        munmap(data, sbuf.st_size);
82
    } else {
83
        count = 0;
84
@@ -406,8 +417,17 @@
85
    return 0;
86
 
87
 error:
88
-   pw_log_warn("%p: error loading config '%s': %m", conf, path);
89
-   return -errno;
90
+   res = -errno;
91
+   if (loc.line != 0)
92
+       spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN, &loc,
93
+               "%p: error in config '%s': %s", conf, path, loc.reason);
94
+   else
95
+       pw_log_warn("%p: error loading config '%s': %m", conf, path);
96
+
97
+   if (data != MAP_FAILED)
98
+       munmap(data, sbuf.st_size);
99
+
100
+   return res;
101
 }
102
 
103
 static bool check_override(struct pw_properties *conf, const char *name, int level)
104
@@ -547,17 +567,27 @@
105
    struct pw_context *context = d->context;
106
    struct spa_json it2;
107
    char key512, value512;
108
+   int res;
109
 
110
    spa_json_init(&it0, str, len);
111
    if (spa_json_enter_object(&it0, &it1) < 0) {
112
-       pw_log_error("config file error: context.spa-libs is not an object");
113
+       pw_log_error("config file error: context.spa-libs is not an "
114
+               "object in '%.*s'", (int)len, str);
115
        return -EINVAL;
116
    }
117
 
118
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
119
        if (spa_json_get_string(&it1, value, sizeof(value)) > 0) {
120
-           pw_context_add_spa_lib(context, key, value);
121
+           if ((res = pw_context_add_spa_lib(context, key, value)) < 0) {
122
+               pw_log_error("error adding spa-libs for '%s' in '%.*s': %s",
123
+                   key, (int)len, str, spa_strerror(res));
124
+               return res;
125
+           }
126
            d->count++;
127
+       } else {
128
+           pw_log_warn("config file error: missing spa-libs "
129
+                   "library name for '%s' in '%.*s'",
130
+                   key, (int)len, str);
131
        }
132
    }
133
    return 0;
134
@@ -590,54 +620,98 @@
135
  *     <key> = <value>
136
  *     ...
137
  * }
138
+ *
139
+ * Some things that can match:
140
+ *
141
+ *  null -> matches when the property is not found
142
+ *  "null" -> matches when the property is found and has the string "null"
143
+ *  !null -> matches when the property is found (any value)
144
+ *  "!null" -> same as !null
145
+ *  !"null" and "!\"null\"" matches anything that is not the string "null"
146
  */
147
-static bool find_match(struct spa_json *arr, const struct spa_dict *props)
148
+static bool find_match(struct spa_json *arr, const struct spa_dict *props, bool condition)
149
 {
150
    struct spa_json it1;
151
+   const char *as = arr->cur;
152
+   int az = (int)(arr->end - arr->cur), r, count = 0;
153
 
154
-   while (spa_json_enter_object(arr, &it0) > 0) {
155
+   while ((r = spa_json_enter_object(arr, &it0)) > 0) {
156
        char key256, val1024;
157
        const char *str, *value;
158
        int match = 0, fail = 0;
159
        int len;
160
 
161
        while (spa_json_get_string(&it0, key, sizeof(key)) > 0) {
162
-           bool success = false;
163
+           bool success = false, is_null, reg = false, parse_string = true;
164
            int skip = 0;
165
 
166
-           if ((len = spa_json_next(&it0, &value)) <= 0)
167
+           if ((len = spa_json_next(&it0, &value)) <= 0) {
168
+               pw_log_warn("malformed match rule: key '%s' has "
169
+                       "no value in '%.*s'", key, az, as);
170
                break;
171
+           }
172
 
173
-           str = spa_dict_lookup(props, key);
174
-
175
-           if (spa_json_is_null(value, len)) {
176
-               success = str == NULL;
177
-           } else {
178
-               if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0)
179
+           /* first decode a string, when there was a string, we assume it
180
+            * can not be null but the "null" string, unless there is a modifier,
181
+            * see below. */
182
+           if (spa_json_is_string(value, len)) {
183
+               if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) {
184
+                   pw_log_warn("invalid string '%.*s' in '%.*s'",
185
+                           len, value, az, as);
186
                    continue;
187
+               }
188
                value = val;
189
                len = strlen(val);
190
-               if (len > 0 && value0 == '!') {
191
+               parse_string = false;
192
+           }
193
+
194
+           /* parse the modifiers, after the modifier we unescape the string
195
+            * again to be able to detect and handle null and "null" */
196
+           if (len > skip && valueskip == '!') {
197
+               success = !success;
198
+               skip++;
199
+               parse_string = true;
200
+           }
201
+           if (len > skip && valueskip == '~') {
202
+               reg = true;
203
+               skip++;
204
+               parse_string = true;
205
+           }
206
+
207
+           str = spa_dict_lookup(props, key);
208
+
209
+           /* parse the remaining part of the string, if there was a modifier,
210
+            * we need to check for null again. Otherwise null was in quotes without
211
+            * a modifier. */
212
+           is_null = parse_string && spa_json_is_null(value+skip, len-skip);
213
+           if (is_null || str == NULL) {
214
+               if (is_null && str == NULL)
215
                    success = !success;
216
-                   skip++;
217
+           } else {
218
+               /* only unescape string once or again after modifier */
219
+               if (!parse_string) {
220
+                   memmove(val, value+skip, len-skip);
221
+                   vallen-skip = '\0';
222
+               } else if (spa_json_parse_stringn(value+skip, len-skip, val, sizeof(val)) < 0) {
223
+                   pw_log_warn("invalid string '%.*s' in '%.*s'",
224
+                           len-skip, value+skip, az, as);
225
+                   continue;
226
                }
227
-           }
228
-           if (str != NULL) {
229
-               if (valueskip == '~') {
230
+
231
+               if (reg) {
232
                    regex_t preg;
233
                    int res;
234
-                   skip++;
235
-                   if ((res = regcomp(&preg, value+skip, REG_EXTENDED | REG_NOSUB)) != 0) {
236
+                   if ((res = regcomp(&preg, val, REG_EXTENDED | REG_NOSUB)) != 0) {
237
                        char errbuf1024;
238
                        regerror(res, &preg, errbuf, sizeof(errbuf));
239
-                       pw_log_warn("invalid regex %s: %s", value+skip, errbuf);
240
+                       pw_log_warn("invalid regex %s: %s in '%.*s'",
241
+                               val, errbuf, az, as);
242
                    } else {
243
                        if (regexec(&preg, str, 0, NULL, 0) == 0)
244
                            success = !success;
245
                        regfree(&preg);
246
                    }
247
-               } else if (strncmp(str, value+skip, len-skip) == 0 &&
248
-                   strlen(str) == (size_t)(len-skip)) {
249
+               } else if (strcmp(str, val) == 0) {
250
                    success = !success;
251
                }
252
            }
253
@@ -653,7 +727,14 @@
254
        }
255
        if (match > 0 && fail == 0)
256
            return true;
257
+       count++;
258
    }
259
+   if (r < 0)
260
+       pw_log_warn("malformed object array in '%.*s'", az, as);
261
+   else if (count == 0 && condition)
262
+       /* empty match for condition means success */
263
+       return true;
264
+
265
    return false;
266
 }
267
 
268
@@ -673,58 +754,69 @@
269
    struct pw_context *context = d->context;
270
    struct spa_json it4;
271
    char key512;
272
-   int res = 0;
273
+   int res = 0, r;
274
 
275
    spa_autofree char *s = strndup(str, len);
276
    spa_json_init(&it0, s, len);
277
    if (spa_json_enter_array(&it0, &it1) < 0) {
278
-       pw_log_error("config file error: context.modules is not an array");
279
+       pw_log_error("context.modules is not an array in '%.*s'",
280
+               (int)len, str);
281
        return -EINVAL;
282
    }
283
 
284
-   while (spa_json_enter_object(&it1, &it2) > 0) {
285
+   while ((r = spa_json_enter_object(&it1, &it2)) > 0) {
286
        char *name = NULL, *args = NULL, *flags = NULL;
287
        bool have_match = true;
288
 
289
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
290
            const char *val;
291
-           int len;
292
+           int l;
293
 
294
-           if ((len = spa_json_next(&it2, &val)) <= 0)
295
+           if ((l = spa_json_next(&it2, &val)) <= 0) {
296
+               pw_log_warn("malformed module: key '%s' has no "
297
+                       "value in '%.*s'", key, (int)len, str);
298
                break;
299
+           }
300
 
301
            if (spa_streq(key, "name")) {
302
                name = (char*)val;
303
-               spa_json_parse_stringn(val, len, name, len+1);
304
+               spa_json_parse_stringn(val, l, name, l+1);
305
            } else if (spa_streq(key, "args")) {
306
-               if (spa_json_is_container(val, len))
307
-                   len = spa_json_container_len(&it2, val, len);
308
+               if (spa_json_is_container(val, l))
309
+                   l = spa_json_container_len(&it2, val, l);
310
 
311
                args = (char*)val;
312
-               spa_json_parse_stringn(val, len, args, len+1);
313
+               spa_json_parse_stringn(val, l, args, l+1);
314
            } else if (spa_streq(key, "flags")) {
315
-               if (spa_json_is_container(val, len))
316
-                   len = spa_json_container_len(&it2, val, len);
317
+               if (spa_json_is_container(val, l))
318
+                   l = spa_json_container_len(&it2, val, l);
319
                flags = (char*)val;
320
-               spa_json_parse_stringn(val, len, flags, len+1);
321
+               spa_json_parse_stringn(val, l, flags, l+1);
322
            } else if (spa_streq(key, "condition")) {
323
-               if (!spa_json_is_array(val, len))
324
+               if (!spa_json_is_array(val, l)) {
325
+                   pw_log_warn("expected array for condition in '%.*s'",
326
+                           (int)len, str);
327
                    break;
328
+               }
329
                spa_json_enter(&it2, &it3);
330
-               have_match = find_match(&it3, &context->properties->dict);
331
+               have_match = find_match(&it3, &context->properties->dict, true);
332
+           } else {
333
+               pw_log_warn("unknown module key '%s' in '%.*s'", key,
334
+                       (int)len, str);
335
            }
336
        }
337
        if (!have_match)
338
            continue;
339
 
340
-       if (name != NULL)
341
+       if (name != NULL) {
342
            res = load_module(context, name, args, flags);
343
-
344
-       if (res < 0)
345
-           break;
346
-
347
-       d->count++;
348
+           if (res < 0)
349
+               break;
350
+           d->count++;
351
+       }
352
    }
353
+   if (r < 0)
354
+       pw_log_warn("malformed object array in '%.*s'", (int)len, str);
355
 
356
    return res;
357
 }
358
@@ -772,7 +864,7 @@
359
    struct pw_context *context = d->context;
360
    struct spa_json it4;
361
    char key512;
362
-   int res = 0;
363
+   int res = 0, r;
364
 
365
    spa_autofree char *s = strndup(str, len);
366
    spa_json_init(&it0, s, len);
367
@@ -781,85 +873,127 @@
368
        return -EINVAL;
369
    }
370
 
371
-   while (spa_json_enter_object(&it1, &it2) > 0) {
372
+   while ((r = spa_json_enter_object(&it1, &it2)) > 0) {
373
        char *factory = NULL, *args = NULL, *flags = NULL;
374
        bool have_match = true;
375
 
376
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
377
            const char *val;
378
-           int len;
379
+           int l;
380
 
381
-           if ((len = spa_json_next(&it2, &val)) <= 0)
382
+           if ((l = spa_json_next(&it2, &val)) <= 0) {
383
+               pw_log_warn("malformed object: key '%s' has no "
384
+                       "value in '%.*s'", key, (int)len, str);
385
                break;
386
+           }
387
 
388
            if (spa_streq(key, "factory")) {
389
                factory = (char*)val;
390
-               spa_json_parse_stringn(val, len, factory, len+1);
391
+               spa_json_parse_stringn(val, l, factory, l+1);
392
            } else if (spa_streq(key, "args")) {
393
-               if (spa_json_is_container(val, len))
394
-                   len = spa_json_container_len(&it2, val, len);
395
+               if (spa_json_is_container(val, l))
396
+                   l = spa_json_container_len(&it2, val, l);
397
 
398
                args = (char*)val;
399
-               spa_json_parse_stringn(val, len, args, len+1);
400
+               spa_json_parse_stringn(val, l, args, l+1);
401
            } else if (spa_streq(key, "flags")) {
402
-               if (spa_json_is_container(val, len))
403
-                   len = spa_json_container_len(&it2, val, len);
404
+               if (spa_json_is_container(val, l))
405
+                   l = spa_json_container_len(&it2, val, l);
406
 
407
                flags = (char*)val;
408
-               spa_json_parse_stringn(val, len, flags, len+1);
409
+               spa_json_parse_stringn(val, l, flags, l+1);
410
            } else if (spa_streq(key, "condition")) {
411
-               if (!spa_json_is_array(val, len))
412
+               if (!spa_json_is_array(val, l)) {
413
+                   pw_log_warn("expected array for condition in '%.*s'",
414
+                           (int)len, str);
415
                    break;
416
+               }
417
                spa_json_enter(&it2, &it3);
418
-               have_match = find_match(&it3, &context->properties->dict);
419
+               have_match = find_match(&it3, &context->properties->dict, true);
420
+           } else {
421
+               pw_log_warn("unknown object key '%s' in '%.*s'", key,
422
+                       (int)len, str);
423
            }
424
        }
425
        if (!have_match)
426
            continue;
427
 
428
-       if (factory != NULL)
429
+       if (factory != NULL) {
430
            res = create_object(context, factory, args, flags);
431
-
432
-       if (res < 0)
433
-           break;
434
-       d->count++;
435
+           if (res < 0)
436
+               break;
437
+           d->count++;
438
+       }
439
    }
440
+   if (r < 0)
441
+       pw_log_warn("malformed object array in '%.*s'", (int)len, str);
442
 
443
    return res;
444
 }
445
 
446
-static int do_exec(struct pw_context *context, const char *key, const char *args)
447
+static char **pw_strv_insert_at(char **strv, int len, int pos, const char *str)
448
 {
449
-   int pid, res, n_args;
450
+   char **n;
451
+
452
+   if (len < 0) {
453
+       len = 0;
454
+       while (strv != NULL && strvlen != NULL)
455
+           len++;
456
+   }
457
+   if (pos < 0 || pos > len)
458
+       pos = len+1;
459
+
460
+   n = realloc(strv, sizeof(char*) * (len + 2));
461
+   if (n == NULL) {
462
+       free(strv);
463
+       return NULL;
464
+   }
465
+   strv = n;
466
+
467
+   memmove(strv+pos+1, strv+pos, sizeof(char*) * (len+1-pos));
468
+   strvpos = strdup(str);
469
+   return strv;
470
+}
471
+
472
+static int do_exec(struct pw_context *context, const char *path, const char *args)
473
+{
474
+   int pid, res;
475
 
476
    pid = fork();
477
 
478
    if (pid == 0) {
479
-       char *cmd, **argv;
480
+       char **arg;
481
+       int n_args;
482
 
483
        /* Double fork to avoid zombies; we don't want to set SIGCHLD handler */
484
        pid = fork();
485
 
486
        if (pid < 0) {
487
            pw_log_error("fork error: %m");
488
-           exit(1);
489
+           goto done;
490
        } else if (pid != 0) {
491
            exit(0);
492
        }
493
 
494
-       cmd = spa_aprintf("%s %s", key, args ? args : "");
495
-       argv = pw_split_strv(cmd, " \t", INT_MAX, &n_args);
496
-       free(cmd);
497
-
498
-       pw_log_info("exec %s '%s'", key, args);
499
-       res = execvp(key, argv);
500
-       pw_free_strv(argv);
501
+       arg = pw_strv_parse(args, strlen(args), INT_MAX, &n_args);
502
+       if (arg == NULL) {
503
+           pw_log_error("error parsing arguments: %m");
504
+           goto done;
505
+       }
506
+       arg = pw_strv_insert_at(arg, n_args, 0, path);
507
+       if (arg == NULL) {
508
+           pw_log_error("error constructing arguments: %m");
509
+           goto done;
510
+       }
511
+       pw_log_info("exec %s '%s'", path, args);
512
+       res = execvp(path, arg);
513
+       pw_free_strv(arg);
514
 
515
        if (res == -1) {
516
            res = -errno;
517
-           pw_log_error("execvp error '%s': %m", key);
518
+           pw_log_error("execvp error '%s': %m", path);
519
        }
520
-
521
+done:
522
        exit(1);
523
    } else if (pid < 0) {
524
        pw_log_error("fork error: %m");
525
@@ -877,7 +1011,7 @@
526
 /*
527
  * context.exec = 
528
  *   {   path = <program-name>
529
- *       ( args = "<arguments>" )
530
+ *       ( args = "<arguments>" |  <arg1> <arg2> ...  )
531
  *       ( condition =  { key = value, .. } ..  )
532
  *   }
533
  * 
534
@@ -889,50 +1023,63 @@
535
    struct pw_context *context = d->context;
536
    struct spa_json it4;
537
    char key512;
538
-   int res = 0;
539
+   int r, res = 0;
540
 
541
    spa_autofree char *s = strndup(str, len);
542
    spa_json_init(&it0, s, len);
543
    if (spa_json_enter_array(&it0, &it1) < 0) {
544
-       pw_log_error("config file error: context.exec is not an array");
545
+       pw_log_error("config file error: context.exec is not an array in '%.*s'",
546
+               (int)len, str);
547
        return -EINVAL;
548
    }
549
 
550
-   while (spa_json_enter_object(&it1, &it2) > 0) {
551
+   while ((r = spa_json_enter_object(&it1, &it2)) > 0) {
552
        char *path = NULL, *args = NULL;
553
        bool have_match = true;
554
 
555
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
556
            const char *val;
557
-           int len;
558
+           int l;
559
 
560
-           if ((len = spa_json_next(&it2, &val)) <= 0)
561
+           if ((l = spa_json_next(&it2, &val)) <= 0) {
562
+               pw_log_warn("malformed exec: key '%s' has no "
563
+                       "value in '%.*s'", key, (int)len, str);
564
                break;
565
+           }
566
 
567
            if (spa_streq(key, "path")) {
568
                path = (char*)val;
569
-               spa_json_parse_stringn(val, len, path, len+1);
570
+               spa_json_parse_stringn(val, l, path, l+1);
571
            } else if (spa_streq(key, "args")) {
572
+               if (spa_json_is_container(val, l))
573
+                   l = spa_json_container_len(&it2, val, l);
574
                args = (char*)val;
575
-               spa_json_parse_stringn(val, len, args, len+1);
576
+               spa_json_parse_stringn(val, l, args, l+1);
577
            } else if (spa_streq(key, "condition")) {
578
-               if (!spa_json_is_array(val, len))
579
+               if (!spa_json_is_array(val, l)) {
580
+                   pw_log_warn("expected array for condition in '%.*s'",
581
+                           (int)len, str);
582
                    break;
583
+               }
584
                spa_json_enter(&it2, &it3);
585
-               have_match = find_match(&it3, &context->properties->dict);
586
+               have_match = find_match(&it3, &context->properties->dict, true);
587
+           } else {
588
+               pw_log_warn("unknown exec key '%s' in '%.*s'", key,
589
+                       (int)len, str);
590
            }
591
        }
592
        if (!have_match)
593
            continue;
594
 
595
-       if (path != NULL)
596
+       if (path != NULL) {
597
            res = do_exec(context, path, args);
598
-
599
-       if (res < 0)
600
-           break;
601
-
602
-       d->count++;
603
+           if (res < 0)
604
+               break;
605
+           d->count++;
606
+       }
607
    }
608
+   if (r < 0)
609
+       pw_log_warn("malformed object array in '%.*s'", (int)len, str);
610
 
611
    return res;
612
 }
613
@@ -977,26 +1124,38 @@
614
 }
615
 
616
 SPA_EXPORT
617
-int pw_conf_section_update_props(const struct spa_dict *conf,
618
-       const char *section, struct pw_properties *props)
619
+int pw_conf_section_update_props_rules(const struct spa_dict *conf,
620
+       const struct spa_dict *context, const char *section,
621
+       struct pw_properties *props)
622
 {
623
    struct data data = { .props = props };
624
    int res;
625
    const char *str;
626
+   char key128;
627
 
628
    res = pw_conf_section_for_each(conf, section,
629
            update_props, &data);
630
 
631
    str = pw_properties_get(props, "config.ext");
632
    if (res == 0 && str != NULL) {
633
-       char key128;
634
        snprintf(key, sizeof(key), "%s.%s", section, str);
635
        res = pw_conf_section_for_each(conf, key,
636
                update_props, &data);
637
    }
638
+   if (res == 0 && context != NULL) {
639
+       snprintf(key, sizeof(key), "%s.rules", section);
640
+       res = pw_conf_section_match_rules(conf, key, context, update_props, &data);
641
+   }
642
    return res == 0 ? data.count : res;
643
 }
644
 
645
+SPA_EXPORT
646
+int pw_conf_section_update_props(const struct spa_dict *conf,
647
+       const char *section, struct pw_properties *props)
648
+{
649
+   return pw_conf_section_update_props_rules(conf, NULL, section, props);
650
+}
651
+
652
 static bool valid_conf_name(const char *str)
653
 {
654
    return spa_streq(str, "null") || spa_strendswith(str, ".conf");
655
@@ -1086,7 +1245,7 @@
656
  *     {
657
  *         matches = 
658
  *             # any of the items in matches needs to match, if one does,
659
- *             # actions are emited.
660
+ *             # actions are emitted.
661
  *             {
662
  *                 # all keys must match the value. ! negates. ~ starts regex.
663
  *                 <key> = <value>
664
@@ -1110,38 +1269,60 @@
665
 {
666
    const char *val;
667
    struct spa_json it4, actions;
668
+   int r;
669
 
670
    spa_json_init(&it0, str, len);
671
-   if (spa_json_enter_array(&it0, &it1) < 0)
672
+   if (spa_json_enter_array(&it0, &it1) < 0) {
673
+       pw_log_warn("expect array of match rules in: '%.*s'", (int)len, str);
674
        return 0;
675
+   }
676
 
677
-   while (spa_json_enter_object(&it1, &it2) > 0) {
678
+   while ((r = spa_json_enter_object(&it1, &it2)) > 0) {
679
        char key64;
680
        bool have_match = false, have_actions = false;
681
 
682
        while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
683
            if (spa_streq(key, "matches")) {
684
-               if (spa_json_enter_array(&it2, &it3) < 0)
685
+               if (spa_json_enter_array(&it2, &it3) < 0) {
686
+                   pw_log_warn("expected array as matches in '%.*s'",
687
+                           (int)len, str);
688
                    break;
689
+               }
690
 
691
-               have_match = find_match(&it3, props);
692
+               have_match = find_match(&it3, props, false);
693
            }
694
            else if (spa_streq(key, "actions")) {
695
                if (spa_json_enter_object(&it2, &actions) > 0)
696
                    have_actions = true;
697
+               else
698
+                   pw_log_warn("expected object as match actions in '%.*s'",
699
+                           (int)len, str);
700
+           }
701
+           else {
702
+               pw_log_warn("unknown match key '%s'", key);
703
+               if (spa_json_next(&it2, &val) <= 0) {
704
+                   pw_log_warn("malformed match rule: key '%s' has "
705
+                           "no value in '%.*s'", key, (int)len, str);
706
+                   break;
707
+               }
708
            }
709
-           else if (spa_json_next(&it2, &val) <= 0)
710
-                                break;
711
        }
712
-       if (!have_match || !have_actions)
713
+       if (!have_match)
714
            continue;
715
+       if (!have_actions) {
716
+           pw_log_warn("no actions for match rule '%.*s'", (int)len, str);
717
+           continue;
718
+       }
719
 
720
        while (spa_json_get_string(&actions, key, sizeof(key)) > 0) {
721
            int res, len;
722
            pw_log_debug("action %s", key);
723
 
724
-           if ((len = spa_json_next(&actions, &val)) <= 0)
725
+           if ((len = spa_json_next(&actions, &val)) <= 0) {
726
+               pw_log_warn("malformed action: key '%s' has no value in '%.*s'",
727
+                       key, (int)len, str);
728
                break;
729
+           }
730
 
731
            if (spa_json_is_container(val, len))
732
                len = spa_json_container_len(&actions, val, len);
733
@@ -1150,6 +1331,8 @@
734
                return res;
735
        }
736
    }
737
+   if (r < 0)
738
+       pw_log_warn("malformed object array in '%.*s'", (int)len, str);
739
    return 0;
740
 }
741
 
742
@@ -1199,8 +1382,8 @@
743
 int pw_context_conf_update_props(struct pw_context *context,
744
        const char *section, struct pw_properties *props)
745
 {
746
-   return pw_conf_section_update_props(&context->conf->dict,
747
-           section, props);
748
+   return pw_conf_section_update_props_rules(&context->conf->dict,
749
+           &context->properties->dict, section, props);
750
 }
751
 
752
 SPA_EXPORT
753
@@ -1221,16 +1404,16 @@
754
    int res;
755
 
756
    if (spa_streq(section, "context.spa-libs"))
757
-       res = pw_context_conf_section_for_each(context, section,
758
+       res = pw_conf_section_for_each(&conf->dict, section,
759
                parse_spa_libs, &data);
760
    else if (spa_streq(section, "context.modules"))
761
-       res = pw_context_conf_section_for_each(context, section,
762
+       res = pw_conf_section_for_each(&conf->dict, section,
763
                parse_modules, &data);
764
    else if (spa_streq(section, "context.objects"))
765
-       res = pw_context_conf_section_for_each(context, section,
766
+       res = pw_conf_section_for_each(&conf->dict, section,
767
                parse_objects, &data);
768
    else if (spa_streq(section, "context.exec"))
769
-       res = pw_context_conf_section_for_each(context, section,
770
+       res = pw_conf_section_for_each(&conf->dict, section,
771
                parse_exec, &data);
772
    else
773
        res = -EINVAL;
774
pipewire-1.0.1.tar.bz2/src/pipewire/conf.h -> pipewire-1.2.0.tar.gz/src/pipewire/conf.h Changed
12
 
1
@@ -24,6 +24,10 @@
2
 int pw_conf_section_update_props(const struct spa_dict *conf,
3
        const char *section, struct pw_properties *props);
4
 
5
+int pw_conf_section_update_props_rules(const struct spa_dict *conf,
6
+       const struct spa_dict *context, const char *section,
7
+       struct pw_properties *props);
8
+
9
 int pw_conf_section_for_each(const struct spa_dict *conf, const char *section,
10
        int (*callback) (void *data, const char *location, const char *section,
11
            const char *str, size_t len),
12
pipewire-1.0.1.tar.bz2/src/pipewire/context.c -> pipewire-1.2.0.tar.gz/src/pipewire/context.c Changed
861
 
1
@@ -9,6 +9,7 @@
2
 #include <regex.h>
3
 #include <limits.h>
4
 #include <sys/mman.h>
5
+#include <fnmatch.h>
6
 
7
 #include <pipewire/log.h>
8
 
9
@@ -20,6 +21,8 @@
10
 #include <spa/utils/atomic.h>
11
 #include <spa/utils/names.h>
12
 #include <spa/utils/string.h>
13
+#include <spa/utils/json.h>
14
+#include <spa/utils/cleanup.h>
15
 #include <spa/debug/types.h>
16
 
17
 #include <pipewire/impl.h>
18
@@ -33,6 +36,21 @@
19
 #define PW_LOG_TOPIC_DEFAULT log_context
20
 
21
 #define MAX_HOPS   64
22
+#define MAX_SYNC   4u
23
+#define MAX_LOOPS  64u
24
+
25
+#define DEFAULT_DATA_LOOPS 1
26
+
27
+#if !defined(FNM_EXTMATCH)
28
+#define FNM_EXTMATCH 0
29
+#endif
30
+
31
+struct data_loop {
32
+   struct pw_data_loop *impl;
33
+   bool autostart;
34
+   bool started;
35
+   int ref;
36
+};
37
 
38
 /** \cond */
39
 struct impl {
40
@@ -42,7 +60,10 @@
41
    unsigned int recalc:1;
42
    unsigned int recalc_pending:1;
43
 
44
-   struct pw_data_loop *data_loop_impl;
45
+   uint32_t cpu_count;
46
+
47
+   uint32_t n_data_loops;
48
+   struct data_loop data_loopsMAX_LOOPS;
49
 };
50
 
51
 
52
@@ -82,6 +103,10 @@
53
        pw_properties_set(properties, PW_KEY_WINDOW_X11_DISPLAY,
54
                  getenv("DISPLAY"));
55
    }
56
+}
57
+static void fill_core_properties(struct pw_context *context)
58
+{
59
+   struct pw_properties *properties = context->properties;
60
    pw_properties_set(properties, PW_KEY_CORE_VERSION, context->core->info.version);
61
    pw_properties_set(properties, PW_KEY_CORE_NAME, context->core->info.name);
62
 }
63
@@ -90,24 +115,27 @@
64
 {
65
    struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
66
    struct spa_thread *thr;
67
+   uint32_t i;
68
    int res = 0;
69
 
70
-   if ((thr = pw_data_loop_get_thread(impl->data_loop_impl)) == NULL)
71
-       return -EIO;
72
+   for (i = 0; i < impl->n_data_loops; i++) {
73
+       if (impl->data_loopsi.impl == NULL ||
74
+           (thr = pw_data_loop_get_thread(impl->data_loopsi.impl)) == NULL)
75
+           continue;
76
 
77
-   if (freewheel) {
78
-       pw_log_info("%p: enter freewheel", context);
79
-       if (context->thread_utils)
80
-           res = spa_thread_utils_drop_rt(context->thread_utils, thr);
81
-   } else {
82
-       pw_log_info("%p: exit freewheel", context);
83
-       /* Use the priority as configured within the realtime module */
84
-       if (context->thread_utils)
85
-           res = spa_thread_utils_acquire_rt(context->thread_utils, thr, -1);
86
+       if (freewheel) {
87
+           pw_log_info("%p: enter freewheel", context);
88
+           if (context->thread_utils)
89
+               res = spa_thread_utils_drop_rt(context->thread_utils, thr);
90
+       } else {
91
+           pw_log_info("%p: exit freewheel", context);
92
+           /* Use the priority as configured within the realtime module */
93
+           if (context->thread_utils)
94
+               res = spa_thread_utils_acquire_rt(context->thread_utils, thr, -1);
95
+       }
96
+       if (res < 0)
97
+           pw_log_info("%p: freewheel error:%s", context, spa_strerror(res));
98
    }
99
-   if (res < 0)
100
-       pw_log_info("%p: freewheel error:%s", context, spa_strerror(res));
101
-
102
    context->freewheeling = freewheel;
103
 
104
    return res;
105
@@ -163,6 +191,120 @@
106
    return 0;
107
 }
108
 
109
+static int setup_data_loops(struct impl *impl)
110
+{
111
+   struct pw_properties *pr;
112
+   struct pw_context *this = &impl->this;
113
+   const char *str, *lib_name;
114
+   uint32_t i;
115
+   int res = 0;
116
+
117
+   pr = pw_properties_copy(this->properties);
118
+
119
+   lib_name = pw_properties_get(this->properties, "context.data-loop." PW_KEY_LIBRARY_NAME_SYSTEM);
120
+
121
+   if ((str = pw_properties_get(this->properties, "context.data-loops")) != NULL) {
122
+       struct spa_json it4;
123
+       char key512;
124
+       int r, len = strlen(str);
125
+       spa_autofree char *s = strndup(str, len);
126
+
127
+       i = 0;
128
+       spa_json_init(&it0, s, len);
129
+       if (spa_json_enter_array(&it0, &it1) < 0) {
130
+           pw_log_error("context.data-loops is not an array in '%s'", str);
131
+           res = -EINVAL;
132
+           goto exit;
133
+       }
134
+       while ((r = spa_json_enter_object(&it1, &it2)) > 0) {
135
+           char *props = NULL;
136
+
137
+           if (i >= MAX_LOOPS) {
138
+               pw_log_warn("too many context.data-loops, using first %d",
139
+                   MAX_LOOPS);
140
+               break;
141
+           }
142
+
143
+           pw_properties_clear(pr);
144
+           pw_properties_update(pr, &this->properties->dict);
145
+           pw_properties_set(pr, PW_KEY_LIBRARY_NAME_SYSTEM, lib_name);
146
+
147
+           while (spa_json_get_string(&it2, key, sizeof(key)) > 0) {
148
+               const char *val;
149
+               int l;
150
+
151
+               if ((l = spa_json_next(&it2, &val)) <= 0) {
152
+                   pw_log_warn("malformed data-loop: key '%s' has no "
153
+                           "value in '%.*s'", key, (int)len, str);
154
+                   break;
155
+               }
156
+               if (spa_json_is_container(val, l))
157
+                   l = spa_json_container_len(&it2, val, l);
158
+
159
+               props = (char*)val;
160
+               spa_json_parse_stringn(val, l, props, l+1);
161
+               pw_properties_set(pr, key, props);
162
+               pw_log_info("loop %d: \"%s\" = %s", i, key, props);
163
+           }
164
+           impl->data_loopsi.impl = pw_data_loop_new(&pr->dict);
165
+           if (impl->data_loopsi.impl == NULL)  {
166
+               res = -errno;
167
+               goto exit;
168
+           }
169
+           i++;
170
+       }
171
+       impl->n_data_loops = i;
172
+   } else {
173
+       int32_t count = pw_properties_get_int32(pr, "context.num-data-loops",
174
+               DEFAULT_DATA_LOOPS);
175
+       if (count < 0)
176
+           count = impl->cpu_count;
177
+
178
+       impl->n_data_loops = count;
179
+       if (impl->n_data_loops > MAX_LOOPS) {
180
+           pw_log_warn("too many context.num-data-loops: %d, using %d",
181
+                   impl->n_data_loops, MAX_LOOPS);
182
+           impl->n_data_loops = MAX_LOOPS;
183
+       }
184
+       for (i = 0; i < impl->n_data_loops; i++) {
185
+           pw_properties_setf(pr, SPA_KEY_THREAD_NAME,  "data-loop.%d", i);
186
+           impl->data_loopsi.impl = pw_data_loop_new(&pr->dict);
187
+           if (impl->data_loopsi.impl == NULL)  {
188
+               res = -errno;
189
+               goto exit;
190
+           }
191
+           pw_log_info("created data loop '%s'", impl->data_loopsi.impl->loop->name);
192
+       }
193
+   }
194
+   pw_log_info("created %d data-loops", impl->n_data_loops);
195
+exit:
196
+   pw_properties_free(pr);
197
+   return res;
198
+}
199
+
200
+static int data_loop_start(struct impl *impl, struct data_loop *loop)
201
+{
202
+   int res;
203
+   if (loop->started || loop->impl == NULL)
204
+       return 0;
205
+
206
+   pw_log_info("starting data loop %s", loop->impl->loop->name);
207
+   if ((res = pw_data_loop_start(loop->impl)) < 0)
208
+       return res;
209
+
210
+   pw_data_loop_invoke(loop->impl, do_data_loop_setup, 0, NULL, 0, false, &impl->this);
211
+   loop->started = true;
212
+   return 0;
213
+}
214
+
215
+static void data_loop_stop(struct impl *impl, struct data_loop *loop)
216
+{
217
+   if (!loop->started || loop->impl == NULL)
218
+       return;
219
+   pw_data_loop_stop(loop->impl);
220
+   loop->started = false;
221
+}
222
+
223
 /** Create a new context object
224
  *
225
  * \param main_loop the main loop to use
226
@@ -179,8 +321,8 @@
227
    struct pw_context *this;
228
    const char *lib, *str;
229
    void *dbus_iface = NULL;
230
-   uint32_t n_support;
231
-   struct pw_properties *pr, *conf;
232
+   uint32_t i, n_support, vm_type;
233
+   struct pw_properties *conf;
234
    struct spa_cpu *cpu;
235
    int res = 0;
236
 
237
@@ -231,6 +373,8 @@
238
    }
239
    this->properties = properties;
240
 
241
+   fill_properties(this);
242
+
243
    conf = pw_properties_new(NULL, NULL);
244
    if (conf == NULL) {
245
        res = -errno;
246
@@ -243,6 +387,10 @@
247
    n_support = pw_get_support(this->support, SPA_N_ELEMENTS(this->support) - 6);
248
    cpu = spa_support_find(this->support, n_support, SPA_TYPE_INTERFACE_CPU);
249
 
250
+   vm_type = SPA_CPU_VM_NONE;
251
+   if (cpu != NULL && (vm_type = spa_cpu_get_vm_type(cpu)) != SPA_CPU_VM_NONE)
252
+       pw_properties_set(properties, "cpu.vm.name", spa_cpu_vm_type_to_string(vm_type));
253
+
254
    res = pw_context_conf_update_props(this, "context.properties", properties);
255
    pw_log_info("%p: parsed %d context.properties items", this, res);
256
 
257
@@ -252,7 +400,9 @@
258
    }
259
 
260
    if ((str = pw_properties_get(properties, "vm.overrides")) != NULL) {
261
-       if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
262
+       pw_log_warn("vm.overrides in context.properties are deprecated, "
263
+               "use context.properties.rules instead");
264
+       if (vm_type != SPA_CPU_VM_NONE)
265
            pw_properties_update_string(properties, str, strlen(str));
266
        pw_properties_set(properties, "vm.overrides", NULL);
267
    }
268
@@ -260,11 +410,14 @@
269
        if (pw_properties_get(properties, PW_KEY_CPU_MAX_ALIGN) == NULL)
270
            pw_properties_setf(properties, PW_KEY_CPU_MAX_ALIGN,
271
                "%u", spa_cpu_get_max_align(cpu));
272
+       impl->cpu_count = spa_cpu_get_count(cpu);
273
    }
274
 
275
    if (getenv("PIPEWIRE_DEBUG") == NULL &&
276
-       (str = pw_properties_get(properties, "log.level")) != NULL)
277
-       pw_log_set_level(atoi(str));
278
+           (str = pw_properties_get(properties, "log.level")) != NULL) {
279
+       if (pw_log_set_level_string(str) < 0)
280
+           pw_log_warn("%p: invalid log.level in context properties", this);
281
+   }
282
 
283
    if (pw_properties_get_bool(properties, "mem.mlock-all", false)) {
284
        if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
285
@@ -276,16 +429,8 @@
286
    pw_settings_init(this);
287
    this->settings = this->defaults;
288
 
289
-   pr = pw_properties_copy(properties);
290
-   if ((str = pw_properties_get(pr, "context.data-loop." PW_KEY_LIBRARY_NAME_SYSTEM)))
291
-       pw_properties_set(pr, PW_KEY_LIBRARY_NAME_SYSTEM, str);
292
-
293
-   impl->data_loop_impl = pw_data_loop_new(&pr->dict);
294
-   pw_properties_free(pr);
295
-   if (impl->data_loop_impl == NULL)  {
296
-       res = -errno;
297
+   if ((res = setup_data_loops(impl)) < 0)
298
        goto error_free;
299
-   }
300
 
301
    this->pool = pw_mempool_new(NULL);
302
    if (this->pool == NULL) {
303
@@ -293,10 +438,7 @@
304
        goto error_free;
305
    }
306
 
307
-   this->data_loop = pw_data_loop_get_loop(impl->data_loop_impl);
308
-   this->data_system = this->data_loop->system;
309
    this->main_loop = main_loop;
310
-
311
    this->work_queue = pw_work_queue_new(this->main_loop);
312
    if (this->work_queue == NULL) {
313
        res = -errno;
314
@@ -308,8 +450,6 @@
315
    this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, this->main_loop->system);
316
    this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, this->main_loop->loop);
317
    this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopUtils, this->main_loop->utils);
318
-   this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, this->data_system);
319
-   this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, this->data_loop->loop);
320
    this->supportn_support++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_PluginLoader, &impl->plugin_loader);
321
 
322
    if ((str = pw_properties_get(properties, "support.dbus")) == NULL ||
323
@@ -341,14 +481,14 @@
324
    }
325
    pw_impl_core_register(this->core, NULL);
326
 
327
-   fill_properties(this);
328
+   fill_core_properties(this);
329
 
330
    if ((res = pw_context_parse_conf_section(this, conf, "context.spa-libs")) < 0)
331
        goto error_free;
332
    pw_log_info("%p: parsed %d context.spa-libs items", this, res);
333
    if ((res = pw_context_parse_conf_section(this, conf, "context.modules")) < 0)
334
        goto error_free;
335
-   if (res > 0)
336
+   if (res > 0 || pw_properties_get_bool(properties, "context.modules.allow-empty", false))
337
        pw_log_info("%p: parsed %d context.modules items", this, res);
338
    else
339
        pw_log_warn("%p: no modules loaded from context.modules", this);
340
@@ -359,11 +499,13 @@
341
        goto error_free;
342
    pw_log_info("%p: parsed %d context.exec items", this, res);
343
 
344
-   if ((res = pw_data_loop_start(impl->data_loop_impl)) < 0)
345
-       goto error_free;
346
-
347
-   pw_data_loop_invoke(impl->data_loop_impl,
348
-           do_data_loop_setup, 0, NULL, 0, false, this);
349
+   for (i = 0; i < impl->n_data_loops; i++) {
350
+       struct data_loop *dl = &impl->data_loopsi;
351
+       if (!dl->autostart)
352
+           continue;
353
+       if ((res = data_loop_start(impl, dl)) < 0)
354
+           goto error_free;
355
+   }
356
 
357
    pw_settings_expose(this);
358
 
359
@@ -396,6 +538,7 @@
360
    struct factory_entry *entry;
361
    struct pw_impl_metadata *metadata;
362
    struct pw_impl_core *core_impl;
363
+   uint32_t i;
364
 
365
    pw_log_debug("%p: destroy", context);
366
    pw_context_emit_destroy(context);
367
@@ -415,8 +558,8 @@
368
    spa_list_consume(resource, &context->registry_resource_list, link)
369
        pw_resource_destroy(resource);
370
 
371
-   if (impl->data_loop_impl)
372
-       pw_data_loop_stop(impl->data_loop_impl);
373
+   for (i = 0; i < impl->n_data_loops; i++)
374
+       data_loop_stop(impl, &impl->data_loopsi);
375
 
376
    spa_list_consume(module, &context->module_list, link)
377
        pw_impl_module_destroy(module);
378
@@ -433,8 +576,11 @@
379
    pw_log_debug("%p: free", context);
380
    pw_context_emit_free(context);
381
 
382
-   if (impl->data_loop_impl)
383
-       pw_data_loop_destroy(impl->data_loop_impl);
384
+   for (i = 0; i < impl->n_data_loops; i++) {
385
+       if (impl->data_loopsi.impl)
386
+           pw_data_loop_destroy(impl->data_loopsi.impl);
387
+
388
+   }
389
 
390
    if (context->pool)
391
        pw_mempool_destroy(context->pool);
392
@@ -481,11 +627,25 @@
393
    spa_hook_list_append(&context->listener_list, listener, events, data);
394
 }
395
 
396
+const struct spa_support *context_get_support(struct pw_context *context, uint32_t *n_support,
397
+       const struct spa_dict *info)
398
+{
399
+   uint32_t n = context->n_support;
400
+   struct pw_loop *loop;
401
+
402
+   loop = pw_context_acquire_loop(context, info);
403
+   if (loop != NULL) {
404
+       context->supportn++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, loop->system);
405
+       context->supportn++ = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, loop->loop);
406
+   }
407
+   *n_support = n;
408
+   return context->support;
409
+}
410
+
411
 SPA_EXPORT
412
 const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support)
413
 {
414
-   *n_support = context->n_support;
415
-   return context->support;
416
+   return context_get_support(context, n_support, NULL);
417
 }
418
 
419
 SPA_EXPORT
420
@@ -494,11 +654,102 @@
421
    return context->main_loop;
422
 }
423
 
424
+static struct pw_data_loop *acquire_data_loop(struct impl *impl, const char *name, const char *klass)
425
+{
426
+   uint32_t i, j;
427
+   struct data_loop *best_loop = NULL;
428
+   int best_score = 0, res;
429
+
430
+   for (i = 0; i < impl->n_data_loops; i++) {
431
+       struct data_loop *l = &impl->data_loopsi;
432
+       const char *ln = l->impl->loop->name;
433
+       int score = 0;
434
+
435
+       if (klass == NULL)
436
+           klass = l->impl->class;
437
+
438
+       if (name && ln && fnmatch(name, ln, FNM_EXTMATCH) == 0)
439
+           score += 2;
440
+       if (klass && l->impl->classes) {
441
+           for (j = 0; l->impl->classesj; j++) {
442
+               if (fnmatch(klass, l->impl->classesj, FNM_EXTMATCH) == 0) {
443
+                   score += 1;
444
+                   break;
445
+               }
446
+           }
447
+       }
448
+
449
+       pw_log_debug("%d: name:'%s' class:'%s' score:%d ref:%d", i,
450
+               ln, l->impl->class, score, l->ref);
451
+
452
+       if ((best_loop == NULL) ||
453
+           (score > best_score) ||
454
+           (score == best_score && l->ref < best_loop->ref)) {
455
+           best_loop = l;
456
+           best_score = score;
457
+       }
458
+   }
459
+   if (best_loop == NULL)
460
+       return NULL;
461
+
462
+   best_loop->ref++;
463
+   if ((res = data_loop_start(impl, best_loop)) < 0) {
464
+       errno = -res;
465
+       return NULL;
466
+   }
467
+
468
+   pw_log_info("%p: using name:'%s' class:'%s' ref:%d", impl,
469
+           best_loop->impl->loop->name,
470
+           best_loop->impl->class, best_loop->ref);
471
+
472
+   return best_loop->impl;
473
+}
474
+
475
 SPA_EXPORT
476
 struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context)
477
 {
478
    struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
479
-   return impl->data_loop_impl;
480
+   return acquire_data_loop(impl, NULL, NULL);
481
+}
482
+
483
+SPA_EXPORT
484
+struct pw_loop *pw_context_acquire_loop(struct pw_context *context, const struct spa_dict *props)
485
+{
486
+   struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
487
+   const char *name, *klass;
488
+   struct pw_data_loop *loop;
489
+
490
+   name = props ? spa_dict_lookup(props, PW_KEY_NODE_LOOP_NAME) : NULL;
491
+   klass = props ? spa_dict_lookup(props, PW_KEY_NODE_LOOP_CLASS) : NULL;
492
+
493
+   pw_log_info("%p: looking for name:'%s' class:'%s'", context, name, klass);
494
+
495
+   if ((impl->n_data_loops == 0) ||
496
+       (name && fnmatch(name, context->main_loop->name, FNM_EXTMATCH) == 0) ||
497
+       (klass && fnmatch(klass, "main", FNM_EXTMATCH) == 0)) {
498
+       pw_log_info("%p: using main loop num-data-loops:%d", context, impl->n_data_loops);
499
+       return context->main_loop;
500
+   }
501
+
502
+   loop = acquire_data_loop(impl, name, klass);
503
+   return loop ? loop->loop : NULL;
504
+}
505
+
506
+SPA_EXPORT
507
+void pw_context_release_loop(struct pw_context *context, struct pw_loop *loop)
508
+{
509
+   struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
510
+   uint32_t i;
511
+
512
+   for (i = 0; i < impl->n_data_loops; i++) {
513
+       struct data_loop *l = &impl->data_loopsi;
514
+       if (l->impl->loop == loop) {
515
+           l->ref--;
516
+           pw_log_info("release name:'%s' class:'%s' ref:%d", l->impl->loop->name,
517
+                   l->impl->class, l->ref);
518
+           return;
519
+       }
520
+   }
521
 }
522
 
523
 SPA_EXPORT
524
@@ -551,6 +802,19 @@
525
    return true;
526
 }
527
 
528
+static bool global_is_stale(struct pw_context *context, struct pw_global *global)
529
+{
530
+   struct pw_impl_client *client = context->current_client;
531
+
532
+   if (!client)
533
+       return false;
534
+
535
+   if (client->recv_generation != 0 && global->generation > client->recv_generation)
536
+       return true;
537
+
538
+   return false;
539
+}
540
+
541
 SPA_EXPORT
542
 int pw_context_for_each_global(struct pw_context *context,
543
                int (*callback) (void *data, struct pw_global *global),
544
@@ -560,7 +824,7 @@
545
    int res;
546
 
547
    spa_list_for_each_safe(g, t, &context->global_list, link) {
548
-       if (!global_can_read(context, g))
549
+       if (!global_can_read(context, g) || global_is_stale(context, g))
550
            continue;
551
        if ((res = callback(data, g)) != 0)
552
            return res;
553
@@ -579,6 +843,11 @@
554
        return NULL;
555
    }
556
 
557
+   if (global_is_stale(context, global)) {
558
+       errno = global_can_read(context, global) ? ESTALE : ENOENT;
559
+       return NULL;
560
+   }
561
+
562
    if (!global_can_read(context, global)) {
563
        errno = EACCES;
564
        return NULL;
565
@@ -643,7 +912,9 @@
566
  */
567
 int pw_context_find_format(struct pw_context *context,
568
            struct pw_impl_port *output,
569
+           uint32_t output_mix,
570
            struct pw_impl_port *input,
571
+           uint32_t input_mix,
572
            struct pw_properties *props,
573
            uint32_t n_format_filters,
574
            struct spa_pod **format_filters,
575
@@ -657,10 +928,27 @@
576
    struct spa_pod_builder fb = { 0 };
577
    uint8_t fbuf4096;
578
    struct spa_pod *filter;
579
+   struct spa_node *in_node, *out_node;
580
+   uint32_t in_port, out_port;
581
 
582
    out_state = output->state;
583
    in_state = input->state;
584
 
585
+   if (output_mix == SPA_ID_INVALID) {
586
+       out_node = output->node->node;
587
+       out_port = output->port_id;
588
+   } else {
589
+       out_node = output->mix;
590
+       out_port = output_mix;
591
+   }
592
+   if (input_mix == SPA_ID_INVALID) {
593
+       in_node = input->node->node;
594
+       in_port = input->port_id;
595
+   } else {
596
+       in_node = input->mix;
597
+       in_port = input_mix;
598
+   }
599
+
600
    pw_log_debug("%p: finding best format %d %d", context, out_state, in_state);
601
 
602
    /* when a port is configured but the node is idle, we can reconfigure with a different format */
603
@@ -674,8 +962,8 @@
604
    if (in_state == PW_IMPL_PORT_STATE_CONFIGURE && out_state > PW_IMPL_PORT_STATE_CONFIGURE) {
605
        /* only input needs format */
606
        spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
607
-       if ((res = spa_node_port_enum_params_sync(output->node->node,
608
-                            output->direction, output->port_id,
609
+       if ((res = spa_node_port_enum_params_sync(out_node,
610
+                            output->direction, out_port,
611
                             SPA_PARAM_Format, &oidx,
612
                             NULL, &filter, &fb)) != 1) {
613
            if (res < 0)
614
@@ -687,8 +975,8 @@
615
        pw_log_debug("%p: Got output format:", context);
616
        pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
617
 
618
-       if ((res = spa_node_port_enum_params_sync(input->node->node,
619
-                            input->direction, input->port_id,
620
+       if ((res = spa_node_port_enum_params_sync(in_node,
621
+                            input->direction, in_port,
622
                             SPA_PARAM_EnumFormat, &iidx,
623
                             filter, format, builder)) <= 0) {
624
            if (res == -ENOENT || res == 0) {
625
@@ -703,8 +991,8 @@
626
    } else if (out_state >= PW_IMPL_PORT_STATE_CONFIGURE && in_state > PW_IMPL_PORT_STATE_CONFIGURE) {
627
        /* only output needs format */
628
        spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
629
-       if ((res = spa_node_port_enum_params_sync(input->node->node,
630
-                            input->direction, input->port_id,
631
+       if ((res = spa_node_port_enum_params_sync(in_node,
632
+                            input->direction, in_port,
633
                             SPA_PARAM_Format, &iidx,
634
                             NULL, &filter, &fb)) != 1) {
635
            if (res < 0)
636
@@ -716,8 +1004,8 @@
637
        pw_log_debug("%p: Got input format:", context);
638
        pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
639
 
640
-       if ((res = spa_node_port_enum_params_sync(output->node->node,
641
-                            output->direction, output->port_id,
642
+       if ((res = spa_node_port_enum_params_sync(out_node,
643
+                            output->direction, out_port,
644
                             SPA_PARAM_EnumFormat, &oidx,
645
                             filter, format, builder)) <= 0) {
646
            if (res == -ENOENT || res == 0) {
647
@@ -734,8 +1022,8 @@
648
        /* both ports need a format */
649
        pw_log_debug("%p: do enum input %d", context, iidx);
650
        spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
651
-       if ((res = spa_node_port_enum_params_sync(input->node->node,
652
-                            input->direction, input->port_id,
653
+       if ((res = spa_node_port_enum_params_sync(in_node,
654
+                            input->direction, in_port,
655
                             SPA_PARAM_EnumFormat, &iidx,
656
                             NULL, &filter, &fb)) != 1) {
657
            if (res == -ENOENT) {
658
@@ -752,8 +1040,8 @@
659
        pw_log_debug("%p: enum output %d with filter: %p", context, oidx, filter);
660
        pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
661
 
662
-       if ((res = spa_node_port_enum_params_sync(output->node->node,
663
-                            output->direction, output->port_id,
664
+       if ((res = spa_node_port_enum_params_sync(out_node,
665
+                            output->direction, out_port,
666
                             SPA_PARAM_EnumFormat, &oidx,
667
                             filter, format, builder)) != 1) {
668
            if (res == 0 && filter != NULL) {
669
@@ -875,7 +1163,8 @@
670
  * This ensures that we only activate the paths from the runnable nodes to the
671
  * driver nodes and leave the other nodes idle.
672
  */
673
-static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect)
674
+static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, struct spa_list *collect,
675
+       char **sync)
676
 {
677
    struct spa_list queue;
678
    struct pw_impl_node *n, *t;
679
@@ -900,6 +1189,11 @@
680
        if (!n->active)
681
            continue;
682
 
683
+       if (sync0 != NULL) {
684
+           if (pw_strv_find_common(n->sync_groups, sync) < 0)
685
+               continue;
686
+       }
687
+
688
        spa_list_for_each(p, &n->input_ports, link) {
689
            spa_list_for_each(l, &p->links, input_link) {
690
                t = l->output->node;
691
@@ -944,13 +1238,15 @@
692
        }
693
        /* now go through all the nodes that have the same group and
694
         * that are not yet visited */
695
-       if (n->groups != NULL || n->link_groups != NULL) {
696
+       if (n->groups != NULL || n->link_groups != NULL || sync0 != NULL) {
697
            spa_list_for_each(t, &context->node_list, link) {
698
                if (t->exported || !t->active || t->visited)
699
                    continue;
700
                if (pw_strv_find_common(t->groups, n->groups) < 0 &&
701
-                   pw_strv_find_common(t->link_groups, n->link_groups) < 0)
702
+                   pw_strv_find_common(t->link_groups, n->link_groups) < 0 &&
703
+                   pw_strv_find_common(t->sync_groups, sync) < 0)
704
                    continue;
705
+
706
                pw_log_debug("%p: %s join group of %s",
707
                        t, t->name, n->name);
708
                t->visited = true;
709
@@ -994,7 +1290,7 @@
710
 }
711
 
712
 static inline void get_quantums(struct pw_context *context, uint32_t *def,
713
-       uint32_t *min, uint32_t *max, uint32_t *limit, uint32_t *rate)
714
+       uint32_t *min, uint32_t *max, uint32_t *rate, uint32_t *floor, uint32_t *ceil)
715
 {
716
    struct settings *s = &context->settings;
717
    if (s->clock_force_quantum != 0) {
718
@@ -1006,7 +1302,8 @@
719
        *max = s->clock_max_quantum;
720
        *rate = s->clock_rate;
721
    }
722
-   *limit = s->clock_quantum_limit;
723
+   *floor = s->clock_quantum_floor;
724
+   *ceil = s->clock_quantum_limit;
725
 }
726
 
727
 static inline const uint32_t *get_rates(struct pw_context *context, uint32_t *def, uint32_t *n_rates,
728
@@ -1115,7 +1412,7 @@
729
 
730
    /* first find higher nearest GCD. This tries to find next bigest rate that
731
     * requires the least amount of resample filter banks. Usually these are
732
-    * rates that are multiples of eachother or multiples of a common rate.
733
+    * rates that are multiples of each other or multiples of a common rate.
734
     *
735
     * 44100 and  32000 56000 88200 96000   -> 88200
736
     * 48000 and  32000 56000 88200 96000   -> 96000
737
@@ -1199,10 +1496,11 @@
738
    struct settings *settings = &context->settings;
739
    struct pw_impl_node *n, *s, *target, *fallback;
740
    const uint32_t *rates;
741
-   uint32_t max_quantum, min_quantum, def_quantum, lim_quantum, rate_quantum;
742
-   uint32_t n_rates, def_rate;
743
-   bool freewheel = false, global_force_rate, global_force_quantum;
744
+   uint32_t max_quantum, min_quantum, def_quantum, rate_quantum, floor_quantum, ceil_quantum;
745
+   uint32_t n_rates, def_rate, n_sync;
746
+   bool freewheel, global_force_rate, global_force_quantum, transport_start;
747
    struct spa_list collect;
748
+   char *syncMAX_SYNC+1;
749
 
750
    pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason);
751
 
752
@@ -1213,15 +1511,30 @@
753
 
754
 again:
755
    impl->recalc = true;
756
+   freewheel = false;
757
+   transport_start = false;
758
 
759
-   /* clean up the flags first */
760
+   /* clean up the flags first and collect sync */
761
+   n_sync = 0;
762
+   sync0 = NULL;
763
    spa_list_for_each(n, &context->node_list, link) {
764
        n->visited = false;
765
        n->checked = 0;
766
        n->runnable = n->always_process && n->active;
767
+       if (n->sync) {
768
+           for (uint32_t i = 0; n->sync_groupsi; i++) {
769
+               if (n_sync >= MAX_SYNC)
770
+                   break;
771
+               if (pw_strv_find(sync, n->sync_groupsi) >= 0)
772
+                   continue;
773
+               syncn_sync++ = n->sync_groupsi;
774
+               syncn_sync = NULL;
775
+           }
776
+       }
777
    }
778
 
779
-   get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &lim_quantum, &rate_quantum);
780
+   get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &rate_quantum,
781
+           &floor_quantum, &ceil_quantum);
782
    rates = get_rates(context, &def_rate, &n_rates, &global_force_rate);
783
 
784
    global_force_quantum = rate_quantum == 0;
785
@@ -1238,7 +1551,7 @@
786
 
787
        if (!n->visited) {
788
            spa_list_init(&collect);
789
-           collect_nodes(context, n, &collect);
790
+           collect_nodes(context, n, &collect, sync);
791
            move_to_driver(context, &collect, n);
792
        }
793
        /* from now on we are only interested in active driving nodes
794
@@ -1292,7 +1605,7 @@
795
 
796
        /* collect all nodes in this group */
797
        spa_list_init(&collect);
798
-       collect_nodes(context, n, &collect);
799
+       collect_nodes(context, n, &collect, sync);
800
 
801
        driver = NULL;
802
        spa_list_for_each(t, &collect, sort_link) {
803
@@ -1500,7 +1813,7 @@
804
            if (latency.denom != 0)
805
                target_quantum = (latency.num * current_rate / latency.denom);
806
            target_quantum = SPA_CLAMP(target_quantum, node_min_quantum, node_max_quantum);
807
-           target_quantum = SPA_MIN(target_quantum, lim_quantum);
808
+           target_quantum = SPA_CLAMP(target_quantum, floor_quantum, ceil_quantum);
809
 
810
            if (settings->clock_power_of_two_quantum && !force_quantum)
811
                target_quantum = flp2(target_quantum);
812
@@ -1554,12 +1867,20 @@
813
 
814
        /* first change the node states of the followers to the new target */
815
        spa_list_for_each(s, &n->follower_list, follower_link) {
816
+           if (s->transport)
817
+               transport_start = true;
818
            if (s == n)
819
                continue;
820
            pw_log_debug("%p: follower %p: active:%d '%s'",
821
                    context, s, s->active, s->name);
822
            ensure_state(s, running);
823
        }
824
+
825
+       SPA_ATOMIC_STORE(n->rt.target.activation->command,
826
+               transport_start ?
827
+                   PW_NODE_ACTIVATION_COMMAND_START :
828
+                   PW_NODE_ACTIVATION_COMMAND_STOP);
829
+
830
        /* now that all the followers are ready, start the driver */
831
        ensure_state(n, running);
832
    }
833
@@ -1631,7 +1952,7 @@
834
        return NULL;
835
    }
836
 
837
-   support = pw_context_get_support(context, &n_support);
838
+   support = context_get_support(context, &n_support, info);
839
 
840
    handle = pw_load_spa_handle(lib, factory_name,
841
            info, n_support, support);
842
@@ -1698,10 +2019,15 @@
843
        entry->value = value;
844
    }
845
    if (spa_streq(type, SPA_TYPE_INTERFACE_ThreadUtils)) {
846
+       uint32_t i;
847
+
848
        context->thread_utils = value;
849
-       if (impl->data_loop_impl)
850
-           pw_data_loop_set_thread_utils(impl->data_loop_impl,
851
-                   context->thread_utils);
852
+
853
+       for (i = 0; i < impl->n_data_loops; i++) {
854
+           if (impl->data_loopsi.impl)
855
+               pw_data_loop_set_thread_utils(impl->data_loopsi.impl,
856
+                       context->thread_utils);
857
+       }
858
    }
859
    return 0;
860
 }
861
pipewire-1.0.1.tar.bz2/src/pipewire/context.h -> pipewire-1.2.0.tar.gz/src/pipewire/context.h Changed
96
 
1
@@ -71,10 +71,17 @@
2
    void (*driver_removed) (void *data, struct pw_impl_node *node);
3
 };
4
 
5
-/** Make a new context object for a given main_loop. Ownership of the properties is taken */
6
-struct pw_context * pw_context_new(struct pw_loop *main_loop,      /**< a main loop to run in */
7
-                struct pw_properties *props,   /**< extra properties */
8
-                size_t user_data_size      /**< extra user data size */);
9
+/** Make a new context object for a given main_loop. Ownership of the properties is taken, even
10
+ * if the function returns NULL.
11
+ *
12
+ * \param main_loop A main loop to run in. This must stay alive unil pw_context_destroy() is called.
13
+ * \param props extra properties
14
+ * \param user_data_size extra user data size
15
+ * \return The context object on success, or NULL on failure, in which case errno is set.
16
+ * */
17
+struct pw_context * pw_context_new(struct pw_loop *main_loop,
18
+                struct pw_properties *props,
19
+                size_t user_data_size);
20
 
21
 /** destroy a context object, all resources except the main_loop will be destroyed */
22
 void pw_context_destroy(struct pw_context *context);
23
@@ -119,16 +126,25 @@
24
 /** Get the context support objects */
25
 const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support);
26
 
27
-/** get the context main loop */
28
+/** Get the context main loop. Returns the value passed to pw_context_new(). */
29
 struct pw_loop *pw_context_get_main_loop(struct pw_context *context);
30
 
31
-/** get the context data loop. Since 0.3.56 */
32
+/** Get the context data loop. This loop runs on the realtime thread. This
33
+ * acquires a loop from the generic data.rt class. Use pw_context_acquire_loop() instead.
34
+ * Since 0.3.56 */
35
 struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context);
36
 
37
+/** Get a data-loop.
38
+ * Since 1.1.0 */
39
+struct pw_loop *pw_context_acquire_loop(struct pw_context *context, const struct spa_dict *props);
40
+/** Release a data-loop.
41
+ * Since 1.1.0 */
42
+void pw_context_release_loop(struct pw_context *context, struct pw_loop *loop);
43
+
44
 /** Get the work queue from the context: Since 0.3.26 */
45
 struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context);
46
 
47
-/** Get the memmory pool from the context: Since 0.3.74 */
48
+/** Get the memory pool from the context: Since 0.3.74 */
49
 struct pw_mempool *pw_context_get_mempool(struct pw_context *context);
50
 
51
 /** Iterate the globals of the context. The callback should return
52
@@ -139,7 +155,10 @@
53
                int (*callback) (void *data, struct pw_global *global),
54
                void *data);
55
 
56
-/** Find a context global by id */
57
+/** Find a context global by id.
58
+ *
59
+ * \return The global on success, or NULL on failure. If id is \ref PW_ID_CORE,
60
+ *         this function will always return a non-NULL value. */
61
 struct pw_global *pw_context_find_global(struct pw_context *context,   /**< the context */
62
                      uint32_t id       /**< the global id */);
63
 
64
@@ -149,6 +168,7 @@
65
 /** find the library name for a spa factory */
66
 const char * pw_context_find_spa_lib(struct pw_context *context, const char *factory_name);
67
 
68
+/** Load a SPA handle from a context. On failure returns NULL and sets errno. */
69
 struct spa_handle *pw_context_load_spa_handle(struct pw_context *context,
70
        const char *factory_name,
71
        const struct spa_dict *info);
72
@@ -169,9 +189,21 @@
73
 /** find information about registered export type */
74
 const struct pw_export_type *pw_context_find_export_type(struct pw_context *context, const char *type);
75
 
76
-/** add an object to the context */
77
+/** add an object to the context
78
+ *
79
+ * \param context The context.
80
+ * \param type The type of the object, usually a `TYPE_INTERFACE_` value.
81
+ * \param value The object value. Must last as long as the context and must
82
+ *              be of the type corresponding to the type.
83
+ * \return A negative number on failure (out of memory).
84
+ * */
85
 int pw_context_set_object(struct pw_context *context, const char *type, void *value);
86
-/** get an object from the context */
87
+/** get an object from the context
88
+ *
89
+ * \param context The context.
90
+ * \param type The string corresponding to the object's interface.
91
+ * \return The object, or NULL if the object does not exist.
92
+ * */
93
 void *pw_context_get_object(struct pw_context *context, const char *type);
94
 
95
 /**
96
pipewire-1.0.1.tar.bz2/src/pipewire/control.h -> pipewire-1.2.0.tar.gz/src/pipewire/control.h Changed
11
 
1
@@ -45,7 +45,8 @@
2
 /** Get the control parent port or NULL when not set */
3
 struct pw_impl_port *pw_control_get_port(struct pw_control *control);
4
 
5
-/** Add an event listener on the control */
6
+/** Add an event listener on the control. May be called multiple times.
7
+ * Each listener must be removed, but they may be removed in any order. */
8
 void pw_control_add_listener(struct pw_control *control,
9
                 struct spa_hook *listener,
10
                 const struct pw_control_events *events,
11
pipewire-1.0.1.tar.bz2/src/pipewire/core.c -> pipewire-1.2.0.tar.gz/src/pipewire/core.c Changed
17
 
1
@@ -78,10 +78,13 @@
2
    struct pw_core *this = data;
3
    struct pw_memblock *m;
4
 
5
-   pw_log_debug("%p: add mem %u type:%u fd:%d flags:%u", this, id, type, fd, flags);
6
+   pw_log_debug("%p: add mem %u type:%u fd:%d flags:%08x", this, id, type, fd, flags);
7
 
8
    m = pw_mempool_import(this->pool, flags, type, fd);
9
-   if (m->id != id) {
10
+   if (m == NULL) {
11
+       pw_log_error("%p: can't import mem id:%u fd:%d: %m", this, id, fd);
12
+       pw_proxy_errorf(&this->proxy, -errno, "can't import mem id:%u: %m", id);
13
+   } else if (m->id != id) {
14
        pw_log_error("%p: invalid mem id %u, fd:%d expected %u",
15
                this, id, fd, m->id);
16
        pw_proxy_errorf(&this->proxy, -EINVAL, "invalid mem id %u, expected %u", id, m->id);
17
pipewire-1.0.1.tar.bz2/src/pipewire/core.h -> pipewire-1.2.0.tar.gz/src/pipewire/core.h Changed
49
 
1
@@ -69,11 +69,13 @@
2
 #include <pipewire/properties.h>
3
 #include <pipewire/proxy.h>
4
 
5
-/** Update an existing \ref pw_core_info with \a update with reset */
6
+/** Update an existing \ref pw_core_info with \a update with reset. When info is NULL,
7
+ * a new one will be allocated. Returns NULL on failure. */
8
 struct pw_core_info *
9
 pw_core_info_update(struct pw_core_info *info,
10
        const struct pw_core_info *update);
11
-/** Update an existing \ref pw_core_info with \a update */
12
+/** Update an existing \ref pw_core_info with \a update. When info is NULL, a new one
13
+ * will be allocated. Returns NULL on failure */
14
 struct pw_core_info *
15
 pw_core_info_merge(struct pw_core_info *info,
16
        const struct pw_core_info *update, bool reset);
17
@@ -164,6 +166,9 @@
18
     * global ID. It is emitted before the global becomes visible in the
19
     * registry.
20
     *
21
+    * The bound_props event is an enhanced version of this event that
22
+    * also contains the extra global properties.
23
+    *
24
     * \param id bound object ID
25
     * \param global_id the global id bound to
26
     */
27
@@ -192,6 +197,21 @@
28
     */
29
    void (*remove_mem) (void *data, uint32_t id);
30
 
31
+   /**
32
+    * Notify an object binding
33
+    *
34
+    * This event is emitted when a local object ID is bound to a
35
+    * global ID. It is emitted before the global becomes visible in the
36
+    * registry.
37
+    *
38
+    * This is an enhanced version of the bound_id event.
39
+    *
40
+    * \param id bound object ID
41
+    * \param global_id the global id bound to
42
+    * \param props The properties of the new global object.
43
+    *
44
+    * Since version 4:1
45
+    */
46
    void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props);
47
 };
48
 
49
pipewire-1.0.1.tar.bz2/src/pipewire/data-loop.c -> pipewire-1.2.0.tar.gz/src/pipewire/data-loop.c Changed
141
 
1
@@ -4,12 +4,14 @@
2
 
3
 #include <pthread.h>
4
 #include <errno.h>
5
+#include <limits.h>
6
 #include <sys/resource.h>
7
 
8
 #include "pipewire/log.h"
9
 #include "pipewire/data-loop.h"
10
 #include "pipewire/private.h"
11
 #include "pipewire/thread.h"
12
+#include "pipewire/utils.h"
13
 
14
 PW_LOG_TOPIC_EXTERN(log_data_loop);
15
 #define PW_LOG_TOPIC_DEFAULT log_data_loop
16
@@ -86,7 +88,7 @@
17
 static struct pw_data_loop *loop_new(struct pw_loop *loop, const struct spa_dict *props)
18
 {
19
    struct pw_data_loop *this;
20
-   const char *str;
21
+   const char *str, *name = NULL, *class = NULL;
22
    int res;
23
 
24
    this = calloc(1, sizeof(struct pw_data_loop));
25
@@ -107,11 +109,29 @@
26
        goto error_free;
27
    }
28
    this->loop = loop;
29
-
30
-   if (props != NULL &&
31
-       (str = spa_dict_lookup(props, "loop.cancel")) != NULL)
32
-       this->cancel = pw_properties_parse_bool(str);
33
-
34
+   this->rt_prio = -1;
35
+
36
+   if (props != NULL) {
37
+       if ((str = spa_dict_lookup(props, PW_KEY_LOOP_CANCEL)) != NULL)
38
+           this->cancel = pw_properties_parse_bool(str);
39
+       if ((str = spa_dict_lookup(props, PW_KEY_LOOP_CLASS)) != NULL)
40
+           class = str;
41
+       if ((str = spa_dict_lookup(props, PW_KEY_LOOP_RT_PRIO)) != NULL)
42
+           this->rt_prio = atoi(str);
43
+       if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_NAME)) != NULL)
44
+           name = str;
45
+       if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_AFFINITY)) != NULL)
46
+           this->affinity = strdup(str);
47
+   }
48
+   if (class == NULL)
49
+       class = this->rt_prio != 0 ? "data.rt" : "data";
50
+   if (name == NULL)
51
+       name = "data-loop";
52
+
53
+   this->class = strdup(class);
54
+   this->classes = pw_strv_parse(class, strlen(class), INT_MAX, NULL);
55
+   if (!this->loop->name0)
56
+       pw_loop_set_name(this->loop, name);
57
    spa_hook_list_init(&this->listener_list);
58
 
59
    return this;
60
@@ -151,6 +171,9 @@
61
 
62
    spa_hook_list_clean(&loop->listener_list);
63
 
64
+   free(loop->affinity);
65
+   free(loop->class);
66
+   pw_free_strv(loop->classes);
67
    free(loop);
68
 }
69
 
70
@@ -170,6 +193,36 @@
71
    return loop->loop;
72
 }
73
 
74
+/** Get the loop name
75
+ * \param loop the data loop to query
76
+ * \return the data loop name
77
+ *
78
+ * Get the name of the data loop. The data loop name is a unique name that
79
+ * identifies this data loop.
80
+ *
81
+ * \since 1.1.0
82
+ */
83
+SPA_EXPORT
84
+const char * pw_data_loop_get_name(struct pw_data_loop *loop)
85
+{
86
+   return loop->loop->name;
87
+}
88
+
89
+/** Get the loop class
90
+ * \param loop the data loop to query
91
+ * \return the data loop class
92
+ *
93
+ * Get the class of the data loop. Multiple data loop can have the same class
94
+ * and processing can be assigned to any data loop from the same class.
95
+ *
96
+ * \since 1.1.0
97
+ */
98
+SPA_EXPORT
99
+const char * pw_data_loop_get_class(struct pw_data_loop *loop)
100
+{
101
+   return loop->class;
102
+}
103
+
104
 /** Start a data loop
105
  * \param loop the data loop to start
106
  * \return 0 if ok, -1 on error
107
@@ -183,24 +236,28 @@
108
    if (!loop->running) {
109
        struct spa_thread_utils *utils;
110
        struct spa_thread *thr;
111
+       struct spa_dict_item items2;
112
+       uint32_t n_items = 0;
113
 
114
        loop->running = true;
115
 
116
        if ((utils = loop->thread_utils) == NULL)
117
            utils = pw_thread_utils_get();
118
 
119
-       static const struct spa_dict_item items = {
120
-           { SPA_KEY_THREAD_NAME, "pw-data-loop" },
121
-       };
122
+       itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_NAME, loop->loop->name);
123
+       if (loop->affinity)
124
+           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_AFFINITY,
125
+                   loop->affinity);
126
 
127
-       thr = spa_thread_utils_create(utils, &SPA_DICT_INIT_ARRAY(items), do_loop, loop);
128
+       thr = spa_thread_utils_create(utils, &SPA_DICT_INIT(items, n_items), do_loop, loop);
129
        loop->thread = (pthread_t)thr;
130
        if (thr == NULL) {
131
            pw_log_error("%p: can't create thread: %m", loop);
132
            loop->running = false;
133
            return -errno;
134
        }
135
-       spa_thread_utils_acquire_rt(utils, thr, -1);
136
+       if (loop->rt_prio != 0)
137
+           spa_thread_utils_acquire_rt(utils, thr, loop->rt_prio);
138
    }
139
    return 0;
140
 }
141
pipewire-1.0.1.tar.bz2/src/pipewire/data-loop.h -> pipewire-1.2.0.tar.gz/src/pipewire/data-loop.h Changed
54
 
1
@@ -58,6 +58,11 @@
2
 struct pw_loop *
3
 pw_data_loop_get_loop(struct pw_data_loop *loop);
4
 
5
+/** Get the loop name. Since 1.1.0 */
6
+const char * pw_data_loop_get_name(struct pw_data_loop *loop);
7
+/** Get the loop class. Since 1.1.0 */
8
+const char * pw_data_loop_get_class(struct pw_data_loop *loop);
9
+
10
 /** Destroy the loop */
11
 void pw_data_loop_destroy(struct pw_data_loop *loop);
12
 
13
@@ -67,13 +72,38 @@
14
 /** Stop the processing thread */
15
 int pw_data_loop_stop(struct pw_data_loop *loop);
16
 
17
-/** Check if the current thread is the processing thread */
18
+/** Check if the current thread is the processing thread.
19
+ * May be called from any thread. */
20
 bool pw_data_loop_in_thread(struct pw_data_loop *loop);
21
 /** Get the thread object */
22
 struct spa_thread *pw_data_loop_get_thread(struct pw_data_loop *loop);
23
 
24
 /** invoke func in the context of the thread or in the caller thread when
25
- * the loop is not running. Since 0.3.3 */
26
+ * the loop is not running. May be called from the loop's thread, but otherwise
27
+ * can only be called by a single thread at a time.
28
+ * If called from the loop's thread, all callbacks previously queued with
29
+ * pw_data_loop_invoke() will be run synchronously, which might cause
30
+ * unexpected reentrancy problems.
31
+ *
32
+ * \paramin loop The loop to invoke func on.
33
+ * \param func The function to be invoked.
34
+ * \param seq A sequence number, opaque to PipeWire. This will be made
35
+ *            available to func.
36
+ * \paramin data Data that will be copied into the internal ring buffer and made
37
+ *             available to func. Because this data is copied, it is okay to
38
+ *             pass a pointer to a local variable, but do not pass a pointer to
39
+ *             an object that has identity.
40
+ * \param size The size of data to copy.
41
+ * \param block If \true, do not return until func has been called. Otherwise,
42
+ *              returns immediately. Passing \true does not risk a deadlock because
43
+ *              the data thread is never allowed to wait on any other thread.
44
+ * \param user_data An opaque pointer passed to func.
45
+ * \return `-EPIPE` if the internal ring buffer filled up,
46
+ *         if block is \false, 0 is returned when seq is SPA_ID_INVALID or the
47
+ *         sequence number with the ASYNC bit set otherwise. When block is \true,
48
+ *         the return value of func is returned.
49
+ *
50
+ * Since 0.3.3 */
51
 int pw_data_loop_invoke(struct pw_data_loop *loop,
52
        spa_invoke_func_t func, uint32_t seq, const void *data, size_t size,
53
        bool block, void *user_data);
54
pipewire-1.0.1.tar.bz2/src/pipewire/extensions/client-node.h -> pipewire-1.2.0.tar.gz/src/pipewire/extensions/client-node.h Changed
15
 
1
@@ -22,7 +22,12 @@
2
  */
3
 #define PW_TYPE_INTERFACE_ClientNode       PW_TYPE_INFO_INTERFACE_BASE "ClientNode"
4
 
5
-#define PW_VERSION_CLIENT_NODE         5
6
+/*
7
+ * version 4: new port_set_mix_info event added
8
+ * version 5: driver nodes are scheduled on the client
9
+ * version 6: client needs to set activation INACTIVE -> FINISHED
10
+ */
11
+#define PW_VERSION_CLIENT_NODE         6
12
 struct pw_client_node;
13
 
14
 #define PW_EXTENSION_MODULE_CLIENT_NODE        PIPEWIRE_MODULE_PREFIX "module-client-node"
15
pipewire-1.0.1.tar.bz2/src/pipewire/extensions/meson.build -> pipewire-1.2.0.tar.gz/src/pipewire/extensions/meson.build Changed
9
 
1
@@ -11,6 +11,7 @@
2
   'metadata.h',
3
   'profiler.h',
4
   'protocol-native.h',
5
+  'security-context.h',
6
   'session-manager.h',
7
 
8
 
9
pipewire-1.2.0.tar.gz/src/pipewire/extensions/security-context.h Added
120
 
1
@@ -0,0 +1,118 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#ifndef PIPEWIRE_EXT_SECURITY_CONTEXT_H
7
+#define PIPEWIRE_EXT_SECURITY_CONTEXT_H
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+#include <spa/utils/defs.h>
14
+
15
+/** \defgroup pw_security_context Security Context
16
+ * Security Context interface
17
+ */
18
+
19
+/**
20
+ * \addtogroup pw_security_context
21
+ * \{
22
+ */
23
+#define PW_TYPE_INTERFACE_SecurityContext      PW_TYPE_INFO_INTERFACE_BASE "SecurityContext"
24
+
25
+#define PW_SECURITY_CONTEXT_PERM_MASK          PW_PERM_RWX
26
+
27
+#define PW_VERSION_SECURITY_CONTEXT            3
28
+struct pw_security_context;
29
+
30
+#define PW_EXTENSION_MODULE_SECURITY_CONTEXT       PIPEWIRE_MODULE_PREFIX "module-security-context"
31
+
32
+#define PW_SECURITY_CONTEXT_EVENT_NUM          0
33
+
34
+
35
+/** \ref pw_security_context events */
36
+struct pw_security_context_events {
37
+#define PW_VERSION_SECURITY_CONTEXT_EVENTS     0
38
+   uint32_t version;
39
+};
40
+
41
+#define PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER        0
42
+#define PW_SECURITY_CONTEXT_METHOD_CREATE      1
43
+#define PW_SECURITY_CONTEXT_METHOD_NUM         2
44
+
45
+/** \ref pw_security_context methods */
46
+struct pw_security_context_methods {
47
+#define PW_VERSION_SECURITY_CONTEXT_METHODS        0
48
+   uint32_t version;
49
+
50
+   int (*add_listener) (void *object,
51
+           struct spa_hook *listener,
52
+           const struct pw_security_context_events *events,
53
+           void *data);
54
+
55
+   /**
56
+    * Create a new security context
57
+    *
58
+    * Creates a new security context with a socket listening FD.
59
+    * PipeWire will accept new client connections on listen_fd.
60
+    *
61
+    * listen_fd must be ready to accept new connections when this request is
62
+    * sent by the client. In other words, the client must call bind(2) and
63
+    * listen(2) before sending the FD.
64
+    *
65
+    * close_fd is a FD closed by the client when PipeWire should stop
66
+    * accepting new connections on listen_fd.
67
+    *
68
+    * PipeWire must continue to accept connections on listen_fd when
69
+    * the client which created the security context disconnects.
70
+    *
71
+    * After sending this request, closing listen_fd and close_fd remains the
72
+    * only valid operation on them.
73
+    *
74
+    * \param listen_fd the fd to listen on for new connections
75
+    * \param close_fd the fd used to stop listening
76
+    * \param props extra properties. These will be copied on the client
77
+    *     that connects through this context.
78
+    *
79
+    * Some properties to set:
80
+    *
81
+    *  - pipewire.sec.engine with the engine name.
82
+    *  - pipewire.sec.app-id with the application id, this is an opaque,
83
+    *      engine specific id for an application
84
+    *  - pipewire.sec.instance-id with the instance id, this is an opaque,
85
+    *      engine specific id for a running instance of an application.
86
+    *
87
+    * See https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/security-context/engines.md
88
+    * For a list of engine names and the properties to set.
89
+    *
90
+    * This requires X and W permissions on the security_context.
91
+    */
92
+   int (*create) (void *object,
93
+           int listen_fd,
94
+           int close_fd,
95
+           const struct spa_dict *props);
96
+};
97
+
98
+
99
+#define pw_security_context_method(o,method,version,...)       \
100
+({                                 \
101
+   int _res = -ENOTSUP;                        \
102
+   spa_interface_call_res((struct spa_interface*)o,        \
103
+           struct pw_security_context_methods, _res,   \
104
+           method, version, ##__VA_ARGS__);        \
105
+   _res;                               \
106
+})
107
+
108
+#define pw_security_context_add_listener(c,...)    pw_security_context_method(c,add_listener,0,__VA_ARGS__)
109
+#define pw_security_context_create(c,...)  pw_security_context_method(c,create,0,__VA_ARGS__)
110
+
111
+/**
112
+ * \}
113
+ */
114
+
115
+#ifdef __cplusplus
116
+}  /* extern "C" */
117
+#endif
118
+
119
+#endif /* PIPEWIRE_EXT_SECURITY_CONTEXT_H */
120
pipewire-1.0.1.tar.bz2/src/pipewire/filter.c -> pipewire-1.2.0.tar.gz/src/pipewire/filter.c Changed
360
 
1
@@ -12,13 +12,13 @@
2
 #include <spa/param/props.h>
3
 #include <spa/node/io.h>
4
 #include <spa/node/utils.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/ringbuffer.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/pod/filter.h>
9
 #include <spa/pod/dynamic.h>
10
 #include <spa/debug/types.h>
11
 
12
-#include <pipewire/cleanup.h>
13
 #include "pipewire/pipewire.h"
14
 #include "pipewire/filter.h"
15
 #include "pipewire/private.h"
16
@@ -32,8 +32,6 @@
17
 
18
 static bool mlock_warned = false;
19
 
20
-static uint32_t mappable_dataTypes = (1<<SPA_DATA_MemFd);
21
-
22
 struct buffer {
23
    struct pw_buffer this;
24
    uint32_t id;
25
@@ -116,12 +114,6 @@
26
    struct spa_node impl_node;
27
    struct spa_hook_list hooks;
28
    struct spa_callbacks callbacks;
29
-   struct spa_io_clock *clock;
30
-   struct spa_io_position *position;
31
-
32
-   struct {
33
-       struct spa_io_position *position;
34
-   } rt;
35
 
36
    struct spa_list port_list;
37
    struct pw_map ports2;
38
@@ -152,8 +144,6 @@
39
    unsigned int drained:1;
40
    unsigned int allow_mlock:1;
41
    unsigned int warn_mlock:1;
42
-   unsigned int process_rt:1;
43
-   unsigned int driving:1;
44
    unsigned int trigger:1;
45
    int in_emit_param_changed;
46
 };
47
@@ -218,28 +208,31 @@
48
    pw_log_debug("dataType: %u", dataType);
49
    if (dataType & (1u << SPA_DATA_MemPtr)) {
50
        SPA_POD_VALUE(struct spa_pod_int, &vals0) =
51
-           dataType | mappable_dataTypes;
52
+           dataType | (1<<SPA_DATA_MemFd);
53
        pw_log_debug("Change dataType: %u -> %u", dataType,
54
                SPA_POD_VALUE(struct spa_pod_int, &vals0));
55
    }
56
 }
57
 
58
-static struct param *add_param(struct filter *impl, struct port *port,
59
+static int add_param(struct filter *impl, struct port *port,
60
        uint32_t id, uint32_t flags, const struct spa_pod *param)
61
 {
62
    struct param *p;
63
    int idx;
64
 
65
-   if (param == NULL || !spa_pod_is_object(param)) {
66
-       errno = EINVAL;
67
-       return NULL;
68
-   }
69
+   if (param != NULL && !spa_pod_is_object(param))
70
+       return -EINVAL;
71
+   if (param == NULL || !spa_pod_object_has_props((struct spa_pod_object*)param))
72
+       return 0;
73
+
74
+   pw_log_pod(SPA_LOG_LEVEL_DEBUG, param);
75
+
76
    if (id == SPA_ID_INVALID)
77
        id = SPA_POD_OBJECT_ID(param);
78
 
79
    p = malloc(sizeof(struct param) + SPA_POD_SIZE(param));
80
    if (p == NULL)
81
-       return NULL;
82
+       return -errno;
83
 
84
    if (id == SPA_PARAM_ProcessLatency && port == NULL)
85
        spa_process_latency_parse(param, &impl->process_latency);
86
@@ -275,7 +268,7 @@
87
            impl->paramsidx.user++;
88
        }
89
    }
90
-   return p;
91
+   return 0;
92
 }
93
 
94
 static void clear_params(struct filter *impl, struct port *port, uint32_t id)
95
@@ -490,38 +483,12 @@
96
    return 0;
97
 }
98
 
99
-static int
100
-do_set_position(struct spa_loop *loop,
101
-       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
102
-{
103
-   struct filter *impl = user_data;
104
-   impl->rt.position = impl->position;
105
-   return 0;
106
-}
107
-
108
 static int impl_set_io(void *object, uint32_t id, void *data, size_t size)
109
 {
110
    struct filter *impl = object;
111
 
112
    pw_log_debug("%p: io %d %p/%zd", impl, id, data, size);
113
 
114
-   switch(id) {
115
-   case SPA_IO_Clock:
116
-       if (data && size >= sizeof(struct spa_io_clock))
117
-           impl->clock = data;
118
-       else
119
-           impl->clock = NULL;
120
-       break;
121
-   case SPA_IO_Position:
122
-       if (data && size >= sizeof(struct spa_io_position))
123
-           impl->position = data;
124
-       else
125
-           impl->position = NULL;
126
-       pw_loop_invoke(impl->data_loop,
127
-           do_set_position, 1, NULL, 0, true, impl);
128
-       break;
129
-   }
130
-   impl->driving = impl->clock && impl->position && impl->position->clock.id == impl->clock->id;
131
    pw_filter_emit_io_changed(&impl->this, NULL, id, data, size);
132
 
133
    return 0;
134
@@ -702,10 +669,8 @@
135
            }
136
            continue;
137
        }
138
-       if (add_param(impl, port, id, 0, paramsi) == NULL) {
139
-           res = -errno;
140
+       if ((res = add_param(impl, port, id, 0, paramsi)) < 0)
141
            break;
142
-       }
143
    }
144
    if (port != NULL && update_latency) {
145
        uint8_t buffer4096;
146
@@ -779,9 +744,11 @@
147
        if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) {
148
            for (j = 0; j < b->this.buffer->n_datas; j++) {
149
                struct spa_data *d = &b->this.buffer->datasj;
150
-               pw_log_debug("%p: clear buffer %d mem",
151
-                       impl, b->id);
152
-               unmap_data(impl, d);
153
+               if (SPA_FLAG_IS_SET(d->flags, SPA_DATA_FLAG_MAPPABLE)) {
154
+                   pw_log_debug("%p: clear buffer %d mem",
155
+                           impl, b->id);
156
+                   unmap_data(impl, d);
157
+               }
158
            }
159
        }
160
    }
161
@@ -944,7 +911,7 @@
162
        if (SPA_FLAG_IS_SET(impl_flags, PW_FILTER_PORT_FLAG_MAP_BUFFERS)) {
163
            for (j = 0; j < buffersi->n_datas; j++) {
164
                struct spa_data *d = &buffersi->datasj;
165
-               if ((mappable_dataTypes & (1<<d->type)) > 0) {
166
+               if (SPA_FLAG_IS_SET(d->flags, SPA_DATA_FLAG_MAPPABLE)) {
167
                    if ((res = map_data(impl, d, prot)) < 0)
168
                        return res;
169
                    SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
170
@@ -954,6 +921,8 @@
171
                    return -EINVAL;
172
                }
173
                buf_size += d->maxsize;
174
+               pw_log_debug("%p:  data:%d type:%d flags:%08x size:%d", filter, j,
175
+                       d->type, d->flags, d->maxsize);
176
            }
177
 
178
            if (size > 0 && buf_size != size) {
179
@@ -962,7 +931,7 @@
180
            } else
181
                size = buf_size;
182
        }
183
-       pw_log_debug("%p: got buffer %d %d datas, mapped size %d", filter, i,
184
+       pw_log_debug("%p: got buffer id:%d datas:%d mapped size %d", filter, i,
185
                buffersi->n_datas, size);
186
    }
187
 
188
@@ -1000,28 +969,12 @@
189
    return 0;
190
 }
191
 
192
-static int
193
-do_call_process(struct spa_loop *loop,
194
-                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
195
-{
196
-   struct filter *impl = user_data;
197
-   struct pw_filter *filter = &impl->this;
198
-   pw_log_trace("%p: do process", filter);
199
-   pw_filter_emit_process(filter, impl->position);
200
-   return 0;
201
-}
202
-
203
 static void call_process(struct filter *impl)
204
 {
205
    pw_log_trace_fp("%p: call process", impl);
206
-   if (SPA_FLAG_IS_SET(impl->flags, PW_FILTER_FLAG_RT_PROCESS)) {
207
-       if (impl->rt_callbacks.funcs)
208
-           spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_filter_events,
209
-                   process, 0, impl->rt.position);
210
-   } else {
211
-       pw_loop_invoke(impl->main_loop,
212
-           do_call_process, 1, NULL, 0, false, impl);
213
-   }
214
+   if (impl->rt_callbacks.funcs)
215
+       spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_filter_events,
216
+               process, 0, impl->this.node->rt.position);
217
 }
218
 
219
 static int
220
@@ -1049,7 +1002,7 @@
221
    bool drained = true;
222
    int res = 0;
223
 
224
-   pw_log_trace_fp("%p: do process %p", impl, impl->rt.position);
225
+   pw_log_trace_fp("%p: do process %p", impl, impl->this.node->rt.position);
226
 
227
    /** first dequeue and recycle buffers */
228
    spa_list_for_each(p, &impl->port_list, link) {
229
@@ -1254,10 +1207,12 @@
230
    spa_hook_list_init(&impl->hooks);
231
    this->properties = props;
232
 
233
-   if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL && extra) {
234
-       str = pw_properties_get(extra, PW_KEY_APP_NAME);
235
-       if (str == NULL)
236
-           str = pw_properties_get(extra, PW_KEY_APP_PROCESS_BINARY);
237
+   if ((str = pw_properties_get(props, PW_KEY_NODE_NAME)) == NULL) {
238
+       if (extra) {
239
+           str = pw_properties_get(extra, PW_KEY_APP_NAME);
240
+           if (str == NULL)
241
+               str = pw_properties_get(extra, PW_KEY_APP_PROCESS_BINARY);
242
+       }
243
        if (str == NULL)
244
            str = name;
245
        pw_properties_set(props, PW_KEY_NODE_NAME, str);
246
@@ -1605,8 +1560,6 @@
247
    pw_log_debug("%p: connect", filter);
248
    impl->flags = flags;
249
 
250
-   impl->process_rt = SPA_FLAG_IS_SET(flags, PW_FILTER_FLAG_RT_PROCESS);
251
-
252
    impl->warn_mlock = pw_properties_get_bool(filter->properties, "mem.warn-mlock", impl->warn_mlock);
253
 
254
    impl->impl_node.iface = SPA_INTERFACE_INIT(
255
@@ -1623,7 +1576,7 @@
256
    impl->info.max_input_ports = UINT32_MAX;
257
    impl->info.max_output_ports = UINT32_MAX;
258
    impl->info.flags = SPA_NODE_FLAG_RT;
259
-   if (!impl->process_rt || SPA_FLAG_IS_SET(flags, PW_FILTER_FLAG_ASYNC))
260
+   if (SPA_FLAG_IS_SET(flags, PW_FILTER_FLAG_ASYNC))
261
        impl->info.flags |= SPA_NODE_FLAG_ASYNC;
262
    impl->info.props = &filter->properties->dict;
263
    impl->paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, 0);
264
@@ -1636,15 +1589,18 @@
265
    impl->info.change_mask = impl->change_mask_all;
266
 
267
    clear_params(impl, NULL, SPA_ID_INVALID);
268
-   for (i = 0; i < n_params; i++) {
269
+   for (i = 0; i < n_params; i++)
270
        add_param(impl, NULL, SPA_ID_INVALID, 0, paramsi);
271
-   }
272
 
273
    impl->disconnecting = false;
274
    impl->draining = false;
275
-   impl->driving = false;
276
    filter_set_state(filter, PW_FILTER_STATE_CONNECTING, 0, NULL);
277
 
278
+   if (!SPA_FLAG_IS_SET(flags, PW_FILTER_FLAG_RT_PROCESS)) {
279
+       if (pw_properties_get(filter->properties, PW_KEY_NODE_LOOP_CLASS) == NULL)
280
+           pw_properties_set(filter->properties, PW_KEY_NODE_LOOP_CLASS, "main");
281
+       pw_properties_set(filter->properties, PW_KEY_NODE_ASYNC, "true");
282
+   }
283
    if (flags & PW_FILTER_FLAG_DRIVER)
284
        pw_properties_set(filter->properties, PW_KEY_NODE_DRIVER, "true");
285
    if (flags & PW_FILTER_FLAG_TRIGGER) {
286
@@ -1662,7 +1618,7 @@
287
        struct spa_fraction q;
288
        if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
289
            pw_properties_setf(filter->properties, PW_KEY_NODE_FORCE_RATE,
290
-                   "1/%u", q.denom);
291
+                   "%u", q.denom);
292
            pw_properties_setf(filter->properties, PW_KEY_NODE_FORCE_QUANTUM,
293
                    "%u", q.num);
294
        }
295
@@ -1837,6 +1793,9 @@
296
    if ((p = alloc_port(impl, direction, port_data_size)) == NULL)
297
        goto error_cleanup;
298
 
299
+   if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL)
300
+       pw_properties_setf(props, PW_KEY_PORT_GROUP, "stream.%u", p->id);
301
+
302
    p->props = props;
303
    p->flags = flags;
304
 
305
@@ -1974,7 +1933,7 @@
306
 int pw_filter_get_time(struct pw_filter *filter, struct pw_time *time)
307
 {
308
    struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
309
-   struct spa_io_position *p = impl->position;
310
+   struct spa_io_position *p = filter->node->rt.position;
311
 
312
    if (SPA_LIKELY(p != NULL)) {
313
        impl->time.now = p->clock.nsec;
314
@@ -1994,6 +1953,21 @@
315
 }
316
 
317
 SPA_EXPORT
318
+uint64_t pw_filter_get_nsec(struct pw_filter *filter)
319
+{
320
+   struct timespec ts;
321
+   clock_gettime(CLOCK_MONOTONIC, &ts);
322
+   return SPA_TIMESPEC_TO_NSEC(&ts);
323
+}
324
+
325
+SPA_EXPORT
326
+struct pw_loop *pw_filter_get_data_loop(struct pw_filter *filter)
327
+{
328
+   struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
329
+   return impl->data_loop;
330
+}
331
+
332
+SPA_EXPORT
333
 struct pw_buffer *pw_filter_dequeue_buffer(void *port_data)
334
 {
335
    struct port *p = SPA_CONTAINER_OF(port_data, struct port, user_data);
336
@@ -2083,8 +2057,7 @@
337
 SPA_EXPORT
338
 bool pw_filter_is_driving(struct pw_filter *filter)
339
 {
340
-   struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
341
-   return impl->driving;
342
+   return filter->node->driving;
343
 }
344
 
345
 static int
346
@@ -2116,11 +2089,11 @@
347
    struct filter *impl = SPA_CONTAINER_OF(filter, struct filter, this);
348
    int res = 0;
349
 
350
-   pw_log_trace_fp("%p: driving:%d", impl, impl->driving);
351
+   pw_log_trace_fp("%p: driving:%d", impl, filter->node->driving);
352
 
353
    if (impl->trigger) {
354
        pw_impl_node_trigger(filter->node);
355
-   } else if (impl->driving) {
356
+   } else if (filter->node->driving) {
357
        res = pw_loop_invoke(impl->data_loop,
358
            do_trigger_process, 1, NULL, 0, false, impl);
359
    } else {
360
pipewire-1.0.1.tar.bz2/src/pipewire/filter.h -> pipewire-1.2.0.tar.gz/src/pipewire/filter.h Changed
33
 
1
@@ -120,13 +120,14 @@
2
 
3
 enum pw_filter_port_flags {
4
    PW_FILTER_PORT_FLAG_NONE        = 0,        /**< no flags */
5
-   PW_FILTER_PORT_FLAG_MAP_BUFFERS     = (1 << 0), /**< mmap the buffers except DmaBuf */
6
+   PW_FILTER_PORT_FLAG_MAP_BUFFERS     = (1 << 0), /**< mmap the buffers except DmaBuf that is not
7
+                                 *  explicitly marked as mappable. */
8
    PW_FILTER_PORT_FLAG_ALLOC_BUFFERS   = (1 << 1), /**< the application will allocate buffer
9
                                  *  memory. In the add_buffer event, the
10
                                  *  data of the buffer should be set */
11
 };
12
 
13
-/** Create a new unconneced \ref pw_filter
14
+/** Create a new unconnected \ref pw_filter
15
  * \return a newly allocated \ref pw_filter */
16
 struct pw_filter *
17
 pw_filter_new(struct pw_core *core,        /**< a \ref pw_core */
18
@@ -214,6 +215,14 @@
19
 SPA_DEPRECATED
20
 int pw_filter_get_time(struct pw_filter *filter, struct pw_time *time);
21
 
22
+/** Get the current time in nanoseconds. This value can be compared with
23
+ * the nsec value in the spa_io_position. Since 1.1.0 */
24
+uint64_t pw_filter_get_nsec(struct pw_filter *filter);
25
+
26
+/** Get the data loop that is doing the processing of this filter. This loop
27
+ * is assigned after pw_filter_connect().  * Since 1.1.0 */
28
+struct pw_loop *pw_filter_get_data_loop(struct pw_filter *filter);
29
+
30
 /** Get a buffer that can be filled for output ports or consumed
31
  * for input ports.  */
32
 struct pw_buffer *pw_filter_dequeue_buffer(void *port_data);
33
pipewire-1.0.1.tar.bz2/src/pipewire/global.c -> pipewire-1.2.0.tar.gz/src/pipewire/global.c Changed
24
 
1
@@ -54,6 +54,7 @@
2
 {
3
    struct impl *impl;
4
    struct pw_global *this;
5
+   uint64_t serial;
6
    int res;
7
 
8
    if (properties == NULL)
9
@@ -87,6 +88,14 @@
10
    spa_list_init(&this->resource_list);
11
    spa_hook_list_init(&this->listener_list);
12
 
13
+   serial = pw_global_get_serial(this);
14
+   res = pw_properties_setf(properties, PW_KEY_OBJECT_SERIAL, "%" PRIu64, serial);
15
+   if (res < 0) {
16
+       pw_global_destroy(this);
17
+       errno = -res;
18
+       return NULL;
19
+   }
20
+
21
    pw_log_debug("%p: new %s %d", this, this->type, this->id);
22
 
23
    return this;
24
pipewire-1.0.1.tar.bz2/src/pipewire/impl-client.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-client.c Changed
63
 
1
@@ -117,15 +117,16 @@
2
    struct pw_impl_client *sender = resource->client;
3
    struct pw_impl_client *client = data->client;
4
    struct error_data d = { id, res, error };
5
-   struct pw_global *global;
6
 
7
    /* Check the global id provided by sender refers to a registered global
8
     * known to the sender.
9
     */
10
-   if ((global = pw_context_find_global(resource->context, id)) == NULL)
11
-       goto error_no_id;
12
-   if (sender->recv_generation != 0 && global->generation > sender->recv_generation)
13
-       goto error_stale_id;
14
+   if (pw_context_find_global(resource->context, id) == NULL) {
15
+       if (errno == ESTALE)
16
+           goto error_stale_id;
17
+       else
18
+           goto error_no_id;
19
+   }
20
 
21
    pw_log_debug("%p: sender %p: error for global %u", client, sender, id);
22
    pw_map_for_each(&client->objects, error_resource, &d);
23
@@ -136,8 +137,8 @@
24
    pw_resource_errorf(resource, -ENOENT, "no global %u", id);
25
    return -ENOENT;
26
 error_stale_id:
27
-   pw_log_debug("%p: sender %p: error for stale global %u generation:%"PRIu64" recv-generation:%"PRIu64,
28
-           client, sender, id, global->generation, sender->recv_generation);
29
+   pw_log_debug("%p: sender %p: error for stale global %u recv-generation:%"PRIu64,
30
+           client, sender, id, sender->recv_generation);
31
    pw_resource_errorf(resource, -ESTALE, "no global %u any more", id);
32
    return -ESTALE;
33
 }
34
@@ -381,7 +382,7 @@
35
    if (client->core_resource) {
36
        pw_core_resource_add_mem(client->core_resource,
37
                block->id, block->type, block->fd,
38
-               block->flags & PW_MEMBLOCK_FLAG_READWRITE);
39
+               block->flags & (PW_MEMBLOCK_FLAG_READWRITE | PW_MEMBLOCK_FLAG_UNMAPPABLE));
40
    }
41
 }
42
 
43
@@ -518,7 +519,6 @@
44
               struct pw_properties *properties)
45
 {
46
    static const char * const keys = {
47
-       PW_KEY_OBJECT_SERIAL,
48
        PW_KEY_MODULE_ID,
49
        PW_KEY_PROTOCOL,
50
        PW_KEY_SEC_PID,
51
@@ -804,10 +804,7 @@
52
    uint32_t perms;
53
 
54
    if ((global = pw_context_find_global(context, global_id)) == NULL)
55
-       return -ENOENT;
56
-
57
-   if (client->recv_generation != 0 && global->generation > client->recv_generation)
58
-       return -ESTALE;
59
+       return (errno == ESTALE) ? -ESTALE : -ENOENT;
60
 
61
    perms = pw_global_get_permissions(global, client);
62
    if ((perms & permissions) != permissions)
63
pipewire-1.0.1.tar.bz2/src/pipewire/impl-core.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-core.c Changed
79
 
1
@@ -33,17 +33,18 @@
2
    struct pw_global *global;
3
    uint32_t permissions, new_id = user_data_size;
4
 
5
-   if ((global = pw_context_find_global(context, id)) == NULL)
6
-       goto error_no_id;
7
+   if ((global = pw_context_find_global(context, id)) == NULL) {
8
+       if (errno == ESTALE)
9
+           goto error_stale_id;
10
+       else
11
+           goto error_no_id;
12
+   }
13
 
14
    permissions = pw_global_get_permissions(global, client);
15
 
16
    if (!PW_PERM_IS_R(permissions))
17
        goto error_no_id;
18
 
19
-   if (resource->client->recv_generation != 0 && global->generation > resource->client->recv_generation)
20
-       goto error_stale_id;
21
-
22
    if (!spa_streq(global->type, type))
23
        goto error_wrong_interface;
24
 
25
@@ -57,8 +58,8 @@
26
 
27
 error_stale_id:
28
    pw_log_debug("registry %p: not binding stale global "
29
-           "id %u to %u, generation:%"PRIu64" recv-generation:%"PRIu64,
30
-           resource, id, new_id, global->generation, resource->client->recv_generation);
31
+           "id %u to %u recv-generation:%"PRIu64,
32
+           resource, id, new_id, resource->client->recv_generation);
33
    pw_resource_errorf_id(resource, new_id, -ESTALE, "no global %u any more", id);
34
    goto error_exit_clean;
35
 error_no_id:
36
@@ -88,17 +89,18 @@
37
    uint32_t permissions;
38
    int res;
39
 
40
-   if ((global = pw_context_find_global(context, id)) == NULL)
41
-       goto error_no_id;
42
+   if ((global = pw_context_find_global(context, id)) == NULL) {
43
+       if (errno == ESTALE)
44
+           goto error_stale_id;
45
+       else
46
+           goto error_no_id;
47
+   }
48
 
49
    permissions = pw_global_get_permissions(global, client);
50
 
51
    if (!PW_PERM_IS_R(permissions))
52
        goto error_no_id;
53
 
54
-   if (resource->client->recv_generation != 0 && global->generation > resource->client->recv_generation)
55
-       goto error_stale_id;
56
-
57
    if (id == PW_ID_CORE || !PW_PERM_IS_X(permissions))
58
        goto error_not_allowed;
59
 
60
@@ -109,8 +111,8 @@
61
 
62
 error_stale_id:
63
    pw_log_debug("registry %p: not destroying stale global "
64
-           "id %u, generation:%"PRIu64" recv-generation:%"PRIu64,
65
-           resource, id, global->generation, resource->client->recv_generation);
66
+           "id %u, recv-generation:%"PRIu64,
67
+           resource, id, resource->client->recv_generation);
68
    pw_resource_errorf(resource, -ESTALE, "no global %u any more", id);
69
    res = -ESTALE;
70
    goto error_exit;
71
@@ -571,7 +573,6 @@
72
             struct pw_properties *properties)
73
 {
74
    static const char * const keys = {
75
-       PW_KEY_OBJECT_SERIAL,
76
        PW_KEY_USER_NAME,
77
        PW_KEY_HOST_NAME,
78
        PW_KEY_CORE_NAME,
79
pipewire-1.0.1.tar.bz2/src/pipewire/impl-device.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-device.c Changed
313
 
1
@@ -9,6 +9,7 @@
2
 #include <spa/pod/filter.h>
3
 #include <spa/pod/dynamic.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/json-pod.h>
6
 
7
 #include "pipewire/impl.h"
8
 #include "pipewire/private.h"
9
@@ -66,6 +67,7 @@
10
 #define OBJECT_DEVICE  1
11
    uint32_t type;
12
    struct spa_handle *handle;
13
+   struct spa_handle *subhandle;
14
    void *object;
15
    struct spa_hook listener;
16
 };
17
@@ -94,22 +96,53 @@
18
    }
19
 }
20
 
21
-static void object_register(struct object_data *od)
22
+static void object_register(struct object_data *od, uint32_t device_id)
23
 {
24
+   char id64;
25
+   struct spa_dict_item it1;
26
+   snprintf(id, sizeof(id), "%u", device_id);
27
+
28
+   it0 = SPA_DICT_ITEM_INIT(PW_KEY_DEVICE_ID, id);
29
    switch (od->type) {
30
    case OBJECT_NODE:
31
+       pw_impl_node_update_properties(od->object, &SPA_DICT_INIT_ARRAY(it));
32
        pw_impl_node_register(od->object, NULL);
33
        pw_impl_node_set_active(od->object, true);
34
        break;
35
    case OBJECT_DEVICE:
36
+       pw_impl_device_update_properties(od->object, &SPA_DICT_INIT_ARRAY(it));
37
        pw_impl_device_register(od->object, NULL);
38
        break;
39
    }
40
 }
41
 
42
+struct match {
43
+   struct pw_impl_device *device;
44
+   int count;
45
+};
46
+#define MATCH_INIT(n) ((struct match){ .device = (n) })
47
+
48
+static int execute_match(void *data, const char *location, const char *action,
49
+       const char *val, size_t len)
50
+{
51
+   struct match *match = data;
52
+   struct pw_impl_device *this = match->device;
53
+   if (spa_streq(action, "update-props")) {
54
+       match->count += pw_properties_update_string(this->properties, val, len);
55
+       this->info.props = &this->properties->dict;
56
+   }
57
+   return 1;
58
+}
59
+
60
 static void check_properties(struct pw_impl_device *device)
61
 {
62
+   struct pw_context *context = device->context;
63
    const char *str;
64
+   struct match match;
65
+
66
+   match = MATCH_INIT(device);
67
+   pw_context_conf_section_match_rules(context, "device.rules",
68
+           &device->properties->dict, execute_match, &match);
69
 
70
    if ((str = pw_properties_get(device->properties, PW_KEY_DEVICE_NAME)) &&
71
        (device->name == NULL || !spa_streq(str, device->name))) {
72
@@ -352,6 +385,16 @@
73
    return res;
74
 }
75
 
76
+SPA_EXPORT
77
+int pw_impl_device_set_param(struct pw_impl_device *device,
78
+       uint32_t id, uint32_t flags, const struct spa_pod *param)
79
+{
80
+   pw_log_debug("%p: set_param id:%d (%s) flags:%08x param:%p", device, id,
81
+           spa_debug_type_find_name(spa_type_param, id), flags, param);
82
+   return spa_device_set_param(device->device, id, flags, param);
83
+}
84
+
85
+
86
 static int reply_param(void *data, int seq, uint32_t id,
87
        uint32_t index, uint32_t next, struct spa_pod *param)
88
 {
89
@@ -538,7 +581,6 @@
90
               struct pw_properties *properties)
91
 {
92
    static const char * const keys = {
93
-       PW_KEY_OBJECT_SERIAL,
94
        PW_KEY_OBJECT_PATH,
95
        PW_KEY_MODULE_ID,
96
        PW_KEY_FACTORY_ID,
97
@@ -584,7 +626,7 @@
98
    pw_global_register(device->global);
99
 
100
    spa_list_for_each(od, &device->object_list, link)
101
-       object_register(od);
102
+       object_register(od, device->info.id);
103
 
104
    return 0;
105
 
106
@@ -603,6 +645,8 @@
107
 {
108
    struct object_data *od = data;
109
    pw_unload_spa_handle(od->handle);
110
+   if (od->subhandle)
111
+       pw_unload_spa_handle(od->subhandle);
112
 }
113
 
114
 static const struct pw_impl_node_events node_object_events = {
115
@@ -647,11 +691,10 @@
116
 
117
    pw_log_debug("%p: updated %d properties", device, changed);
118
 
119
-   if (!changed)
120
-       return 0;
121
-
122
-   device->info.change_mask |= PW_DEVICE_CHANGE_MASK_PROPS;
123
-
124
+   if (changed) {
125
+       check_properties(device);
126
+       device->info.change_mask |= PW_DEVICE_CHANGE_MASK_PROPS;
127
+   }
128
    return changed;
129
 }
130
 
131
@@ -758,37 +801,72 @@
132
        const struct spa_device_object_info *info)
133
 {
134
    struct pw_context *context = device->context;
135
-   struct spa_handle *handle;
136
-   struct pw_properties *props;
137
+   struct spa_handle *handle, *subhandle = NULL;
138
+   spa_autoptr(pw_properties) props = NULL;
139
+   const struct pw_properties *p;
140
    int res;
141
    void *iface;
142
    struct object_data *od = NULL;
143
+   const char *str;
144
 
145
    if (info->factory_name == NULL) {
146
        pw_log_debug("%p: missing factory name", device);
147
        return;
148
    }
149
 
150
-   handle = pw_context_load_spa_handle(context, info->factory_name, info->props);
151
+   props = pw_properties_new(NULL, NULL);
152
+   if (props == NULL) {
153
+       pw_log_warn("%p: allocation error: %m", device);
154
+       return;
155
+   }
156
+   if (info->props)
157
+       pw_properties_update(props, info->props);
158
+   if ((str = pw_properties_get(device->properties, "device.object.properties")))
159
+       pw_properties_update_string(props, str, strlen(str));
160
+
161
+   p = pw_context_get_properties(context);
162
+   pw_properties_set(props, "clock.quantum-limit",
163
+           pw_properties_get(p, "default.clock.quantum-limit"));
164
+
165
+   handle = pw_context_load_spa_handle(context, info->factory_name, &props->dict);
166
    if (handle == NULL) {
167
        pw_log_warn("%p: can't load handle %s: %m",
168
                device, info->factory_name);
169
-       return;
170
+       goto cleanup;
171
    }
172
 
173
    if ((res = spa_handle_get_interface(handle, info->type, &iface)) < 0) {
174
        pw_log_error("%p: can't get %s interface: %s", device, info->type,
175
                spa_strerror(res));
176
-       return;
177
+       goto cleanup;
178
    }
179
 
180
-   props = pw_properties_copy(device->properties);
181
-   if (info->props && props)
182
-       pw_properties_update(props, info->props);
183
-
184
    if (spa_streq(info->type, SPA_TYPE_INTERFACE_Node)) {
185
        struct pw_impl_node *node;
186
-       node = pw_context_create_node(context, props, sizeof(struct object_data));
187
+
188
+       if ((str = pw_properties_get(props, "node.adapter")) != NULL) {
189
+           char name64;
190
+           snprintf(name, sizeof(name), "%s.follower", str);
191
+                   pw_properties_setf(props, name, "pointer:%p", iface);
192
+
193
+           subhandle = handle;
194
+
195
+           handle = pw_context_load_spa_handle(context, str, &props->dict);
196
+           if (handle == NULL) {
197
+               pw_log_warn("%p: can't load handle %s: %m", device, str);
198
+               goto cleanup;
199
+           }
200
+           if ((res = spa_handle_get_interface(handle, info->type, &iface)) < 0) {
201
+               pw_log_error("%p: can't get %s interface: %s", device, info->type,
202
+                       spa_strerror(res));
203
+               goto cleanup;
204
+           }
205
+       }
206
+
207
+       node = pw_context_create_node(context, spa_steal_ptr(props),
208
+               sizeof(struct object_data));
209
+       if (node == NULL)
210
+           goto cleanup;
211
 
212
        od = pw_impl_node_get_user_data(node);
213
        od->object = node;
214
@@ -797,7 +875,10 @@
215
        pw_impl_node_set_implementation(node, iface);
216
    } else if (spa_streq(info->type, SPA_TYPE_INTERFACE_Device)) {
217
        struct pw_impl_device *dev;
218
-       dev = pw_context_create_device(context, props, sizeof(struct object_data));
219
+       dev = pw_context_create_device(context, spa_steal_ptr(props),
220
+               sizeof(struct object_data));
221
+       if (dev == NULL)
222
+           goto cleanup;
223
 
224
        od = pw_impl_device_get_user_data(dev);
225
        od->object = dev;
226
@@ -806,17 +887,24 @@
227
        pw_impl_device_set_implementation(dev, iface);
228
    } else {
229
        pw_log_warn("%p: unknown type %s", device, info->type);
230
-       pw_properties_free(props);
231
+       goto cleanup;
232
    }
233
-
234
    if (od) {
235
        od->id = id;
236
        od->handle = handle;
237
+       od->subhandle = subhandle;
238
        spa_list_append(&device->object_list, &od->link);
239
        if (device->global)
240
-           object_register(od);
241
+           object_register(od, device->info.id);
242
    }
243
    return;
244
+
245
+cleanup:
246
+   if (handle)
247
+       pw_unload_spa_handle(handle);
248
+   if (subhandle)
249
+       pw_unload_spa_handle(subhandle);
250
+   return;
251
 }
252
 
253
 static struct object_data *find_object(struct pw_impl_device *device, uint32_t id)
254
@@ -857,9 +945,36 @@
255
    .object_info = device_object_info,
256
 };
257
 
258
+static int handle_device_param(struct pw_impl_device *device, const char *key, const char *value)
259
+{
260
+   const struct spa_type_info *ti;
261
+   uint8_t buffer1024;
262
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
263
+   struct spa_pod *pod;
264
+   int res;
265
+
266
+   ti = spa_debug_type_find_short(spa_type_param, key);
267
+   if (ti == NULL)
268
+       return -ENOENT;
269
+
270
+   if ((res = spa_json_to_pod(&b, 0, ti, value, strlen(value))) < 0)
271
+       return res;
272
+
273
+   if ((pod = spa_pod_builder_deref(&b, 0)) == NULL)
274
+       return -ENOSPC;
275
+
276
+   if ((res = pw_impl_device_set_param(device, ti->type, 0, pod)) < 0)
277
+       return res;
278
+
279
+   return 0;
280
+}
281
+
282
 SPA_EXPORT
283
 int pw_impl_device_set_implementation(struct pw_impl_device *device, struct spa_device *spa_device)
284
 {
285
+   int res;
286
+   const struct spa_dict_item *it;
287
+
288
    pw_log_debug("%p: implementation %p", device, spa_device);
289
 
290
    if (device->device) {
291
@@ -868,10 +983,19 @@
292
        return -EEXIST;
293
    }
294
    device->device = spa_device;
295
-   spa_device_add_listener(device->device,
296
+   res = spa_device_add_listener(device->device,
297
            &device->listener, &device_events, device);
298
 
299
-   return 0;
300
+again:
301
+   spa_dict_for_each(it, &device->properties->dict) {
302
+       if (spa_strstartswith(it->key, "device.param.")) {
303
+           if ((res = handle_device_param(device, &it->key13, it->value)) < 0)
304
+               pw_log_warn("can't set device param: %s", spa_strerror(res));
305
+           pw_properties_set(device->properties, it->key, NULL);
306
+           goto again;
307
+       }
308
+   }
309
+   return res;
310
 }
311
 
312
 SPA_EXPORT
313
pipewire-1.0.1.tar.bz2/src/pipewire/impl-factory.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-factory.c Changed
9
 
1
@@ -158,7 +158,6 @@
2
             struct pw_properties *properties)
3
 {
4
    static const char * const keys = {
5
-       PW_KEY_OBJECT_SERIAL,
6
        PW_KEY_MODULE_ID,
7
        PW_KEY_FACTORY_NAME,
8
        PW_KEY_FACTORY_TYPE_NAME,
9
pipewire-1.0.1.tar.bz2/src/pipewire/impl-link.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-link.c Changed
372
 
1
@@ -44,86 +44,14 @@
2
    struct spa_hook output_node_listener;
3
    struct spa_hook output_global_listener;
4
 
5
-   struct spa_io_buffers io;
6
+   struct spa_io_buffers io2;
7
 
8
    struct pw_impl_node *inode, *onode;
9
+   bool async;
10
 };
11
 
12
 /** \endcond */
13
 
14
-static struct pw_node_peer *pw_node_peer_ref(struct pw_impl_node *onode, struct pw_impl_node *inode)
15
-{
16
-   struct pw_node_peer *peer;
17
-
18
-   spa_list_for_each(peer, &onode->peer_list, link) {
19
-       if (peer->target.id == inode->info.id) {
20
-           pw_log_debug("exiting peer %p from %p to %p", peer, onode, inode);
21
-           peer->ref++;
22
-           return peer;
23
-       }
24
-   }
25
-   peer = calloc(1, sizeof(*peer));
26
-   if (peer == NULL)
27
-       return NULL;
28
-
29
-   peer->ref = 1;
30
-   peer->output = onode;
31
-   peer->active_count = 0;
32
-   copy_target(&peer->target, &inode->rt.target);
33
-   peer->target.flags = PW_NODE_TARGET_PEER;
34
-
35
-   spa_list_append(&onode->peer_list, &peer->link);
36
-   pw_log_debug("new peer %p from %p to %p", peer, onode, inode);
37
-   pw_impl_node_emit_peer_added(onode, inode);
38
-
39
-   return peer;
40
-}
41
-
42
-static void pw_node_peer_unref(struct pw_node_peer *peer)
43
-{
44
-   if (--peer->ref > 0)
45
-       return;
46
-   spa_list_remove(&peer->link);
47
-   pw_log_debug("remove peer %p from %p to %p", peer, peer->output, peer->target.node);
48
-   pw_impl_node_emit_peer_removed(peer->output, peer->target.node);
49
-   free(peer);
50
-}
51
-
52
-static void pw_node_peer_activate(struct pw_node_peer *peer)
53
-{
54
-   struct pw_node_activation_state *state;
55
-
56
-   state = &peer->target.activation->state0;
57
-
58
-   if (peer->active_count++ == 0) {
59
-       spa_list_append(&peer->output->rt.target_list, &peer->target.link);
60
-       if (!peer->target.active && peer->output->rt.driver_target.node != NULL) {
61
-           state->required++;
62
-           peer->target.active = true;
63
-       }
64
-   }
65
-   pw_log_trace("%p: node:%s state:%p pending:%d/%d", peer->output,
66
-           peer->target.name, state, state->pending, state->required);
67
-}
68
-
69
-static void pw_node_peer_deactivate(struct pw_node_peer *peer)
70
-{
71
-   struct pw_node_activation_state *state;
72
-   state = &peer->target.activation->state0;
73
-   if (--peer->active_count == 0) {
74
-
75
-       spa_list_remove(&peer->target.link);
76
-
77
-       if (peer->target.active) {
78
-           state->required--;
79
-           peer->target.active = false;
80
-       }
81
-   }
82
-   pw_log_trace("%p: node:%s state:%p pending:%d/%d", peer->output,
83
-           peer->target.name, state, state->pending, state->required);
84
-}
85
-
86
-
87
 static void info_changed(struct pw_impl_link *link)
88
 {
89
    struct pw_resource *resource;
90
@@ -333,6 +261,8 @@
91
    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
92
    uint32_t index, busy_id;
93
    uint32_t in_state, out_state;
94
+   struct spa_node *in_node, *out_node;
95
+   uint32_t in_port, out_port;
96
 
97
    if (this->info.state >= PW_LINK_STATE_NEGOTIATING)
98
        return 0;
99
@@ -352,10 +282,23 @@
100
 
101
    input = this->input;
102
    output = this->output;
103
+#if 0
104
+   in_node = input->mix;
105
+   in_port = this->rt.in_mix.port.port_id;
106
+   out_node = output->mix;
107
+   out_port = this->rt.out_mix.port.port_id;
108
+#else
109
+   in_node = input->node->node;
110
+   in_port = input->port_id;
111
+   out_node = output->node->node;
112
+   out_port = output->port_id;
113
+#endif
114
 
115
    /* find a common format for the ports */
116
    if ((res = pw_context_find_format(context,
117
-                   output, input, NULL, 0, NULL,
118
+                   output, SPA_ID_INVALID,
119
+                   input, SPA_ID_INVALID,
120
+                   NULL, 0, NULL,
121
                    &format, &b, &error)) < 0) {
122
        format = NULL;
123
        goto error;
124
@@ -369,8 +312,8 @@
125
    /* if output port had format and is idle, check if it changed. If so, renegotiate */
126
    if (out_state > PW_IMPL_PORT_STATE_CONFIGURE && output->node->info.state == PW_NODE_STATE_IDLE) {
127
        index = 0;
128
-       res = spa_node_port_enum_params_sync(output->node->node,
129
-               output->direction, output->port_id,
130
+       res = spa_node_port_enum_params_sync(out_node,
131
+               output->direction, out_port,
132
                SPA_PARAM_Format, &index,
133
                NULL, &current, &b);
134
        switch (res) {
135
@@ -403,8 +346,8 @@
136
    /* if input port had format and is idle, check if it changed. If so, renegotiate */
137
    if (in_state > PW_IMPL_PORT_STATE_CONFIGURE && input->node->info.state == PW_NODE_STATE_IDLE) {
138
        index = 0;
139
-       res = spa_node_port_enum_params_sync(input->node->node,
140
-               input->direction, input->port_id,
141
+       res = spa_node_port_enum_params_sync(in_node,
142
+               input->direction, in_port,
143
                SPA_PARAM_Format, &index,
144
                NULL, &current, &b);
145
        switch (res) {
146
@@ -492,12 +435,12 @@
147
    return res;
148
 
149
 error:
150
-   pw_context_debug_port_params(context, input->node->node, input->direction,
151
-           input->port_id, SPA_PARAM_EnumFormat, res,
152
-           "input format (%s)", error);
153
-   pw_context_debug_port_params(context, output->node->node, output->direction,
154
-           output->port_id, SPA_PARAM_EnumFormat, res,
155
-           "output format (%s)", error);
156
+   pw_context_debug_port_params(context, in_node,
157
+           input->direction, in_port,
158
+           SPA_PARAM_EnumFormat, res, "input format (%s)", error);
159
+   pw_context_debug_port_params(context, out_node,
160
+           output->direction, out_port,
161
+           SPA_PARAM_EnumFormat, res, "output format (%s)", error);
162
    link_update_state(this, PW_LINK_STATE_ERROR, res, error);
163
    free(format);
164
    return res;
165
@@ -532,14 +475,15 @@
166
    struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
167
    struct spa_io_buffers *io;
168
 
169
-   io = this->rt.in_mix.io;
170
+   io = this->rt.in_mix.io_data;
171
    if (io == NULL)
172
-       io = this->rt.out_mix.io;
173
+       io = this->rt.out_mix.io_data;
174
    if (io == NULL)
175
-       io = &impl->io;
176
+       io = impl->io;
177
 
178
    this->io = io;
179
-   *this->io = SPA_IO_BUFFERS_INIT;
180
+   this->io0 = SPA_IO_BUFFERS_INIT;
181
+   this->io1 = SPA_IO_BUFFERS_INIT;
182
 }
183
 
184
 static int do_allocation(struct pw_impl_link *this)
185
@@ -584,10 +528,15 @@
186
        this->rt.out_mix.have_buffers = true;
187
    } else {
188
        uint32_t flags, alloc_flags;
189
+       struct spa_node *in_node, *out_node;
190
+       uint32_t in_port, out_port;
191
 
192
        flags = 0;
193
        /* always shared buffers for the link */
194
        alloc_flags = PW_BUFFERS_FLAG_SHARED;
195
+       /* always enable async mode */
196
+       alloc_flags |= PW_BUFFERS_FLAG_ASYNC;
197
+
198
        if (output->node->remote || input->node->remote)
199
            alloc_flags |= PW_BUFFERS_FLAG_SHARED_MEM;
200
 
201
@@ -599,10 +548,21 @@
202
            SPA_FLAG_SET(alloc_flags, PW_BUFFERS_FLAG_NO_MEM);
203
            flags |= SPA_NODE_BUFFERS_FLAG_ALLOC;
204
        }
205
+#if 0
206
+       in_node = input->mix;
207
+       in_port = this->rt.in_mix.port.port_id;
208
+       out_node = output->mix;
209
+       out_port = this->rt.out_mix.port.port_id;
210
+#else
211
+       in_node = input->node->node;
212
+       in_port = input->port_id;
213
+       out_node = output->node->node;
214
+       out_port = output->port_id;
215
+#endif
216
 
217
        if ((res = pw_buffers_negotiate(this->context, alloc_flags,
218
-                       output->node->node, output->port_id,
219
-                       input->node->node, input->port_id,
220
+                       out_node, out_port,
221
+                       in_node, in_port,
222
                        &output->buffers)) < 0) {
223
            error = spa_aprintf("error alloc buffers: %s", spa_strerror(res));
224
            goto error;
225
@@ -658,21 +618,11 @@
226
    return res;
227
 }
228
 
229
-static int
230
-do_activate_link(struct spa_loop *loop,
231
-        bool async, uint32_t seq, const void *data, size_t size, void *user_data)
232
-{
233
-   struct pw_impl_link *this = user_data;
234
-   pw_log_trace("%p: activate", this);
235
-   if (this->peer)
236
-       pw_node_peer_activate(this->peer);
237
-   return 0;
238
-}
239
-
240
 int pw_impl_link_activate(struct pw_impl_link *this)
241
 {
242
    struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
243
    int res;
244
+   uint32_t io_type, io_size;
245
 
246
    pw_log_debug("%p: activate activated:%d state:%s", this, impl->activated,
247
            pw_link_state_as_string(this->info.state));
248
@@ -681,26 +631,34 @@
249
        !impl->inode->runnable || !impl->onode->runnable)
250
        return 0;
251
 
252
-   if ((res = port_set_io(this, this->input, SPA_IO_Buffers, this->io,
253
-           sizeof(struct spa_io_buffers), &this->rt.in_mix)) < 0)
254
-       return res;
255
-
256
-   if ((res = port_set_io(this, this->output, SPA_IO_Buffers, this->io,
257
-           sizeof(struct spa_io_buffers), &this->rt.out_mix)) < 0) {
258
-       port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0,
259
-               &this->rt.in_mix);
260
-       return res;
261
+   if (impl->async) {
262
+       io_type = SPA_IO_AsyncBuffers;
263
+       io_size = sizeof(struct spa_io_async_buffers);
264
+   } else {
265
+       io_type = SPA_IO_Buffers;
266
+       io_size = sizeof(struct spa_io_buffers);
267
    }
268
 
269
-   pw_loop_invoke(this->output->node->data_loop,
270
-          do_activate_link, SPA_ID_INVALID, NULL, 0, false, this);
271
+   if ((res = port_set_io(this, this->input, io_type, this->io,
272
+                   io_size, &this->rt.in_mix)) < 0)
273
+       goto error;
274
+   if ((res = port_set_io(this, this->output, io_type, this->io,
275
+                   io_size, &this->rt.out_mix)) < 0)
276
+       goto error_clean;
277
 
278
    impl->activated = true;
279
    pw_log_info("(%s) activated", this->name);
280
    link_update_state(this, PW_LINK_STATE_ACTIVE, 0, NULL);
281
 
282
    return 0;
283
+
284
+error_clean:
285
+   port_set_io(this, this->input, io_type, NULL, 0, &this->rt.in_mix);
286
+error:
287
+   pw_log_error("%p: can't activate link: %s", this, spa_strerror(res));
288
+   return res;
289
 }
290
+
291
 static void check_states(void *obj, void *user_data, int res, uint32_t id)
292
 {
293
    struct pw_impl_link *this = obj;
294
@@ -854,17 +812,6 @@
295
    return 0;
296
 }
297
 
298
-static int
299
-do_deactivate_link(struct spa_loop *loop,
300
-          bool async, uint32_t seq, const void *data, size_t size, void *user_data)
301
-{
302
-        struct pw_impl_link *this = user_data;
303
-   pw_log_trace("%p: disable out %p", this, &this->rt.out_mix);
304
-   if (this->peer)
305
-       pw_node_peer_deactivate(this->peer);
306
-   return 0;
307
-}
308
-
309
 int pw_impl_link_deactivate(struct pw_impl_link *this)
310
 {
311
    struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
312
@@ -874,9 +821,6 @@
313
    if (!impl->activated)
314
        return 0;
315
 
316
-   pw_loop_invoke(this->output->node->data_loop,
317
-              do_deactivate_link, SPA_ID_INVALID, NULL, 0, true, this);
318
-
319
    port_set_io(this, this->output, SPA_IO_Buffers, NULL, 0,
320
            &this->rt.out_mix);
321
    port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0,
322
@@ -884,7 +828,7 @@
323
 
324
    impl->activated = false;
325
    pw_log_info("(%s) deactivated", this->name);
326
-   
327
+
328
    if (this->info.state < PW_LINK_STATE_PAUSED || this->destroyed)
329
        link_update_state(this, PW_LINK_STATE_INIT, 0, NULL);
330
    else
331
@@ -1377,6 +1321,13 @@
332
    if (this->passive && str == NULL)
333
         pw_properties_set(properties, PW_KEY_LINK_PASSIVE, "true");
334
 
335
+   impl->async = (output_node->async || input_node->async) &&
336
+       SPA_FLAG_IS_SET(output->flags, PW_IMPL_PORT_FLAG_ASYNC) &&
337
+       SPA_FLAG_IS_SET(input->flags, PW_IMPL_PORT_FLAG_ASYNC);
338
+
339
+   if (impl->async)
340
+        pw_properties_set(properties, PW_KEY_LINK_ASYNC, "true");
341
+
342
    spa_hook_list_init(&this->listener_list);
343
 
344
    impl->format_filter = format_filter;
345
@@ -1406,8 +1357,6 @@
346
    spa_list_append(&output->links, &this->output_link);
347
    spa_list_append(&input->links, &this->input_link);
348
 
349
-   impl->io = SPA_IO_BUFFERS_INIT;
350
-
351
    select_io(this);
352
 
353
    if (this->feedback) {
354
@@ -1426,7 +1375,8 @@
355
    this->name = spa_aprintf("%d.%d.%d -> %d.%d.%d",
356
            output_node->info.id, output->port_id, this->rt.out_mix.port.port_id,
357
            input_node->info.id, input->port_id, this->rt.in_mix.port.port_id);
358
-   pw_log_info("(%s) (%s) -> (%s)", this->name, output_node->name, input_node->name);
359
+   pw_log_info("(%s) (%s) -> (%s) async:%04x:%04x:%d", this->name, output_node->name,
360
+           input_node->name, output->flags, input->flags, impl->async);
361
 
362
    pw_impl_port_emit_link_added(output, this);
363
    pw_impl_port_emit_link_added(input, this);
364
@@ -1496,7 +1446,6 @@
365
             struct pw_properties *properties)
366
 {
367
    static const char * const keys = {
368
-       PW_KEY_OBJECT_SERIAL,
369
        PW_KEY_OBJECT_PATH,
370
        PW_KEY_MODULE_ID,
371
        PW_KEY_FACTORY_ID,
372
pipewire-1.0.1.tar.bz2/src/pipewire/impl-metadata.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-metadata.c Changed
78
 
1
@@ -5,9 +5,9 @@
2
 #include <errno.h>
3
 
4
 #include <spa/debug/types.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/string.h>
7
 
8
-#include <pipewire/cleanup.h>
9
 #include "pipewire/impl.h"
10
 #include "pipewire/private.h"
11
 
12
@@ -353,6 +353,7 @@
13
        spa_hook_remove(&metadata->global_listener);
14
        pw_global_destroy(metadata->global);
15
    }
16
+   spa_hook_remove(&metadata->context_listener);
17
 
18
    pw_impl_metadata_emit_free(metadata);
19
    pw_log_debug("%p: free", metadata);
20
@@ -382,7 +383,9 @@
21
    struct pw_resource *resource = d->resource;
22
    struct pw_impl_client *client = pw_resource_get_client(resource);
23
 
24
-   if (pw_impl_client_check_permissions(client, subject, PW_PERM_R) >= 0)
25
+   int res = pw_impl_client_check_permissions(client, subject, PW_PERM_R);
26
+   if (res >= 0 ||
27
+           (res == -ENOENT && key == NULL && type == NULL && value == NULL))
28
        pw_metadata_resource_property(d->resource, subject, key, type, value);
29
    return 0;
30
 }
31
@@ -462,7 +465,7 @@
32
         data->impl = this;
33
         data->resource = resource;
34
 
35
-   pw_log_debug("%p: bound to %d", this, resource->id);
36
+   pw_log_debug("%p: %u bound to %d", this, id, resource->id);
37
    pw_global_add_resource(global, resource);
38
 
39
    /* listen for when the resource goes away */
40
@@ -487,6 +490,20 @@
41
    return -errno;
42
 }
43
 
44
+static void context_global_removed(void *data, struct pw_global *global)
45
+{
46
+   struct impl *impl = data;
47
+   uint32_t id = pw_global_get_id(global);
48
+   pw_log_trace("Clearing properties for global %u in %u",
49
+                id, pw_global_get_id(impl->this.global));
50
+   impl_set_property(&impl->def, id, NULL, NULL, NULL);
51
+}
52
+
53
+static const struct pw_context_events context_events = {
54
+   PW_VERSION_CONTEXT_EVENTS,
55
+   .global_removed = context_global_removed,
56
+};
57
+
58
 static void global_destroy(void *data)
59
 {
60
    struct pw_impl_metadata *metadata = data;
61
@@ -506,7 +523,6 @@
62
 {
63
    struct pw_context *context = metadata->context;
64
    static const char * const keys = {
65
-       PW_KEY_OBJECT_SERIAL,
66
        PW_KEY_MODULE_ID,
67
        PW_KEY_FACTORY_ID,
68
        PW_KEY_METADATA_NAME,
69
@@ -534,6 +550,8 @@
70
 
71
    pw_global_update_keys(metadata->global, &metadata->properties->dict, keys);
72
 
73
+   pw_context_add_listener(context, &metadata->context_listener, &context_events,
74
+                           SPA_CONTAINER_OF(metadata, struct impl, this));
75
    pw_global_add_listener(metadata->global, &metadata->global_listener, &global_events, metadata);
76
    pw_global_register(metadata->global);
77
 
78
pipewire-1.0.1.tar.bz2/src/pipewire/impl-module.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-module.c Changed
20
 
1
@@ -13,9 +13,9 @@
2
 #include <sys/stat.h>
3
 #include <errno.h>
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/string.h>
7
 
8
-#include <pipewire/cleanup.h>
9
 #include "pipewire/impl.h"
10
 #include "pipewire/private.h"
11
 
12
@@ -149,7 +149,6 @@
13
    size_t len;
14
    char path_partPATH_MAX;
15
    static const char * const keys = {
16
-       PW_KEY_OBJECT_SERIAL,
17
        PW_KEY_MODULE_NAME,
18
        NULL
19
    };
20
pipewire-1.0.1.tar.bz2/src/pipewire/impl-module.h -> pipewire-1.2.0.tar.gz/src/pipewire/impl-module.h Changed
14
 
1
@@ -45,9 +45,10 @@
2
 #define PW_VERSION_IMPL_MODULE_EVENTS  0
3
    uint32_t version;
4
 
5
-   /** The module is destroyed */
6
+   /** The module is destroyed. This is the time to unregister and
7
+    * destroy any objects created by the module. */
8
    void (*destroy) (void *data);
9
-   /** The module is freed */
10
+   /** The module is freed. This will be called after destroy() returns. */
11
    void (*free) (void *data);
12
    /** The module is initialized */
13
    void (*initialized) (void *data);
14
pipewire-1.0.1.tar.bz2/src/pipewire/impl-node.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-node.c Changed
1378
 
1
@@ -18,6 +18,7 @@
2
 #include <spa/node/utils.h>
3
 #include <spa/debug/types.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/json-pod.h>
6
 
7
 #include "pipewire/impl-node.h"
8
 #include "pipewire/private.h"
9
@@ -46,6 +47,7 @@
10
 
11
    char *group;
12
    char *link_group;
13
+   char *sync_group;
14
 };
15
 
16
 #define pw_node_resource(r,m,v,...)    pw_resource_call(r,struct pw_node_events,m,v,__VA_ARGS__)
17
@@ -68,133 +70,177 @@
18
    struct spa_hook listener;
19
 };
20
 
21
-/** \endcond */
22
-
23
-/* Called from the node data loop when a node needs to be scheduled by
24
- * the given driver. 3 things needs to happen:
25
- *
26
- * - the node is added to the driver target list and the required state
27
- *   is incremented. This makes sure the node is woken up when the driver
28
- *   starts a new cycle.
29
- * - the node needs to trigger the driver when it completes. This means
30
- *   the driver is added to the target list.
31
- * - the node targets (including the driver we added above) have their
32
- *   required state incremented.
33
- *
34
- * This code is called from the data-loop to ensure synchronization
35
- */
36
-static void add_node(struct pw_impl_node *this, struct pw_impl_node *driver)
37
+SPA_EXPORT
38
+struct pw_node_peer *pw_node_peer_ref(struct pw_impl_node *onode, struct pw_impl_node *inode)
39
 {
40
-   struct pw_node_activation_state *dstate, *nstate;
41
-   struct pw_node_target *t;
42
+   struct pw_node_peer *peer;
43
 
44
-   if (this->exported)
45
-       return;
46
-
47
-   pw_log_trace("%p: add to driver %p %p %p", this, driver,
48
-           driver->rt.target.activation, this->rt.target.activation);
49
-
50
-   /* let the driver trigger us as part of the processing cycle */
51
-   spa_list_append(&driver->rt.target_list, &this->rt.target.link);
52
-   nstate = &this->rt.target.activation->state0;
53
-   if (!this->rt.target.active) {
54
-       nstate->required++;
55
-       this->rt.target.active = true;
56
+   spa_list_for_each(peer, &onode->peer_list, link) {
57
+       if (peer->target.id == inode->info.id) {
58
+           pw_log_debug("exiting peer %p from %p to %p", peer, onode, inode);
59
+           peer->ref++;
60
+           return peer;
61
+       }
62
    }
63
+   peer = calloc(1, sizeof(*peer));
64
+   if (peer == NULL)
65
+       return NULL;
66
 
67
-   /* trigger the driver when we complete */
68
-   copy_target(&this->rt.driver_target, &driver->rt.target);
69
-   spa_list_append(&this->rt.target_list, &this->rt.driver_target.link);
70
+   peer->ref = 1;
71
+   peer->output = onode;
72
+   copy_target(&peer->target, &inode->rt.target);
73
 
74
-   /* now increment the required states of all this node targets, including
75
-    * the driver we added above */
76
-   spa_list_for_each(t, &this->rt.target_list, link) {
77
-       dstate = &t->activation->state0;
78
-       if (!t->active) {
79
-           dstate->required++;
80
-           t->active = true;
81
-       }
82
-       pw_log_trace("%p: driver state:%p pending:%d/%d, node state:%p pending:%d/%d",
83
-               this, dstate, dstate->pending, dstate->required,
84
-               nstate, nstate->pending, nstate->required);
85
-   }
86
+   spa_list_append(&onode->peer_list, &peer->link);
87
+   pw_log_debug("new peer %p from %p to %p", peer, onode, inode);
88
+   pw_impl_node_add_target(onode, &peer->target);
89
+
90
+   return peer;
91
 }
92
 
93
-/* called from the data loop and undoes the changes done in add_node.  */
94
-static void remove_node(struct pw_impl_node *this)
95
+SPA_EXPORT
96
+void pw_node_peer_unref(struct pw_node_peer *peer)
97
 {
98
-   struct pw_node_activation_state *dstate, *nstate;
99
-   struct pw_node_target *t;
100
-
101
-   if (this->exported)
102
+   if (peer == NULL || --peer->ref > 0)
103
        return;
104
+   spa_list_remove(&peer->link);
105
+   pw_log_debug("remove peer %p from %p to %p", peer, peer->output, peer->target.node);
106
+   pw_impl_node_remove_target(peer->output, &peer->target);
107
+   free(peer);
108
+}
109
 
110
-   pw_log_trace("%p: remove from driver %s %p %p",
111
-           this, this->rt.driver_target.name,
112
-           this->rt.driver_target.activation, this->rt.target.activation);
113
-
114
-   spa_list_remove(&this->rt.target.link);
115
-
116
-   nstate = &this->rt.target.activation->state0;
117
-   if (this->rt.target.active) {
118
-       nstate->required--;
119
-       this->rt.target.active = false;
120
+static inline void activate_target(struct pw_impl_node *node, struct pw_node_target *t)
121
+{
122
+   struct pw_node_activation_state *state = &t->activation->state0;
123
+   if (!t->active) {
124
+       if ((!node->async || node->driving) && !node->exported) {
125
+           SPA_ATOMIC_INC(state->required);
126
+           SPA_ATOMIC_INC(state->pending);
127
+       }
128
+       t->active = true;
129
+       pw_log_debug("%p: target state:%p id:%d pending:%d/%d",
130
+               node, state, t->id, state->pending, state->required);
131
    }
132
+}
133
 
134
-   spa_list_for_each(t, &this->rt.target_list, link) {
135
-       dstate = &t->activation->state0;
136
-       if (t->active) {
137
-           dstate->required--;
138
-           t->active = false;
139
+static inline void deactivate_target(struct pw_impl_node *node, struct pw_node_target *t, uint64_t trigger)
140
+{
141
+   if (t->active) {
142
+       struct pw_node_activation_state *state = &t->activation->state0;
143
+       if (!node->async || node->driving) {
144
+           /* the driver copies the required to the pending state
145
+            * so first try to resume the node and then decrement the
146
+            * required state. This way we either resume with the old value
147
+            * or we don't when the driver has not yet copied */
148
+           if (trigger != 0)
149
+               t->trigger(t, trigger);
150
+           if (!node->exported)
151
+               SPA_ATOMIC_DEC(state->required);
152
        }
153
-       pw_log_trace("%p: driver state:%p pending:%d/%d, node state:%p pending:%d/%d",
154
-               this, dstate, dstate->pending, dstate->required,
155
-               nstate, nstate->pending, nstate->required);
156
+       t->active = false;
157
+       pw_log_debug("%p: target state:%p id:%d pending:%d/%d trigger:%"PRIu64,
158
+               node, state, t->id, state->pending, state->required, trigger);
159
    }
160
-   spa_list_remove(&this->rt.driver_target.link);
161
+}
162
+
163
+/* called from data-loop when all the targets of a node need to be triggered */
164
+static inline void trigger_targets(struct pw_impl_node *node, int status, uint64_t nsec)
165
+{
166
+   struct pw_node_target *ta;
167
 
168
-   spa_zero(this->rt.driver_target);
169
+   pw_log_trace_fp("%p: (%s-%u) trigger targets %"PRIu64,
170
+           node, node->name, node->info.id, nsec);
171
+
172
+   spa_list_for_each(ta, &node->rt.target_list, link)
173
+       ta->trigger(ta, nsec);
174
 }
175
 
176
+/** \endcond */
177
+
178
+/* Called from the node data loop when a node needs to be scheduled by
179
+ * the given driver.
180
+ *
181
+ * - the node adds the source to the data loop
182
+ * - from all the targets of the node, we can now be scheduled ourselves
183
+ *   so activate our target.
184
+ * - When we get scheduled, we will activate our peer targets
185
+ */
186
 static int
187
-do_node_add(struct spa_loop *loop, bool async, uint32_t seq,
188
+do_node_prepare(struct spa_loop *loop, bool async, uint32_t seq,
189
        const void *data, size_t size, void *user_data)
190
 {
191
    struct pw_impl_node *this = user_data;
192
-   struct pw_impl_node *driver = this->driver_node;
193
+   struct pw_node_target *t;
194
+   uint64_t dummy;
195
+   int res;
196
 
197
-   if (!this->added) {
198
-       uint64_t dummy;
199
-       int res;
200
+   pw_log_trace("%p: prepare %d remote:%d", this, this->rt.prepared, this->remote);
201
 
202
+   if (this->rt.prepared)
203
+       return 0;
204
+
205
+   if (!this->remote) {
206
        /* clear the eventfd in case it was written to while the node was stopped */
207
-       res = spa_system_eventfd_read(this->data_system, this->source.fd, &dummy);
208
+       res = spa_system_eventfd_read(this->rt.target.system, this->source.fd, &dummy);
209
        if (SPA_UNLIKELY(res != -EAGAIN && res != 0))
210
            pw_log_warn("%p: read failed %m", this);
211
 
212
-       this->added = true;
213
-       /* remote nodes have their source added in client-node instead */
214
-       if (!this->remote)
215
-           spa_loop_add_source(loop, &this->source);
216
-       add_node(this, driver);
217
+       spa_loop_add_source(loop, &this->source);
218
    }
219
+   if (!this->remote || this->rt.target.activation->client_version < 1)
220
+       SPA_ATOMIC_STORE(this->rt.target.activation->status, PW_NODE_ACTIVATION_FINISHED);
221
+
222
+   spa_list_for_each(t, &this->rt.target_list, link)
223
+       activate_target(this, t);
224
+
225
+   this->rt.prepared = true;
226
+
227
    return 0;
228
 }
229
 
230
+static void add_node_to_graph(struct pw_impl_node *node)
231
+{
232
+   pw_loop_invoke(node->data_loop, do_node_prepare, 1, NULL, 0, true, node);
233
+}
234
+
235
+/* called from the node data loop and undoes the changes done in do_node_prepare.  */
236
 static int
237
-do_node_remove(struct spa_loop *loop, bool async, uint32_t seq,
238
+do_node_unprepare(struct spa_loop *loop, bool async, uint32_t seq,
239
        const void *data, size_t size, void *user_data)
240
 {
241
    struct pw_impl_node *this = user_data;
242
-   if (this->added) {
243
-       if (!this->remote)
244
-           spa_loop_remove_source(loop, &this->source);
245
-       remove_node(this);
246
-       this->added = false;
247
+   struct pw_node_target *t;
248
+   int old_state;
249
+   uint64_t trigger = 0;
250
+
251
+   pw_log_trace("%p: unprepare %d remote:%d", this, this->rt.prepared, this->remote);
252
+
253
+   if (!this->rt.prepared)
254
+       return 0;
255
+
256
+   if (!this->remote || this->rt.target.activation->client_version < 1) {
257
+       /* We mark ourself as finished now, this will avoid going further into the process loop
258
+        * in case our fd was ready (removing ourselfs from the loop should avoid that as well).
259
+        * If we were supposed to be scheduled make sure we continue the graph for the peers we
260
+        * were supposed to trigger */
261
+       old_state = SPA_ATOMIC_XCHG(this->rt.target.activation->status, PW_NODE_ACTIVATION_INACTIVE);
262
+       if (old_state != PW_NODE_ACTIVATION_FINISHED)
263
+           trigger = get_time_ns(this->rt.target.system);
264
    }
265
+   if (!this->remote)
266
+       spa_loop_remove_source(loop, &this->source);
267
+
268
+   spa_list_for_each(t, &this->rt.target_list, link)
269
+       deactivate_target(this, t, trigger);
270
+
271
+   this->rt.prepared = false;
272
    return 0;
273
 }
274
 
275
+static void remove_node_from_graph(struct pw_impl_node *node)
276
+{
277
+   pw_loop_invoke(node->data_loop, do_node_unprepare, 1, NULL, 0, true, node);
278
+}
279
+
280
 static void node_deactivate(struct pw_impl_node *this)
281
 {
282
    struct pw_impl_port *port;
283
@@ -203,7 +249,7 @@
284
    pw_log_debug("%p: deactivate", this);
285
 
286
    /* make sure the node doesn't get woken up while not active */
287
-   pw_loop_invoke(this->data_loop, do_node_remove, 1, NULL, 0, true, this);
288
+   remove_node_from_graph(this);
289
 
290
    spa_list_for_each(port, &this->input_ports, link) {
291
        spa_list_for_each(link, &port->links, input_link)
292
@@ -268,8 +314,8 @@
293
    if (impl->pending_state >= PW_NODE_STATE_RUNNING)
294
        return 0;
295
 
296
-   pw_log_debug("%p: start node driving:%d driver:%d added:%d", this,
297
-           this->driving, this->driver, this->added);
298
+   pw_log_debug("%p: start node driving:%d driver:%d prepared:%d", this,
299
+           this->driving, this->driver, this->rt.prepared);
300
 
301
    if (!(this->driving && this->driver)) {
302
        impl->pending_play = true;
303
@@ -368,11 +414,11 @@
304
 
305
    switch (state) {
306
    case PW_NODE_STATE_RUNNING:
307
-       pw_log_debug("%p: start node driving:%d driver:%d added:%d", node,
308
-               node->driving, node->driver, node->added);
309
+       pw_log_debug("%p: start node driving:%d driver:%d prepared:%d", node,
310
+               node->driving, node->driver, node->rt.prepared);
311
 
312
        if (res >= 0) {
313
-           pw_loop_invoke(node->data_loop, do_node_add, 1, NULL, 0, true, node);
314
+           add_node_to_graph(node);
315
        }
316
        if (node->driving && node->driver) {
317
            res = spa_node_send_command(node->node,
318
@@ -380,7 +426,7 @@
319
            if (res < 0) {
320
                state = PW_NODE_STATE_ERROR;
321
                error = spa_aprintf("Start error: %s", spa_strerror(res));
322
-               pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node);
323
+               remove_node_from_graph(node);
324
            }
325
        }
326
        break;
327
@@ -388,7 +434,7 @@
328
    case PW_NODE_STATE_SUSPENDED:
329
    case PW_NODE_STATE_ERROR:
330
        if (state != PW_NODE_STATE_IDLE || node->pause_on_idle)
331
-           pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node);
332
+           remove_node_from_graph(node);
333
        break;
334
    default:
335
        break;
336
@@ -442,8 +488,8 @@
337
 
338
    node_deactivate(this);
339
 
340
-   pw_log_debug("%p: suspend node driving:%d driver:%d added:%d", this,
341
-           this->driving, this->driver, this->added);
342
+   pw_log_debug("%p: suspend node driving:%d driver:%d prepared:%d", this,
343
+           this->driving, this->driver, this->rt.prepared);
344
 
345
    res = spa_node_send_command(this->node,
346
                    &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Suspend));
347
@@ -479,6 +525,7 @@
348
 {
349
    pw_free_strv(this->groups);
350
    pw_free_strv(this->link_groups);
351
+   pw_free_strv(this->sync_groups);
352
    free(this->name);
353
    free((char*)this->info.error);
354
 }
355
@@ -704,34 +751,143 @@
356
    pw_context_emit_driver_removed(context, node);
357
 }
358
 
359
-static void update_io(struct pw_impl_node *node)
360
+static int
361
+do_update_position(struct spa_loop *loop,
362
+       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
363
 {
364
-   struct pw_node_target *t = &node->rt.target;
365
+   struct pw_impl_node *node = user_data;
366
+   void *position = *(void**)data;
367
+   pw_log_trace("%p: set position %p", node, position);
368
+   node->rt.position = position;
369
+   if (position) {
370
+       node->target_rate = node->rt.position->clock.target_rate;
371
+       node->target_quantum = node->rt.position->clock.target_duration;
372
+   }
373
+   return 0;
374
+}
375
 
376
-   pw_log_debug("%p: id:%d", node, node->info.id);
377
+SPA_EXPORT
378
+int pw_impl_node_set_io(struct pw_impl_node *this, uint32_t id, void *data, size_t size)
379
+{
380
+   int res;
381
+   struct pw_impl_port *port;
382
 
383
-   if (spa_node_set_io(node->node,
384
-               SPA_IO_Position,
385
-               &t->activation->position,
386
-               sizeof(struct spa_io_position)) >= 0) {
387
-       pw_log_debug("%p: set position %p", node, &t->activation->position);
388
-       node->rt.position = &t->activation->position;
389
+   switch (id) {
390
+   case SPA_IO_Position:
391
+       if (data != NULL && size < sizeof(struct spa_io_position))
392
+           return -EINVAL;
393
+       pw_log_debug("%p: set position %p", this, data);
394
+       pw_loop_invoke(this->data_loop,
395
+               do_update_position, SPA_ID_INVALID, &data, sizeof(void*), true, this);
396
+       break;
397
+   case SPA_IO_Clock:
398
+       if (data != NULL && size < sizeof(struct spa_io_clock))
399
+           return -EINVAL;
400
+       pw_log_debug("%p: set clock %p", this, data);
401
+       this->rt.clock = data;
402
+       if (this->rt.clock) {
403
+           this->info.id = this->rt.clock->id;
404
+           this->rt.target.id = this->info.id;
405
+       }
406
+       break;
407
+   }
408
+   this->driving = this->driver && this->rt.clock && this->rt.position &&
409
+       this->rt.position->clock.id == this->rt.clock->id;
410
 
411
-       node->target_rate = node->rt.position->clock.target_rate;
412
-       node->target_quantum = node->rt.position->clock.target_duration;
413
-       node->target_pending = false;
414
+   pw_log_debug("%p: driving:%d clock-id:%d driver-id:%d", this, this->driving,
415
+           this->rt.clock ? this->rt.clock->id : SPA_ID_INVALID,
416
+           this->rt.position ? this->rt.position->clock.id : SPA_ID_INVALID);
417
+
418
+   spa_list_for_each(port, &this->input_ports, link)
419
+       spa_node_set_io(port->mix, id, data, size);
420
+   spa_list_for_each(port, &this->output_ports, link)
421
+       spa_node_set_io(port->mix, id, data, size);
422
+
423
+   res = spa_node_set_io(this->node, id, data, size);
424
+
425
+   if (res >= 0 && !SPA_RESULT_IS_ASYNC(res) && this->rt.position)
426
+       this->rt.target.activation->active_driver_id = this->rt.position->clock.id;
427
 
428
-       pw_impl_node_emit_peer_added(node, node);
429
-   } else if (node->driver) {
430
-       pw_log_warn("%p: can't set position on driver", node);
431
+   pw_log_debug("%p: set io: %s", this, spa_strerror(res));
432
+
433
+   return res;
434
+}
435
+
436
+static int
437
+do_add_target(struct spa_loop *loop,
438
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
439
+{
440
+   struct pw_node_target *t = user_data;
441
+   struct pw_impl_node *node = *(struct pw_impl_node**)data;
442
+
443
+   pw_log_debug("%p: target:%p id:%d added:%d prepared:%d", node, t, t->id, t->added, node->rt.prepared);
444
+
445
+   if (!t->added) {
446
+       spa_list_append(&node->rt.target_list, &t->link);
447
+       t->added = true;
448
+       if (node->rt.prepared)
449
+           activate_target(node, t);
450
    }
451
-   if (spa_node_set_io(node->node,
452
-               SPA_IO_Clock,
453
-               &t->activation->position.clock,
454
-               sizeof(struct spa_io_clock)) >= 0) {
455
-       pw_log_debug("%p: set clock %p", node, &t->activation->position.clock);
456
-       node->rt.clock = &t->activation->position.clock;
457
+   return 0;
458
+}
459
+
460
+SPA_EXPORT
461
+int pw_impl_node_add_target(struct pw_impl_node *node, struct pw_node_target *t)
462
+{
463
+   pw_loop_invoke(node->data_loop,
464
+           do_add_target, SPA_ID_INVALID, &node, sizeof(void *), true, t);
465
+   if (t->node)
466
+       pw_impl_node_emit_peer_added(node, t->node);
467
+
468
+   return 0;
469
+}
470
+
471
+static int
472
+do_remove_target(struct spa_loop *loop,
473
+                bool async, uint32_t seq, const void *data, size_t size, void *user_data)
474
+{
475
+   struct pw_node_target *t = user_data;
476
+   struct pw_impl_node *node = *(struct pw_impl_node**)data;
477
+
478
+   pw_log_debug("%p: target:%p id:%d added:%d prepared:%d", node, t, t->id, t->added, node->rt.prepared);
479
+
480
+   if (t->added) {
481
+       spa_list_remove(&t->link);
482
+       t->added = false;
483
+       if (node->rt.prepared) {
484
+           int old_state = SPA_ATOMIC_LOAD(node->rt.target.activation->status);
485
+           uint64_t trigger = 0;
486
+           if (old_state != PW_NODE_ACTIVATION_FINISHED)
487
+               trigger = get_time_ns(node->rt.target.system);
488
+           deactivate_target(node, t, trigger);
489
+       }
490
    }
491
+   return 0;
492
+}
493
+
494
+SPA_EXPORT
495
+int pw_impl_node_remove_target(struct pw_impl_node *node, struct pw_node_target *t)
496
+{
497
+   /* we also update the target list for remote nodes so that the profiler
498
+    * can inspect the nodes as well */
499
+   pw_loop_invoke(node->data_loop,
500
+           do_remove_target, SPA_ID_INVALID, &node, sizeof(void *), true, t);
501
+   if (t->node)
502
+       pw_impl_node_emit_peer_removed(node, t->node);
503
+
504
+   return 0;
505
+}
506
+
507
+static void update_io(struct pw_impl_node *node)
508
+{
509
+   struct pw_node_target *t = &node->rt.target;
510
+
511
+   pw_log_debug("%p: id:%d", node, node->info.id);
512
+
513
+   pw_impl_node_set_io(node, SPA_IO_Clock, &t->activation->position.clock,
514
+                            sizeof(struct spa_io_clock));
515
+   pw_impl_node_set_io(node, SPA_IO_Position, &t->activation->position,
516
+                            sizeof(struct spa_io_position));
517
 }
518
 
519
 SPA_EXPORT
520
@@ -739,7 +895,6 @@
521
             struct pw_properties *properties)
522
 {
523
    static const char * const keys = {
524
-       PW_KEY_OBJECT_SERIAL,
525
        PW_KEY_OBJECT_PATH,
526
        PW_KEY_MODULE_ID,
527
        PW_KEY_FACTORY_ID,
528
@@ -763,7 +918,7 @@
529
    struct pw_context *context = this->context;
530
    struct pw_impl_port *port;
531
 
532
-   pw_log_debug("%p: register", this);
533
+   pw_log_debug("%p: register remote:%d exported:%d", this, this->remote, this->exported);
534
 
535
    if (this->registered)
536
        goto error_existed;
537
@@ -783,11 +938,14 @@
538
        insert_driver(context, this);
539
    this->registered = true;
540
 
541
-   this->rt.target.activation->position.clock.id = this->global->id;
542
-
543
    this->info.id = this->global->id;
544
    this->rt.target.id = this->info.id;
545
-   pw_properties_setf(this->properties, PW_KEY_OBJECT_ID, "%d", this->info.id);
546
+   this->rt.target.activation->position.clock.id = this->global->id;
547
+
548
+   this->from_driver_peer = pw_node_peer_ref(this, this);
549
+   this->to_driver_peer = pw_node_peer_ref(this, this);
550
+
551
+   pw_properties_setf(this->properties, PW_KEY_OBJECT_ID, "%d", this->global->id);
552
    pw_properties_setf(this->properties, PW_KEY_OBJECT_SERIAL, "%"PRIu64,
553
            pw_global_get_serial(this->global));
554
    this->info.props = &this->properties->dict;
555
@@ -826,29 +984,6 @@
556
    return 0;
557
 }
558
 
559
-static int
560
-do_move_nodes(struct spa_loop *loop,
561
-       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
562
-{
563
-   struct impl *impl = user_data;
564
-   struct pw_impl_node *driver = *(struct pw_impl_node **)data;
565
-   struct pw_impl_node *node = &impl->this;
566
-
567
-   pw_log_trace("%p: driver:%p->%p", node, node->driver_node, driver);
568
-
569
-   pw_log_trace("%p: set position %p", node, &driver->rt.target.activation->position);
570
-   node->rt.position = &driver->rt.target.activation->position;
571
-
572
-   node->target_rate = node->rt.position->clock.target_rate;
573
-   node->target_quantum = node->rt.position->clock.target_duration;
574
-
575
-   if (node->added) {
576
-       remove_node(node);
577
-       add_node(node, driver);
578
-   }
579
-   return 0;
580
-}
581
-
582
 static void remove_segment_owner(struct pw_impl_node *driver, uint32_t node_id)
583
 {
584
    struct pw_node_activation *a = driver->rt.target.activation;
585
@@ -861,10 +996,9 @@
586
 {
587
    struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
588
    struct pw_impl_node *old = node->driver_node;
589
-   int res;
590
-   bool was_driving;
591
+   bool was_driving, no_driver = (driver == NULL);
592
 
593
-   if (driver == NULL)
594
+   if (no_driver)
595
        driver = node;
596
 
597
    spa_list_remove(&node->follower_link);
598
@@ -876,7 +1010,6 @@
599
    remove_segment_owner(old, node->info.id);
600
 
601
    was_driving = node->driving;
602
-   node->driving = node->driver && driver == node;
603
 
604
    /* When a node was driver (and is waiting for all nodes to complete
605
     * the Start command) cancel the pending state and let the new driver
606
@@ -891,28 +1024,61 @@
607
            node->name, node->info.id,
608
            old->name, old->info.id, driver->name, driver->info.id);
609
 
610
+   /* make sure the old driver doesn't trigger the node anymore */
611
+   pw_node_peer_unref(spa_steal_ptr(node->from_driver_peer));
612
+   /* make sure the node doesn't trigger the old driver anymore */
613
+   pw_node_peer_unref(spa_steal_ptr(node->to_driver_peer));
614
+
615
    node->driver_node = driver;
616
    node->moved = true;
617
 
618
-   if ((res = spa_node_set_io(node->node,
619
-           SPA_IO_Position,
620
-           &driver->rt.target.activation->position,
621
-           sizeof(struct spa_io_position))) < 0) {
622
-       pw_log_debug("%p: set position: %s", node, spa_strerror(res));
623
-   }
624
+   /* first send new driver target to node, the node is not yet being
625
+    * scheduled so it won't trigger yet */
626
+   node->to_driver_peer = pw_node_peer_ref(node, driver);
627
 
628
-   pw_loop_invoke(node->data_loop,
629
-              do_move_nodes, SPA_ID_INVALID, &driver, sizeof(struct pw_impl_node *),
630
-              true, impl);
631
+   /* then set the new driver node activation */
632
+   pw_impl_node_set_io(node, SPA_IO_Position,
633
+           &driver->rt.target.activation->position,
634
+           sizeof(struct spa_io_position));
635
+
636
+   /* and then make the driver trigger the node */
637
+   node->from_driver_peer = pw_node_peer_ref(driver, node);
638
 
639
    pw_impl_node_emit_driver_changed(node, old, driver);
640
 
641
-   pw_impl_node_emit_peer_removed(old, node);
642
-   pw_impl_node_emit_peer_added(driver, node);
643
+   if (no_driver) {
644
+       /* We don't have a driver, so remove the property */
645
+       pw_properties_set(node->properties, PW_KEY_NODE_DRIVER_ID, NULL);
646
+   } else if (node->driver_node->global) {
647
+       /* Expose the driver ID if it is available as a global */
648
+       pw_properties_setf(node->properties, PW_KEY_NODE_DRIVER_ID, "%u",
649
+               pw_global_get_id(node->driver_node->global));
650
+   }
651
+
652
+   node->info.change_mask |= PW_NODE_CHANGE_MASK_PROPS;
653
+   pw_impl_node_emit_info_changed(driver, &node->info);
654
 
655
    return 0;
656
 }
657
 
658
+struct match {
659
+   struct pw_impl_node *node;
660
+   int count;
661
+};
662
+#define MATCH_INIT(n) ((struct match){ .node = (n) })
663
+
664
+static int execute_match(void *data, const char *location, const char *action,
665
+       const char *val, size_t len)
666
+{
667
+   struct match *match = data;
668
+   struct pw_impl_node *this = match->node;
669
+   if (spa_streq(action, "update-props")) {
670
+       match->count += pw_properties_update_string(this->properties, val, len);
671
+       this->info.props = &this->properties->dict;
672
+   }
673
+   return 1;
674
+}
675
+
676
 static void check_properties(struct pw_impl_node *node)
677
 {
678
    struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
679
@@ -920,11 +1086,24 @@
680
    const char *str, *recalc_reason = NULL;
681
    struct spa_fraction frac;
682
    uint32_t value;
683
-   bool driver, trigger;
684
+   bool driver, trigger, transport, sync, async;
685
+   struct match match;
686
+
687
+   match = MATCH_INIT(node);
688
+   pw_context_conf_section_match_rules(context, "node.rules",
689
+           &node->properties->dict, execute_match, &match);
690
 
691
    if ((str = pw_properties_get(node->properties, PW_KEY_PRIORITY_DRIVER))) {
692
-       node->priority_driver = pw_properties_parse_int(str);
693
-       pw_log_debug("%p: priority driver %d", node, node->priority_driver);
694
+       value = pw_properties_parse_int(str);
695
+       if (value != node->priority_driver) {
696
+           pw_log_debug("%p: priority driver %d -> %d", node, node->priority_driver, value);
697
+           node->priority_driver = value;
698
+           if (node->registered && node->driver) {
699
+               remove_driver(context, node);
700
+               insert_driver(context, node);
701
+               recalc_reason = "driver priority changed";
702
+           }
703
+       }
704
    }
705
 
706
    if ((str = pw_properties_get(node->properties, PW_KEY_NODE_NAME)) &&
707
@@ -947,9 +1126,8 @@
708
        if (node->registered) {
709
            if (driver)
710
                insert_driver(context, node);
711
-           else {
712
+           else
713
                remove_driver(context, node);
714
-           }
715
        }
716
        if (driver && node->driver_node == node)
717
            node->driving = true;
718
@@ -961,9 +1139,9 @@
719
    if (trigger != node->trigger) {
720
        node->trigger = trigger;
721
        if (trigger)
722
-           node->rt.target.activation->state0.required++;
723
+           SPA_ATOMIC_INC(node->rt.target.activation->state0.required);
724
        else
725
-           node->rt.target.activation->state0.required--;
726
+           SPA_ATOMIC_DEC(node->rt.target.activation->state0.required);
727
    }
728
 
729
    /* group defines what nodes are scheduled together */
730
@@ -991,6 +1169,38 @@
731
        recalc_reason = "link group changed";
732
    }
733
 
734
+   /* sync group defines what nodes are part of the same sync */
735
+   str = pw_properties_get(node->properties, PW_KEY_NODE_SYNC_GROUP);
736
+   if (str == NULL)
737
+       str = "group.sync.0";
738
+   if (!spa_streq(str, impl->sync_group)) {
739
+       pw_log_info("%p: sync group '%s'->'%s'", node, impl->sync_group, str);
740
+       free(impl->sync_group);
741
+       impl->sync_group = str ? strdup(str) : NULL;
742
+       pw_free_strv(node->sync_groups);
743
+       node->sync_groups = impl->sync_group ?
744
+           pw_strv_parse(impl->sync_group, strlen(impl->sync_group), INT_MAX, NULL) : NULL;
745
+       recalc_reason = "sync group changed";
746
+   }
747
+   sync = pw_properties_get_bool(node->properties, PW_KEY_NODE_SYNC, false);
748
+   if (sync != node->sync) {
749
+       pw_log_info("%p: sync %d -> %d", node, node->sync, sync);
750
+       node->sync = sync;
751
+       recalc_reason = "sync changed";
752
+   }
753
+
754
+   transport = pw_properties_get_bool(node->properties, PW_KEY_NODE_TRANSPORT, false);
755
+   if (transport != node->transport) {
756
+       pw_log_info("%p: transport %d -> %d", node, node->transport, transport);
757
+       node->transport = transport;
758
+       recalc_reason = "transport changed";
759
+   }
760
+   async = pw_properties_get_bool(node->properties, PW_KEY_NODE_ASYNC, false);
761
+   if (async != node->async) {
762
+       pw_log_info("%p: async %d -> %d", node, node->async, async);
763
+       node->async = async;
764
+   }
765
+
766
    if ((str = pw_properties_get(node->properties, PW_KEY_MEDIA_CLASS)) != NULL &&
767
        (strstr(str, "/Sink") != NULL || strstr(str, "/Source") != NULL)) {
768
        node->can_suspend = true;
769
@@ -1090,95 +1300,106 @@
770
        return "awake";
771
    case PW_NODE_ACTIVATION_FINISHED:
772
        return "finished";
773
+   case PW_NODE_ACTIVATION_INACTIVE:
774
+       return "inactive";
775
    }
776
    return "unknown";
777
 }
778
 
779
-static void update_xrun_stats(struct pw_node_activation *a, uint64_t trigger, uint64_t delay)
780
+static inline void update_xrun_stats(struct pw_node_activation *a, uint32_t count, uint64_t trigger, uint64_t delay)
781
 {
782
-   a->xrun_count++;
783
+   a->xrun_count += count;
784
    a->xrun_time = trigger;
785
    a->xrun_delay = delay;
786
    a->max_delay = SPA_MAX(a->max_delay, delay);
787
 }
788
 
789
-static void check_states(struct pw_impl_node *driver, uint64_t nsec)
790
+static inline void debug_xrun_target(struct pw_impl_node *driver,
791
+       struct pw_node_target *t, int status, uint64_t nsec)
792
 {
793
-   struct pw_node_target *t;
794
-   struct pw_node_activation *na = driver->rt.target.activation;
795
-   struct spa_io_clock *cl = &na->position.clock;
796
+   struct pw_node_activation *a = t->activation;
797
+   struct pw_node_activation_state *state = &a->state0;
798
+   int suppressed;
799
    enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
800
+
801
+   if ((suppressed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
802
+       level = SPA_LOG_LEVEL_WARN;
803
+
804
+   pw_log(level, "(%s-%u) xrun state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
805
+       " waiting:%"PRIu64" process:%"PRIu64" status:%s (%d suppressed)",
806
+       t->name, t->id, state,
807
+       state->pending, state->required,
808
+       a->signal_time,
809
+       a->awake_time,
810
+       a->finish_time,
811
+       a->awake_time - a->signal_time,
812
+       a->finish_time - a->awake_time,
813
+       str_status(status), suppressed);
814
+}
815
+
816
+static inline void debug_xrun_graph(struct pw_impl_node *driver, uint64_t nsec)
817
+{
818
    int suppressed;
819
+   enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
820
+   struct pw_node_target *t;
821
 
822
    if ((suppressed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
823
-       level = SPA_LOG_LEVEL_INFO;
824
+       level = SPA_LOG_LEVEL_WARN;
825
+
826
+   pw_log(level, "(%s-%u) graph xrun (%d suppressed)",
827
+           driver->name, driver->info.id, suppressed);
828
 
829
    spa_list_for_each(t, &driver->rt.target_list, link) {
830
        struct pw_node_activation *a = t->activation;
831
        struct pw_node_activation_state *state = &a->state0;
832
 
833
-       if (t->id == driver->info.id)
834
-           continue;
835
-
836
        if (a->status == PW_NODE_ACTIVATION_TRIGGERED ||
837
            a->status == PW_NODE_ACTIVATION_AWAKE) {
838
-           update_xrun_stats(a, nsec / 1000, 0);
839
+           pw_log(level, "(%s-%u) xrun state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
840
+                   " waiting:%"PRIu64" process:%"PRIu64" status:%s",
841
+                   t->name, t->id, state,
842
+                   state->pending, state->required,
843
+                   a->signal_time,
844
+                   a->awake_time,
845
+                   a->finish_time,
846
+                   a->awake_time - a->signal_time,
847
+                   a->finish_time - a->awake_time,
848
+                   str_status(a->status));
849
 
850
-           pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u suppressed)",
851
-               t->name, t->id,
852
-               (uint32_t)(cl->rate.num * cl->duration), cl->rate.denom,
853
-               cl->position, str_status(a->status),
854
-               suppressed);
855
        }
856
-       pw_log_debug("(%s-%u) state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
857
-               " waiting:%"PRIu64" process:%"PRIu64" status:%s sync:%d",
858
-               t->name, t->id, state,
859
-               state->pending, state->required,
860
-               a->signal_time,
861
-               a->awake_time,
862
-               a->finish_time,
863
-               a->awake_time - a->signal_time,
864
-               a->finish_time - a->awake_time,
865
-               str_status(a->status), a->pending_sync);
866
    }
867
 }
868
 
869
-static inline uint64_t get_time_ns(struct spa_system *system)
870
-{
871
-   struct timespec ts;
872
-   spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts);
873
-   return SPA_TIMESPEC_TO_NSEC(&ts);
874
-}
875
-
876
-static inline void node_trigger(struct pw_impl_node *this)
877
-{
878
-   pw_log_trace_fp("node %p %s", this, this->name);
879
-   if (SPA_UNLIKELY(spa_system_eventfd_write(this->data_system, this->source.fd, 1) < 0))
880
-       pw_log_warn("node %p: write failed %m", this);
881
-}
882
-
883
-/* called from data-loop when all the targets of a node need to be triggered */
884
-static inline int trigger_targets(struct pw_impl_node *this, int status, uint64_t nsec)
885
+static void debug_sync_timeout(struct pw_impl_node *driver, uint64_t nsec)
886
 {
887
    struct pw_node_target *t;
888
+   enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
889
+   int suppressed;
890
 
891
-   pw_log_trace_fp("%p: %s trigger targets %"PRIu64, this, this->name, nsec);
892
+   if ((suppressed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
893
+       level = SPA_LOG_LEVEL_WARN;
894
+
895
+   pw_log(level, "(%s-%u) sync timeout, going to RUNNING (%d suppressed)",
896
+               driver->name, driver->info.id, suppressed);
897
 
898
-   spa_list_for_each(t, &this->rt.target_list, link) {
899
+   spa_list_for_each(t, &driver->rt.target_list, link) {
900
        struct pw_node_activation *a = t->activation;
901
        struct pw_node_activation_state *state = &a->state0;
902
 
903
-       pw_log_trace_fp("%p: (%s-%u) state:%p pending:%d/%d", t->node,
904
-               t->name, t->id, state, state->pending, state->required);
905
+       if (!a->pending_sync)
906
+           continue;
907
 
908
-       if (pw_node_activation_state_dec(state)) {
909
-           a->status = PW_NODE_ACTIVATION_TRIGGERED;
910
-           a->signal_time = nsec;
911
-           if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0))
912
-               pw_log_warn("node %p: write failed %m", this);
913
-       }
914
+       pw_log(level, "(%s-%u) sync state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
915
+               " waiting:%"PRIu64" process:%"PRIu64" status:%s",
916
+               t->name, t->id, state,
917
+               state->pending, state->required,
918
+               a->signal_time,
919
+               a->awake_time,
920
+               a->finish_time,
921
+               a->awake_time - a->signal_time,
922
+               a->finish_time - a->awake_time,
923
+               str_status(a->status));
924
    }
925
-   return 0;
926
 }
927
 
928
 static inline void calculate_stats(struct pw_impl_node *this,  struct pw_node_activation *a)
929
@@ -1207,26 +1428,29 @@
930
  *
931
  * This code runs on the client and the server, depending on where the node is.
932
  */
933
-static inline int process_node(void *data)
934
+static inline int process_node(void *data, uint64_t nsec)
935
 {
936
    struct pw_impl_node *this = data;
937
    struct pw_impl_port *p;
938
    struct pw_node_activation *a = this->rt.target.activation;
939
-   struct spa_system *data_system = this->data_system;
940
-   int status;
941
-   uint64_t nsec;
942
+   struct spa_system *data_system = this->rt.target.system;
943
+   int status, old_status;
944
+
945
+   if (!SPA_ATOMIC_CAS(a->status,
946
+               PW_NODE_ACTIVATION_TRIGGERED,
947
+               PW_NODE_ACTIVATION_AWAKE))
948
+       return 0;
949
 
950
-   nsec = get_time_ns(data_system);
951
-   pw_log_trace_fp("%p: %s process remote:%u exported:%u %"PRIu64,
952
-           this, this->name, this->remote, this->exported, nsec);
953
-   a->status = PW_NODE_ACTIVATION_AWAKE;
954
    a->awake_time = nsec;
955
+   pw_log_trace_fp("%p: %s process remote:%u exported:%u %"PRIu64" %"PRIu64,
956
+           this, this->name, this->remote, this->exported,
957
+           a->signal_time, nsec);
958
 
959
    /* when transport sync is not supported, just clear the flag */
960
    if (SPA_UNLIKELY(!this->transport_sync))
961
        a->pending_sync = false;
962
 
963
-   if (SPA_LIKELY(this->added)) {
964
+   if (SPA_LIKELY(this->rt.prepared)) {
965
        /* process input mixers */
966
        spa_list_for_each(p, &this->rt.input_mix, rt.node_link)
967
            spa_node_process_fast(p->mix);
968
@@ -1249,21 +1473,21 @@
969
    a->state0.status = status;
970
 
971
    nsec = get_time_ns(data_system);
972
+   old_status = SPA_ATOMIC_XCHG(a->status, PW_NODE_ACTIVATION_FINISHED);
973
+   a->finish_time = nsec;
974
 
975
    pw_log_trace_fp("%p: finished status:%d %"PRIu64, this, status, nsec);
976
-   a->status = PW_NODE_ACTIVATION_FINISHED;
977
-   a->finish_time = nsec;
978
 
979
    /* we don't need to trigger targets when the node was driving the
980
     * graph because that means we finished the graph. */
981
    if (SPA_LIKELY(!this->driving)) {
982
-       trigger_targets(this, status, nsec);
983
+       if ((!this->async || a->server_version < 1) && old_status == PW_NODE_ACTIVATION_AWAKE)
984
+           trigger_targets(this, status, nsec);
985
    } else {
986
        /* calculate CPU time when finished */
987
        a->signal_time = this->driver_start;
988
        calculate_stats(this, a);
989
        pw_impl_node_rt_emit_complete(this);
990
-//     pw_context_driver_emit_complete(this->context, this);
991
    }
992
 
993
    if (SPA_UNLIKELY(status & SPA_STATUS_DRAINED))
994
@@ -1274,15 +1498,9 @@
995
 
996
 int pw_impl_node_trigger(struct pw_impl_node *node)
997
 {
998
-   struct pw_node_activation *a = node->rt.target.activation;
999
-   struct pw_node_activation_state *state = &a->state0;
1000
-
1001
-   if (pw_node_activation_state_dec(state)) {
1002
-       uint64_t nsec = get_time_ns(node->data_system);
1003
-       a->status = PW_NODE_ACTIVATION_TRIGGERED;
1004
-       a->signal_time = nsec;
1005
-       node_trigger(node);
1006
-   }
1007
+   uint64_t nsec = get_time_ns(node->rt.target.system);
1008
+   struct pw_node_target *t = &node->rt.target;
1009
+   t->trigger(t, nsec);
1010
    return 0;
1011
 }
1012
 
1013
@@ -1295,17 +1513,24 @@
1014
        return;
1015
    }
1016
    if (SPA_LIKELY(source->rmask & SPA_IO_IN)) {
1017
-       uint64_t cmd;
1018
+       uint64_t cmd, nsec;
1019
+       struct spa_system *data_system = this->rt.target.system;
1020
+
1021
+       nsec = get_time_ns(data_system);
1022
 
1023
-       if (SPA_UNLIKELY(spa_system_eventfd_read(this->data_system, this->source.fd, &cmd) < 0))
1024
+       if (SPA_UNLIKELY(spa_system_eventfd_read(data_system, this->source.fd, &cmd) < 0))
1025
            pw_log_warn("%p: read failed %m", this);
1026
-       else if (SPA_UNLIKELY(cmd > 1))
1027
+       else if (SPA_UNLIKELY(cmd > 1)) {
1028
            pw_log_info("(%s-%u) client missed %"PRIu64" wakeups",
1029
                this->name, this->info.id, cmd - 1);
1030
+           update_xrun_stats(this->rt.target.activation, cmd - 1,
1031
+                   nsec / 1000, 0);
1032
+       }
1033
+
1034
+       pw_log_trace_fp("%p: remote:%u exported:%u %s got process %"PRIu64,
1035
+               this, this->remote, this->exported, this->name, nsec);
1036
 
1037
-       pw_log_trace_fp("%p: remote:%u exported:%u %s got process", this, this->remote,
1038
-               this->exported, this->name);
1039
-       process_node(this);
1040
+       process_node(this, nsec);
1041
    }
1042
 }
1043
 
1044
@@ -1361,12 +1586,7 @@
1045
    this = &impl->this;
1046
    this->context = context;
1047
    this->name = strdup("node");
1048
-
1049
-   this->data_loop = pw_context_get_data_loop(context)->loop;
1050
-   this->data_system = this->data_loop->system;
1051
-
1052
-   if (user_data_size > 0)
1053
-                this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
1054
+   this->source.fd = -1;
1055
 
1056
    if (properties == NULL)
1057
        properties = pw_properties_new(NULL, NULL);
1058
@@ -1375,14 +1595,24 @@
1059
        goto error_clean;
1060
    }
1061
 
1062
+   this->data_loop = pw_context_acquire_loop(context, &properties->dict);
1063
+   if (this->data_loop == NULL) {
1064
+       pw_log_error("can't find data-loop");
1065
+       res = -ENOENT;
1066
+       goto error_clean;
1067
+   }
1068
+
1069
+   if (user_data_size > 0)
1070
+                this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
1071
+
1072
    this->properties = properties;
1073
 
1074
    /* the eventfd used to signal the node */
1075
-   if ((res = spa_system_eventfd_create(this->data_system,
1076
+   if ((res = spa_system_eventfd_create(this->data_loop->system,
1077
                    SPA_FD_CLOEXEC | SPA_FD_NONBLOCK)) < 0)
1078
        goto error_clean;
1079
 
1080
-   pw_log_debug("%p: new fd:%d", this, res);
1081
+   pw_log_debug("%p: new fd:%d loop:%s", this, res, this->data_loop->name);
1082
 
1083
    this->source.fd = res;
1084
    this->source.func = node_on_fd_events;
1085
@@ -1426,21 +1656,24 @@
1086
 
1087
    this->rt.target.activation = this->activation->map->ptr;
1088
    this->rt.target.node = this;
1089
-   this->rt.target.system = this->data_system;
1090
+   this->rt.target.system = this->data_loop->system;
1091
    this->rt.target.fd = this->source.fd;
1092
+   this->rt.target.trigger = trigger_target_v1;
1093
 
1094
    reset_position(this, &this->rt.target.activation->position);
1095
    this->rt.target.activation->sync_timeout = DEFAULT_SYNC_TIMEOUT;
1096
    this->rt.target.activation->sync_left = 0;
1097
+   this->rt.target.activation->status = PW_NODE_ACTIVATION_INACTIVE;
1098
+   this->rt.target.activation->server_version = PW_VERSION_NODE_ACTIVATION;
1099
+   this->rt.target.activation->client_version = PW_VERSION_NODE_ACTIVATION;
1100
 
1101
    this->rt.rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
1102
    this->rt.rate_limit.burst = 1;
1103
 
1104
-   check_properties(this);
1105
-
1106
    this->driver_node = this;
1107
    spa_list_append(&this->follower_list, &this->follower_link);
1108
-   this->driving = this->driver;
1109
+
1110
+   check_properties(this);
1111
 
1112
    return this;
1113
 
1114
@@ -1448,7 +1681,10 @@
1115
    if (this->activation)
1116
        pw_memblock_unref(this->activation);
1117
    if (this->source.fd != -1)
1118
-       spa_system_close(this->data_system, this->source.fd);
1119
+       spa_system_close(this->data_loop->system, this->source.fd);
1120
+   if (this->data_loop)
1121
+       pw_context_release_loop(context, this->data_loop);
1122
+   free(this->name);
1123
    free(impl);
1124
 error_exit:
1125
    pw_properties_free(properties);
1126
@@ -1739,10 +1975,8 @@
1127
 
1128
    if (SPA_UNLIKELY(a->position.state == SPA_IO_POSITION_STATE_STARTING)) {
1129
        if (!all_ready && --a->sync_left == 0) {
1130
-           pw_log_warn("(%s-%u) sync timeout, going to RUNNING",
1131
-                   node->name, node->info.id);
1132
-           check_states(node, nsec);
1133
            pw_impl_node_rt_emit_timeout(node);
1134
+           debug_sync_timeout(node, nsec);
1135
            all_ready = true;
1136
        }
1137
        if (all_ready)
1138
@@ -1762,15 +1996,15 @@
1139
    struct pw_impl_node *node = data;
1140
    struct pw_impl_node *driver = node->driver_node;
1141
    struct pw_node_activation *a = node->rt.target.activation;
1142
-   struct spa_system *data_system = node->data_system;
1143
+   struct spa_system *data_system = node->rt.target.system;
1144
    struct pw_node_target *t, *reposition_target = NULL;;
1145
    struct pw_impl_port *p;
1146
    uint64_t nsec;
1147
 
1148
-   pw_log_trace_fp("%p: ready driver:%d exported:%d %p status:%d added:%d", node,
1149
-           node->driver, node->exported, driver, status, node->added);
1150
+   pw_log_trace_fp("%p: ready driver:%d exported:%d %p status:%d prepared:%d", node,
1151
+           node->driver, node->exported, driver, status, node->rt.prepared);
1152
 
1153
-   if (SPA_UNLIKELY(!node->added)) {
1154
+   if (SPA_UNLIKELY(!node->rt.prepared)) {
1155
        /* This can happen when we are stopping a node and removed it from the
1156
         * graph but we still have not completed the Pause/Suspend command on
1157
         * the node. In that case, the node might still emit ready events,
1158
@@ -1783,33 +2017,35 @@
1159
 
1160
    if (SPA_LIKELY(node == driver)) {
1161
        struct pw_node_activation_state *state = &a->state0;
1162
+       struct spa_io_clock *cl = &node->rt.position->clock;
1163
        int sync_type, all_ready, update_sync, target_sync;
1164
        uint32_t owner2, reposition_owner;
1165
        uint64_t min_timeout = UINT64_MAX;
1166
-
1167
-       if (SPA_UNLIKELY(a->status != PW_NODE_ACTIVATION_FINISHED)) {
1168
-           pw_log_debug("(%s-%u) graph not finished: state:%p quantum:%"PRIu64
1169
-                   " pending %d/%d", node->name, node->info.id,
1170
-                   state, a->position.clock.duration,
1171
-                   state->pending, state->required);
1172
-           check_states(node, nsec);
1173
-           pw_impl_node_rt_emit_incomplete(node);
1174
+       int32_t pending;
1175
+       int old_status;
1176
+
1177
+       if (SPA_UNLIKELY((pending = pw_node_activation_state_xchg(state)) > 0)) {
1178
+           pw_impl_node_rt_emit_incomplete(driver);
1179
+           old_status = SPA_ATOMIC_LOAD(a->status);
1180
+           if (old_status != PW_NODE_ACTIVATION_FINISHED) {
1181
+               SPA_ATOMIC_STORE(a->status, PW_NODE_ACTIVATION_TRIGGERED);
1182
+               process_node(node, nsec);
1183
+               debug_xrun_graph(node, nsec);
1184
+           }
1185
        }
1186
 
1187
        /* This update is done too late, the driver should do this
1188
         * before calling the ready callback so that it can use the new target
1189
         * duration and rate to schedule the next update. We do this here to
1190
         * help drivers that don't support this yet */
1191
-       if (SPA_UNLIKELY(node->rt.position->clock.duration != node->rt.position->clock.target_duration ||
1192
-           node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom)) {
1193
+       if (SPA_UNLIKELY(cl->duration != cl->target_duration ||
1194
+           cl->rate.denom != cl->target_rate.denom)) {
1195
            pw_log_warn("driver %s did not update duration/rate (%"PRIu64"/%"PRIu64" %u/%u)",
1196
                    node->name,
1197
-                   node->rt.position->clock.duration,
1198
-                   node->rt.position->clock.target_duration,
1199
-                   node->rt.position->clock.rate.denom,
1200
-                   node->rt.position->clock.target_rate.denom);
1201
-           node->rt.position->clock.duration = node->rt.position->clock.target_duration;
1202
-           node->rt.position->clock.rate = node->rt.position->clock.target_rate;
1203
+                   cl->duration, cl->target_duration,
1204
+                   cl->rate.denom, cl->target_rate.denom);
1205
+           cl->duration = cl->target_duration;
1206
+           cl->rate = cl->target_rate;
1207
        }
1208
 
1209
        sync_type = check_updates(node, &reposition_owner);
1210
@@ -1824,8 +2060,27 @@
1211
            struct pw_node_activation *ta = t->activation;
1212
            uint32_t id = t->id;
1213
 
1214
-           ta->status = PW_NODE_ACTIVATION_NOT_TRIGGERED;
1215
+           ta->driver_id = driver->info.id;
1216
+retry_status:
1217
            pw_node_activation_state_reset(&ta->state0);
1218
+           /* we don't change the state of inactive nodes and don't use them
1219
+            * for reposition. The pending will be at least 1 and they might
1220
+            * get decremented to 0 but since the status is inactive, we don't
1221
+            * do the atomic CAS from NOT_TRIGGERED to TRIGGERED and we don't
1222
+            * write the eventfd. */
1223
+           old_status = SPA_ATOMIC_LOAD(ta->status);
1224
+           if (SPA_UNLIKELY(old_status == PW_NODE_ACTIVATION_INACTIVE))
1225
+               continue;
1226
+
1227
+           /* if this fails, the node might just have stopped and we need to retry */
1228
+           if (SPA_UNLIKELY(!SPA_ATOMIC_CAS(ta->status, old_status, PW_NODE_ACTIVATION_NOT_TRIGGERED)))
1229
+               goto retry_status;
1230
+
1231
+           if (old_status == PW_NODE_ACTIVATION_TRIGGERED ||
1232
+               old_status == PW_NODE_ACTIVATION_AWAKE) {
1233
+               update_xrun_stats(ta, 1, nsec / 1000, 0);
1234
+               debug_xrun_target(node, t, old_status, nsec);
1235
+           }
1236
 
1237
            /* this is the node with reposition info */
1238
            if (SPA_UNLIKELY(id == reposition_owner))
1239
@@ -1847,9 +2102,7 @@
1240
            }
1241
        }
1242
 
1243
-       a->status = PW_NODE_ACTIVATION_TRIGGERED;
1244
        a->prev_signal_time = a->signal_time;
1245
-       a->signal_time = nsec;
1246
        node->driver_start = nsec;
1247
 
1248
        a->sync_timeout = SPA_MIN(min_timeout, DEFAULT_SYNC_TIMEOUT);
1249
@@ -1864,6 +2117,7 @@
1250
 
1251
        update_position(node, all_ready, nsec);
1252
 
1253
+       a->position.clock.cycle++;
1254
        pw_impl_node_rt_emit_start(node);
1255
    }
1256
    /* this should not happen, driver nodes that are not currently driving
1257
@@ -1882,7 +2136,8 @@
1258
            spa_node_process_fast(p->mix);
1259
    }
1260
    /* now signal all the nodes we drive */
1261
-   return trigger_targets(node, status, nsec);
1262
+   trigger_targets(node, status, nsec);
1263
+   return 0;
1264
 }
1265
 
1266
 static int node_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id)
1267
@@ -1903,17 +2158,16 @@
1268
 {
1269
    struct pw_impl_node *this = data;
1270
    struct pw_node_activation *a = this->rt.target.activation;
1271
-   struct pw_node_activation *da = this->rt.driver_target.activation;
1272
-   struct spa_system *data_system = this->data_system;
1273
+   struct spa_system *data_system = this->rt.target.system;
1274
    uint64_t nsec = get_time_ns(data_system);
1275
    int suppressed;
1276
 
1277
-   update_xrun_stats(a, trigger, delay);
1278
+   update_xrun_stats(a, 1, trigger, delay);
1279
 
1280
    if ((suppressed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) {
1281
        struct spa_fraction rate;
1282
-       if (da) {
1283
-           struct spa_io_clock *cl = &da->position.clock;
1284
+       if (a) {
1285
+           struct spa_io_clock *cl = &a->position.clock;
1286
            rate.num = cl->rate.num * cl->duration;
1287
            rate.denom = cl->rate.denom;
1288
        } else {
1289
@@ -1939,11 +2193,36 @@
1290
    .xrun = node_xrun,
1291
 };
1292
 
1293
+static int handle_node_param(struct pw_impl_node *node, const char *key, const char *value)
1294
+{
1295
+   const struct spa_type_info *ti;
1296
+   uint8_t buffer1024;
1297
+   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
1298
+   struct spa_pod *pod;
1299
+   int res;
1300
+
1301
+   ti = spa_debug_type_find_short(spa_type_param, key);
1302
+   if (ti == NULL)
1303
+       return -ENOENT;
1304
+
1305
+   if ((res = spa_json_to_pod(&b, 0, ti, value, strlen(value))) < 0)
1306
+       return res;
1307
+
1308
+   if ((pod = spa_pod_builder_deref(&b, 0)) == NULL)
1309
+       return -ENOSPC;
1310
+
1311
+   if ((res = pw_impl_node_set_param(node, ti->type, 0, pod)) < 0)
1312
+       return res;
1313
+
1314
+   return 0;
1315
+}
1316
+
1317
 SPA_EXPORT
1318
 int pw_impl_node_set_implementation(struct pw_impl_node *node,
1319
            struct spa_node *spa_node)
1320
 {
1321
    int res;
1322
+   const struct spa_dict_item *it;
1323
 
1324
    pw_log_debug("%p: implementation %p", node, spa_node);
1325
 
1326
@@ -1956,6 +2235,16 @@
1327
    spa_node_set_callbacks(node->node, &node_callbacks, node);
1328
    res = spa_node_add_listener(node->node, &node->listener, &node_events, node);
1329
 
1330
+again:
1331
+   spa_dict_for_each(it, &node->properties->dict) {
1332
+       if (spa_strstartswith(it->key, "node.param.")) {
1333
+           if ((res = handle_node_param(node, &it->key11, it->value)) < 0)
1334
+               pw_log_warn("can't set node param: %s", spa_strerror(res));
1335
+           pw_properties_set(node->properties, it->key, NULL);
1336
+           goto again;
1337
+       }
1338
+   }
1339
+
1340
    if (node->registered)
1341
        update_io(node);
1342
 
1343
@@ -2054,7 +2343,8 @@
1344
 
1345
    /* remove ourself as a follower from the driver node */
1346
    spa_list_remove(&node->follower_link);
1347
-   pw_impl_node_emit_peer_removed(node->driver_node, node);
1348
+   pw_node_peer_unref(spa_steal_ptr(node->from_driver_peer));
1349
+   pw_node_peer_unref(spa_steal_ptr(node->to_driver_peer));
1350
    remove_segment_owner(node->driver_node, node->info.id);
1351
 
1352
    spa_list_consume(follower, &node->follower_list, follower_link) {
1353
@@ -2107,9 +2397,14 @@
1354
 
1355
    clear_info(node);
1356
 
1357
-   spa_system_close(node->data_system, node->source.fd);
1358
+   spa_system_close(node->rt.target.system, node->source.fd);
1359
+
1360
+   if (node->data_loop)
1361
+       pw_context_release_loop(context, node->data_loop);
1362
+
1363
    free(impl->group);
1364
    free(impl->link_group);
1365
+   free(impl->sync_group);
1366
    free(impl);
1367
 
1368
 #ifdef HAVE_MALLOC_TRIM
1369
@@ -2468,7 +2763,7 @@
1370
            pw_context_recalc_graph(node->context,
1371
                    active ? "node activate" : "node deactivate");
1372
        else if (!active && node->exported)
1373
-           pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node);
1374
+           remove_node_from_graph(node);
1375
    }
1376
    return 0;
1377
 }
1378
pipewire-1.0.1.tar.bz2/src/pipewire/impl-node.h -> pipewire-1.2.0.tar.gz/src/pipewire/impl-node.h Changed
10
 
1
@@ -88,7 +88,7 @@
2
    void (*complete) (void *data);
3
    /** the driver node did not complete processing */
4
    void (*incomplete) (void *data);
5
-   /** the node had */
6
+   /** the node had a timeout */
7
    void (*timeout) (void *data);
8
 };
9
 
10
pipewire-1.0.1.tar.bz2/src/pipewire/impl-port.c -> pipewire-1.2.0.tar.gz/src/pipewire/impl-port.c Changed
588
 
1
@@ -29,7 +29,11 @@
2
 struct impl {
3
    struct pw_impl_port this;
4
    struct spa_node mix_node;   /**< mix node implementation */
5
-   struct spa_list mix_list;
6
+   struct spa_hook_list mix_hooks;
7
+
8
+   struct {
9
+       struct spa_list mix_list;
10
+   } rt;
11
 
12
    struct spa_list param_list;
13
    struct spa_list pending_list;
14
@@ -115,6 +119,71 @@
15
    }
16
 }
17
 
18
+static int mix_add_listener(void *object, struct spa_hook *listener,
19
+       const struct spa_node_events *events, void *data)
20
+{
21
+   struct impl *impl = object;
22
+   spa_hook_list_append(&impl->mix_hooks, listener, events, data);
23
+   return 0;
24
+}
25
+
26
+static int mix_port_enum_params(void *object, int seq,
27
+       enum spa_direction direction, uint32_t port_id,
28
+       uint32_t id, uint32_t start, uint32_t max,
29
+       const struct spa_pod *filter)
30
+{
31
+   struct impl *impl = object;
32
+   struct spa_pod *param;
33
+   struct spa_result_node_params result;
34
+   struct spa_pod_builder b = { 0 };
35
+   uint8_t buffer1024;
36
+   uint32_t count = 0;
37
+   int res;
38
+
39
+   pw_log_trace("%p: %d", impl, id);
40
+
41
+   result.id = id;
42
+   result.next = start;
43
+next:
44
+   result.index = result.next++;
45
+
46
+   spa_pod_builder_init(&b, buffer, sizeof(buffer));
47
+
48
+   switch (id) {
49
+   case SPA_PARAM_IO:
50
+       switch (result.index) {
51
+       case 0:
52
+           param = spa_pod_builder_add_object(&b,
53
+               SPA_TYPE_OBJECT_ParamIO, id,
54
+               SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_Buffers),
55
+               SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
56
+           break;
57
+       case 1:
58
+           param = spa_pod_builder_add_object(&b,
59
+               SPA_TYPE_OBJECT_ParamIO, id,
60
+               SPA_PARAM_IO_id,   SPA_POD_Id(SPA_IO_AsyncBuffers),
61
+               SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
62
+           break;
63
+       default:
64
+           return 0;
65
+       }
66
+       break;
67
+   default:
68
+       res = -ENOTSUP;
69
+       return res;
70
+   }
71
+
72
+   if (spa_pod_filter(&b, &result.param, param, filter) < 0)
73
+       goto next;
74
+
75
+   spa_node_emit_result(&impl->mix_hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
76
+
77
+   if (++count != max)
78
+       goto next;
79
+
80
+   return 0;
81
+}
82
+
83
 static struct pw_impl_port_mix *find_mix(struct pw_impl_port *port,
84
        enum spa_direction direction, uint32_t port_id)
85
 {
86
@@ -134,9 +203,9 @@
87
    struct pw_impl_port *this = mix->p;
88
    struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
89
    pw_log_trace("%p: add mix %p", this, mix);
90
-   if (!mix->active) {
91
-       spa_list_append(&impl->mix_list, &mix->rt_link);
92
-       mix->active = true;
93
+   if (!mix->rt.active) {
94
+       spa_list_append(&impl->rt.mix_list, &mix->rt.link);
95
+       mix->rt.active = true;
96
    }
97
    return 0;
98
 }
99
@@ -148,9 +217,9 @@
100
    struct pw_impl_port_mix *mix = user_data;
101
    struct pw_impl_port *this = mix->p;
102
    pw_log_trace("%p: remove mix %p", this, mix);
103
-   if (mix->active) {
104
-       spa_list_remove(&mix->rt_link);
105
-       mix->active = false;
106
+   if (mix->rt.active) {
107
+       spa_list_remove(&mix->rt.link);
108
+       mix->rt.active = false;
109
    }
110
    return 0;
111
 }
112
@@ -167,13 +236,22 @@
113
    if (mix == NULL)
114
        return -ENOENT;
115
 
116
-   if (id == SPA_IO_Buffers) {
117
+   switch (id) {
118
+   case SPA_IO_Buffers:
119
+   case SPA_IO_AsyncBuffers:
120
        if (data == NULL || size == 0) {
121
            pw_loop_invoke(this->node->data_loop,
122
                   do_remove_mix, SPA_ID_INVALID, NULL, 0, true, mix);
123
-           mix->io = NULL;
124
+           mix->io_data = mix->io0 = mix->io1 = NULL;
125
        } else if (data != NULL && size >= sizeof(struct spa_io_buffers)) {
126
-           mix->io = data;
127
+           if (size >= sizeof(struct spa_io_async_buffers)) {
128
+               struct spa_io_async_buffers *ab = data;
129
+               mix->io_data = data;
130
+               mix->io0 = &ab->buffersthis->direction;
131
+               mix->io1 = &ab->buffersthis->direction^1;
132
+           } else {
133
+               mix->io_data = mix->io0 = mix->io1 = data;
134
+           }
135
            pw_loop_invoke(this->node->data_loop,
136
                   do_add_mix, SPA_ID_INVALID, NULL, 0, false, mix);
137
        }
138
@@ -187,12 +265,13 @@
139
    struct pw_impl_port *this = &impl->this;
140
    struct pw_impl_port_mix *mix;
141
    struct spa_io_buffers *io = &this->rt.io;
142
+   uint32_t cycle = this->node->rt.position->clock.cycle & 1;
143
 
144
-   pw_log_trace_fp("%p: tee input %d %d", this, io->status, io->buffer_id);
145
-   spa_list_for_each(mix, &impl->mix_list, rt_link) {
146
-       pw_log_trace_fp("%p: port %d %p->%p %d", this,
147
-               mix->port.port_id, io, mix->io, mix->io->buffer_id);
148
-       *mix->io = *io;
149
+   pw_log_trace_fp("%p: tee input status:%d id:%d cycle:%d", this, io->status, io->buffer_id, cycle);
150
+   spa_list_for_each(mix, &impl->rt.mix_list, rt.link) {
151
+       pw_log_trace_fp("%p: port %d %p->%p id:%d", this,
152
+               mix->port.port_id, io, mix->iocycle, mix->iocycle->buffer_id);
153
+       *mix->iocycle = *io;
154
    }
155
    io->status = SPA_STATUS_NEED_DATA;
156
 
157
@@ -211,9 +290,11 @@
158
 
159
 static const struct spa_node_methods schedule_tee_node = {
160
    SPA_VERSION_NODE_METHODS,
161
-   .process = tee_process,
162
+   .add_listener = mix_add_listener,
163
+   .port_enum_params = mix_port_enum_params,
164
    .port_set_io = port_set_io,
165
    .port_reuse_buffer = tee_reuse_buffer,
166
+   .process = tee_process,
167
 };
168
 
169
 static int schedule_mix_input(void *object)
170
@@ -222,15 +303,17 @@
171
    struct pw_impl_port *this = &impl->this;
172
    struct spa_io_buffers *io = &this->rt.io;
173
    struct pw_impl_port_mix *mix;
174
+   uint32_t cycle = this->node->rt.position->clock.cycle & 1;
175
 
176
    if (SPA_UNLIKELY(PW_IMPL_PORT_IS_CONTROL(this)))
177
        return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA;
178
 
179
-   spa_list_for_each(mix, &impl->mix_list, rt_link) {
180
-       pw_log_trace_fp("%p: mix input %d %p->%p %d %d", this,
181
-               mix->port.port_id, mix->io, io, mix->io->status, mix->io->buffer_id);
182
-       *io = *mix->io;
183
-       mix->io->status = SPA_STATUS_NEED_DATA;
184
+   spa_list_for_each(mix, &impl->rt.mix_list, rt.link) {
185
+       pw_log_trace_fp("%p: mix input %d %p->%p status:%d id:%d cycle:%d", this,
186
+               mix->port.port_id, mix->iocycle, io,
187
+               mix->iocycle->status, mix->iocycle->buffer_id, cycle);
188
+       *io = *mix->iocycle;
189
+       mix->iocycle->status = SPA_STATUS_NEED_DATA;
190
        break;
191
    }
192
         return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA;
193
@@ -241,7 +324,7 @@
194
    struct impl *impl = object;
195
    struct pw_impl_port_mix *mix;
196
 
197
-   spa_list_for_each(mix, &impl->mix_list, rt_link) {
198
+   spa_list_for_each(mix, &impl->rt.mix_list, rt.link) {
199
        pw_log_trace_fp("%p: reuse buffer %d %d", impl, port_id, buffer_id);
200
        /* FIXME send reuse buffer to peer */
201
        break;
202
@@ -251,9 +334,11 @@
203
 
204
 static const struct spa_node_methods schedule_mix_node = {
205
    SPA_VERSION_NODE_METHODS,
206
-   .process = schedule_mix_input,
207
+   .add_listener = mix_add_listener,
208
+   .port_enum_params = mix_port_enum_params,
209
    .port_set_io = port_set_io,
210
    .port_reuse_buffer = schedule_mix_reuse_buffer,
211
+   .process = schedule_mix_input,
212
 };
213
 
214
 SPA_EXPORT
215
@@ -348,7 +433,7 @@
216
        spa_node_port_set_io(node->node,
217
                     port->direction, port->port_id,
218
                     SPA_IO_Buffers,
219
-                    NULL, sizeof(port->rt.io));
220
+                    NULL, 0);
221
 
222
        pw_impl_port_set_param(port, SPA_PARAM_Format, 0, NULL);
223
    }
224
@@ -440,6 +525,40 @@
225
    }
226
 }
227
 
228
+static int check_param_io(void *data, int seq, uint32_t id,
229
+       uint32_t index, uint32_t next, struct spa_pod *param)
230
+{
231
+   struct pw_impl_port *port = data;
232
+   struct pw_impl_node *node = port->node;
233
+   uint32_t pid, psize;
234
+
235
+   if (spa_pod_parse_object(param,
236
+           SPA_TYPE_OBJECT_ParamIO, NULL,
237
+           SPA_PARAM_IO_id,   SPA_POD_Id(&pid),
238
+           SPA_PARAM_IO_size, SPA_POD_Int(&psize)) < 0)
239
+       return 0;
240
+
241
+   pw_log_debug("%p: got io id:%d (%s)", port, pid,
242
+           spa_debug_type_find_name(spa_type_io, pid));
243
+
244
+   switch (pid) {
245
+   case SPA_IO_Control:
246
+   case SPA_IO_Notify:
247
+       pw_control_new(node->context, port, pid, psize, 0);
248
+       SPA_FLAG_SET(port->flags, PW_IMPL_PORT_FLAG_CONTROL);
249
+       break;
250
+   case SPA_IO_AsyncBuffers:
251
+       SPA_FLAG_SET(port->flags, PW_IMPL_PORT_FLAG_ASYNC);
252
+       SPA_FALLTHROUGH;
253
+   case SPA_IO_Buffers:
254
+       SPA_FLAG_SET(port->flags, PW_IMPL_PORT_FLAG_BUFFERS);
255
+       break;
256
+   default:
257
+       break;
258
+   }
259
+   return 0;
260
+}
261
+
262
 static int process_latency_param(void *data, int seq,
263
        uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param)
264
 {
265
@@ -498,6 +617,21 @@
266
    return 0;
267
 }
268
 
269
+static void check_params(struct pw_impl_port *port)
270
+{
271
+   uint32_t i;
272
+   for (i = 0; i < port->info.n_params; i++)
273
+       port->info.paramsi.user = 0;
274
+
275
+   port->flags &= ~(PW_IMPL_PORT_FLAG_CONTROL |
276
+           PW_IMPL_PORT_FLAG_ASYNC |
277
+           PW_IMPL_PORT_FLAG_BUFFERS);
278
+
279
+   pw_impl_port_for_each_param(port, 0, SPA_PARAM_IO, 0, 0, NULL, check_param_io, port);
280
+   pw_impl_port_for_each_param(port, 0, SPA_PARAM_Latency, 0, 0, NULL, process_latency_param, port);
281
+   pw_impl_port_for_each_param(port, 0, SPA_PARAM_Tag, 0, 0, NULL, process_tag_param, port);
282
+}
283
+
284
 static void update_info(struct pw_impl_port *port, const struct spa_port_info *info)
285
 {
286
    uint32_t changed_idsMAX_PARAMS, n_changed_ids = 0;
287
@@ -585,7 +719,7 @@
288
    spa_list_init(&impl->param_list);
289
    spa_list_init(&impl->pending_list);
290
    impl->cache_params = true;
291
-   spa_list_init(&impl->mix_list);
292
+   spa_list_init(&impl->rt.mix_list);
293
 
294
    this = &impl->this;
295
 
296
@@ -641,6 +775,7 @@
297
            SPA_TYPE_INTERFACE_Node,
298
            SPA_VERSION_NODE,
299
            mix_methods, impl);
300
+   spa_hook_list_init(&impl->mix_hooks);
301
 
302
    pw_impl_port_set_mix(this, NULL, 0);
303
 
304
@@ -682,6 +817,7 @@
305
                 pw_direction_reverse(port->direction), 0,
306
                 SPA_IO_Buffers, NULL, 0);
307
    }
308
+
309
    if (port->mix_handle != NULL) {
310
        pw_unload_spa_handle(port->mix_handle);
311
        port->mix_handle = NULL;
312
@@ -690,7 +826,7 @@
313
    port->mix_flags = flags;
314
    port->mix = node;
315
 
316
-   if (port->mix) {
317
+   if (port->mix && !port->destroying) {
318
        spa_list_for_each(mix, &port->mix_list, link)
319
            spa_node_add_port(port->mix, mix->port.direction, mix->port.port_id, NULL);
320
 
321
@@ -698,6 +834,14 @@
322
                 pw_direction_reverse(port->direction), 0,
323
                 SPA_IO_Buffers,
324
                 &port->rt.io, sizeof(port->rt.io));
325
+
326
+       if (port->node && port->node->rt.position) {
327
+           spa_node_set_io(port->mix,
328
+                    SPA_IO_Position,
329
+                    port->node->rt.position,
330
+                    sizeof(struct spa_io_position));
331
+       }
332
+       check_params(port);
333
    }
334
    return 0;
335
 }
336
@@ -708,7 +852,7 @@
337
    int res;
338
    const char *fallback_lib, *factory_name;
339
    struct spa_handle *handle;
340
-   struct spa_dict_item items2;
341
+   struct spa_dict_item items3;
342
    char quantum_limit16;
343
    void *iface;
344
    struct pw_context *context = port->node->context;
345
@@ -716,7 +860,7 @@
346
    if ((res = spa_format_parse(param, &media_type, &media_subtype)) < 0)
347
        return res;
348
 
349
-   pw_log_debug("%p: %s/%s", port,
350
+   pw_log_debug("%p/%p: %s/%s", port->node, port,
351
            spa_debug_type_find_name(spa_type_media_type, media_type),
352
            spa_debug_type_find_name(spa_type_media_subtype, media_subtype));
353
 
354
@@ -764,6 +908,7 @@
355
    spa_scnprintf(quantum_limit, sizeof(quantum_limit), "%u",
356
            context->settings.clock_quantum_limit);
357
    items1 = SPA_DICT_ITEM_INIT("clock.quantum-limit", quantum_limit);
358
+   items2 = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LOOP_NAME, port->node->data_loop->name);
359
 
360
    handle = pw_context_load_spa_handle(context, factory_name,
361
            &SPA_DICT_INIT_ARRAY(items));
362
@@ -776,7 +921,8 @@
363
        return res;
364
    }
365
 
366
-   pw_log_debug("mix node handle:%p iface:%p", handle, iface);
367
+   pw_log_debug("mix node %s (%s) handle:%p iface:%p", factory_name, fallback_lib,
368
+           handle, iface);
369
    pw_impl_port_set_mix(port, (struct spa_node*)iface,
370
            PW_IMPL_PORT_MIX_FLAG_MULTI |
371
            PW_IMPL_PORT_MIX_FLAG_NEGOTIATE);
372
@@ -849,46 +995,20 @@
373
 {
374
         struct pw_impl_port *this = user_data;
375
 
376
-   pw_log_trace("%p: add port", this);
377
+   pw_log_trace("%p: add port, added:%d", this, this->rt.added);
378
+
379
+   if (this->rt.added)
380
+       return 0;
381
+
382
    if (this->direction == PW_DIRECTION_INPUT)
383
        spa_list_append(&this->node->rt.input_mix, &this->rt.node_link);
384
    else
385
        spa_list_append(&this->node->rt.output_mix, &this->rt.node_link);
386
+   this->rt.added = true;
387
 
388
    return 0;
389
 }
390
 
391
-static int check_param_io(void *data, int seq, uint32_t id,
392
-       uint32_t index, uint32_t next, struct spa_pod *param)
393
-{
394
-   struct pw_impl_port *port = data;
395
-   struct pw_impl_node *node = port->node;
396
-   uint32_t pid, psize;
397
-
398
-   if (spa_pod_parse_object(param,
399
-           SPA_TYPE_OBJECT_ParamIO, NULL,
400
-           SPA_PARAM_IO_id,   SPA_POD_Id(&pid),
401
-           SPA_PARAM_IO_size, SPA_POD_Int(&psize)) < 0)
402
-       return 0;
403
-
404
-   pw_log_debug("%p: got io id:%d (%s)", port, pid,
405
-           spa_debug_type_find_name(spa_type_io, pid));
406
-
407
-   switch (pid) {
408
-   case SPA_IO_Control:
409
-   case SPA_IO_Notify:
410
-       pw_control_new(node->context, port, pid, psize, 0);
411
-       SPA_FLAG_SET(port->flags, PW_IMPL_PORT_FLAG_CONTROL);
412
-       break;
413
-   case SPA_IO_Buffers:
414
-       SPA_FLAG_SET(port->flags, PW_IMPL_PORT_FLAG_BUFFERS);
415
-       break;
416
-   default:
417
-       break;
418
-   }
419
-   return 0;
420
-}
421
-
422
 static int reply_param(void *data, int seq, uint32_t id,
423
        uint32_t index, uint32_t next, struct spa_pod *param)
424
 {
425
@@ -1014,7 +1134,6 @@
426
             struct pw_properties *properties)
427
 {
428
    static const char * const keys = {
429
-       PW_KEY_OBJECT_SERIAL,
430
        PW_KEY_OBJECT_PATH,
431
        PW_KEY_FORMAT_DSP,
432
        PW_KEY_NODE_ID,
433
@@ -1029,6 +1148,7 @@
434
        PW_KEY_PORT_ALIAS,
435
        PW_KEY_PORT_EXTRA,
436
        PW_KEY_PORT_IGNORE_LATENCY,
437
+       PW_KEY_PORT_GROUP,
438
        NULL
439
    };
440
 
441
@@ -1099,9 +1219,7 @@
442
 
443
    pw_impl_node_emit_port_init(node, port);
444
 
445
-   pw_impl_port_for_each_param(port, 0, SPA_PARAM_IO, 0, 0, NULL, check_param_io, port);
446
-   pw_impl_port_for_each_param(port, 0, SPA_PARAM_Latency, 0, 0, NULL, process_latency_param, port);
447
-   pw_impl_port_for_each_param(port, 0, SPA_PARAM_Tag, 0, 0, NULL, process_tag_param, port);
448
+   check_params(port);
449
 
450
    nprops = pw_impl_node_get_properties(node);
451
    media_class = pw_properties_get(nprops, PW_KEY_MEDIA_CLASS);
452
@@ -1224,11 +1342,11 @@
453
    if (is_control) {
454
        pw_log_debug("%p: setting node control", port);
455
    } else {
456
-       pw_log_debug("%p: setting mixer io", port);
457
-       spa_node_port_set_io(port->mix,
458
-                pw_direction_reverse(port->direction), 0,
459
-                SPA_IO_Buffers,
460
-                &port->rt.io, sizeof(port->rt.io));
461
+       pw_log_debug("%p: setting mixer position io", port);
462
+       spa_node_set_io(port->mix,
463
+                SPA_IO_Position,
464
+                node->rt.position,
465
+                sizeof(struct spa_io_position));
466
    }
467
 
468
    pw_log_debug("%p: %d add to node %p", port, port_id, node);
469
@@ -1271,8 +1389,11 @@
470
 {
471
    struct pw_impl_port *this = user_data;
472
 
473
-   pw_log_trace("%p: remove port", this);
474
-   spa_list_remove(&this->rt.node_link);
475
+   pw_log_trace("%p: remove port, added:%d", this, this->rt.added);
476
+   if (this->rt.added) {
477
+       spa_list_remove(&this->rt.node_link);
478
+       this->rt.added = false;
479
+   }
480
 
481
    return 0;
482
 }
483
@@ -1285,13 +1406,9 @@
484
    if (node == NULL)
485
        return;
486
 
487
-   pw_log_debug("%p: remove added:%d", port, port->added);
488
+   pw_log_debug("%p: remove", port);
489
 
490
-   if (port->added) {
491
-       pw_loop_invoke(node->data_loop, do_remove_port,
492
-                  SPA_ID_INVALID, NULL, 0, true, port);
493
-       port->added = false;
494
-   }
495
+   pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
496
 
497
    if (SPA_FLAG_IS_SET(port->flags, PW_IMPL_PORT_FLAG_TO_REMOVE)) {
498
        if ((res = spa_node_remove_port(node->node, port->direction, port->port_id)) < 0)
499
@@ -1403,7 +1520,6 @@
500
 {
501
    int res;
502
    struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this);
503
-   struct pw_impl_node *node = port->node;
504
    struct result_port_params_data user_data = { impl, data, callback, seq, false };
505
    struct spa_hook listener;
506
    struct spa_param_info *pi;
507
@@ -1455,6 +1571,9 @@
508
        }
509
        res = 0;
510
    } else {
511
+       struct spa_node *qnode;
512
+       uint32_t qport;
513
+
514
        user_data.cache = impl->cache_params &&
515
            (filter == NULL && index == 0 && max == UINT32_MAX);
516
 
517
@@ -1462,11 +1581,20 @@
518
            pw_param_add(&impl->pending_list, seq, param_id, NULL);
519
 
520
        spa_zero(listener);
521
-       spa_node_add_listener(node->node, &listener, &node_events, &user_data);
522
-       res = spa_node_port_enum_params(node->node, seq,
523
-                       port->direction, port->port_id,
524
-                       param_id, index, max,
525
-                       filter);
526
+       switch (param_id) {
527
+       case SPA_PARAM_IO:
528
+           qnode = port->mix;
529
+           qport = SPA_ID_INVALID;
530
+           break;
531
+       default:
532
+           qnode = port->node->node;
533
+           qport = port->port_id;
534
+           break;
535
+       }
536
+       spa_node_add_listener(qnode, &listener, &node_events, &user_data);
537
+       res = spa_node_port_enum_params(qnode, seq,
538
+               port->direction, qport,
539
+               param_id, index, max, filter);
540
        spa_hook_remove(&listener);
541
 
542
        if (user_data.cache) {
543
@@ -1716,7 +1844,7 @@
544
    pw_log_debug("%p: %d set param on node %d:%d id:%d (%s): %d (%s)", port, port->state,
545
            port->direction, port->port_id, id,
546
            spa_debug_type_find_name(spa_type_param, id),
547
-           res, spa_strerror(res));
548
+           res, res <= 0 ? spa_strerror(res) : "modified");
549
 
550
    /* set the parameters on all ports of the mixer node if possible */
551
    if (res >= 0) {
552
@@ -1741,10 +1869,7 @@
553
    if (id == SPA_PARAM_Format) {
554
        pw_log_debug("%p: %d %p %d", port, port->state, param, res);
555
 
556
-       if (port->added) {
557
-           pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
558
-           port->added = false;
559
-       }
560
+       pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
561
        /* setting the format always destroys the negotiated buffers */
562
        if (port->direction == PW_DIRECTION_OUTPUT) {
563
            struct pw_impl_link *l;
564
@@ -1790,10 +1915,7 @@
565
                port, port->direction, port->port_id, n_buffers, node->node,
566
                alloc_flags);
567
 
568
-       if (port->added) {
569
-           pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
570
-           port->added = false;
571
-       }
572
+       pw_loop_invoke(node->data_loop, do_remove_port, SPA_ID_INVALID, NULL, 0, true, port);
573
 
574
        pw_buffers_clear(&port->mix_buffers);
575
 
576
@@ -1824,10 +1946,8 @@
577
                 pw_direction_reverse(port->direction), 0,
578
                 0, buffers, n_buffers);
579
    }
580
-   if (!port->added && n_buffers > 0) {
581
+   if (n_buffers > 0)
582
        pw_loop_invoke(node->data_loop, do_add_port, SPA_ID_INVALID, NULL, 0, false, port);
583
-       port->added = true;
584
-   }
585
    return res;
586
 }
587
 
588
pipewire-1.0.1.tar.bz2/src/pipewire/keys.h -> pipewire-1.2.0.tar.gz/src/pipewire/keys.h Changed
87
 
1
@@ -40,6 +40,12 @@
2
 
3
 #define PW_KEY_SEC_SOCKET      "pipewire.sec.socket"   /**< client socket name, set by protocol */
4
 
5
+#define PW_KEY_SEC_ENGINE      "pipewire.sec.engine"   /**< client secure context engine, set by protocol.
6
+                                 *  This can also be set by a client when making a
7
+                                 *  new security context. */
8
+#define PW_KEY_SEC_APP_ID      "pipewire.sec.app-id"   /**< client secure application id */
9
+#define PW_KEY_SEC_INSTANCE_ID     "pipewire.sec.instance-id"  /**< client secure instance id */
10
+
11
 #define PW_KEY_LIBRARY_NAME_SYSTEM "library.name.system"   /**< name of the system library to use */
12
 #define PW_KEY_LIBRARY_NAME_LOOP   "library.name.loop" /**< name of the loop library to use */
13
 #define PW_KEY_LIBRARY_NAME_DBUS   "library.name.dbus" /**< name of the dbus library to use */
14
@@ -63,6 +69,15 @@
15
 #define PW_KEY_CONFIG_OVERRIDE_PREFIX  "config.override.prefix"    /**< a config override prefix directory */
16
 #define PW_KEY_CONFIG_OVERRIDE_NAME    "config.override.name"  /**< a config override file name */
17
 
18
+/* loop */
19
+#define PW_KEY_LOOP_NAME       "loop.name"     /**< the name of a loop */
20
+#define PW_KEY_LOOP_CLASS      "loop.class"        /**< the classes this loop handles, array of strings */
21
+#define PW_KEY_LOOP_RT_PRIO        "loop.rt-prio"      /**< realtime priority of the loop */
22
+#define PW_KEY_LOOP_CANCEL     "loop.cancel"       /**< if the loop can be canceled */
23
+#define PW_KEY_LOOP_RETRY_TIMEOUT  "loop.retry-timeout"    /**< when the loop invoke queue is full, the timeout
24
+                                 *  in microseconds before retrying.
25
+                                 *  default = 1 second, 0 = disable */
26
+
27
 /* context */
28
 #define PW_KEY_CONTEXT_PROFILE_MODULES "context.profile.modules"   /**< a context profile for modules, deprecated */
29
 #define PW_KEY_USER_NAME       "context.user-name" /**< The user name that runs pipewire */
30
@@ -139,6 +154,12 @@
31
                                  *  in the same group are always scheduled
32
                                  *  with the same driver. Can be an array of
33
                                  *  group names. */
34
+#define PW_KEY_NODE_SYNC_GROUP     "node.sync-group"   /**< the sync group this node is part of. Nodes
35
+                                 *  in the same sync group are always scheduled
36
+                                 *  together with the same driver when the sync
37
+                                 *  is active. Can be an array of sync names. */
38
+#define PW_KEY_NODE_SYNC       "node.sync"     /**< if the sync-group is active or not */
39
+#define PW_KEY_NODE_TRANSPORT      "node.transport"    /**< if the transport is active or not */
40
 #define PW_KEY_NODE_EXCLUSIVE      "node.exclusive"    /**< node wants exclusive access to resources */
41
 #define PW_KEY_NODE_AUTOCONNECT        "node.autoconnect"  /**< node wants to be automatically connected
42
                                  *  to a compatible node */
43
@@ -170,6 +191,11 @@
44
 #define PW_KEY_NODE_CACHE_PARAMS   "node.cache-params" /**< cache the node params */
45
 #define PW_KEY_NODE_TRANSPORT_SYNC "node.transport.sync"   /**< the node handles transport sync */
46
 #define PW_KEY_NODE_DRIVER     "node.driver"       /**< node can drive the graph */
47
+#define PW_KEY_NODE_DRIVER_ID      "node.driver-id"    /**< the node id of the node assigned as driver
48
+                                 *   for this node */
49
+#define PW_KEY_NODE_ASYNC      "node.async"        /**< the node wants async scheduling */
50
+#define PW_KEY_NODE_LOOP_NAME      "node.loop.name"    /**< the loop name fnmatch pattern to run in */
51
+#define PW_KEY_NODE_LOOP_CLASS     "node.loop.class"   /**< the loop class fnmatch pattern to run in */
52
 #define PW_KEY_NODE_STREAM     "node.stream"       /**< node is a stream, the server side should
53
                                  *  add a converter */
54
 #define PW_KEY_NODE_VIRTUAL        "node.virtual"      /**< the node is some sort of virtual
55
@@ -205,6 +231,7 @@
56
                                  *  should be prefixed. "jack:flags:56" */
57
 #define PW_KEY_PORT_PASSIVE        "port.passive"      /**< the ports wants passive links, since 0.3.67 */
58
 #define PW_KEY_PORT_IGNORE_LATENCY "port.ignore-latency"   /**< latency ignored by peers, since 0.3.71 */
59
+#define PW_KEY_PORT_GROUP      "port.group"        /**< the port group of the port 1.2.0 */
60
 
61
 /** link properties */
62
 #define PW_KEY_LINK_ID         "link.id"       /**< a link id */
63
@@ -218,6 +245,7 @@
64
 #define PW_KEY_LINK_FEEDBACK       "link.feedback"     /**< indicate that a link is a feedback
65
                                  *  link and the target will receive data
66
                                  *  in the next cycle */
67
+#define PW_KEY_LINK_ASYNC      "link.async"        /**< the link is using async io */
68
 
69
 /** device properties */
70
 #define PW_KEY_DEVICE_ID       "device.id"     /**< device id */
71
@@ -268,6 +296,7 @@
72
 #define PW_KEY_MODULE_USAGE        "module.usage"      /**< a human readable usage description of
73
                                  *  the module's arguments. */
74
 #define PW_KEY_MODULE_VERSION      "module.version"    /**< a version string for the module. */
75
+#define PW_KEY_MODULE_DEPRECATED   "module.deprecated" /**< the module is deprecated with this message */
76
 
77
 /** Factory properties */
78
 #define PW_KEY_FACTORY_ID      "factory.id"        /**< the factory id */
79
@@ -303,6 +332,7 @@
80
 #define PW_KEY_MEDIA_NAME      "media.name"        /**< media name. Ex: "Pink Floyd: Time" */
81
 #define PW_KEY_MEDIA_TITLE     "media.title"       /**< title. Ex: "Time" */
82
 #define PW_KEY_MEDIA_ARTIST        "media.artist"      /**< artist. Ex: "Pink Floyd" */
83
+#define PW_KEY_MEDIA_ALBUM     "media.album"       /**< album. Ex: "Dark Side of the Moon" */
84
 #define PW_KEY_MEDIA_COPYRIGHT     "media.copyright"   /**< copyright string */
85
 #define PW_KEY_MEDIA_SOFTWARE      "media.software"    /**< generator software */
86
 #define PW_KEY_MEDIA_LANGUAGE      "media.language"    /**< language in POSIX format. Ex: en_GB */
87
pipewire-1.0.1.tar.bz2/src/pipewire/log.c -> pipewire-1.2.0.tar.gz/src/pipewire/log.c Changed
349
 
1
@@ -4,6 +4,7 @@
2
 
3
 #include <limits.h>
4
 #include <fnmatch.h>
5
+#include <pthread.h>
6
 
7
 #include <spa/support/log-impl.h>
8
 
9
@@ -12,23 +13,38 @@
10
 #include <spa/debug/format.h>
11
 #include <spa/pod/iter.h>
12
 #include <spa/utils/list.h>
13
+#include <spa/utils/string.h>
14
 
15
 #include <pipewire/log.h>
16
 #include <pipewire/private.h>
17
 
18
 SPA_LOG_IMPL(default_log);
19
 
20
-#define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_WARN
21
-
22
 SPA_EXPORT
23
 enum spa_log_level pw_log_level = DEFAULT_LOG_LEVEL;
24
 
25
 static struct spa_log *global_log = &default_log.log;
26
 
27
 SPA_EXPORT
28
-struct spa_log_topic *PW_LOG_TOPIC_DEFAULT;
29
+struct spa_log_topic * const PW_LOG_TOPIC_DEFAULT;
30
+
31
+struct topic {
32
+   struct spa_list link;
33
+   struct spa_log_topic *t;
34
+   unsigned int refcnt;
35
+};
36
+
37
+struct pattern {
38
+   struct spa_list link;
39
+   enum spa_log_level level;
40
+   char pattern;
41
+};
42
+
43
+static struct spa_list topics = SPA_LIST_INIT(&topics);
44
+static struct spa_list patterns = SPA_LIST_INIT(&patterns);
45
+
46
+static pthread_mutex_t topics_lock = PTHREAD_MUTEX_INITIALIZER;
47
 
48
-PW_LOG_TOPIC_STATIC(log_topic, "pw.log"); /* log topic for this file here */
49
 PW_LOG_TOPIC(log_buffers, "pw.buffers");
50
 PW_LOG_TOPIC(log_client, "pw.client");
51
 PW_LOG_TOPIC(log_conf, "pw.conf");
52
@@ -57,6 +73,126 @@
53
 
54
 PW_LOG_TOPIC(PW_LOG_TOPIC_DEFAULT, "default");
55
 
56
+
57
+static struct topic *find_topic(struct spa_log_topic *t)
58
+{
59
+   struct topic *topic;
60
+
61
+   spa_list_for_each(topic, &topics, link)
62
+       if (topic->t == t)
63
+           return topic;
64
+
65
+   return NULL;
66
+}
67
+
68
+static struct topic *add_topic(struct spa_log_topic *t)
69
+{
70
+   struct topic *topic;
71
+
72
+   topic = calloc(1, sizeof(struct topic));
73
+   if (!topic)
74
+       return NULL;
75
+
76
+   topic->t = t;
77
+   spa_list_append(&topics, &topic->link);
78
+   return topic;
79
+}
80
+
81
+static void update_topic_level(struct spa_log_topic *t)
82
+{
83
+   enum spa_log_level level = pw_log_level;
84
+   bool has_custom_level = false;
85
+   const char *topic = t->topic;
86
+   struct pattern *pattern;
87
+
88
+   spa_list_for_each(pattern, &patterns, link) {
89
+       if (fnmatch(pattern->pattern, topic, 0) != 0)
90
+           continue;
91
+
92
+       level = pattern->level;
93
+       has_custom_level = true;
94
+       break;
95
+   }
96
+
97
+   t->level = level;
98
+   t->has_custom_level = has_custom_level;
99
+}
100
+
101
+static void update_all_topic_levels(void)
102
+{
103
+   struct topic *topic;
104
+
105
+   pthread_mutex_lock(&topics_lock);
106
+
107
+   spa_list_for_each(topic, &topics, link)
108
+       update_topic_level(topic->t);
109
+
110
+   pthread_mutex_unlock(&topics_lock);
111
+}
112
+
113
+SPA_EXPORT
114
+void pw_log_topic_register(struct spa_log_topic *t)
115
+{
116
+   struct topic *topic;
117
+
118
+   pthread_mutex_lock(&topics_lock);
119
+
120
+   topic = find_topic(t);
121
+   if (!topic) {
122
+       update_topic_level(t);
123
+       topic = add_topic(t);
124
+       if (!topic)
125
+           goto done;
126
+   }
127
+
128
+   ++topic->refcnt;
129
+
130
+done:
131
+   pthread_mutex_unlock(&topics_lock);
132
+}
133
+
134
+SPA_EXPORT
135
+void pw_log_topic_unregister(struct spa_log_topic *t)
136
+{
137
+   struct topic *topic;
138
+
139
+   pthread_mutex_lock(&topics_lock);
140
+
141
+   topic = find_topic(t);
142
+   if (!topic)
143
+       goto done;
144
+
145
+   if (topic->refcnt-- <= 1) {
146
+       spa_list_remove(&topic->link);
147
+       free(topic);
148
+   }
149
+
150
+done:
151
+   pthread_mutex_unlock(&topics_lock);
152
+}
153
+
154
+void pw_log_topic_register_enum(const struct spa_log_topic_enum *e)
155
+{
156
+   struct spa_log_topic * const *t;
157
+
158
+   if (!e)
159
+       return;
160
+
161
+   for (t = e->topics; t < e->topics_end; ++t)
162
+       pw_log_topic_register(*t);
163
+}
164
+
165
+void pw_log_topic_unregister_enum(const struct spa_log_topic_enum *e)
166
+{
167
+   struct spa_log_topic * const *t;
168
+
169
+   if (!e)
170
+       return;
171
+
172
+   for (t = e->topics; t < e->topics_end; ++t)
173
+       pw_log_topic_unregister(*t);
174
+}
175
+
176
 /** Set the global log interface
177
  * \param log the global log to set
178
  */
179
@@ -89,6 +225,118 @@
180
 {
181
    pw_log_level = level;
182
    global_log->level = level;
183
+
184
+   update_all_topic_levels();
185
+}
186
+
187
+static int add_pattern(struct spa_list *list, const char *str, enum spa_log_level level)
188
+{
189
+   struct pattern *pattern;
190
+   size_t len = strlen(str);
191
+
192
+   pattern = calloc(1, sizeof(struct pattern) + len + 1);
193
+   if (!pattern)
194
+       return -errno;
195
+
196
+   pattern->level = level;
197
+   memcpy(pattern->pattern, str, len);
198
+   spa_list_append(list, &pattern->link);
199
+   return 0;
200
+}
201
+
202
+static bool
203
+parse_log_level(const char *str, enum spa_log_level *l)
204
+{
205
+   uint32_t lvl;
206
+
207
+   if (!str)
208
+       return false;
209
+
210
+   if (strlen(str) == 1) {
211
+       /* SPA levels, plus a few duplicate codes that
212
+        * WirePlumber supports for some GLib levels. */
213
+       switch (str0) {
214
+       case 'X': lvl = SPA_LOG_LEVEL_NONE; break;
215
+       case 'F': lvl = SPA_LOG_LEVEL_NONE; break; /* fatal */
216
+       case 'E': lvl = SPA_LOG_LEVEL_ERROR; break;
217
+       case 'W': lvl = SPA_LOG_LEVEL_WARN; break;
218
+       case 'N': lvl = SPA_LOG_LEVEL_WARN; break; /* notice */
219
+       case 'I': lvl = SPA_LOG_LEVEL_INFO; break;
220
+       case 'D': lvl = SPA_LOG_LEVEL_DEBUG; break;
221
+       case 'T': lvl = SPA_LOG_LEVEL_TRACE; break;
222
+       default:
223
+           goto check_int;
224
+       }
225
+   } else {
226
+   check_int:
227
+       if (!spa_atou32(str, &lvl, 0))
228
+           return false;
229
+       if (lvl > SPA_LOG_LEVEL_TRACE)
230
+           return false;
231
+   }
232
+
233
+   *l = lvl;
234
+   return true;
235
+}
236
+
237
+static int
238
+parse_log_string(const char *str, struct spa_list *list, enum spa_log_level *level)
239
+{
240
+   struct spa_list new_patterns = SPA_LIST_INIT(&new_patterns);
241
+   int n_tokens;
242
+
243
+   *level = DEFAULT_LOG_LEVEL;
244
+   if (!str || !*str)
245
+       return 0;
246
+
247
+   spa_auto(pw_strv) tokens = pw_split_strv(str, ",", INT_MAX, &n_tokens);
248
+   if (n_tokens > 0) {
249
+       int i;
250
+
251
+       for (i = 0; i < n_tokens; i++) {
252
+           int n_tok;
253
+           char *tok2;
254
+           enum spa_log_level lvl;
255
+
256
+           n_tok = pw_split_ip(tokensi, ":", SPA_N_ELEMENTS(tok), tok);
257
+           if (n_tok == 2 && parse_log_level(tok1, &lvl)) {
258
+               add_pattern(&new_patterns, tok0, lvl);
259
+           } else if (n_tok == 1 && parse_log_level(tok0, &lvl)) {
260
+               *level = lvl;
261
+           } else {
262
+               pw_log_warn("Ignoring invalid format in log level: '%s'",
263
+                       tokensi);
264
+           }
265
+       }
266
+   }
267
+   spa_list_insert_list(list, &new_patterns);
268
+   return 0;
269
+}
270
+
271
+SPA_EXPORT
272
+int pw_log_set_level_string(const char *str)
273
+{
274
+   struct spa_list new_patterns = SPA_LIST_INIT(&new_patterns);
275
+   struct pattern *pattern;
276
+   enum spa_log_level level;
277
+   int res;
278
+
279
+   if ((res = parse_log_string(str, &new_patterns, &level)) < 0)
280
+       return res;
281
+
282
+   pthread_mutex_lock(&topics_lock);
283
+
284
+   spa_list_consume(pattern, &patterns, link) {
285
+       spa_list_remove(&pattern->link);
286
+       free(pattern);
287
+   }
288
+
289
+   spa_list_insert_list(&patterns, &new_patterns);
290
+
291
+   pthread_mutex_unlock(&topics_lock);
292
+
293
+   pw_log_set_level(level);
294
+   return 0;
295
 }
296
 
297
 /** Log a message for the given topic
298
@@ -236,31 +484,23 @@
299
 void
300
 pw_log_init(void)
301
 {
302
-   PW_LOG_TOPIC_INIT(PW_LOG_TOPIC_DEFAULT);
303
-   PW_LOG_TOPIC_INIT(log_buffers);
304
-   PW_LOG_TOPIC_INIT(log_client);
305
-   PW_LOG_TOPIC_INIT(log_conf);
306
-   PW_LOG_TOPIC_INIT(log_context);
307
-   PW_LOG_TOPIC_INIT(log_core);
308
-   PW_LOG_TOPIC_INIT(log_data_loop);
309
-   PW_LOG_TOPIC_INIT(log_device);
310
-   PW_LOG_TOPIC_INIT(log_factory);
311
-   PW_LOG_TOPIC_INIT(log_filter);
312
-   PW_LOG_TOPIC_INIT(log_global);
313
-   PW_LOG_TOPIC_INIT(log_link);
314
-   PW_LOG_TOPIC_INIT(log_loop);
315
-   PW_LOG_TOPIC_INIT(log_main_loop);
316
-   PW_LOG_TOPIC_INIT(log_mem);
317
-   PW_LOG_TOPIC_INIT(log_metadata);
318
-   PW_LOG_TOPIC_INIT(log_module);
319
-   PW_LOG_TOPIC_INIT(log_node);
320
-   PW_LOG_TOPIC_INIT(log_port);
321
-   PW_LOG_TOPIC_INIT(log_properties);
322
-   PW_LOG_TOPIC_INIT(log_protocol);
323
-   PW_LOG_TOPIC_INIT(log_proxy);
324
-   PW_LOG_TOPIC_INIT(log_resource);
325
-   PW_LOG_TOPIC_INIT(log_stream);
326
-   PW_LOG_TOPIC_INIT(log_thread_loop);
327
-   PW_LOG_TOPIC_INIT(log_topic);
328
-   PW_LOG_TOPIC_INIT(log_work_queue);
329
+}
330
+
331
+void
332
+pw_log_deinit(void)
333
+{
334
+   struct pattern *pattern;
335
+
336
+   pthread_mutex_lock(&topics_lock);
337
+
338
+   spa_list_consume(pattern, &patterns, link) {
339
+       spa_list_remove(&pattern->link);
340
+       free(pattern);
341
+   }
342
+
343
+   pthread_mutex_unlock(&topics_lock);
344
+
345
+   /* don't free log topics, since they usually won't get re-registered */
346
+
347
+   pw_log_set(NULL);
348
 }
349
pipewire-1.0.1.tar.bz2/src/pipewire/log.h -> pipewire-1.2.0.tar.gz/src/pipewire/log.h Changed
117
 
1
@@ -6,6 +6,7 @@
2
 #define PIPEWIRE_LOG_H
3
 
4
 #include <spa/support/log.h>
5
+#include <spa/utils/defs.h>
6
 
7
 #ifdef __cplusplus
8
 extern "C" {
9
@@ -27,7 +28,7 @@
10
 /** The global log level */
11
 extern enum spa_log_level pw_log_level;
12
 
13
-extern struct spa_log_topic *PW_LOG_TOPIC_DEFAULT;
14
+extern struct spa_log_topic * const PW_LOG_TOPIC_DEFAULT;
15
 
16
 /** Configure a logging module. This is usually done automatically
17
  * in pw_init() but you can install a custom logger before calling
18
@@ -40,6 +41,14 @@
19
 /** Configure the logging level */
20
 void pw_log_set_level(enum spa_log_level level);
21
 
22
+/**
23
+ * Configure the logging level using a string
24
+ * in PIPEWIRE_DEBUG format.
25
+ *
26
+ * \since 1.1.0
27
+ */
28
+int pw_log_set_level_string(const char *str);
29
+
30
 /** Log a message for a topic */
31
 void
32
 pw_log_logt(enum spa_log_level level,
33
@@ -73,6 +82,16 @@
34
        const char *fmt, va_list args) SPA_PRINTF_FUNC(5, 0);
35
 
36
 /**
37
+ * Define a static \ref spa_log_topic and its constructor/destructor functions.
38
+ *
39
+ * \since 1.1.0
40
+ */
41
+#define PW_LOG_TOPIC_DEFINE_STATIC(var, topic) \
42
+  static struct spa_log_topic var = SPA_LOG_TOPIC(SPA_VERSION_LOG_TOPIC, topic); \
43
+  static void __attribute__((constructor)) var ## _register_construct(void) { pw_log_topic_register(&var); } \
44
+  static void __attribute__((destructor)) var ## _register_destroy(void) { pw_log_topic_unregister(&var); }
45
+
46
+/**
47
  * Declare a static log topic named \a var. The usual usage is:
48
  * \code
49
  *  PW_LOG_TOPIC_STATIC(my_topic);
50
@@ -82,33 +101,61 @@
51
  *      pw_log_debug("bar");
52
  *  }
53
  * \endcode
54
+ *
55
+ * This macro also emits GCC attribute constructor/destructor
56
+ * functions that automatically call pw_log_topic_register/unregister.
57
  */
58
 #define PW_LOG_TOPIC_STATIC(var, topic) \
59
-  static struct spa_log_topic var##__LINE__ = SPA_LOG_TOPIC(0, topic); \
60
-  static struct spa_log_topic *var = &(var##__LINE__)
61
+  PW_LOG_TOPIC_DEFINE_STATIC(var ## _value, topic) \
62
+  static struct spa_log_topic * const var = &(var ## _value)
63
 
64
 /**
65
  * Declare a static log topic named \a var.
66
  * See \ref PW_LOG_TOPIC_STATIC for an example usage.
67
  */
68
 #define PW_LOG_TOPIC_EXTERN(var) \
69
-  extern struct spa_log_topic *var
70
+  extern struct spa_log_topic * const var
71
 
72
 /**
73
  * Declare a static log topic named \a var.
74
  * See \ref PW_LOG_TOPIC_STATIC for an example usage.
75
  */
76
 #define PW_LOG_TOPIC(var, topic) \
77
-  struct spa_log_topic var##__LINE__ = SPA_LOG_TOPIC(0, topic); \
78
-  struct spa_log_topic *var = &(var##__LINE__)
79
+  PW_LOG_TOPIC_DEFINE_STATIC(var ## _value, topic) \
80
+  struct spa_log_topic * const var = &(var ## _value)
81
 
82
+/**
83
+ * \deprecated Use \ref pw_log_topic_register and \ref pw_log_topic_unregister
84
+ * instead, or rely on the auto-registration by \ref PW_LOG_TOPIC and
85
+ * \ref PW_LOG_TOPIC_STATIC.
86
+ */
87
 #define PW_LOG_TOPIC_INIT(var) \
88
    spa_log_topic_init(pw_log_get(), var);
89
 
90
+/**
91
+ * Register log topic with the logger, to enable dynamic log levels.
92
+ * Topic must be unregistered before freeing it or plugin unload.
93
+ * May be used instead of \ref PW_LOG_TOPIC_INIT
94
+ * This function is threadsafe.
95
+ *
96
+ * \since 1.1.0
97
+ */
98
+void pw_log_topic_register(struct spa_log_topic *t);
99
+
100
+/**
101
+ * Unregister log topic. This function is threadsafe.
102
+ *
103
+ * \since 1.1.0
104
+ */
105
+void pw_log_topic_unregister(struct spa_log_topic *t);
106
+
107
 /** Check if a loglevel is enabled */
108
 #define pw_log_level_enabled(lev) (pw_log_level >= (lev))
109
 #define pw_log_topic_enabled(lev,t) ((t) && (t)->has_custom_level ? (t)->level >= (lev) : pw_log_level_enabled((lev)))
110
 
111
+/* check is a custom level was assigned to a topic. \since 1.1.0 */
112
+#define pw_log_topic_custom_enabled(lev,t) ((t) && (t)->has_custom_level && (t)->level >= (lev))
113
+
114
 #define pw_logtv(lev,topic,fmt,ap)                     \
115
 ({                                     \
116
    if (SPA_UNLIKELY(pw_log_topic_enabled(lev,topic)))          \
117
pipewire-1.0.1.tar.bz2/src/pipewire/loop.c -> pipewire-1.2.0.tar.gz/src/pipewire/loop.c Changed
49
 
1
@@ -22,6 +22,8 @@
2
 struct impl {
3
    struct pw_loop this;
4
 
5
+   char name16;
6
+
7
    struct spa_handle *system_handle;
8
    struct spa_handle *loop_handle;
9
 
10
@@ -42,7 +44,7 @@
11
    void *iface;
12
    struct spa_support support32;
13
    uint32_t n_support;
14
-   const char *lib;
15
+   const char *lib, *str, *name = NULL;
16
 
17
    n_support = pw_get_support(support, 32);
18
 
19
@@ -125,6 +127,14 @@
20
         }
21
    this->utils = iface;
22
 
23
+   if (props != NULL) {
24
+       if ((str = spa_dict_lookup(props, PW_KEY_LOOP_NAME)) != NULL)
25
+           name = str;
26
+   }
27
+   if (name)
28
+       snprintf(impl->name, sizeof(impl->name), "%s", name);
29
+   this->name = impl->name;
30
+
31
    return this;
32
 
33
 error_unload_loop:
34
@@ -161,6 +171,14 @@
35
 }
36
 
37
 SPA_EXPORT
38
+int pw_loop_set_name(struct pw_loop *loop, const char *name)
39
+{
40
+   struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this);
41
+   snprintf(impl->name, sizeof(impl->name), "%s", name);
42
+   return 0;
43
+}
44
+
45
+SPA_EXPORT
46
 int pw_loop_check(struct pw_loop *loop)
47
 {
48
    struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this);
49
pipewire-1.0.1.tar.bz2/src/pipewire/loop.h -> pipewire-1.2.0.tar.gz/src/pipewire/loop.h Changed
27
 
1
@@ -17,6 +17,8 @@
2
  * PipeWire loop object provides an implementation of
3
  * the spa loop interfaces. It can be used to implement various
4
  * event loops.
5
+ *
6
+ * The members of \ref pw_loop are read-only.
7
  */
8
 
9
 /**
10
@@ -29,6 +31,7 @@
11
    struct spa_loop *loop;          /**< wrapped loop */
12
    struct spa_loop_control *control;   /**< loop control */
13
    struct spa_loop_utils *utils;       /**< loop utils */
14
+   const char *name;
15
 };
16
 
17
 struct pw_loop *
18
@@ -37,6 +40,8 @@
19
 void
20
 pw_loop_destroy(struct pw_loop *loop);
21
 
22
+int pw_loop_set_name(struct pw_loop *loop, const char *name);
23
+
24
 #define pw_loop_add_source(l,...)  spa_loop_add_source((l)->loop,__VA_ARGS__)
25
 #define pw_loop_update_source(l,...)   spa_loop_update_source((l)->loop,__VA_ARGS__)
26
 #define pw_loop_remove_source(l,...)   spa_loop_remove_source((l)->loop,__VA_ARGS__)
27
pipewire-1.0.1.tar.bz2/src/pipewire/main-loop.c -> pipewire-1.2.0.tar.gz/src/pipewire/main-loop.c Changed
32
 
1
@@ -29,8 +29,6 @@
2
        goto error_cleanup;
3
    }
4
 
5
-   pw_log_debug("%p: new", this);
6
-
7
    if (loop == NULL) {
8
        loop = pw_loop_new(props);
9
        this->created = true;
10
@@ -41,8 +39,12 @@
11
    }
12
    this->loop = loop;
13
 
14
+   if (!this->loop->name0)
15
+       pw_loop_set_name(this->loop, "main-loop");
16
    spa_hook_list_init(&this->listener_list);
17
 
18
+   pw_log_debug("%p: new '%s'", this, loop->name);
19
+
20
    return this;
21
 
22
 error_free:
23
@@ -133,5 +135,8 @@
24
        }
25
    }
26
    pw_loop_leave(loop->loop);
27
+
28
+   if (res > 0) // This is the number of fds last polled, not useful for the caller.
29
+       res = 0;
30
    return res;
31
 }
32
pipewire-1.0.1.tar.bz2/src/pipewire/main-loop.h -> pipewire-1.2.0.tar.gz/src/pipewire/main-loop.h Changed
13
 
1
@@ -49,7 +49,10 @@
2
 /** Destroy a loop */
3
 void pw_main_loop_destroy(struct pw_main_loop *loop);
4
 
5
-/** Run a main loop. This blocks until \ref pw_main_loop_quit is called */
6
+/** Run a main loop. This blocks until \ref pw_main_loop_quit is called.
7
+ *
8
+ * @return 0 on success, otherwise a negative number.
9
+ */
10
 int pw_main_loop_run(struct pw_main_loop *loop);
11
 
12
 /** Quit a main loop */
13
pipewire-1.0.1.tar.bz2/src/pipewire/mem.c -> pipewire-1.2.0.tar.gz/src/pipewire/mem.c Changed
287
 
1
@@ -57,6 +57,32 @@
2
 #define MFD_ALLOW_SEALING 0x0002U
3
 #endif
4
 
5
+#ifndef MFD_HUGETLB
6
+#define MFD_HUGETLB 0x0004U
7
+#endif
8
+
9
+#ifndef MFD_NOEXEC_SEAL
10
+#define MFD_NOEXEC_SEAL 0x0008U
11
+#endif
12
+
13
+#ifndef MFD_EXEC
14
+#define MFD_EXEC 0x0010U
15
+#endif
16
+
17
+#ifdef HAVE_MEMFD_CREATE
18
+static int pw_memfd_create(const char *name, unsigned int flags)
19
+{
20
+   int res;
21
+
22
+   res = memfd_create(name, flags);
23
+
24
+   if (res == -1 && errno == EINVAL && flags & MFD_NOEXEC_SEAL)
25
+       res = memfd_create(name, flags & ~MFD_NOEXEC_SEAL);
26
+
27
+   return res;
28
+}
29
+#endif
30
+
31
 /* fcntl() seals-related flags */
32
 
33
 #ifndef F_LINUX_SPECIFIC_BASE
34
@@ -78,6 +104,9 @@
35
 #define pw_mempool_emit_added(p,b) pw_mempool_emit(p, added, 0, b)
36
 #define pw_mempool_emit_removed(p,b)   pw_mempool_emit(p, removed, 0, b)
37
 
38
+#define memblock_emit(b,m,v,...) spa_hook_list_call(&b->listener_list, struct memblock_events, m, v, ##__VA_ARGS__)
39
+#define memblock_emit_invalidated(b)   memblock_emit(b, invalidated, 0)
40
+
41
 struct mempool {
42
    struct pw_mempool this;
43
 
44
@@ -93,6 +122,15 @@
45
    struct spa_list link;       /* link in mempool */
46
    struct spa_list mappings;   /* list of struct mapping */
47
    struct spa_list memmaps;    /* list of struct memmap */
48
+   struct memblock *owner;     /* owner of fd, if another memblock */
49
+   struct spa_hook owner_listener; /* listen for fd owner memblock events */
50
+   struct spa_hook_list listener_list;
51
+};
52
+
53
+struct memblock_events {
54
+#define VERSION_MEMBLOCK_EVENTS    0
55
+   uint32_t version;
56
+   void (*invalidated) (void *data);
57
 };
58
 
59
 /* a mapped region of a block */
60
@@ -285,6 +323,16 @@
61
        return NULL;
62
    }
63
 
64
+   if ((b->this.flags & PW_MEMBLOCK_FLAG_UNMAPPABLE)) {
65
+       pw_log_error("%p: block:%p can't be mmaped", p, &b->this);
66
+       errno = EPERM;
67
+       return NULL;
68
+   }
69
+   if (b->this.fd == -1) {
70
+       pw_log_error("%p: block:%p cannot map memory with stale fd", p, b);
71
+       errno = EINVAL;
72
+       return NULL;
73
+   }
74
 
75
    ptr = mmap(NULL, size, prot, fl, b->this.fd, offset);
76
    if (ptr == MAP_FAILED) {
77
@@ -306,8 +354,8 @@
78
    b->this.ref++;
79
    spa_list_append(&b->mappings, &m->link);
80
 
81
-        pw_log_debug("%p: block:%p fd:%d map:%p ptr:%p (%u %u) block-ref:%d", p, &b->this,
82
-           b->this.fd, m, m->ptr, offset, size, b->this.ref);
83
+        pw_log_debug("%p: block:%p fd:%d flags:%08x map:%p ptr:%p (%u %u) block-ref:%d", p, &b->this,
84
+           b->this.fd, b->this.flags, m, m->ptr, offset, size, b->this.ref);
85
 
86
    return m;
87
 }
88
@@ -347,6 +395,12 @@
89
    struct pw_map_range range;
90
    struct stat sb;
91
 
92
+   if (b->this.fd == -1) {
93
+       pw_log_error("%p: block:%p cannot map memory with stale fd", p, block);
94
+       errno = EINVAL;
95
+       return NULL;
96
+   }
97
+
98
    if (fstat(b->this.fd, &sb) != 0)
99
        return NULL;
100
 
101
@@ -359,7 +413,7 @@
102
        (int64_t) sb.st_size);
103
 
104
    if (!valid) {
105
-       errno = -EINVAL;
106
+       errno = EINVAL;
107
        return NULL;
108
    }
109
 
110
@@ -386,8 +440,8 @@
111
    mm->this.size = size;
112
    mm->this.ptr = SPA_PTROFF(m->ptr, range.start, void);
113
 
114
-        pw_log_debug("%p: map:%p block:%p fd:%d ptr:%p (%u %u) mapping:%p ref:%d", p,
115
-           &mm->this, b, b->this.fd, mm->this.ptr, offset, size, m, m->ref);
116
+        pw_log_debug("%p: map:%p block:%p fd:%d flags:%08x ptr:%p (%u %u) mapping:%p ref:%d", p,
117
+           &mm->this, b, b->this.fd, b->this.flags, mm->this.ptr, offset, size, m, m->ref);
118
 
119
    if (tag) {
120
        memcpy(mm->this.tag, tag, sizeof(mm->this.tag));
121
@@ -475,6 +529,12 @@
122
    if (b == NULL)
123
        return NULL;
124
 
125
+   if (type != SPA_DATA_MemFd) {
126
+       res = -ENOTSUP;
127
+       pw_log_error("%p: alloc failure: only MemFd is supported", pool);
128
+       goto error_free;
129
+   }
130
+
131
    b->this.ref = 1;
132
    b->this.pool = pool;
133
    b->this.flags = flags;
134
@@ -482,6 +542,7 @@
135
    b->this.size = size;
136
    spa_list_init(&b->mappings);
137
    spa_list_init(&b->memmaps);
138
+   spa_hook_list_init(&b->listener_list);
139
 
140
 #ifdef HAVE_MEMFD_CREATE
141
    char name128;
142
@@ -489,7 +550,7 @@
143
         "pipewire-memfd:flags=0x%08x,type=%" PRIu32 ",size=%zu",
144
         (unsigned int) flags, type, size);
145
 
146
-   b->this.fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
147
+   b->this.fd = pw_memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL);
148
    if (b->this.fd == -1) {
149
        res = -errno;
150
        pw_log_error("%p: Failed to create memfd: %m", pool);
151
@@ -544,8 +605,8 @@
152
 
153
    b->this.id = pw_map_insert_new(&impl->map, b);
154
    spa_list_append(&impl->blocks, &b->link);
155
-   pw_log_debug("%p: block:%p id:%d type:%u size:%zu", pool,
156
-           &b->this, b->this.id, type, size);
157
+   pw_log_debug("%p: block:%p id:%d type:%u flags:%08x size:%zu", pool,
158
+           &b->this, b->this.id, type, flags, size);
159
 
160
    if (!SPA_FLAG_IS_SET(flags, PW_MEMBLOCK_FLAG_DONT_NOTIFY))
161
        pw_mempool_emit_added(impl, &b->this);
162
@@ -567,6 +628,9 @@
163
    struct memblock *b;
164
 
165
    spa_list_for_each(b, &impl->blocks, link) {
166
+       if (b->this.fd == -1)
167
+           continue;
168
+
169
        if (fd == b->this.fd) {
170
            pw_log_debug("%p: found %p id:%u fd:%d ref:%d",
171
                    pool, &b->this, b->this.id, fd, b->this.ref);
172
@@ -583,6 +647,12 @@
173
    struct mempool *impl = SPA_CONTAINER_OF(pool, struct mempool, this);
174
    struct memblock *b;
175
 
176
+   if (fd < 0) {
177
+       pw_log_error("%p: cannot import invalid fd:%d", pool, fd);
178
+       errno = EINVAL;
179
+       return NULL;
180
+   }
181
+
182
    b = mempool_find_fd(pool, fd);
183
    if (b != NULL) {
184
        b->this.ref++;
185
@@ -595,6 +665,7 @@
186
 
187
    spa_list_init(&b->memmaps);
188
    spa_list_init(&b->mappings);
189
+   spa_hook_list_init(&b->listener_list);
190
 
191
    b->this.ref = 1;
192
    b->this.pool = pool;
193
@@ -605,7 +676,7 @@
194
    spa_list_append(&impl->blocks, &b->link);
195
 
196
    pw_log_debug("%p: block:%p id:%u flags:%08x type:%u fd:%d",
197
-           pool, b, b->this.id, flags, type, fd);
198
+           pool, &b->this, b->this.id, flags, type, fd);
199
 
200
    if (!SPA_FLAG_IS_SET(flags, PW_MEMBLOCK_FLAG_DONT_NOTIFY))
201
        pw_mempool_emit_added(impl, &b->this);
202
@@ -613,15 +684,57 @@
203
    return &b->this;
204
 }
205
 
206
+static void memblock_invalidated(void *data)
207
+{
208
+   struct memblock *b = data;
209
+
210
+   if (!b->owner)
211
+       return;
212
+
213
+   pw_log_debug("%p: invalidated block:%p id:%u fd:%d ref:%d owner:%p",
214
+           b->this.pool, b, b->this.id, b->this.fd, b->this.ref, b->owner);
215
+
216
+   spa_hook_remove(&b->owner_listener);
217
+   b->owner = NULL;
218
+
219
+   b->this.fd = -1;
220
+}
221
+
222
+static const struct memblock_events memblock_events = {
223
+   VERSION_MEMBLOCK_EVENTS,
224
+   .invalidated = memblock_invalidated,
225
+};
226
+
227
 SPA_EXPORT
228
 struct pw_memblock * pw_mempool_import_block(struct pw_mempool *pool,
229
        struct pw_memblock *mem)
230
 {
231
-   pw_log_debug("%p: import block:%p type:%d fd:%d", pool,
232
-           mem, mem->type, mem->fd);
233
-   return pw_mempool_import(pool,
234
+   struct pw_memblock *block;
235
+   struct memblock *b;
236
+
237
+   block = pw_mempool_import(pool,
238
            mem->flags | PW_MEMBLOCK_FLAG_DONT_CLOSE,
239
            mem->type, mem->fd);
240
+   if (!block)
241
+       return NULL;
242
+
243
+   pw_log_debug("%p: import block:%p flags:%08x type:%d fd:%d as %p", pool,
244
+           mem, mem->flags, mem->type, mem->fd, block);
245
+
246
+   b = SPA_CONTAINER_OF(block, struct memblock, this);
247
+   if (!b->owner) {
248
+       struct memblock *bmem = SPA_CONTAINER_OF(mem, struct memblock, this);
249
+
250
+       while (bmem->owner)
251
+           bmem = bmem->owner;
252
+
253
+       if (!(bmem->this.flags & PW_MEMBLOCK_FLAG_DONT_CLOSE)) {
254
+           b->owner = bmem;
255
+           spa_hook_list_append(&bmem->listener_list, &b->owner_listener, &memblock_events, b);
256
+       }
257
+   }
258
+
259
+   return block;
260
 }
261
 
262
 SPA_EXPORT
263
@@ -725,6 +838,13 @@
264
    if (!SPA_FLAG_IS_SET(block->flags, PW_MEMBLOCK_FLAG_DONT_NOTIFY))
265
        pw_mempool_emit_removed(impl, block);
266
 
267
+   if (b->owner) {
268
+       spa_hook_remove(&b->owner_listener);
269
+       b->owner = NULL;
270
+   }
271
+
272
+   memblock_emit_invalidated(b);
273
+
274
    spa_list_consume(mm, &b->memmaps, link)
275
        pw_memmap_free(&mm->this);
276
 
277
@@ -737,6 +857,9 @@
278
        pw_log_debug("%p: close fd:%d", pool, block->fd);
279
        close(block->fd);
280
    }
281
+
282
+   spa_hook_list_clean(&b->listener_list);
283
+
284
    free(b);
285
 }
286
 
287
pipewire-1.0.1.tar.bz2/src/pipewire/mem.h -> pipewire-1.2.0.tar.gz/src/pipewire/mem.h Changed
9
 
1
@@ -29,6 +29,7 @@
2
    PW_MEMBLOCK_FLAG_MAP =      (1 << 3),   /**< mmap the fd */
3
    PW_MEMBLOCK_FLAG_DONT_CLOSE =   (1 << 4),   /**< don't close fd */
4
    PW_MEMBLOCK_FLAG_DONT_NOTIFY =  (1 << 5),   /**< don't notify events */
5
+   PW_MEMBLOCK_FLAG_UNMAPPABLE =   (1 << 6),   /**< the fd can not be mmapped */
6
 
7
    PW_MEMBLOCK_FLAG_READWRITE = PW_MEMBLOCK_FLAG_READABLE | PW_MEMBLOCK_FLAG_WRITABLE,
8
 };
9
pipewire-1.0.1.tar.bz2/src/pipewire/pipewire.c -> pipewire-1.2.0.tar.gz/src/pipewire/pipewire.c Changed
156
 
1
@@ -25,7 +25,6 @@
2
 #include <spa/support/cpu.h>
3
 #include <spa/support/i18n.h>
4
 
5
-#include <pipewire/cleanup.h>
6
 #include "pipewire.h"
7
 #include "private.h"
8
 #include "i18n.h"
9
@@ -47,6 +46,7 @@
10
    void *hnd;
11
    spa_handle_factory_enum_func_t enum_func;
12
    int ref;
13
+   const struct spa_log_topic_enum *log_topic_enum;
14
 };
15
 
16
 struct handle {
17
@@ -131,9 +131,12 @@
18
    plugin->filename = strdup(filename);
19
    plugin->hnd = hnd;
20
    plugin->enum_func = enum_func;
21
+   plugin->log_topic_enum = dlsym(hnd, SPA_LOG_TOPIC_ENUM_NAME);
22
 
23
    spa_list_append(&registry->plugins, &plugin->link);
24
 
25
+   pw_log_topic_register_enum(plugin->log_topic_enum);
26
+
27
    return plugin;
28
 
29
 error_dlclose:
30
@@ -148,6 +151,7 @@
31
 {
32
    if (--plugin->ref == 0) {
33
        spa_list_remove(&plugin->link);
34
+       pw_log_topic_unregister_enum(plugin->log_topic_enum);
35
        pw_log_debug("unloaded plugin:'%s'", plugin->filename);
36
        if (pw_should_dlclose())
37
            dlclose(plugin->hnd);
38
@@ -469,78 +473,6 @@
39
 }
40
 #endif
41
 
42
-static bool
43
-parse_log_level(const char *str, enum spa_log_level *l)
44
-{
45
-   uint32_t lvl;
46
-   if (strlen(str) == 1) {
47
-       switch(str0) {
48
-       case 'X': lvl = SPA_LOG_LEVEL_NONE; break;
49
-       case 'E': lvl = SPA_LOG_LEVEL_ERROR; break;
50
-       case 'W': lvl = SPA_LOG_LEVEL_WARN; break;
51
-       case 'I': lvl = SPA_LOG_LEVEL_INFO; break;
52
-       case 'D': lvl = SPA_LOG_LEVEL_DEBUG; break;
53
-       case 'T': lvl = SPA_LOG_LEVEL_TRACE; break;
54
-       default:
55
-             goto check_int;
56
-       }
57
-   } else {
58
-check_int:
59
-         if (!spa_atou32(str, &lvl, 0))
60
-             return false;
61
-         if (lvl > SPA_LOG_LEVEL_TRACE)
62
-             return false;
63
-   }
64
-   *l = lvl;
65
-   return true;
66
-}
67
-
68
-static char *
69
-parse_pw_debug_env(void)
70
-{
71
-   const char *str;
72
-   int n_tokens;
73
-   char json1024 = {0};
74
-   char *pos = json;
75
-   char *end = pos + sizeof(json) - 1;
76
-   enum spa_log_level lvl;
77
-
78
-   str = getenv("PIPEWIRE_DEBUG");
79
-
80
-   if (!str || !*str)
81
-       return NULL;
82
-
83
-   /* String format is PIPEWIRE_DEBUG=<glob>:<level>,...,
84
-    * converted into { conn.* = 0}, {glob = level}, {glob = level}, .... ,
85
-    * with the connection namespace disabled by default.
86
-    */
87
-   pos += spa_scnprintf(pos, end - pos, " { conn.* = %d },", SPA_LOG_LEVEL_NONE);
88
-
89
-   spa_auto(pw_strv) tokens = pw_split_strv(str, ",", INT_MAX, &n_tokens);
90
-   if (n_tokens > 0) {
91
-       int i;
92
-       for (i = 0; i < n_tokens; i++) {
93
-           int n_tok;
94
-           char *tok2;
95
-
96
-           n_tok = pw_split_ip(tokensi, ":", SPA_N_ELEMENTS(tok), tok);
97
-           if (n_tok == 2 && parse_log_level(tok1, &lvl)) {
98
-               char *pattern = tok0;
99
-               pos += spa_scnprintf(pos, end - pos, "{ %s = %d },",
100
-                            pattern, lvl);
101
-           } else if (n_tok == 1 && parse_log_level(tok0, &lvl)) {
102
-               pw_log_set_level(lvl);
103
-           } else {
104
-               pw_log_warn("Ignoring invalid format in PIPEWIRE_DEBUG: '%s'",
105
-                       tokensi);
106
-           }
107
-       }
108
-   }
109
-
110
-   pos += spa_scnprintf(pos, end - pos, "");
111
-   return strdup(json);
112
-}
113
-
114
 /** Initialize PipeWire
115
  *
116
  * \param argc pointer to argc
117
@@ -595,8 +527,6 @@
118
    spa_list_init(&support->registry.handles);
119
 
120
    if (pw_log_is_default()) {
121
-       char *patterns = NULL;
122
-
123
        n_items = 0;
124
        if (!support->no_color) {
125
            if ((str = getenv("PIPEWIRE_LOG_COLOR")) == NULL)
126
@@ -610,8 +540,6 @@
127
        itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_LEVEL, level);
128
        if ((str = getenv("PIPEWIRE_LOG")) != NULL)
129
            itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_FILE, str);
130
-       if ((patterns = parse_pw_debug_env()) != NULL)
131
-           itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_PATTERNS, patterns);
132
        info = SPA_DICT_INIT(items, n_items);
133
 
134
        log = add_interface(support, SPA_NAME_SUPPORT_LOG, SPA_TYPE_INTERFACE_Log, &info);
135
@@ -625,7 +553,10 @@
136
                pw_log_set(log);
137
        }
138
 #endif
139
-       free(patterns);
140
+
141
+       str = getenv("PIPEWIRE_DEBUG");
142
+       if (str && *str)
143
+           pw_log_set_level_string(str);
144
    } else {
145
        support->supportsupport->n_support++ =
146
            SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, pw_log_get());
147
@@ -678,7 +609,7 @@
148
        goto done;
149
 
150
    pthread_mutex_lock(&support_lock);
151
-   pw_log_set(NULL);
152
+   pw_log_deinit();
153
 
154
    spa_list_consume(h, &registry->handles, link)
155
        unref_handle(h);
156
pipewire-1.0.1.tar.bz2/src/pipewire/private.h -> pipewire-1.2.0.tar.gz/src/pipewire/private.h Changed
346
 
1
@@ -5,6 +5,8 @@
2
 #ifndef PIPEWIRE_PRIVATE_H
3
 #define PIPEWIRE_PRIVATE_H
4
 
5
+/** \privatesection */
6
+
7
 #ifdef __cplusplus
8
 extern "C" {
9
 #endif
10
@@ -28,8 +30,9 @@
11
 #endif
12
 
13
 #define MAX_RATES              32u
14
-#define CLOCK_MIN_QUANTUM          4u
15
-#define CLOCK_MAX_QUANTUM          65536u
16
+#define CLOCK_QUANTUM_FLOOR            1u
17
+#define CLOCK_QUANTUM_LIMIT            65536u
18
+#define DEFAULT_LOG_LEVEL          SPA_LOG_LEVEL_WARN
19
 
20
 struct settings {
21
    uint32_t log_level;
22
@@ -39,7 +42,8 @@
23
    uint32_t clock_quantum;         /* default quantum */
24
    uint32_t clock_min_quantum;     /* min quantum */
25
    uint32_t clock_max_quantum;     /* max quantum */
26
-   uint32_t clock_quantum_limit;       /* quantum limit */
27
+   uint32_t clock_quantum_limit;       /* quantum limit (upper bound) */
28
+   uint32_t clock_quantum_floor;       /* quantum floor (lower bound) */
29
    struct spa_rectangle video_size;
30
    struct spa_fraction video_rate;
31
    uint32_t link_max_buffers;
32
@@ -214,6 +218,7 @@
33
    struct spa_list link;           /**< link in context metadata_list */
34
    struct pw_global *global;       /**< global for this metadata */
35
    struct spa_hook global_listener;
36
+   struct spa_hook context_listener;
37
 
38
    struct pw_properties *properties;   /**< properties of the metadata */
39
 
40
@@ -414,8 +419,6 @@
41
 
42
    struct spa_thread_utils *thread_utils;
43
    struct pw_loop *main_loop;      /**< main loop for control */
44
-   struct pw_loop *data_loop;      /**< data loop for data passing */
45
-   struct spa_system *data_system;     /**< data system for data passing */
46
    struct pw_work_queue *work_queue;   /**< work queue */
47
 
48
    struct spa_support support16;   /**< support for spa plugins */
49
@@ -438,6 +441,10 @@
50
 struct pw_data_loop {
51
    struct pw_loop *loop;
52
 
53
+   char *affinity;
54
+   char *class;
55
+   char **classes;
56
+   int rt_prio;
57
    struct spa_hook_list listener_list;
58
 
59
    struct spa_thread_utils *thread_utils;
60
@@ -517,15 +524,14 @@
61
 
62
 static inline void pw_node_activation_state_reset(struct pw_node_activation_state *state)
63
 {
64
-        state->pending = state->required;
65
+   SPA_ATOMIC_STORE(state->pending, SPA_ATOMIC_LOAD(state->required));
66
 }
67
 
68
 #define pw_node_activation_state_dec(s) (SPA_ATOMIC_DEC(s->pending) == 0)
69
+#define pw_node_activation_state_xchg(s) SPA_ATOMIC_XCHG(s->pending, 0)
70
 
71
 struct pw_node_target {
72
    struct spa_list link;
73
-#define PW_NODE_TARGET_NONE    0
74
-#define PW_NODE_TARGET_PEER    1
75
    uint32_t flags;
76
    uint32_t id;
77
    char name128;
78
@@ -533,7 +539,9 @@
79
    struct pw_node_activation *activation;
80
    struct spa_system *system;
81
    int fd;
82
+   void (*trigger)(struct pw_node_target *t, uint64_t nsec);
83
    unsigned int active:1;
84
+   unsigned int added:1;
85
 };
86
 
87
 static inline void copy_target(struct pw_node_target *dst, const struct pw_node_target *src)
88
@@ -544,13 +552,37 @@
89
    dst->activation = src->activation;
90
    dst->system = src->system;
91
    dst->fd = src->fd;
92
+   dst->trigger = src->trigger;
93
 }
94
 
95
+/* versions:
96
+ * 0 baseline
97
+ * 1 the activation status needs to be CAS
98
+ *   async nodes, driver resumes async nodes
99
+ *   transport with sync.group properties instead of client command
100
+ */
101
+#define PW_VERSION_NODE_ACTIVATION 1
102
+
103
+/* nodes start as INACTIVE, when they are ready to be scheduled, they add their
104
+ * fd to the loop and change status to FINISHED. When the node shuts down, the
105
+ * status is set back to INACTIVE.
106
+ *
107
+ * We have status changes (using compare-and-swap) from
108
+ *
109
+ *   INACTIVE -> FINISHED (node is added to loop and can be scheduled)
110
+ *   * -> INACTIVE (node can not be scheduled anymore)
111
+ *
112
+ *   !INACTIVE -> NOT_TRIGGERED (node is prepared by the driver)
113
+ *   NOT_TRIGGERED -> TRIGGERED (eventfd is written)
114
+ *   TRIGGERED -> AWAKE (eventfd is read, node starts processing)
115
+ *   AWAKE -> FINISHED (node completed processing and triggered the peers)
116
+ */
117
 struct pw_node_activation {
118
 #define PW_NODE_ACTIVATION_NOT_TRIGGERED   0
119
 #define PW_NODE_ACTIVATION_TRIGGERED       1
120
 #define PW_NODE_ACTIVATION_AWAKE       2
121
 #define PW_NODE_ACTIVATION_FINISHED        3
122
+#define PW_NODE_ACTIVATION_INACTIVE        4
123
    uint32_t status;
124
 
125
    unsigned int version:1;
126
@@ -574,7 +606,12 @@
127
    uint32_t segment_owner16;           /* id of owners for each segment info struct.
128
                             * nodes that want to update segment info need to
129
                             * CAS their node id in this array. */
130
-   uint32_t padding15;
131
+   uint32_t padding11;             /* must be 0 */
132
+   uint32_t client_version;            /* verions of client, see above */
133
+   uint32_t server_version;            /* verions of server, see above */
134
+
135
+   uint32_t active_driver_id;          /* driver active on client */
136
+   uint32_t driver_id;             /* the current node driver id */
137
 #define PW_NODE_ACTIVATION_FLAG_NONE       0
138
 #define PW_NODE_ACTIVATION_FLAG_PROFILER   (1<<0)  /* the profiler is running */
139
    uint32_t flags;                 /* extra flags */
140
@@ -602,6 +639,60 @@
141
                             * to update wins */
142
 };
143
 
144
+static inline uint64_t get_time_ns(struct spa_system *system)
145
+{
146
+   struct timespec ts;
147
+   spa_system_clock_gettime(system, CLOCK_MONOTONIC, &ts);
148
+   return SPA_TIMESPEC_TO_NSEC(&ts);
149
+}
150
+
151
+/* called from data-loop decrement the dependency counter of the target and when
152
+ * there are no more dependencies, trigger the node. */
153
+static inline void trigger_target_v1(struct pw_node_target *t, uint64_t nsec)
154
+{
155
+   struct pw_node_activation *a = t->activation;
156
+   struct pw_node_activation_state *state = &a->state0;
157
+
158
+   pw_log_trace_fp("%p: (%s-%u) state:%p pending:%d/%d", t->node,
159
+           t->name, t->id, state, state->pending, state->required);
160
+
161
+   if (pw_node_activation_state_dec(state)) {
162
+       if (SPA_ATOMIC_CAS(a->status,
163
+                   PW_NODE_ACTIVATION_NOT_TRIGGERED,
164
+                   PW_NODE_ACTIVATION_TRIGGERED)) {
165
+           a->signal_time = nsec;
166
+           if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0))
167
+               pw_log_warn("%p: write failed %m", t->node);
168
+       }
169
+   }
170
+}
171
+
172
+static inline void trigger_target_v0(struct pw_node_target *t, uint64_t nsec)
173
+{
174
+   struct pw_node_activation *a = t->activation;
175
+   struct pw_node_activation_state *state = &a->state0;
176
+
177
+   pw_log_trace_fp("%p: (%s-%u) state:%p pending:%d/%d", t->node,
178
+           t->name, t->id, state, state->pending, state->required);
179
+
180
+   if (pw_node_activation_state_dec(state)) {
181
+       SPA_ATOMIC_STORE(a->status, PW_NODE_ACTIVATION_TRIGGERED);
182
+       a->signal_time = nsec;
183
+       if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0))
184
+           pw_log_warn("%p: write failed %m", t->node);
185
+   }
186
+}
187
+
188
+struct pw_node_peer {
189
+   int ref;
190
+   struct spa_list link;           /**< link in peer list */
191
+   struct pw_impl_node *output;        /**< the output node */
192
+   struct pw_node_target target;       /**< target of the input node */
193
+};
194
+
195
+struct pw_node_peer *pw_node_peer_ref(struct pw_impl_node *onode, struct pw_impl_node *inode);
196
+void pw_node_peer_unref(struct pw_node_peer *peer);
197
+
198
 #define pw_impl_node_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_impl_node_events, m, v, ##__VA_ARGS__)
199
 #define pw_impl_node_emit_destroy(n)           pw_impl_node_emit(n, destroy, 0)
200
 #define pw_impl_node_emit_free(n)          pw_impl_node_emit(n, free, 0)
201
@@ -645,6 +736,7 @@
202
    uint32_t priority_driver;   /** priority for being driver */
203
    char **groups;          /** groups to schedule this node in */
204
    char **link_groups;     /** groups this node is linked to */
205
+   char **sync_groups;     /** sync groups this node is in */
206
    uint64_t spa_flags;
207
 
208
    unsigned int registered:1;
209
@@ -668,7 +760,6 @@
210
    unsigned int transport_sync:1;  /**< supports transport sync */
211
    unsigned int target_pending:1;  /**< a quantum/rate update is pending */
212
    unsigned int moved:1;       /**< the node was moved drivers */
213
-   unsigned int added:1;       /**< the node was add to graph */
214
    unsigned int pause_on_idle:1;   /**< Pause processing when IDLE */
215
    unsigned int suspend_on_idle:1;
216
    unsigned int need_resume:1;
217
@@ -678,6 +769,9 @@
218
                      *  trigger to start processing. */
219
    unsigned int can_suspend:1;
220
    unsigned int checked;       /**< for sorting */
221
+   unsigned int sync:1;        /**< the sync-groups are active */
222
+   unsigned int transport:1;   /**< the transport is active */
223
+   unsigned int async:1;       /**< async processing, one cycle latency */
224
 
225
    uint32_t port_user_data_size;   /**< extra size for port user data */
226
 
227
@@ -702,7 +796,6 @@
228
    struct spa_hook_list rt_listener_list;
229
 
230
    struct pw_loop *data_loop;      /**< the data loop for this node */
231
-   struct spa_system *data_system;
232
 
233
    struct spa_fraction latency;        /**< requested latency */
234
    struct spa_fraction max_latency;    /**< maximum latency */
235
@@ -718,7 +811,6 @@
236
 
237
        struct spa_list target_list;        /* list of targets to signal after
238
                             * this node */
239
-       struct pw_node_target driver_target;    /* driver target that we signal */
240
        struct spa_list input_mix;      /* our input ports (and mixers) */
241
        struct spa_list output_mix;     /* output ports (and mixers) */
242
 
243
@@ -727,7 +819,11 @@
244
        struct spa_list driver_link;        /* our link in driver */
245
 
246
        struct spa_ratelimit rate_limit;
247
+
248
+       bool prepared;              /**< the node was added to loop */
249
    } rt;
250
+   struct pw_node_peer *to_driver_peer;        /* node -> driver */
251
+   struct pw_node_peer *from_driver_peer;      /* driver -> node */
252
    struct spa_fraction target_rate;
253
    uint64_t target_quantum;
254
 
255
@@ -739,17 +835,21 @@
256
 
257
 struct pw_impl_port_mix {
258
    struct spa_list link;
259
-   struct spa_list rt_link;
260
    struct pw_impl_port *p;
261
    struct {
262
        enum spa_direction direction;
263
        uint32_t port_id;
264
    } port;
265
-   struct spa_io_buffers *io;
266
+   struct spa_io_buffers *io2;
267
+   void *io_data;
268
    uint32_t id;
269
    uint32_t peer_id;
270
-   unsigned int have_buffers:1;
271
-   unsigned int active:1;
272
+   bool have_buffers;
273
+
274
+   struct {
275
+       bool active;
276
+       struct spa_list link;
277
+   } rt;
278
 };
279
 
280
 struct pw_impl_port_implementation {
281
@@ -801,6 +901,7 @@
282
 #define PW_IMPL_PORT_FLAG_BUFFERS      (1<<1)      /**< port has data */
283
 #define PW_IMPL_PORT_FLAG_CONTROL      (1<<2)      /**< port has control */
284
 #define PW_IMPL_PORT_FLAG_NO_MIXER     (1<<3)      /**< don't try to add mixer to port */
285
+#define PW_IMPL_PORT_FLAG_ASYNC            (1<<4)      /**< port support async io */
286
    uint32_t flags;
287
    uint64_t spa_flags;
288
 
289
@@ -840,8 +941,8 @@
290
    struct {
291
        struct spa_io_buffers io;   /**< io area of the port */
292
        struct spa_list node_link;
293
+       bool added;
294
    } rt;                   /**< data only accessed from the data thread */
295
-   unsigned int added:1;
296
    unsigned int destroying:1;
297
    unsigned int passive:1;
298
    int busy_count;
299
@@ -868,14 +969,6 @@
300
    unsigned int valid:1;
301
 };
302
 
303
-struct pw_node_peer {
304
-   int ref;
305
-   int active_count;
306
-   struct spa_list link;           /**< link in peer list */
307
-   struct pw_impl_node *output;        /**< the output node */
308
-   struct pw_node_target target;       /**< target of the input node */
309
-};
310
-
311
 #define pw_impl_link_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_impl_link_events, m, v, ##__VA_ARGS__)
312
 #define pw_impl_link_emit_destroy(l)       pw_impl_link_emit(l, destroy, 0)
313
 #define pw_impl_link_emit_free(l)      pw_impl_link_emit(l, free, 0)
314
@@ -1148,7 +1241,9 @@
315
 /** Find a good format between 2 ports */
316
 int pw_context_find_format(struct pw_context *context,
317
            struct pw_impl_port *output,
318
+           uint32_t output_mix,
319
            struct pw_impl_port *input,
320
+           uint32_t input_mix,
321
            struct pw_properties *props,
322
            uint32_t n_format_filters,
323
            struct spa_pod **format_filters,
324
@@ -1240,6 +1335,11 @@
325
 
326
 int pw_impl_node_trigger(struct pw_impl_node *node);
327
 
328
+int pw_impl_node_set_io(struct pw_impl_node *node, uint32_t id, void *data, size_t size);
329
+
330
+int pw_impl_node_add_target(struct pw_impl_node *node, struct pw_node_target *t);
331
+int pw_impl_node_remove_target(struct pw_impl_node *node, struct pw_node_target *t);
332
+
333
 /** Prepare a link
334
   * Starts the negotiation of formats and buffers on \a link */
335
 int pw_impl_link_prepare(struct pw_impl_link *link);
336
@@ -1294,7 +1394,8 @@
337
 
338
 bool pw_should_dlclose(void);
339
 
340
-/** \endcond */
341
+void pw_log_topic_register_enum(const struct spa_log_topic_enum *e);
342
+void pw_log_topic_unregister_enum(const struct spa_log_topic_enum *e);
343
 
344
 #ifdef __cplusplus
345
 }
346
pipewire-1.0.1.tar.bz2/src/pipewire/properties.c -> pipewire-1.2.0.tar.gz/src/pipewire/properties.c Changed
570
 
1
@@ -8,6 +8,9 @@
2
 #include <spa/utils/ansi.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/utils/string.h>
5
+#include <spa/utils/cleanup.h>
6
+#include <spa/utils/result.h>
7
+#include <spa/debug/log.h>
8
 
9
 #include "pipewire/array.h"
10
 #include "pipewire/log.h"
11
@@ -25,24 +28,38 @@
12
 };
13
 /** \endcond */
14
 
15
-static int add_func(struct pw_properties *this, char *key, char *value)
16
+static int add_item(struct properties *impl, const char *key, bool take_key, const char *value, bool take_value)
17
 {
18
    struct spa_dict_item *item;
19
-   struct properties *impl = SPA_CONTAINER_OF(this, struct properties, this);
20
+   const char *k, *v;
21
 
22
-   item = pw_array_add(&impl->items, sizeof(struct spa_dict_item));
23
-   if (item == NULL) {
24
-       free(key);
25
-       free(value);
26
-       return -errno;
27
-   }
28
+   k = take_key ? key : NULL;
29
+   v = take_value ? value: NULL;
30
+
31
+   if (!take_key && key && (k = strdup(key)) == NULL)
32
+       goto error;
33
+   if (!take_value && value && (v = strdup(value)) == NULL)
34
+       goto error;
35
 
36
-   item->key = key;
37
-   item->value = value;
38
+   item = pw_array_add(&impl->items, sizeof(struct spa_dict_item));
39
+   if (item == NULL)
40
+       goto error;
41
 
42
-   this->dict.items = impl->items.data;
43
-   this->dict.n_items++;
44
+   item->key = k;
45
+   item->value = v;
46
    return 0;
47
+
48
+error:
49
+   free((char*)k);
50
+   free((char*)v);
51
+   return -errno;
52
+}
53
+
54
+static void update_dict(struct pw_properties *properties)
55
+{
56
+   struct properties *impl = SPA_CONTAINER_OF(properties, struct properties, this);
57
+   properties->dict.items = impl->items.data;
58
+   properties->dict.n_items = pw_array_get_len(&impl->items, struct spa_dict_item);
59
 }
60
 
61
 static void clear_item(struct spa_dict_item *item)
62
@@ -51,26 +68,20 @@
63
    free((char *) item->value);
64
 }
65
 
66
-static int find_index(const struct pw_properties *this, const char *key)
67
+static void properties_init(struct properties *impl, int prealloc)
68
 {
69
-   const struct spa_dict_item *item;
70
-   item = spa_dict_lookup_item(&this->dict, key);
71
-   if (item == NULL)
72
-       return -1;
73
-   return item - this->dict.items;
74
+   pw_array_init(&impl->items, 16);
75
+   pw_array_ensure_size(&impl->items, sizeof(struct spa_dict_item) * prealloc);
76
 }
77
 
78
 static struct properties *properties_new(int prealloc)
79
 {
80
    struct properties *impl;
81
 
82
-   impl = calloc(1, sizeof(struct properties));
83
-   if (impl == NULL)
84
+   if ((impl = calloc(1, sizeof(struct properties))) == NULL)
85
        return NULL;
86
 
87
-   pw_array_init(&impl->items, 16);
88
-   pw_array_ensure_size(&impl->items, sizeof(struct spa_dict_item) * prealloc);
89
-
90
+   properties_init(impl, prealloc);
91
    return impl;
92
 }
93
 
94
@@ -86,6 +97,7 @@
95
    struct properties *impl;
96
    va_list varargs;
97
    const char *value;
98
+   int res = 0;
99
 
100
    impl = properties_new(16);
101
    if (impl == NULL)
102
@@ -95,12 +107,18 @@
103
    while (key != NULL) {
104
        value = va_arg(varargs, char *);
105
        if (value && key0)
106
-           add_func(&impl->this, strdup(key), strdup(value));
107
+           if ((res = add_item(impl, key, false, value, false)) < 0)
108
+               goto error;
109
        key = va_arg(varargs, char *);
110
    }
111
    va_end(varargs);
112
+   update_dict(&impl->this);
113
 
114
    return &impl->this;
115
+error:
116
+   pw_properties_free(&impl->this);
117
+   errno = -res;
118
+   return NULL;
119
 }
120
 
121
 /** Make a new properties object from the given dictionary
122
@@ -113,6 +131,7 @@
123
 {
124
    uint32_t i;
125
    struct properties *impl;
126
+   int res;
127
 
128
    impl = properties_new(SPA_ROUND_UP_N(dict->n_items, 16));
129
    if (impl == NULL)
130
@@ -121,28 +140,85 @@
131
    for (i = 0; i < dict->n_items; i++) {
132
        const struct spa_dict_item *it = &dict->itemsi;
133
        if (it->key != NULL && it->key0 && it->value != NULL)
134
-           add_func(&impl->this, strdup(it->key),
135
-                strdup(it->value));
136
+           if ((res = add_item(impl, it->key, false, it->value, false)) < 0)
137
+               goto error;
138
    }
139
+   update_dict(&impl->this);
140
 
141
    return &impl->this;
142
+
143
+error:
144
+   pw_properties_free(&impl->this);
145
+   errno = -res;
146
+   return NULL;
147
 }
148
 
149
-/** Update the properties from the given string, overwriting any
150
- * existing keys with the new values from \a str.
151
- *
152
- * \a str should be a whitespace separated list of key=value
153
- * strings or a json object, see pw_properties_new_string().
154
- *
155
- * \return The number of properties added or updated
156
- */
157
-SPA_EXPORT
158
-int pw_properties_update_string(struct pw_properties *props, const char *str, size_t size)
159
+static int do_replace(struct pw_properties *properties, const char *key, bool take_key,
160
+       const char *value, bool take_value)
161
+{
162
+   struct properties *impl = SPA_CONTAINER_OF(properties, struct properties, this);
163
+   struct spa_dict_item *item;
164
+   int res = 0;
165
+
166
+   if (key == NULL || key0 == 0)
167
+       goto exit_noupdate;
168
+
169
+   item = (struct spa_dict_item*) spa_dict_lookup_item(&properties->dict, key);
170
+
171
+   if (item == NULL) {
172
+       if (value == NULL)
173
+           goto exit_noupdate;
174
+       if ((res = add_item(impl, key, take_key, value, take_value)) < 0)
175
+           return res;
176
+       SPA_FLAG_CLEAR(properties->dict.flags, SPA_DICT_FLAG_SORTED);
177
+   } else {
178
+       if (value && spa_streq(item->value, value))
179
+           goto exit_noupdate;
180
+
181
+       if (value == NULL) {
182
+           struct spa_dict_item *last = pw_array_get_unchecked(&impl->items,
183
+                            pw_array_get_len(&impl->items, struct spa_dict_item) - 1,
184
+                            struct spa_dict_item);
185
+           clear_item(item);
186
+           item->key = last->key;
187
+           item->value = last->value;
188
+           impl->items.size -= sizeof(struct spa_dict_item);
189
+           SPA_FLAG_CLEAR(properties->dict.flags, SPA_DICT_FLAG_SORTED);
190
+       } else {
191
+           char *v = NULL;
192
+           if (!take_value && value && (v = strdup(value)) == NULL) {
193
+               res = -errno;
194
+               goto exit_noupdate;
195
+           }
196
+           free((char *) item->value);
197
+           item->value = take_value ? value : v;
198
+       }
199
+       if (take_key)
200
+           free((char*)key);
201
+   }
202
+   update_dict(properties);
203
+   return 1;
204
+
205
+exit_noupdate:
206
+   if (take_key)
207
+       free((char*)key);
208
+   if (take_value)
209
+       free((char*)value);
210
+   return res;
211
+}
212
+
213
+static int update_string(struct pw_properties *props, const char *str, size_t size,
214
+       int *count, struct spa_error_location *loc)
215
 {
216
-   struct properties *impl = SPA_CONTAINER_OF(props, struct properties, this);
217
    struct spa_json it2;
218
-   char key1024, *val;
219
-   int count = 0;
220
+   char key1024;
221
+   struct spa_error_location el;
222
+   bool err;
223
+   int res, cnt = 0;
224
+   struct properties changes;
225
+
226
+   if (props)
227
+       properties_init(&changes, 16);
228
 
229
    spa_json_init(&it0, str, size);
230
    if (spa_json_enter_object(&it0, &it1) <= 0)
231
@@ -151,6 +227,7 @@
232
    while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
233
        int len;
234
        const char *value;
235
+       char *val = NULL;
236
 
237
        if ((len = spa_json_next(&it1, &value)) <= 0)
238
            break;
239
@@ -160,13 +237,101 @@
240
        else {
241
            if (spa_json_is_container(value, len))
242
                len = spa_json_container_len(&it1, value, len);
243
+           if (len <= 0)
244
+               break;
245
 
246
-           if ((val = malloc(len+1)) != NULL)
247
+           if (props) {
248
+               if ((val = malloc(len+1)) == NULL) {
249
+                   it1.state = SPA_JSON_ERROR_FLAG;
250
+                   break;
251
+               }
252
                spa_json_parse_stringn(value, len, val, len+1);
253
+           }
254
        }
255
-       count += pw_properties_set(&impl->this, key, val);
256
-       free(val);
257
+       if (props) {
258
+           const struct spa_dict_item *item;
259
+           item = spa_dict_lookup_item(&props->dict, key);
260
+           if (item && spa_streq(item->value, val)) {
261
+               free(val);
262
+               continue;
263
+           }
264
+           /* item changed or added, apply changes later */
265
+           if ((errno = -add_item(&changes, key, false, val, true) < 0)) {
266
+               it1.state = SPA_JSON_ERROR_FLAG;
267
+               break;
268
+           }
269
+       }
270
+   }
271
+   if ((err = spa_json_get_error(&it1, str, &el))) {
272
+       if (loc == NULL)
273
+           spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN,
274
+                   &el, "error parsing more than %d properties: %s",
275
+                   cnt, el.reason);
276
+       else
277
+           *loc = el;
278
    }
279
+   if (props) {
280
+       struct spa_dict_item *item;
281
+       if (loc == NULL || !err) {
282
+           pw_array_for_each(item, &changes.items)
283
+               if ((res = do_replace(props, item->key, true, item->value, true)) < 0)
284
+                   pw_log_warn("error updating property: %s", spa_strerror(res));
285
+               else
286
+                   cnt += res;
287
+
288
+       } else {
289
+           pw_array_for_each(item, &changes.items)
290
+               clear_item(item);
291
+       }
292
+       pw_array_clear(&changes.items);
293
+   }
294
+   if (count)
295
+       *count = cnt;
296
+
297
+   return !err;
298
+}
299
+
300
+/** Update the properties from the given string, overwriting any
301
+ * existing keys with the new values from \a str.
302
+ *
303
+ * \a str should be a whitespace separated list of key=value
304
+ * strings or a json object, see pw_properties_new_string().
305
+ *
306
+ * \return The number of properties added or updated
307
+ */
308
+SPA_EXPORT
309
+int pw_properties_update_string(struct pw_properties *props, const char *str, size_t size)
310
+{
311
+   int count = 0;
312
+   update_string(props, str, size, &count, NULL);
313
+   return count;
314
+}
315
+
316
+/** Check \a str is a well-formed properties JSON string and update
317
+ * the properties on success.
318
+ *
319
+ * \a str should be a whitespace separated list of key=value
320
+ * strings or a json object, see pw_properties_new_string().
321
+ *
322
+ * When the check fails, this function will not update \a props.
323
+ *
324
+ * \param props  The properties to attempt to update, maybe be NULL
325
+ *            to simply check the JSON string.
326
+ * \param str  The JSON object with new values
327
+ * \param size  The length of the JSON string.
328
+ * \param loc  Return value for parse error location
329
+ * \return a negative value when string is not valid and \a loc contains
330
+ *         the error location or the number of updated properties in
331
+ *         \a props.
332
+ * \since 1.1.0
333
+ */
334
+SPA_EXPORT
335
+int pw_properties_update_string_checked(struct pw_properties *props,
336
+       const char *str, size_t size, struct spa_error_location *loc)
337
+{
338
+   int count = 0;
339
+   if (!update_string(props, str, size, &count, loc))
340
+       return -EINVAL;
341
    return count;
342
 }
343
 
344
@@ -199,6 +364,27 @@
345
    return NULL;
346
 }
347
 
348
+SPA_EXPORT
349
+struct pw_properties *
350
+pw_properties_new_string_checked(const char *object, size_t size, struct spa_error_location *loc)
351
+{
352
+   struct properties *impl;
353
+   int res;
354
+
355
+   impl = properties_new(16);
356
+   if (impl == NULL)
357
+       return NULL;
358
+
359
+   if ((res = pw_properties_update_string_checked(&impl->this, object, size, loc)) < 0)
360
+       goto error;
361
+
362
+   return &impl->this;
363
+error:
364
+   pw_properties_free(&impl->this);
365
+   errno = -res;
366
+   return NULL;
367
+}
368
+
369
 /** Copy a properties object
370
  *
371
  * \param properties properties to copy
372
@@ -221,12 +407,17 @@
373
 int pw_properties_update_keys(struct pw_properties *props,
374
        const struct spa_dict *dict, const char * const keys)
375
 {
376
-   int i, changed = 0;
377
+   int i, res, changed = 0;
378
    const char *str;
379
 
380
    for (i = 0; keysi; i++) {
381
-       if ((str = spa_dict_lookup(dict, keysi)) != NULL)
382
-           changed += pw_properties_set(props, keysi, str);
383
+       if ((str = spa_dict_lookup(dict, keysi)) != NULL) {
384
+           if ((res = pw_properties_set(props, keysi, str)) < 0)
385
+               pw_log_warn("error updating property %s:%s: %s",
386
+                       keysi, str, spa_strerror(res));
387
+           else
388
+               changed += res;
389
+       }
390
    }
391
    return changed;
392
 }
393
@@ -246,11 +437,16 @@
394
        const struct spa_dict *dict, const char * const ignore)
395
 {
396
    const struct spa_dict_item *it;
397
-   int changed = 0;
398
+   int res, changed = 0;
399
 
400
    spa_dict_for_each(it, dict) {
401
-       if (ignore == NULL || !has_key(ignore, it->key))
402
-           changed += pw_properties_set(props, it->key, it->value);
403
+       if (ignore == NULL || !has_key(ignore, it->key)) {
404
+           if ((res = pw_properties_set(props, it->key, it->value)) < 0)
405
+               pw_log_warn("error updating property %s:%s: %s",
406
+                       it->key, it->value, spa_strerror(res));
407
+           else
408
+               changed += res;
409
+       }
410
    }
411
    return changed;
412
 }
413
@@ -285,11 +481,15 @@
414
                 const struct spa_dict *dict)
415
 {
416
    const struct spa_dict_item *it;
417
-   int changed = 0;
418
-
419
-   spa_dict_for_each(it, dict)
420
-       changed += pw_properties_set(props, it->key, it->value);
421
+   int res, changed = 0;
422
 
423
+   spa_dict_for_each(it, dict) {
424
+       if ((res = pw_properties_set(props, it->key, it->value)) < 0)
425
+           pw_log_warn("error updating property %s:%s: %s",
426
+                   it->key, it->value, spa_strerror(res));
427
+       else
428
+           changed += res;
429
+   }
430
    return changed;
431
 }
432
 
433
@@ -306,11 +506,17 @@
434
                 const struct spa_dict *dict)
435
 {
436
    uint32_t i;
437
-   int added = 0;
438
+   int res, added = 0;
439
 
440
    for (i = 0; i < dict->n_items; i++) {
441
-       if (pw_properties_get(props, dict->itemsi.key) == NULL)
442
-           added += pw_properties_set(props, dict->itemsi.key, dict->itemsi.value);
443
+       const struct spa_dict_item *it = &dict->itemsi;
444
+       if (pw_properties_get(props, it->key) == NULL) {
445
+           if ((res = pw_properties_set(props, it->key, it->value)) < 0)
446
+               pw_log_warn("error updating property %s:%s: %s",
447
+                       it->key, it->value, spa_strerror(res));
448
+           else
449
+               added += res;
450
+       }
451
    }
452
    return added;
453
 }
454
@@ -330,14 +536,19 @@
455
        const struct spa_dict *dict, const char * const keys)
456
 {
457
    uint32_t i;
458
-   int added = 0;
459
+   int res, added = 0;
460
    const char *str;
461
 
462
    for (i = 0; keysi; i++) {
463
        if ((str = spa_dict_lookup(dict, keysi)) == NULL)
464
            continue;
465
-       if (pw_properties_get(props, keysi) == NULL)
466
-           added += pw_properties_set(props, keysi, str);
467
+       if (pw_properties_get(props, keysi) == NULL) {
468
+           if ((res = pw_properties_set(props, keysi, str)) < 0)
469
+               pw_log_warn("error updating property %s:%s: %s",
470
+                       keysi, str, spa_strerror(res));
471
+           else
472
+               added += res;
473
+       }
474
    }
475
    return added;
476
 }
477
@@ -360,50 +571,6 @@
478
    free(impl);
479
 }
480
 
481
-static int do_replace(struct pw_properties *properties, const char *key, char *value, bool copy)
482
-{
483
-   struct properties *impl = SPA_CONTAINER_OF(properties, struct properties, this);
484
-   int index;
485
-
486
-   if (key == NULL || key0 == 0)
487
-       goto exit_noupdate;
488
-
489
-   index = find_index(properties, key);
490
-
491
-   if (index == -1) {
492
-       if (value == NULL)
493
-           return 0;
494
-       add_func(properties, strdup(key), copy ? strdup(value) : value);
495
-       SPA_FLAG_CLEAR(properties->dict.flags, SPA_DICT_FLAG_SORTED);
496
-   } else {
497
-       struct spa_dict_item *item =
498
-           pw_array_get_unchecked(&impl->items, index, struct spa_dict_item);
499
-
500
-       if (value && spa_streq(item->value, value))
501
-           goto exit_noupdate;
502
-
503
-       if (value == NULL) {
504
-           struct spa_dict_item *last = pw_array_get_unchecked(&impl->items,
505
-                            pw_array_get_len(&impl->items, struct spa_dict_item) - 1,
506
-                            struct spa_dict_item);
507
-           clear_item(item);
508
-           item->key = last->key;
509
-           item->value = last->value;
510
-           impl->items.size -= sizeof(struct spa_dict_item);
511
-           properties->dict.n_items--;
512
-           SPA_FLAG_CLEAR(properties->dict.flags, SPA_DICT_FLAG_SORTED);
513
-       } else {
514
-           free((char *) item->value);
515
-           item->value = copy ? strdup(value) : value;
516
-       }
517
-   }
518
-   return 1;
519
-exit_noupdate:
520
-   if (!copy)
521
-       free(value);
522
-   return 0;
523
-}
524
-
525
 /** Set a property value
526
  *
527
  * \param properties the properties to change
528
@@ -411,7 +578,7 @@
529
  * \param value a value or NULL to remove the key
530
  * \return 1 if the properties were changed. 0 if nothing was changed because
531
  *  the property already existed with the same value or because the key to remove
532
- *  did not exist.
533
+ *  did not exist. < 0 if an error occurred and nothing was changed.
534
  *
535
  * Set the property in \a properties with \a key to \a value. Any previous value
536
  * of \a key will be overwritten. When \a value is NULL, the key will be
537
@@ -420,7 +587,7 @@
538
 SPA_EXPORT
539
 int pw_properties_set(struct pw_properties *properties, const char *key, const char *value)
540
 {
541
-   return do_replace(properties, key, (char*)value, true);
542
+   return do_replace(properties, key, false, (char*)value, false);
543
 }
544
 
545
 SPA_EXPORT
546
@@ -432,7 +599,7 @@
547
        if (vasprintf(&value, format, args) < 0)
548
            return -errno;
549
    }
550
-   return do_replace(properties, key, value, false);
551
+   return do_replace(properties, key, false, value, true);
552
 }
553
 
554
 /** Set a property value by format
555
@@ -472,13 +639,7 @@
556
 SPA_EXPORT
557
 const char *pw_properties_get(const struct pw_properties *properties, const char *key)
558
 {
559
-   struct properties *impl = SPA_CONTAINER_OF(properties, struct properties, this);
560
-   int index = find_index(properties, key);
561
-
562
-   if (index == -1)
563
-       return NULL;
564
-
565
-   return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item)->value;
566
+   return spa_dict_lookup(&properties->dict, key);
567
 }
568
 
569
 /** Fetch a property as uint32_t.
570
pipewire-1.0.1.tar.bz2/src/pipewire/properties.h -> pipewire-1.2.0.tar.gz/src/pipewire/properties.h Changed
46
 
1
@@ -11,6 +11,7 @@
2
 
3
 #include <stdarg.h>
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/dict.h>
7
 #include <spa/utils/string.h>
8
 
9
@@ -41,6 +42,10 @@
10
 pw_properties_new_string(const char *args);
11
 
12
 struct pw_properties *
13
+pw_properties_new_string_checked(const char *args, size_t size,
14
+       struct spa_error_location *loc);
15
+
16
+struct pw_properties *
17
 pw_properties_copy(const struct pw_properties *properties);
18
 
19
 int pw_properties_update_keys(struct pw_properties *props,
20
@@ -51,10 +56,14 @@
21
 /* Update props with all key/value pairs from dict */
22
 int pw_properties_update(struct pw_properties *props,
23
             const struct spa_dict *dict);
24
+
25
 /* Update props with all key/value pairs from str */
26
 int pw_properties_update_string(struct pw_properties *props,
27
        const char *str, size_t size);
28
 
29
+int pw_properties_update_string_checked(struct pw_properties *props,
30
+       const char *str, size_t size, struct spa_error_location *loc);
31
+
32
 int pw_properties_add(struct pw_properties *oldprops,
33
             const struct spa_dict *dict);
34
 int pw_properties_add_keys(struct pw_properties *oldprops,
35
@@ -176,6 +185,10 @@
36
  * \}
37
  */
38
 
39
+SPA_DEFINE_AUTOPTR_CLEANUP(pw_properties, struct pw_properties, {
40
+   spa_clear_ptr(*thing, pw_properties_free);
41
+})
42
+
43
 #ifdef __cplusplus
44
 }
45
 #endif
46
pipewire-1.0.1.tar.bz2/src/pipewire/protocol.h -> pipewire-1.2.0.tar.gz/src/pipewire/protocol.h Changed
29
 
1
@@ -81,7 +81,7 @@
2
 };
3
 
4
 struct pw_protocol_implementation {
5
-#define PW_VERSION_PROTOCOL_IMPLEMENTATION 0
6
+#define PW_VERSION_PROTOCOL_IMPLEMENTATION 1
7
    uint32_t version;
8
 
9
    struct pw_protocol_client * (*new_client) (struct pw_protocol *protocol,
10
@@ -90,6 +90,10 @@
11
    struct pw_protocol_server * (*add_server) (struct pw_protocol *protocol,
12
                           struct pw_impl_core *core,
13
                           const struct spa_dict *props);
14
+   struct pw_protocol_server * (*add_fd_server) (struct pw_protocol *protocol,
15
+                          struct pw_impl_core *core,
16
+                          int listen_fd, int close_fd,
17
+                          const struct spa_dict *props);
18
 };
19
 
20
 struct pw_protocol_events {
21
@@ -101,6 +105,7 @@
22
 
23
 #define pw_protocol_new_client(p,...)  (pw_protocol_get_implementation(p)->new_client(p,__VA_ARGS__))
24
 #define pw_protocol_add_server(p,...)  (pw_protocol_get_implementation(p)->add_server(p,__VA_ARGS__))
25
+#define pw_protocol_add_fd_server(p,...)   (pw_protocol_get_implementation(p)->add_fd_server(p,__VA_ARGS__))
26
 #define pw_protocol_ext(p,type,method,...) (((type*)pw_protocol_get_extension(p))->method( __VA_ARGS__))
27
 
28
 struct pw_protocol *pw_protocol_new(struct pw_context *context, const char *name, size_t user_data_size);
29
pipewire-1.0.1.tar.bz2/src/pipewire/settings.c -> pipewire-1.2.0.tar.gz/src/pipewire/settings.c Changed
48
 
1
@@ -26,6 +26,7 @@
2
 #define DEFAULT_CLOCK_MIN_QUANTUM      32u
3
 #define DEFAULT_CLOCK_MAX_QUANTUM      2048u
4
 #define DEFAULT_CLOCK_QUANTUM_LIMIT        8192u
5
+#define DEFAULT_CLOCK_QUANTUM_FLOOR        4u
6
 #define DEFAULT_CLOCK_POWER_OF_TWO_QUANTUM true
7
 #define DEFAULT_VIDEO_WIDTH            640
8
 #define DEFAULT_VIDEO_HEIGHT           480
9
@@ -142,8 +143,8 @@
10
        return 0;
11
 
12
    if (spa_streq(key, "log.level")) {
13
-       v = value ? atoi(value) : 3;
14
-       pw_log_set_level(v);
15
+       if (pw_log_set_level_string(value) < 0)
16
+           pw_log_warn("Ignoring unknown settings metadata log.level '%s'", value);
17
    } else if (spa_streq(key, "clock.rate")) {
18
        v = value ? atoi(value) : 0;
19
        s->clock_rate = v == 0 ? d->clock_rate : v;
20
@@ -212,6 +213,7 @@
21
    d->clock_min_quantum = get_default_int(p, "default.clock.min-quantum", DEFAULT_CLOCK_MIN_QUANTUM);
22
    d->clock_max_quantum = get_default_int(p, "default.clock.max-quantum", DEFAULT_CLOCK_MAX_QUANTUM);
23
    d->clock_quantum_limit = get_default_int(p, "default.clock.quantum-limit", DEFAULT_CLOCK_QUANTUM_LIMIT);
24
+   d->clock_quantum_floor = get_default_int(p, "default.clock.quantum-floor", DEFAULT_CLOCK_QUANTUM_FLOOR);
25
    d->video_size.width = get_default_int(p, "default.video.width", DEFAULT_VIDEO_WIDTH);
26
    d->video_size.height = get_default_int(p, "default.video.height", DEFAULT_VIDEO_HEIGHT);
27
    d->video_rate.num = get_default_int(p, "default.video.rate.num", DEFAULT_VIDEO_RATE_NUM);
28
@@ -227,12 +229,16 @@
29
    d->check_quantum = get_default_bool(p, "settings.check-quantum", DEFAULT_CHECK_QUANTUM);
30
    d->check_rate = get_default_bool(p, "settings.check-rate", DEFAULT_CHECK_RATE);
31
 
32
+   d->link_max_buffers = SPA_MAX(d->link_max_buffers, 1u);
33
+
34
    d->clock_quantum_limit = SPA_CLAMP(d->clock_quantum_limit,
35
-           CLOCK_MIN_QUANTUM, CLOCK_MAX_QUANTUM);
36
+           CLOCK_QUANTUM_FLOOR, CLOCK_QUANTUM_LIMIT);
37
+   d->clock_quantum_floor = SPA_CLAMP(d->clock_quantum_floor,
38
+           CLOCK_QUANTUM_FLOOR, d->clock_quantum_limit);
39
    d->clock_max_quantum = SPA_CLAMP(d->clock_max_quantum,
40
-           CLOCK_MIN_QUANTUM, d->clock_quantum_limit);
41
+           d->clock_quantum_floor, d->clock_quantum_limit);
42
    d->clock_min_quantum = SPA_CLAMP(d->clock_min_quantum,
43
-           CLOCK_MIN_QUANTUM, d->clock_max_quantum);
44
+           d->clock_quantum_floor, d->clock_max_quantum);
45
    d->clock_quantum = SPA_CLAMP(d->clock_quantum,
46
            d->clock_min_quantum, d->clock_max_quantum);
47
 }
48
pipewire-1.0.1.tar.bz2/src/pipewire/stream.c -> pipewire-1.2.0.tar.gz/src/pipewire/stream.c Changed
830
 
1
@@ -13,6 +13,7 @@
2
 #include <spa/param/format-utils.h>
3
 #include <spa/node/io.h>
4
 #include <spa/node/utils.h>
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/ringbuffer.h>
7
 #include <spa/pod/filter.h>
8
 #include <spa/pod/dynamic.h>
9
@@ -20,7 +21,6 @@
10
 
11
 #define PW_ENABLE_DEPRECATED
12
 
13
-#include <pipewire/cleanup.h>
14
 #include "pipewire/pipewire.h"
15
 #include "pipewire/stream.h"
16
 #include "pipewire/private.h"
17
@@ -34,8 +34,6 @@
18
 
19
 static bool mlock_warned = false;
20
 
21
-static uint32_t mappable_dataTypes = (1<<SPA_DATA_MemFd);
22
-
23
 struct buffer {
24
    struct pw_buffer this;
25
    uint32_t id;
26
@@ -95,14 +93,10 @@
27
    struct spa_hook_list hooks;
28
    struct spa_callbacks callbacks;
29
 
30
-   struct spa_io_clock *clock;
31
-   struct spa_io_position *position;
32
    struct spa_io_buffers *io;
33
    struct spa_io_rate_match *rate_match;
34
    uint32_t rate_queued;
35
-   struct {
36
-       struct spa_io_position *position;
37
-   } rt;
38
+   uint64_t rate_size;
39
 
40
    uint64_t port_change_mask_all;
41
    struct spa_port_info port_info;
42
@@ -153,11 +147,10 @@
43
    unsigned int drained:1;
44
    unsigned int allow_mlock:1;
45
    unsigned int warn_mlock:1;
46
-   unsigned int process_rt:1;
47
-   unsigned int driving:1;
48
    unsigned int using_trigger:1;
49
    unsigned int trigger:1;
50
    unsigned int early_process:1;
51
+   unsigned int trigger_done_rt:1;
52
    int in_set_param;
53
    int in_emit_param_changed;
54
 };
55
@@ -220,28 +213,31 @@
56
    pw_log_debug("dataType: %u", dataType);
57
    if (dataType & (1u << SPA_DATA_MemPtr)) {
58
        SPA_POD_VALUE(struct spa_pod_int, &vals0) =
59
-           dataType | mappable_dataTypes;
60
+           dataType | (1<<SPA_DATA_MemFd);
61
        pw_log_debug("Change dataType: %u -> %u", dataType,
62
                SPA_POD_VALUE(struct spa_pod_int, &vals0));
63
    }
64
 }
65
 
66
-static struct param *add_param(struct stream *impl,
67
+static int add_param(struct stream *impl,
68
        uint32_t id, uint32_t flags, const struct spa_pod *param)
69
 {
70
    struct param *p;
71
    int idx;
72
 
73
-   if (param == NULL || !spa_pod_is_object(param)) {
74
-       errno = EINVAL;
75
-       return NULL;
76
-   }
77
+   if (param != NULL && !spa_pod_is_object(param))
78
+       return -EINVAL;
79
+   if (param == NULL || !spa_pod_object_has_props((struct spa_pod_object*)param))
80
+       return 0;
81
+
82
+   pw_log_pod(SPA_LOG_LEVEL_DEBUG, param);
83
+
84
    if (id == SPA_ID_INVALID)
85
        id = SPA_POD_OBJECT_ID(param);
86
 
87
    p = malloc(sizeof(struct param) + SPA_POD_SIZE(param));
88
    if (p == NULL)
89
-       return NULL;
90
+       return -errno;
91
 
92
    p->id = id;
93
    p->flags = flags;
94
@@ -266,7 +262,7 @@
95
        impl->port_paramsidx.flags |= SPA_PARAM_INFO_READ;
96
        impl->port_paramsidx.user++;
97
    }
98
-   return p;
99
+   return 0;
100
 }
101
 
102
 static void clear_params(struct stream *impl, uint32_t id)
103
@@ -326,10 +322,8 @@
104
        }
105
    }
106
    for (i = 0; i < n_params; i++) {
107
-       if (add_param(impl, id, 0, paramsi) == NULL) {
108
-           res = -errno;
109
+       if ((res = add_param(impl, id, 0, paramsi)) < 0)
110
            break;
111
-       }
112
    }
113
    return res;
114
 }
115
@@ -420,9 +414,8 @@
116
 
117
 static inline uint32_t update_requested(struct stream *impl)
118
 {
119
-   uint32_t index, id, res = 0;
120
+   uint32_t index, id;
121
    struct buffer *buffer;
122
-   struct spa_io_rate_match *r = impl->rate_match;
123
 
124
    if (spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index) < 1) {
125
        pw_log_debug("%p: no free buffers %d", impl, impl->n_buffers);
126
@@ -431,42 +424,21 @@
127
 
128
    id = impl->dequeued.idsindex & MASK_BUFFERS;
129
    buffer = &impl->buffersid;
130
-   if (r) {
131
-       buffer->this.requested = r->size;
132
-       res = r->size > 0 ? 1 : 0;
133
-   } else {
134
-       buffer->this.requested = impl->quantum;
135
-       res = 1;
136
-   }
137
+   buffer->this.requested = impl->rate_size;
138
+
139
    pw_log_trace_fp("%p: update buffer:%u req:%"PRIu64, impl, id, buffer->this.requested);
140
-   return res;
141
-}
142
 
143
-static int
144
-do_call_process(struct spa_loop *loop,
145
-                 bool async, uint32_t seq, const void *data, size_t size, void *user_data)
146
-{
147
-   struct stream *impl = user_data;
148
-   struct pw_stream *stream = &impl->this;
149
-   pw_log_trace_fp("%p: do process", stream);
150
-   if (!impl->disconnecting)
151
-       pw_stream_emit_process(stream);
152
-   return 0;
153
+   return buffer->this.requested > 0 ? 1 : 0;
154
 }
155
 
156
 static inline void call_process(struct stream *impl)
157
 {
158
-   pw_log_trace_fp("%p: call process rt:%u", impl, impl->process_rt);
159
+   pw_log_trace_fp("%p: call process buffers:%d", impl, impl->n_buffers);
160
    if (impl->n_buffers == 0 ||
161
        (impl->direction == SPA_DIRECTION_OUTPUT && update_requested(impl) <= 0))
162
        return;
163
-   if (impl->process_rt) {
164
-       if (impl->rt_callbacks.funcs)
165
-           spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_stream_events, process, 0);
166
-   } else {
167
-       pw_loop_invoke(impl->main_loop,
168
-           do_call_process, 1, NULL, 0, false, impl);
169
-   }
170
+   if (impl->rt_callbacks.funcs)
171
+       spa_callbacks_call_fast(&impl->rt_callbacks, struct pw_stream_events, process, 0);
172
 }
173
 
174
 static int
175
@@ -500,17 +472,14 @@
176
 
177
 static void call_trigger_done(struct stream *impl)
178
 {
179
-   pw_loop_invoke(impl->main_loop,
180
-       do_call_trigger_done, 1, NULL, 0, false, impl);
181
-}
182
-
183
-static int
184
-do_set_position(struct spa_loop *loop,
185
-       bool async, uint32_t seq, const void *data, size_t size, void *user_data)
186
-{
187
-   struct stream *impl = user_data;
188
-   impl->rt.position = impl->position;
189
-   return 0;
190
+   pw_log_trace_fp("%p: call trigger_done rt:%u", impl, impl->trigger_done_rt);
191
+   if (impl->trigger_done_rt) {
192
+       if (impl->rt_callbacks.funcs)
193
+           spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, trigger_done, 2);
194
+   } else {
195
+       pw_loop_invoke(impl->main_loop,
196
+           do_call_trigger_done, 1, NULL, 0, false, impl);
197
+   }
198
 }
199
 
200
 static int impl_set_io(void *object, uint32_t id, void *data, size_t size)
201
@@ -521,26 +490,6 @@
202
    pw_log_debug("%p: set io id %d (%s) %p %zd", impl, id,
203
            spa_debug_type_find_name(spa_type_io, id), data, size);
204
 
205
-   switch(id) {
206
-   case SPA_IO_Clock:
207
-       if (data && size >= sizeof(struct spa_io_clock))
208
-           impl->clock = data;
209
-       else
210
-           impl->clock = NULL;
211
-       break;
212
-   case SPA_IO_Position:
213
-       if (data && size >= sizeof(struct spa_io_position))
214
-           impl->position = data;
215
-       else
216
-           impl->position = NULL;
217
-
218
-       pw_loop_invoke(impl->data_loop,
219
-               do_set_position, 1, NULL, 0, true, impl);
220
-       break;
221
-   default:
222
-       break;
223
-   }
224
-   impl->driving = impl->clock && impl->position && impl->position->clock.id == impl->clock->id;
225
    pw_stream_emit_io_changed(stream, id, data, size);
226
 
227
    return 0;
228
@@ -607,9 +556,50 @@
229
    impl->in_emit_param_changed--;
230
 }
231
 
232
+static void emit_node_info(struct stream *d, bool full)
233
+{
234
+   uint32_t i;
235
+   uint64_t old = full ? d->info.change_mask : 0;
236
+   if (full)
237
+       d->info.change_mask = d->change_mask_all;
238
+   if (d->info.change_mask != 0) {
239
+       if (d->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
240
+           for (i = 0; i < d->info.n_params; i++) {
241
+               if (d->paramsi.user > 0) {
242
+                   d->paramsi.flags ^= SPA_PARAM_INFO_SERIAL;
243
+                   d->paramsi.user = 0;
244
+               }
245
+           }
246
+       }
247
+       spa_node_emit_info(&d->hooks, &d->info);
248
+   }
249
+   d->info.change_mask = old;
250
+}
251
+
252
+static void emit_port_info(struct stream *d, bool full)
253
+{
254
+   uint32_t i;
255
+   uint64_t old = full ? d->port_info.change_mask : 0;
256
+   if (full)
257
+       d->port_info.change_mask = d->port_change_mask_all;
258
+   if (d->port_info.change_mask != 0) {
259
+       if (d->port_info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
260
+           for (i = 0; i < d->port_info.n_params; i++) {
261
+               if (d->port_paramsi.user > 0) {
262
+                   d->port_paramsi.flags ^= SPA_PARAM_INFO_SERIAL;
263
+                   d->port_paramsi.user = 0;
264
+               }
265
+           }
266
+       }
267
+       spa_node_emit_port_info(&d->hooks, d->direction, 0, &d->port_info);
268
+   }
269
+   d->port_info.change_mask = old;
270
+}
271
+
272
 static int impl_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param)
273
 {
274
    struct stream *impl = object;
275
+   struct pw_stream *stream = &impl->this;
276
 
277
    if (id != SPA_PARAM_Props)
278
        return -ENOTSUP;
279
@@ -617,12 +607,17 @@
280
    if (impl->in_set_param == 0)
281
        emit_param_changed(impl, id, param);
282
 
283
+   if (stream->state == PW_STREAM_STATE_ERROR)
284
+       return stream->error_res;
285
+
286
+   emit_node_info(impl, false);
287
+   emit_port_info(impl, false);
288
    return 0;
289
 }
290
 
291
 static inline void copy_position(struct stream *impl, int64_t queued)
292
 {
293
-   struct spa_io_position *p = impl->rt.position;
294
+   struct spa_io_position *p = impl->this.node->rt.position;
295
 
296
    SPA_SEQ_WRITE(impl->seq);
297
    if (SPA_LIKELY(p != NULL)) {
298
@@ -637,8 +632,13 @@
299
        impl->time.queued = queued;
300
        impl->quantum = p->clock.duration;
301
    }
302
-   if (SPA_LIKELY(impl->rate_match != NULL))
303
+   if (SPA_LIKELY(impl->rate_match != NULL)) {
304
        impl->rate_queued = impl->rate_match->delay;
305
+       impl->rate_size = impl->rate_match->size;
306
+   } else {
307
+       impl->rate_queued = 0;
308
+       impl->rate_size = impl->quantum;
309
+   }
310
    SPA_SEQ_WRITE(impl->seq);
311
 }
312
 
313
@@ -665,17 +665,13 @@
314
        break;
315
    case SPA_NODE_COMMAND_Start:
316
        if (stream->state == PW_STREAM_STATE_PAUSED) {
317
-           pw_log_debug("%p: start %d", stream, impl->direction);
318
+           pw_log_debug("%p: start direction:%d", stream, impl->direction);
319
 
320
            if (impl->direction == SPA_DIRECTION_INPUT) {
321
                if (impl->io != NULL)
322
                    impl->io->status = SPA_STATUS_NEED_DATA;
323
            }
324
-           else if (!impl->process_rt && !impl->driving) {
325
-               copy_position(impl, impl->queued.incount);
326
-               call_process(impl);
327
-           }
328
-
329
+           copy_position(impl, impl->queued.incount);
330
            stream_set_state(stream, PW_STREAM_STATE_STREAMING, 0, NULL);
331
        }
332
        break;
333
@@ -686,46 +682,6 @@
334
    return 0;
335
 }
336
 
337
-static void emit_node_info(struct stream *d, bool full)
338
-{
339
-   uint32_t i;
340
-   uint64_t old = full ? d->info.change_mask : 0;
341
-   if (full)
342
-       d->info.change_mask = d->change_mask_all;
343
-   if (d->info.change_mask != 0) {
344
-       if (d->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
345
-           for (i = 0; i < d->info.n_params; i++) {
346
-               if (d->paramsi.user > 0) {
347
-                   d->paramsi.flags ^= SPA_PARAM_INFO_SERIAL;
348
-                   d->paramsi.user = 0;
349
-               }
350
-           }
351
-       }
352
-       spa_node_emit_info(&d->hooks, &d->info);
353
-   }
354
-   d->info.change_mask = old;
355
-}
356
-
357
-static void emit_port_info(struct stream *d, bool full)
358
-{
359
-   uint32_t i;
360
-   uint64_t old = full ? d->port_info.change_mask : 0;
361
-   if (full)
362
-       d->port_info.change_mask = d->port_change_mask_all;
363
-   if (d->port_info.change_mask != 0) {
364
-       if (d->port_info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
365
-           for (i = 0; i < d->port_info.n_params; i++) {
366
-               if (d->port_paramsi.user > 0) {
367
-                   d->port_paramsi.flags ^= SPA_PARAM_INFO_SERIAL;
368
-                   d->port_paramsi.user = 0;
369
-               }
370
-           }
371
-       }
372
-       spa_node_emit_port_info(&d->hooks, d->direction, 0, &d->port_info);
373
-   }
374
-   d->port_info.change_mask = old;
375
-}
376
-
377
 static int impl_add_listener(void *object,
378
        struct spa_hook *listener,
379
        const struct spa_node_events *events,
380
@@ -850,9 +806,11 @@
381
        if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) {
382
            for (j = 0; j < b->this.buffer->n_datas; j++) {
383
                struct spa_data *d = &b->this.buffer->datasj;
384
-               pw_log_debug("%p: clear buffer %d mem",
385
-                       stream, b->id);
386
-               unmap_data(impl, d);
387
+               if (SPA_FLAG_IS_SET(d->flags, SPA_DATA_FLAG_MAPPABLE)) {
388
+                   pw_log_debug("%p: clear buffer %d mem",
389
+                           stream, b->id);
390
+                   unmap_data(impl, d);
391
+               }
392
            }
393
        }
394
    }
395
@@ -901,6 +859,7 @@
396
 {
397
    struct stream *impl = object;
398
    struct pw_stream *stream = &impl->this;
399
+   uint32_t user;
400
    int res;
401
 
402
    pw_log_debug("%p: port:%d.%d id:%d (%s) param:%p disconnecting:%d", impl,
403
@@ -920,6 +879,7 @@
404
    switch (id) {
405
    case SPA_PARAM_Format:
406
        clear_buffers(stream);
407
+       user = impl->paramsNODE_Format.user;
408
        break;
409
    case SPA_PARAM_Latency:
410
        parse_latency(stream, param);
411
@@ -933,10 +893,17 @@
412
    if (stream->state == PW_STREAM_STATE_ERROR)
413
        return stream->error_res;
414
 
415
+   switch (id) {
416
+   case SPA_PARAM_Format:
417
+       if (user != impl->paramsNODE_Format.user)
418
+           res++;
419
+       break;
420
+   }
421
+
422
    emit_node_info(impl, false);
423
    emit_port_info(impl, false);
424
 
425
-   return 0;
426
+   return res;
427
 }
428
 
429
 static int impl_port_use_buffers(void *object,
430
@@ -973,7 +940,7 @@
431
        if (SPA_FLAG_IS_SET(impl_flags, PW_STREAM_FLAG_MAP_BUFFERS)) {
432
            for (j = 0; j < buffersi->n_datas; j++) {
433
                struct spa_data *d = &buffersi->datasj;
434
-               if ((mappable_dataTypes & (1<<d->type)) > 0) {
435
+               if (SPA_FLAG_IS_SET(d->flags, SPA_DATA_FLAG_MAPPABLE)) {
436
                    if ((res = map_data(impl, d, prot)) < 0)
437
                        return res;
438
                    SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
439
@@ -983,15 +950,16 @@
440
                    return -EINVAL;
441
                }
442
                buf_size += d->maxsize;
443
+               pw_log_debug("%p:  data:%d type:%d flags:%08x size:%d", stream, j,
444
+                       d->type, d->flags, d->maxsize);
445
            }
446
-
447
            if (size > 0 && buf_size != size) {
448
                pw_log_error("%p: invalid buffer size %d", stream, buf_size);
449
                return -EINVAL;
450
            } else
451
                size = buf_size;
452
        }
453
-       pw_log_debug("%p: got buffer id:%d datas:%d, mapped size %d", stream, i,
454
+       pw_log_debug("%p: got buffer id:%d datas:%d mapped size %d", stream, i,
455
                buffersi->n_datas, size);
456
    }
457
    impl->n_buffers = n_buffers;
458
@@ -1028,7 +996,7 @@
459
    struct stream *impl = object;
460
    struct pw_stream *stream = &impl->this;
461
    struct spa_io_buffers *io = impl->io;
462
-   struct buffer *b;
463
+   struct buffer *b = NULL;
464
 
465
    if (io == NULL)
466
        return -EIO;
467
@@ -1045,10 +1013,13 @@
468
                SPA_ATOMIC_INC(b->busy->count);
469
        }
470
    }
471
-   if (!queue_is_empty(impl, &impl->dequeued)) {
472
-       copy_position(impl, impl->dequeued.incount);
473
+
474
+   copy_position(impl, impl->dequeued.incount);
475
+   if (b != NULL)
476
+       b->this.time = impl->time.now;
477
+
478
+   if (!queue_is_empty(impl, &impl->dequeued))
479
        call_process(impl);
480
-   }
481
 
482
    if (io->status != SPA_STATUS_NEED_DATA || io->buffer_id == SPA_ID_INVALID) {
483
        /* pop buffer to recycle */
484
@@ -1061,7 +1032,7 @@
485
        }
486
        io->status = SPA_STATUS_NEED_DATA;
487
    }
488
-   if (impl->driving && impl->using_trigger)
489
+   if (stream->node->driving && impl->using_trigger)
490
        call_trigger_done(impl);
491
 
492
    return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA;
493
@@ -1096,16 +1067,6 @@
494
            impl->drained = false;
495
            io->buffer_id = b->id;
496
            res = io->status = SPA_STATUS_HAVE_DATA;
497
-           /* we have a buffer, if we are not rt and don't follow
498
-            * any rate matching and there are no more
499
-            * buffers queued and there is a buffer to dequeue, ask for
500
-            * more buffers so that we have one in the next round.
501
-            * If we are using rate matching we need to wait until the
502
-            * rate matching node (audioconvert) has been scheduled to
503
-            * update the values. */
504
-           ask_more = !impl->process_rt && impl->rate_match == NULL &&
505
-               (impl->early_process || queue_is_empty(impl, &impl->queued)) &&
506
-               !queue_is_empty(impl, &impl->dequeued);
507
            pw_log_trace_fp("%p: pop %d %p ask_more:%u %p", stream, b->id, io,
508
                    ask_more, impl->rate_match);
509
        } else if (impl->draining || impl->drained) {
510
@@ -1120,30 +1081,21 @@
511
            pw_log_trace_fp("%p: no more buffers %p", stream, io);
512
            ask_more = true;
513
        }
514
-   } else {
515
-       ask_more = !impl->process_rt &&
516
-           (impl->early_process || queue_is_empty(impl, &impl->queued)) &&
517
-           !queue_is_empty(impl, &impl->dequeued);
518
    }
519
 
520
    copy_position(impl, impl->queued.outcount);
521
 
522
-   if (!impl->draining && !impl->driving) {
523
+   if (!impl->draining && !stream->node->driving && ask_more) {
524
        /* we're not draining, not a driver check if we need to get
525
         * more buffers */
526
-       if (ask_more) {
527
-           call_process(impl);
528
-           /* realtime, we can try again now if there is something.
529
-            * non-realtime, we will have to try in the next round */
530
-           if (impl->process_rt &&
531
-               (impl->draining || !queue_is_empty(impl, &impl->queued)))
532
-               goto again;
533
-       }
534
+       call_process(impl);
535
+       if (impl->draining || !queue_is_empty(impl, &impl->queued))
536
+           goto again;
537
    }
538
 
539
    pw_log_trace_fp("%p: res %d", stream, res);
540
 
541
-   if (impl->driving && impl->using_trigger && res != SPA_STATUS_HAVE_DATA)
542
+   if (stream->node->driving && impl->using_trigger && res != SPA_STATUS_HAVE_DATA)
543
        call_trigger_done(impl);
544
 
545
    return res;
546
@@ -1257,17 +1209,21 @@
547
        }
548
 
549
        pod = spa_pod_get_values(type, &n_vals, &choice);
550
+       if (n_vals == 0) {
551
+           free(c);
552
+           return -EINVAL;
553
+       }
554
 
555
        c->type = SPA_POD_TYPE(pod);
556
        if (spa_pod_is_float(pod))
557
            vals = SPA_POD_BODY(pod);
558
        else if (spa_pod_is_double(pod)) {
559
            double *v = SPA_POD_BODY(pod);
560
-           dbl0 = v0;
561
+           dbl0 = (float)v0;
562
            if (n_vals > 1)
563
-               dbl1 = v1;
564
+               dbl1 = (float)v1;
565
            if (n_vals > 2)
566
-               dbl2 = v2;
567
+               dbl2 = (float)v2;
568
            vals = dbl;
569
        }
570
        else if (spa_pod_is_bool(pod) && n_vals > 0) {
571
@@ -1345,7 +1301,7 @@
572
                if (spa_pod_get_double(&prop->value, &value_d) < 0)
573
                    continue;
574
                n_values = 1;
575
-               value_f = value_d;
576
+               value_f = (float)value_d;
577
                values = &value_f;
578
                break;
579
            case SPA_TYPE_Bool:
580
@@ -1516,10 +1472,12 @@
581
 
582
    if (pw_properties_get(props, PW_KEY_STREAM_IS_LIVE) == NULL)
583
        pw_properties_set(props, PW_KEY_STREAM_IS_LIVE, "true");
584
-   if (pw_properties_get(props, PW_KEY_NODE_NAME) == NULL && extra) {
585
-       str = pw_properties_get(extra, PW_KEY_APP_NAME);
586
-       if (str == NULL)
587
-           str = pw_properties_get(extra, PW_KEY_APP_PROCESS_BINARY);
588
+   if ((str = pw_properties_get(props, PW_KEY_NODE_NAME)) == NULL) {
589
+       if (extra) {
590
+           str = pw_properties_get(extra, PW_KEY_APP_NAME);
591
+           if (str == NULL)
592
+               str = pw_properties_get(extra, PW_KEY_APP_PROCESS_BINARY);
593
+       }
594
        if (str == NULL)
595
            str = name;
596
        pw_properties_set(props, PW_KEY_NODE_NAME, str);
597
@@ -1912,7 +1870,7 @@
598
    else
599
        impl->node_methods.process = impl_node_process_output;
600
 
601
-   impl->process_rt = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_RT_PROCESS);
602
+   impl->trigger_done_rt = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_RT_TRIGGER_DONE);
603
    impl->early_process = SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_EARLY_PROCESS);
604
 
605
    impl->impl_node.iface = SPA_INTERFACE_INIT(
606
@@ -1936,9 +1894,7 @@
607
    /* we're always RT safe, if the stream was marked RT_PROCESS,
608
     * the callback must be RT safe */
609
    impl->info.flags = SPA_NODE_FLAG_RT;
610
-   /* if the callback was not marked RT_PROCESS, we will offload
611
-    * the process callback in the main thread and we are ASYNC */
612
-   if (!impl->process_rt || SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_ASYNC))
613
+   if (SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_ASYNC))
614
        impl->info.flags |= SPA_NODE_FLAG_ASYNC;
615
    impl->info.props = &stream->properties->dict;
616
    impl->paramsNODE_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, 0);
617
@@ -1982,9 +1938,9 @@
618
    impl->disconnecting = false;
619
    impl->drained = false;
620
    impl->draining = false;
621
-   impl->driving = false;
622
    impl->trigger = false;
623
    impl->using_trigger = false;
624
+
625
    stream_set_state(stream, PW_STREAM_STATE_CONNECTING, 0, NULL);
626
 
627
    if (target_id != PW_ID_ANY)
628
@@ -2001,23 +1957,47 @@
629
        if (pw_properties_get(stream->properties, PW_KEY_NODE_DONT_RECONNECT) == NULL)
630
            pw_properties_set(stream->properties, PW_KEY_NODE_DONT_RECONNECT, "true");
631
 
632
+   if (!SPA_FLAG_IS_SET(flags, PW_STREAM_FLAG_RT_PROCESS)) {
633
+       if (pw_properties_get(stream->properties, PW_KEY_NODE_LOOP_CLASS) == NULL)
634
+           pw_properties_set(stream->properties, PW_KEY_NODE_LOOP_CLASS, "main");
635
+       pw_properties_set(stream->properties, PW_KEY_NODE_ASYNC, "true");
636
+   }
637
    if (flags & PW_STREAM_FLAG_DRIVER)
638
        pw_properties_set(stream->properties, PW_KEY_NODE_DRIVER, "true");
639
    if (flags & PW_STREAM_FLAG_TRIGGER) {
640
        pw_properties_set(stream->properties, PW_KEY_NODE_TRIGGER, "true");
641
        impl->trigger = true;
642
    }
643
-   if ((pw_properties_get(stream->properties, PW_KEY_MEDIA_CLASS) == NULL)) {
644
+   if (((str = pw_properties_get(stream->properties, PW_KEY_MEDIA_CLASS)) == NULL)) {
645
        const char *media_type = pw_properties_get(stream->properties, PW_KEY_MEDIA_TYPE);
646
        pw_properties_setf(stream->properties, PW_KEY_MEDIA_CLASS, "Stream/%s/%s",
647
                direction == PW_DIRECTION_INPUT ? "Input" : "Output",
648
                media_type ? media_type : get_media_class(impl));
649
+   } else {
650
+       enum pw_direction expected;
651
+
652
+       if (strstr(str, "Input") || strstr(str, "Sink"))
653
+           expected = PW_DIRECTION_INPUT;
654
+       else if (strstr(str, "Output") || strstr(str, "Source"))
655
+           expected = PW_DIRECTION_OUTPUT;
656
+       else
657
+           expected = direction;
658
+
659
+       if (direction != expected)
660
+           pw_log_warn("media.class %s does not expect %s stream direction", str,
661
+                   direction == PW_DIRECTION_INPUT ? "Input" : "Output");
662
+
663
    }
664
    if ((str = pw_properties_get(stream->properties, PW_KEY_FORMAT_DSP)) != NULL)
665
        pw_properties_set(impl->port_props, PW_KEY_FORMAT_DSP, str);
666
    else if (impl->media_type == SPA_MEDIA_TYPE_application &&
667
        impl->media_subtype == SPA_MEDIA_SUBTYPE_control)
668
        pw_properties_set(impl->port_props, PW_KEY_FORMAT_DSP, "8 bit raw midi");
669
+   if (pw_properties_get(impl->port_props, PW_KEY_PORT_GROUP) == NULL)
670
+       pw_properties_set(impl->port_props, PW_KEY_PORT_GROUP, "stream.0");
671
+
672
+   if ((str = pw_properties_get(stream->properties, PW_KEY_NODE_ASYNC)) != NULL && spa_atob(str))
673
+       SPA_FLAG_SET(impl->info.flags, SPA_NODE_FLAG_ASYNC);
674
 
675
    match = MATCH_INIT(stream);
676
    pw_context_conf_section_match_rules(impl->context, "stream.rules",
677
@@ -2035,7 +2015,7 @@
678
        struct spa_fraction q;
679
        if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
680
            pw_properties_setf(stream->properties, PW_KEY_NODE_FORCE_RATE,
681
-                   "1/%u", q.denom);
682
+                   "%u", q.denom);
683
            pw_properties_setf(stream->properties, PW_KEY_NODE_FORCE_QUANTUM,
684
                    "%u", q.num);
685
        }
686
@@ -2050,8 +2030,6 @@
687
    if ((str = pw_properties_get(stream->properties, "mem.allow-mlock")) != NULL)
688
        impl->allow_mlock = pw_properties_parse_bool(str);
689
 
690
-   impl->port_info.props = &impl->port_props->dict;
691
-
692
    if (stream->core == NULL) {
693
        stream->core = pw_context_connect(impl->context,
694
                pw_properties_copy(stream->properties), 0);
695
@@ -2078,8 +2056,12 @@
696
        pw_properties_set(props, "channelmix.normalize", "true");
697
        pw_properties_set(props, PW_KEY_PORT_IGNORE_LATENCY, "true");
698
    }
699
+   if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL)
700
+       pw_properties_set(props, PW_KEY_PORT_GROUP, "stream.0");
701
 
702
-   if (impl->media_type == SPA_MEDIA_TYPE_audio) {
703
+   if (impl->media_type == SPA_MEDIA_TYPE_audio
704
+           || (impl->media_type == SPA_MEDIA_TYPE_video
705
+               && pw_properties_get(props, "video.adapt.converter"))) {
706
        factory = pw_context_find_factory(impl->context, "adapter");
707
        if (factory == NULL) {
708
            pw_log_error("%p: no adapter factory found", stream);
709
@@ -2199,9 +2181,10 @@
710
    if ((res = update_params(impl, SPA_ID_INVALID, params, n_params)) < 0)
711
        return res;
712
 
713
-   emit_node_info(impl, false);
714
-   emit_port_info(impl, false);
715
-
716
+   if (impl->in_emit_param_changed == 0) {
717
+       emit_node_info(impl, false);
718
+       emit_port_info(impl, false);
719
+   }
720
    return res;
721
 }
722
 
723
@@ -2339,13 +2322,14 @@
724
 {
725
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
726
    uintptr_t seq1, seq2;
727
-   uint32_t buffered, quantum, index;
728
+   uint32_t buffered, quantum, index, rate_size;
729
    int32_t avail_buffers;
730
 
731
    do {
732
        seq1 = SPA_SEQ_READ(impl->seq);
733
        memcpy(time, &impl->time, SPA_MIN(size, sizeof(struct pw_time)));
734
        buffered = impl->rate_queued;
735
+       rate_size = impl->rate_size;
736
        quantum = impl->quantum;
737
        seq2 = SPA_SEQ_READ(impl->seq);
738
    } while (!SPA_SEQ_READ_SUCCESS(seq1, seq2));
739
@@ -2355,7 +2339,7 @@
740
    else
741
        time->queued = (int64_t)(impl->queued.incount - time->queued);
742
 
743
-   time->delay += ((impl->latency.min_quantum + impl->latency.max_quantum) / 2) * quantum;
744
+   time->delay += (int64_t)(((impl->latency.min_quantum + impl->latency.max_quantum) / 2.0f) * quantum);
745
    time->delay += (impl->latency.min_rate + impl->latency.max_rate) / 2;
746
    time->delay += ((impl->latency.min_ns + impl->latency.max_ns) / 2) * time->rate.denom / SPA_NSEC_PER_SEC;
747
 
748
@@ -2366,8 +2350,10 @@
749
        time->buffered = buffered;
750
    if (size >= offsetof(struct pw_time, avail_buffers))
751
        time->queued_buffers = impl->n_buffers - avail_buffers;
752
-   if (size >= sizeof(struct pw_time))
753
+   if (size >= offsetof(struct pw_time, size))
754
        time->avail_buffers = avail_buffers;
755
+   if (size >= sizeof(struct pw_time))
756
+       time->size = rate_size;
757
 
758
    pw_log_trace_fp("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %"
759
            PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %d/%d", stream,
760
@@ -2379,6 +2365,21 @@
761
    return 0;
762
 }
763
 
764
+SPA_EXPORT
765
+uint64_t pw_stream_get_nsec(struct pw_stream *stream)
766
+{
767
+   struct timespec ts;
768
+   clock_gettime(CLOCK_MONOTONIC, &ts);
769
+   return SPA_TIMESPEC_TO_NSEC(&ts);
770
+}
771
+
772
+SPA_EXPORT
773
+struct pw_loop *pw_stream_get_data_loop(struct pw_stream *stream)
774
+{
775
+   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
776
+   return impl->data_loop;
777
+}
778
+
779
 static int
780
 do_trigger_deprecated(struct spa_loop *loop,
781
                  bool async, uint32_t seq, const void *data, size_t size, void *user_data)
782
@@ -2432,7 +2433,7 @@
783
        return res;
784
 
785
    if (impl->direction == SPA_DIRECTION_OUTPUT &&
786
-       impl->driving && !impl->using_trigger) {
787
+       stream->node->driving && !impl->using_trigger) {
788
        pw_log_debug("deprecated: use pw_stream_trigger_process() to drive the stream.");
789
        res = pw_loop_invoke(impl->data_loop,
790
            do_trigger_deprecated, 1, NULL, 0, false, impl);
791
@@ -2500,8 +2501,7 @@
792
 SPA_EXPORT
793
 bool pw_stream_is_driving(struct pw_stream *stream)
794
 {
795
-   struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
796
-   return impl->driving;
797
+   return stream->node->driving;
798
 }
799
 
800
 static int
801
@@ -2511,8 +2511,7 @@
802
    struct stream *impl = user_data;
803
    int res;
804
    if (impl->direction == SPA_DIRECTION_OUTPUT) {
805
-       if (impl->process_rt)
806
-           call_process(impl);
807
+       call_process(impl);
808
        res = impl->node_methods.process(impl);
809
    } else {
810
        res = SPA_STATUS_NEED_DATA;
811
@@ -2540,16 +2539,14 @@
812
    struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
813
    int res = 0;
814
 
815
-   pw_log_trace_fp("%p: trigger:%d driving:%d", impl, impl->trigger, impl->driving);
816
+   pw_log_trace_fp("%p: trigger:%d driving:%d", impl, impl->trigger, stream->node->driving);
817
 
818
    /* flag to check for old or new behaviour */
819
    impl->using_trigger = true;
820
 
821
    if (impl->trigger) {
822
        pw_impl_node_trigger(stream->node);
823
-   } else if (impl->driving) {
824
-       if (!impl->process_rt)
825
-           call_process(impl);
826
+   } else if (stream->node->driving) {
827
        res = pw_loop_invoke(impl->data_loop,
828
            do_trigger_driver, 1, NULL, 0, false, impl);
829
    } else {
830
pipewire-1.0.1.tar.bz2/src/pipewire/stream.h -> pipewire-1.2.0.tar.gz/src/pipewire/stream.h Changed
221
 
1
@@ -58,11 +58,15 @@
2
 
3
  * \li PW_DIRECTION_INPUT for a stream that *consumes* data. This can be a
4
  * stream that captures from a Source or a when the stream is used to
5
- * implement a Sink.
6
+ * implement a Sink. An application will use a \ref PW_DIRECTION_INPUT
7
+ * stream to record data. A virtual sound card will use a
8
+ * \ref PW_DIRECTION_INPUT stream to implement audio playback.
9
  *
10
  * \li PW_DIRECTION_OUTPUT for a stream that *produces* data. This can be a
11
  * stream that plays to a Sink or when the stream is used to implement
12
- * a Source.
13
+ * a Source. An application will use a \ref PW_DIRECTION_OUTPUT
14
+ * stream to produce data. A virtual sound card or camera will use a
15
+ * \ref PW_DIRECTION_OUTPUT stream to implement audio or video recording.
16
  *
17
  * \subsection ssec_stream_target Stream target
18
  *
19
@@ -141,6 +145,18 @@
20
  * The process event is emitted when PipeWire has emptied a buffer that
21
  * can now be refilled.
22
  *
23
+ * \section sec_stream_driving Driving the graph
24
+ *
25
+ * Starting in 0.3.34, it is possible for a stream to drive the graph.
26
+ * This allows interrupt-driven scheduling for drivers implemented as
27
+ * PipeWire streams, without having to reimplement the stream as a SPA
28
+ * plugin.
29
+ *
30
+ * A stream cannot drive the graph unless it is in the
31
+ * \ref PW_STREAM_STATE_STREAMING state and \ref pw_stream_is_driving() returns
32
+ * true. It must then use pw_stream_trigger_process() to start the graph
33
+ * cycle.
34
+ *
35
  * \section sec_stream_disconnect Disconnect
36
  *
37
  * Use \ref pw_stream_disconnect() to disconnect a stream after use.
38
@@ -187,19 +203,29 @@
39
 };
40
 
41
 /** a buffer structure obtained from pw_stream_dequeue_buffer(). The size of this
42
-  * structure can grow as more field are added in the future */
43
+  * structure can grow as more fields are added in the future */
44
 struct pw_buffer {
45
    struct spa_buffer *buffer;  /**< the spa buffer */
46
-   void *user_data;        /**< user data attached to the buffer */
47
+   void *user_data;        /**< user data attached to the buffer. The user of
48
+                     *  the stream can set custom data associated with the
49
+                     *  buffer, typically in the add_buffer event. Any
50
+                     *  cleanup should be performed in the remove_buffer
51
+                     *  event. The user data is returned unmodified each
52
+                     *  time a buffer is dequeued. */
53
    uint64_t size;          /**< This field is set by the user and the sum of
54
-                     *  all queued buffer is returned in the time info.
55
+                     *  all queued buffers is returned in the time info.
56
                      *  For audio, it is advised to use the number of
57
-                     *  samples in the buffer for this field. */
58
+                     *  frames in the buffer for this field. */
59
    uint64_t requested;     /**< For playback streams, this field contains the
60
                      *  suggested amount of data to provide. For audio
61
-                     *  streams this will be the amount of samples
62
+                     *  streams this will be the amount of frames
63
                      *  required by the resampler. This field is 0
64
                      *  when no suggestion is provided. Since 0.3.49 */
65
+   uint64_t time;          /**< For capture streams, this field contains the
66
+                     *  cycle time in nanoseconds when this buffer was
67
+                     *  queued in the stream. It can be compared against
68
+                     *  the pw_time values or pw_stream_get_nsec()
69
+                     *  Since 1.0.5 */
70
 };
71
 
72
 struct pw_stream_control {
73
@@ -229,9 +255,8 @@
74
  * to the current time like this:
75
  *
76
  *\code{.c}
77
- *    struct timespec ts;
78
- *    clock_gettime(CLOCK_MONOTONIC, &ts);
79
- *    int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw_time.now;
80
+ *    uint64_t now = pw_stream_get_nsec(stream);
81
+ *    int64_t diff = now - pw_time.now;
82
  *    int64_t elapsed = (pw_time.rate.denom * diff) / (pw_time.rate.num * SPA_NSEC_PER_SEC);
83
  *\endcode
84
  *
85
@@ -245,10 +270,10 @@
86
  *
87
  * pw_time.queued is the sum of all the pw_buffer.size fields of the buffers that are
88
  * currently queued in the stream but not yet processed. The application can choose
89
- * the units of this value, for example, time, samples or bytes (below expressed
90
- * as app.rate).
91
+ * the units of this value, for example, time, samples, frames or bytes (below
92
+ * expressed as app.rate).
93
  *
94
- * pw_time.buffered is format dependent, for audio/raw it contains the number of samples
95
+ * pw_time.buffered is format dependent, for audio/raw it contains the number of frames
96
  * that are buffered inside the resampler/converter.
97
  *
98
  * The total delay of data in a stream is the sum of the queued and buffered data
99
@@ -288,12 +313,12 @@
100
  *\endcode
101
  */
102
 struct pw_time {
103
-   int64_t now;            /**< the monotonic time in nanoseconds. This is the time
104
-                     *  when this time report was updated. It is usually
105
-                     *  updated every graph cycle. You can use the current
106
-                     *  monotonic time to calculate the elapsed time between
107
-                     *  this report and the current state and calculate
108
-                     *  updated ticks and delay values. */
109
+   int64_t now;            /**< the time in nanoseconds. This is the time when this
110
+                     *  time report was updated. It is usually updated every
111
+                     *  graph cycle. You can use pw_stream_get_nsec() to
112
+                     *  calculate the elapsed time between this report and
113
+                     *  the current time and calculate updated ticks and delay
114
+                     *  values. */
115
    struct spa_fraction rate;   /**< the rate of \a ticks and delay. This is usually
116
                      *  expressed in 1/<samplerate>. */
117
    uint64_t ticks;         /**< the ticks at \a now. This is the current time that
118
@@ -312,10 +337,14 @@
119
                      *  of the size fields in the pw_buffer that are
120
                      *  currently queued */
121
    uint64_t buffered;      /**< for audio/raw streams, this contains the extra
122
-                     *  number of samples buffered in the resampler.
123
+                     *  number of frames buffered in the resampler.
124
                      *  Since 0.3.50. */
125
-   uint32_t queued_buffers;    /**< The number of buffers that are queued. Since 0.3.50 */
126
-   uint32_t avail_buffers;     /**< The number of buffers that can be dequeued. Since 0.3.50 */
127
+   uint32_t queued_buffers;    /**< the number of buffers that are queued. Since 0.3.50 */
128
+   uint32_t avail_buffers;     /**< the number of buffers that can be dequeued. Since 0.3.50 */
129
+   uint64_t size;          /**< for audio/raw playback streams, this contains the number of
130
+                     *  samples requested by the resampler for the current
131
+                     *  quantum. for audio/raw capture streams this will be the number
132
+                     *  of samples available for the current quantum. Since 1.1.0 */
133
 };
134
 
135
 #include <pipewire/port.h>
136
@@ -356,7 +385,10 @@
137
    /** A command notify, Since 0.3.39:1 */
138
    void (*command) (void *data, const struct spa_command *command);
139
 
140
-   /** a trigger_process completed. Since version 0.3.40:2 */
141
+   /** a trigger_process completed. Since version 0.3.40:2.
142
+    *  This is normally called from the mainloop but since 1.1.0 it
143
+    *  can also be called directly from the realtime data
144
+    *  thread if the user is prepared to deal with this. */
145
    void (*trigger_done) (void *data);
146
 };
147
 
148
@@ -371,7 +403,8 @@
149
    PW_STREAM_FLAG_INACTIVE     = (1 << 1), /**< start the stream inactive,
150
                              *  pw_stream_set_active() needs to be
151
                              *  called explicitly */
152
-   PW_STREAM_FLAG_MAP_BUFFERS  = (1 << 2), /**< mmap the buffers except DmaBuf */
153
+   PW_STREAM_FLAG_MAP_BUFFERS  = (1 << 2), /**< mmap the buffers except DmaBuf that is not
154
+                             *  explicitly marked as mappable. */
155
    PW_STREAM_FLAG_DRIVER       = (1 << 3), /**< be a driver */
156
    PW_STREAM_FLAG_RT_PROCESS   = (1 << 4), /**< call process from the realtime
157
                              *  thread. You MUST use RT safe functions
158
@@ -401,9 +434,12 @@
159
                              *  playback and when not using RT_PROCESS. It
160
                              *  can be used to keep the maximum number of
161
                              *  buffers queued. Since 0.3.81 */
162
+   PW_STREAM_FLAG_RT_TRIGGER_DONE  = (1 << 12),    /**< Call trigger_done from the realtime
163
+                             *  thread. You MUST use RT safe functions
164
+                             *  in the trigger_done callback. Since 1.1.0 */
165
 };
166
 
167
-/** Create a new unconneced \ref pw_stream
168
+/** Create a new unconnected \ref pw_stream
169
  * \return a newly allocated \ref pw_stream */
170
 struct pw_stream *
171
 pw_stream_new(struct pw_core *core,        /**< a \ref pw_core */
172
@@ -411,7 +447,7 @@
173
          struct pw_properties *props   /**< stream properties, ownership is taken */);
174
 
175
 struct pw_stream *
176
-pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use */
177
+pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use as the main loop */
178
             const char *name,      /**< a stream media name */
179
             struct pw_properties *props,/**< stream properties, ownership is taken */
180
             const struct pw_stream_events *events, /**< stream events */
181
@@ -496,6 +532,14 @@
182
 /** Query the time on the stream */
183
 int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size);
184
 
185
+/** Get the current time in nanoseconds. This value can be compared with
186
+ * the \ref pw_time.now value. Since 1.1.0 */
187
+uint64_t pw_stream_get_nsec(struct pw_stream *stream);
188
+
189
+/** Get the data loop that is doing the processing of this stream. This loop
190
+ * is assigned after pw_stream_connect().  * Since 1.1.0 */
191
+struct pw_loop *pw_stream_get_data_loop(struct pw_stream *stream);
192
+
193
 /** Query the time on the stream, deprecated since 0.3.50,
194
  * use pw_stream_get_time_n() to get the fields added since 0.3.50. */
195
 SPA_DEPRECATED
196
@@ -522,7 +566,23 @@
197
 bool pw_stream_is_driving(struct pw_stream *stream);
198
 
199
 /** Trigger a push/pull on the stream. One iteration of the graph will
200
- * scheduled and process() will be called. Since 0.3.34 */
201
+ * be scheduled. If it successfully finishes, process() will be called.
202
+ * It is possible for the graph iteration to not finish, so
203
+ * pw_stream_trigger_process() needs to be called again even if process()
204
+ * is not called.
205
+ *
206
+ * If there is a deadline after which the stream will have xrun,
207
+ * pw_stream_trigger_process() should be called then, whether or not
208
+ * process() has been called. Sound hardware will xrun if there is
209
+ * any delay in audio processing, so the ALSA plugin triggers the
210
+ * graph every quantum to ensure audio keeps flowing. Drivers that
211
+ * do not have a deadline, such as the freewheel driver, should
212
+ * use a timeout to ensure that forward progress keeps being made.
213
+ * A reasonable choice of deadline is three times the quantum: if
214
+ * the graph is taking 3x longer than normal, it is likely that it
215
+ * is hung and should be retriggered.
216
+ *
217
+ * Since 0.3.34 */
218
 int pw_stream_trigger_process(struct pw_stream *stream);
219
 
220
 /**
221
pipewire-1.0.1.tar.bz2/src/pipewire/thread-loop.c -> pipewire-1.2.0.tar.gz/src/pipewire/thread-loop.c Changed
27
 
1
@@ -24,7 +24,6 @@
2
 /** \cond */
3
 struct pw_thread_loop {
4
    struct pw_loop *loop;
5
-   char name16;
6
 
7
    struct spa_hook_list listener_list;
8
 
9
@@ -159,7 +158,7 @@
10
        goto clean_this;
11
    }
12
    this->loop = loop;
13
-   snprintf(this->name, sizeof(this->name), "%s", name ? name : "pw-thread-loop");
14
+   pw_loop_set_name(loop, name ? name : "thread-loop");
15
 
16
    spa_hook_list_init(&this->listener_list);
17
 
18
@@ -323,7 +322,7 @@
19
 
20
        loop->running = true;
21
 
22
-       items0 = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_NAME, loop->name);
23
+       items0 = SPA_DICT_ITEM_INIT(SPA_KEY_THREAD_NAME, loop->loop->name);
24
        thr = pw_thread_utils_create(&SPA_DICT_INIT_ARRAY(items), do_loop, loop);
25
        if (thr == NULL)
26
            goto error;
27
pipewire-1.0.1.tar.bz2/src/pipewire/thread.c -> pipewire-1.2.0.tar.gz/src/pipewire/thread.c Changed
73
 
1
@@ -10,6 +10,7 @@
2
 #include <spa/utils/dict.h>
3
 #include <spa/utils/defs.h>
4
 #include <spa/utils/list.h>
5
+#include <spa/utils/json.h>
6
 
7
 #include <pipewire/log.h>
8
 #include <pipewire/private.h>
9
@@ -24,6 +25,23 @@
10
    }                               \
11
 } while(false);
12
 
13
+static int parse_affinity(const char *affinity, cpu_set_t *set)
14
+{
15
+   struct spa_json it2;
16
+   int v;
17
+
18
+   CPU_ZERO(set);
19
+   spa_json_init(&it0, affinity, strlen(affinity));
20
+   if (spa_json_enter_array(&it0, &it1) <= 0)
21
+       spa_json_init(&it1, affinity, strlen(affinity));
22
+
23
+   while (spa_json_get_int(&it1, &v) > 0) {
24
+       if (v >= 0 && v < CPU_SETSIZE)
25
+           CPU_SET(v, set);
26
+        }
27
+   return 0;
28
+}
29
+
30
 SPA_EXPORT
31
 void *pw_thread_fill_attr(const struct spa_dict *props, void *_attr)
32
 {
33
@@ -57,6 +75,13 @@
34
 int pthread_setname_np(pthread_t thread, const char *name) { return 0; }
35
 #endif
36
 
37
+static int thread_setaffinity(pthread_t thread, const char *affinity)
38
+{
39
+   cpu_set_t set;
40
+   parse_affinity(affinity, &set);
41
+   return -pthread_setaffinity_np(thread, sizeof(set), &set);
42
+}
43
+
44
 static struct spa_thread *impl_create(void *object,
45
            const struct spa_dict *props,
46
            void *(*start)(void*), void *arg)
47
@@ -81,6 +106,9 @@
48
        if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_NAME)) != NULL &&
49
            (err = pthread_setname_np(pt, str)) != 0)
50
            pw_log_warn("pthread_setname error: %s", strerror(err));
51
+       if ((str = spa_dict_lookup(props, SPA_KEY_THREAD_AFFINITY)) != NULL &&
52
+           (err = thread_setaffinity(pt, str)) != 0)
53
+           pw_log_warn("pthread_setaffinity error: %s", strerror(err));
54
    }
55
    return (struct spa_thread*)pt;
56
 }
57
@@ -102,13 +130,13 @@
58
 }
59
 static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority)
60
 {
61
-   pw_log_warn("acquire_rt thread:%p prio:%d not implemented", thread, priority);
62
+   pw_log_info("acquire_rt thread:%p prio:%d not implemented", thread, priority);
63
    return -ENOTSUP;
64
 }
65
 
66
 static int impl_drop_rt(void *object, struct spa_thread *thread)
67
 {
68
-   pw_log_warn("drop_rt thread:%p not implemented", thread);
69
+   pw_log_info("drop_rt thread:%p not implemented", thread);
70
    return -ENOTSUP;
71
 }
72
 
73
pipewire-1.0.1.tar.bz2/src/pipewire/utils.c -> pipewire-1.2.0.tar.gz/src/pipewire/utils.c Changed
74
 
1
@@ -14,6 +14,7 @@
2
 #include <time.h>
3
 
4
 #include <spa/utils/json.h>
5
+#include <spa/debug/log.h>
6
 
7
 #include <pipewire/array.h>
8
 #include <pipewire/log.h>
9
@@ -130,28 +131,57 @@
10
 {
11
    struct pw_array arr;
12
    struct spa_json it2;
13
-   char v256;
14
-   int n = 0;
15
+   int n = 0, l, res;
16
+   const char *value;
17
+   struct spa_error_location el;
18
 
19
    if (val == NULL)
20
        return NULL;
21
 
22
-   pw_array_init(&arr, 16);
23
+   pw_array_init(&arr, sizeof(char*) * 16);
24
 
25
    spa_json_init(&it0, val, len);
26
         if (spa_json_enter_array(&it0, &it1) <= 0)
27
                 spa_json_init(&it1, val, len);
28
 
29
-   while (spa_json_get_string(&it1, v, sizeof(v)) > 0 && n + 1 < max_tokens) {
30
-       pw_array_add_ptr(&arr, strdup(v));
31
+   while ((l = spa_json_next(&it1, &value)) > 0 && n + 1 < max_tokens) {
32
+       char *s;
33
+
34
+       if ((s = malloc(l+1)) == NULL)
35
+           goto error_errno;
36
+
37
+       spa_json_parse_stringn(value, l, s, l+1);
38
+
39
+       if ((res = pw_array_add_ptr(&arr, s)) < 0) {
40
+           free(s);
41
+           goto error;
42
+       }
43
        n++;
44
    }
45
-   pw_array_add_ptr(&arr, NULL);
46
-
47
+   if ((res = pw_array_add_ptr(&arr, NULL)) < 0)
48
+       goto error;
49
+done:
50
+   if ((res = spa_json_get_error(&it1, val, &el))) {
51
+       spa_debug_log_error_location(pw_log_get(), SPA_LOG_LEVEL_WARN,
52
+               &el, "error parsing strv: %s", el.reason);
53
+
54
+       char **s;
55
+       pw_array_for_each(s, &arr)
56
+           free(*s);
57
+       pw_array_clear(&arr);
58
+       n = 0;
59
+   }
60
    if (n_tokens != NULL)
61
        *n_tokens = n;
62
 
63
    return arr.data;
64
+
65
+error_errno:
66
+   res = -errno;
67
+error:
68
+   it1.state = SPA_JSON_ERROR_FLAG;
69
+   errno = -res;
70
+   goto done;
71
 }
72
 
73
 /** Find a string in a NULL terminated array of strings.
74
pipewire-1.0.1.tar.bz2/src/pipewire/utils.h -> pipewire-1.2.0.tar.gz/src/pipewire/utils.h Changed
20
 
1
@@ -21,6 +21,7 @@
2
 #define ENODATA 9919
3
 #endif
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/defs.h>
7
 #include <spa/pod/pod.h>
8
 
9
@@ -99,6 +100,10 @@
10
  * \}
11
  */
12
 
13
+SPA_DEFINE_AUTO_CLEANUP(pw_strv, char **, {
14
+   spa_clear_ptr(*thing, pw_free_strv);
15
+})
16
+
17
 #ifdef __cplusplus
18
 } /* extern "C" */
19
 #endif
20
pipewire-1.0.1.tar.bz2/src/pipewire/work-queue.c -> pipewire-1.2.0.tar.gz/src/pipewire/work-queue.c Changed
10
 
1
@@ -190,7 +190,7 @@
2
 /** Cancel a work item
3
  * \param queue the work queue
4
  * \param obj the owner object
5
- * \param id the wotk id to cancel
6
+ * \param id the work id to cancel
7
  *
8
  */
9
 SPA_EXPORT
10
pipewire-1.0.1.tar.bz2/src/tests/meson.build -> pipewire-1.2.0.tar.gz/src/tests/meson.build Changed
9
 
1
@@ -4,6 +4,7 @@
2
   # 'test-remote',
3
   'test-stream',
4
   'test-filter',
5
+  'test-security-context',
6
 
7
 
8
 foreach a : test_apps
9
pipewire-1.0.1.tar.bz2/src/tests/test-endpoint.c -> pipewire-1.2.0.tar.gz/src/tests/test-endpoint.c Changed
10
 
1
@@ -215,7 +215,7 @@
2
    self->info.params = param_info;
3
    self->info.n_params = SPA_N_ELEMENTS (param_info);
4
 
5
-   self->props.volume = 0.9;
6
+   self->props.volume = 0.9f;
7
    self->props.mute = false;
8
 }
9
 
10
pipewire-1.2.0.tar.gz/src/tests/test-security-context.c Added
176
 
1
@@ -0,0 +1,174 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <stdlib.h>
7
+#include <sys/socket.h>
8
+#include <fcntl.h>
9
+#include <limits.h>
10
+#include <unistd.h>
11
+
12
+#include <pipewire/pipewire.h>
13
+#include <pipewire/main-loop.h>
14
+#include <pipewire/extensions/security-context.h>
15
+
16
+#include <spa/utils/string.h>
17
+
18
+#define TEST_FUNC(a,b,func)    \
19
+do {               \
20
+   a.func = b.func;    \
21
+   spa_assert_se(SPA_PTRDIFF(&a.func, &a) == SPA_PTRDIFF(&b.func, &b)); \
22
+} while(0)
23
+
24
+static void test_abi(void)
25
+{
26
+   static const struct {
27
+       uint32_t version;
28
+   } test = { PW_VERSION_SECURITY_CONTEXT_EVENTS,  };
29
+   struct pw_security_context_events ev;
30
+
31
+   spa_assert_se(PW_VERSION_SECURITY_CONTEXT_EVENTS == 0);
32
+   spa_assert_se(sizeof(ev) == sizeof(test));
33
+}
34
+
35
+struct roundtrip_data
36
+{
37
+   struct pw_main_loop *loop;
38
+   int pending;
39
+   int done;
40
+};
41
+
42
+static void core_event_done(void *object, uint32_t id, int seq)
43
+{
44
+   struct roundtrip_data *data = object;
45
+   if (id == PW_ID_CORE && seq == data->pending) {
46
+       data->done = 1;
47
+       pw_main_loop_quit(data->loop);
48
+   }
49
+}
50
+
51
+static int roundtrip(struct pw_core *core, struct pw_main_loop *loop)
52
+{
53
+   struct spa_hook core_listener;
54
+   struct roundtrip_data data = { .loop = loop };
55
+   const struct pw_core_events core_events = {
56
+       PW_VERSION_CORE_EVENTS,
57
+       .done = core_event_done,
58
+   };
59
+   spa_zero(core_listener);
60
+   pw_core_add_listener(core, &core_listener,
61
+           &core_events, &data);
62
+
63
+   data.pending = pw_core_sync(core, PW_ID_CORE, 0);
64
+
65
+   while (!data.done)
66
+       pw_main_loop_run(loop);
67
+
68
+   spa_hook_remove(&core_listener);
69
+   return 0;
70
+}
71
+
72
+struct registry_info {
73
+   struct pw_registry *registry;
74
+   struct pw_security_context *sec;
75
+};
76
+
77
+static void registry_global(void *data, uint32_t id,
78
+       uint32_t permissions, const char *type, uint32_t version,
79
+       const struct spa_dict *props)
80
+{
81
+   struct registry_info *info = data;
82
+
83
+   if (spa_streq(type, PW_TYPE_INTERFACE_SecurityContext)) {
84
+       info->sec = pw_registry_bind(info->registry, id, type, version, 0);
85
+   }
86
+}
87
+
88
+static const struct pw_registry_events registry_events = {
89
+   PW_VERSION_REGISTRY_EVENTS,
90
+   .global = registry_global
91
+};
92
+
93
+static void test_create(void)
94
+{
95
+   struct pw_main_loop *loop;
96
+   struct pw_context *context;
97
+   struct pw_core *core;
98
+   struct registry_info info;
99
+   struct spa_hook listener;
100
+   int res, listen_fd, close_fd2;
101
+   char tempPATH_MAX = "/tmp/pipewire-XXXXXX";
102
+   struct sockaddr_un sockaddr = {0};
103
+
104
+   loop = pw_main_loop_new(NULL);
105
+   context = pw_context_new(pw_main_loop_get_loop(loop), NULL, 12);
106
+   spa_assert_se(context != NULL);
107
+   core = pw_context_connect(context, NULL, 0);
108
+   if (core == NULL && errno == EHOSTDOWN)
109
+       goto cleanup;
110
+   spa_assert_se(core != NULL);
111
+
112
+   spa_zero(info);
113
+   info.registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, 0);
114
+   spa_assert_se(info.registry != NULL);
115
+
116
+   pw_registry_add_listener(info.registry, &listener, &registry_events, &info);
117
+
118
+   roundtrip(core, loop);
119
+
120
+   if (info.sec == NULL)
121
+       goto cleanup;
122
+
123
+   res = mkstemp(temp);
124
+   spa_assert_se(res >= 0);
125
+   close(res);
126
+
127
+   unlink(temp);
128
+
129
+   listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
130
+   spa_assert_se(listen_fd >= 0);
131
+
132
+   sockaddr.sun_family = AF_UNIX;
133
+   snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", temp);
134
+   if (bind(listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) != 0)
135
+       spa_assert_not_reached();
136
+
137
+   if (listen(listen_fd, 0) != 0)
138
+       spa_assert_not_reached();
139
+
140
+   res = pipe2(close_fd, O_CLOEXEC);
141
+   spa_assert_se(res >= 0);
142
+
143
+   static const struct spa_dict_item items = {
144
+       { "pipewire.foo.bar", "baz" },
145
+       { PW_KEY_SEC_ENGINE, "org.flatpak" },
146
+       { PW_KEY_ACCESS, "restricted" },
147
+   };
148
+
149
+   pw_security_context_create(info.sec,
150
+           listen_fd, close_fd1,
151
+           &SPA_DICT_INIT_ARRAY(items));
152
+
153
+   roundtrip(core, loop);
154
+
155
+   unlink(temp);
156
+
157
+   pw_proxy_destroy((struct pw_proxy*)info.sec);
158
+   pw_proxy_destroy((struct pw_proxy*)info.registry);
159
+
160
+cleanup:
161
+   pw_context_destroy(context);
162
+   pw_main_loop_destroy(loop);
163
+}
164
+
165
+int main(int argc, char *argv)
166
+{
167
+   pw_init(&argc, &argv);
168
+
169
+   test_abi();
170
+   test_create();
171
+
172
+   pw_deinit();
173
+
174
+   return 0;
175
+}
176
pipewire-1.0.1.tar.bz2/src/tests/test-stream.c -> pipewire-1.2.0.tar.gz/src/tests/test-stream.c Changed
12
 
1
@@ -47,8 +47,8 @@
2
    TEST_FUNC(ev, test, trigger_done);
3
 
4
 #if defined(__x86_64__) && defined(__LP64__)
5
-   spa_assert_se(sizeof(struct pw_buffer) == 32);
6
-   spa_assert_se(sizeof(struct pw_time) == 56);
7
+   spa_assert_se(sizeof(struct pw_buffer) == 40);
8
+   spa_assert_se(sizeof(struct pw_time) == 64);
9
 #else
10
    fprintf(stderr, "%zd\n", sizeof(struct pw_buffer));
11
    fprintf(stderr, "%zd\n", sizeof(struct pw_time));
12
pipewire-1.0.1.tar.bz2/src/tools/meson.build -> pipewire-1.2.0.tar.gz/src/tools/meson.build Changed
9
 
1
@@ -1,6 +1,7 @@
2
 tools_sources = 
3
    'pw-mon',  'pw-mon.c'  ,
4
    'pw-config',  'pw-config.c'  ,
5
+   'pw-container',  'pw-container.c'  ,
6
    'pw-dot',  'pw-dot.c'  ,
7
    'pw-dump',  'pw-dump.c'  ,
8
    'pw-profiler',  'pw-profiler.c'  ,
9
pipewire-1.0.1.tar.bz2/src/tools/midifile.c -> pipewire-1.2.0.tar.gz/src/tools/midifile.c Changed
10
 
1
@@ -433,7 +433,7 @@
2
 
3
    tr = &mf->tracksevent->track;
4
 
5
-   tick = event->sec * (1000000.0 * mf->info.division) / (double)mf->tempo;
6
+   tick = (uint32_t)(event->sec * (1000000.0 * mf->info.division) / (double)mf->tempo);
7
 
8
    CHECK_RES(write_varlen(mf, tr, tick - tr->tick));
9
    tr->tick = tick;
10
pipewire-1.0.1.tar.bz2/src/tools/pw-cat.c -> pipewire-1.2.0.tar.gz/src/tools/pw-cat.c Changed
158
 
1
@@ -26,8 +26,8 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/file.h>
6
 
7
-#include <pipewire/cleanup.h>
8
 #include <pipewire/pipewire.h>
9
 #include <pipewire/i18n.h>
10
 #include <pipewire/extensions/metadata.h>
11
@@ -845,7 +845,8 @@
12
        bool null_frame = false;
13
 
14
        n_frames = d->maxsize / data->stride;
15
-       n_frames = SPA_MIN(n_frames, (int)b->requested);
16
+       if (b->requested)
17
+           n_frames = SPA_MIN(n_frames, (int)b->requested);
18
 
19
        /* Note that when playing encoded audio, the encoded_playback_fill()
20
         * fill callback actually returns number of bytes, not frames, since
21
@@ -931,11 +932,11 @@
22
    pw_stream_get_time_n(data->stream, &time, sizeof(time));
23
    printf("stream time: now:%"PRIi64" rate:%u/%u ticks:%"PRIu64
24
            " delay:%"PRIi64" queued:%"PRIu64
25
-           " buffered:%"PRIi64" buffers:%u avail:%u\n",
26
+           " buffered:%"PRIi64" buffers:%u avail:%u size:%"PRIu64"\n",
27
        time.now,
28
        time.rate.num, time.rate.denom,
29
        time.ticks, time.delay, time.queued, time.buffered,
30
-       time.queued_buffers, time.avail_buffers);
31
+       time.queued_buffers, time.avail_buffers, time.size);
32
 }
33
 
34
 enum {
35
@@ -986,21 +987,21 @@
36
 
37
    fp = is_error ? stderr : stdout;
38
 
39
-        fprintf(fp,
40
+   fprintf(fp,
41
       _("%s options <file>|-\n"
42
-             "  -h, --help                            Show this help\n"
43
-             "      --version                         Show version\n"
44
-             "  -v, --verbose                         Enable verbose operations\n"
45
+        "  -h, --help                            Show this help\n"
46
+        "      --version                         Show version\n"
47
+        "  -v, --verbose                         Enable verbose operations\n"
48
         "\n"), name);
49
 
50
    fprintf(fp,
51
-           _("  -R, --remote                          Remote daemon name\n"
52
-             "      --media-type                      Set media type (default %s)\n"
53
-             "      --media-category                  Set media category (default %s)\n"
54
-             "      --media-role                      Set media role (default %s)\n"
55
-             "      --target                          Set node target serial or name (default %s)\n"
56
+      _("  -R, --remote                          Remote daemon name\n"
57
+        "      --media-type                      Set media type (default %s)\n"
58
+        "      --media-category                  Set media category (default %s)\n"
59
+        "      --media-role                      Set media role (default %s)\n"
60
+        "      --target                          Set node target serial or name (default %s)\n"
61
         "                                          0 means don't link\n"
62
-             "      --latency                         Set node latency (default %s)\n"
63
+        "      --latency                         Set node latency (default %s)\n"
64
         "                                          Xunit (unit = s, ms, us, ns)\n"
65
         "                                          or direct samples (256)\n"
66
         "                                          the rate is the one of the source file\n"
67
@@ -1012,12 +1013,12 @@
68
         DEFAULT_TARGET, DEFAULT_LATENCY_PLAY);
69
 
70
    fprintf(fp,
71
-           _("      --rate                            Sample rate (req. for rec) (default %u)\n"
72
-             "      --channels                        Number of channels (req. for rec) (default %u)\n"
73
-             "      --channel-map                     Channel map\n"
74
+      _("      --rate                            Sample rate (req. for rec) (default %u)\n"
75
+        "      --channels                        Number of channels (req. for rec) (default %u)\n"
76
+        "      --channel-map                     Channel map\n"
77
         "                                            one of: \"stereo\", \"surround-51\",... or\n"
78
         "                                            comma separated list of channel names: eg. \"FL,FR\"\n"
79
-             "      --format                          Sample format %s (req. for rec) (default %s)\n"
80
+        "      --format                          Sample format %s (req. for rec) (default %s)\n"
81
         "      --volume                          Stream volume 0-1.0 (default %.3f)\n"
82
         "  -q  --quality                         Resampler quality (0 - 15) (default %d)\n"
83
         "\n"),
84
@@ -1051,7 +1052,7 @@
85
    spa_zero(b);
86
    spa_pod_builder_init(&b, src, n_frames);
87
 
88
-        spa_pod_builder_push_sequence(&b, &f, 0);
89
+   spa_pod_builder_push_sequence(&b, &f, 0);
90
 
91
    first_frame = d->clock_time;
92
    last_frame = first_frame + d->position->clock.duration;
93
@@ -1068,7 +1069,7 @@
94
            return res;
95
        }
96
 
97
-       frame = ev.sec * d->position->clock.rate.denom;
98
+       frame = (uint32_t)(ev.sec * d->position->clock.rate.denom);
99
        if (frame < first_frame)
100
            frame = 0;
101
        else if (frame < last_frame)
102
@@ -1263,6 +1264,7 @@
103
        SF_STR_COPYRIGHT = PW_KEY_MEDIA_COPYRIGHT,
104
        SF_STR_SOFTWARE = PW_KEY_MEDIA_SOFTWARE,
105
        SF_STR_ARTIST = PW_KEY_MEDIA_ARTIST,
106
+       SF_STR_ALBUM = PW_KEY_MEDIA_ALBUM,
107
        SF_STR_COMMENT = PW_KEY_MEDIA_COMMENT,
108
        SF_STR_DATE = PW_KEY_MEDIA_DATE
109
    };
110
@@ -1571,13 +1573,13 @@
111
        nom = data->latency_value * data->rate;
112
        break;
113
    case unit_msec:
114
-       nom = nearbyint((data->latency_value * data->rate) / 1000.0);
115
+       nom = (unsigned int)nearbyint((data->latency_value * data->rate) / 1000.0);
116
        break;
117
    case unit_usec:
118
-       nom = nearbyint((data->latency_value * data->rate) / 1000000.0);
119
+       nom = (unsigned int)nearbyint((data->latency_value * data->rate) / 1000000.0);
120
        break;
121
    case unit_nsec:
122
-       nom = nearbyint((data->latency_value * data->rate) / 1000000000.0);
123
+       nom = (unsigned int)nearbyint((data->latency_value * data->rate) / 1000000000.0);
124
        break;
125
    case unit_samples:
126
        nom = data->latency_value;
127
@@ -1607,6 +1609,7 @@
128
    const char *prog;
129
    int exit_code = EXIT_FAILURE, c, ret;
130
    enum pw_stream_flags flags = 0;
131
+   struct spa_error_location loc;
132
 
133
    setlocale(LC_ALL, "");
134
    pw_init(&argc, &argv);
135
@@ -1728,7 +1731,12 @@
136
            break;
137
 
138
        case 'P':
139
-           pw_properties_update_string(data.props, optarg, strlen(optarg));
140
+           if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
141
+               spa_debug_file_error_location(stderr, &loc,
142
+                       "error: syntax error in --properties: %s",
143
+                       loc.reason);
144
+               goto error_usage;
145
+           }
146
            break;
147
 
148
        case OPT_TARGET:
149
@@ -1770,7 +1778,7 @@
150
            break;
151
 
152
        case OPT_VOLUME:
153
-           data.volume = atof(optarg);
154
+           data.volume = (float)atof(optarg);
155
            break;
156
        default:
157
            goto error_usage;
158
pipewire-1.0.1.tar.bz2/src/tools/pw-cli.c -> pipewire-1.2.0.tar.gz/src/tools/pw-cli.c Changed
470
 
1
@@ -66,6 +66,7 @@
2
    struct pw_map vars;
3
    unsigned int interactive:1;
4
    unsigned int monitoring:1;
5
+   unsigned int monitoring_info:1;
6
    unsigned int quit:1;
7
 };
8
 
9
@@ -164,7 +165,7 @@
10
 
11
 static bool do_not_implemented(struct data *data, const char *cmd, char *args, char **error)
12
 {
13
-        *error = spa_aprintf("Command \"%s\" not yet implemented", cmd);
14
+   *error = spa_aprintf("Command \"%s\" not yet implemented", cmd);
15
    return false;
16
 }
17
 
18
@@ -213,10 +214,15 @@
19
    { "quit", "q", "Quit", do_quit },
20
 };
21
 
22
-static bool do_quit(struct data *data, const char *cmd, char *args, char **error)
23
+static void program_quit(struct data *data)
24
 {
25
-   pw_main_loop_quit(data->loop);
26
    data->quit = true;
27
+   pw_main_loop_quit(data->loop);
28
+}
29
+
30
+static bool do_quit(struct data *data, const char *cmd, char *args, char **error)
31
+{
32
+   program_quit(data);
33
    return true;
34
 }
35
 
36
@@ -433,7 +439,7 @@
37
            continue;
38
        if (global_matches(g, pattern))
39
            return g;
40
-        }
41
+   }
42
    return NULL;
43
 }
44
 
45
@@ -446,7 +452,7 @@
46
            id, seq, res, spa_strerror(res), message);
47
 
48
    if (id == PW_ID_CORE && res == -EPIPE)
49
-       pw_main_loop_quit(data->loop);
50
+       program_quit(data);
51
 }
52
 
53
 static const struct pw_core_events remote_core_events = {
54
@@ -490,7 +496,7 @@
55
 static bool do_connect(struct data *data, const char *cmd, char *args, char **error)
56
 {
57
    char *a1;
58
-        int n;
59
+   int n;
60
    struct pw_properties *props = NULL;
61
    struct pw_core *core;
62
    struct remote_data *rd;
63
@@ -535,7 +541,7 @@
64
 static bool do_disconnect(struct data *data, const char *cmd, char *args, char **error)
65
 {
66
    char *a1;
67
-        int n;
68
+   int n;
69
    uint32_t idx;
70
    struct remote_data *rd = data->current;
71
 
72
@@ -559,8 +565,8 @@
73
 
74
    return true;
75
 
76
-      no_remote:
77
-        *error = spa_aprintf("Remote %d does not exist", idx);
78
+no_remote:
79
+   *error = spa_aprintf("Remote %d does not exist", idx);
80
    return false;
81
 }
82
 
83
@@ -577,7 +583,7 @@
84
 static bool do_switch_remote(struct data *data, const char *cmd, char *args, char **error)
85
 {
86
    char *a1;
87
-        int n, idx = 0;
88
+   int n, idx = 0;
89
    struct remote_data *rd;
90
 
91
    n = pw_split_ip(args, WHITESPACE, 1, a);
92
@@ -594,8 +600,8 @@
93
 
94
    return true;
95
 
96
-      no_remote:
97
-        *error = spa_aprintf("Remote %d does not exist", idx);
98
+no_remote:
99
+   *error = spa_aprintf("Remote %d does not exist", idx);
100
    return false;
101
 }
102
 
103
@@ -651,18 +657,20 @@
104
    info_global(pd);
105
    if (info == NULL)
106
        return;
107
-   printf("%c\tinput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_INPUT_PORTS),
108
-           info->n_input_ports, info->max_input_ports);
109
-   printf("%c\toutput ports: %u/%u\n", MARK_CHANGE(PW_NODE_CHANGE_MASK_OUTPUT_PORTS),
110
-           info->n_output_ports, info->max_output_ports);
111
-   printf("%c\tstate: \"%s\"", MARK_CHANGE(PW_NODE_CHANGE_MASK_STATE),
112
-           pw_node_state_as_string(info->state));
113
+   if (info->change_mask & PW_NODE_CHANGE_MASK_INPUT_PORTS)
114
+       printf("%c\tinput ports: %u/%u\n", '*', info->n_input_ports, info->max_input_ports);
115
+   if (info->change_mask & PW_NODE_CHANGE_MASK_OUTPUT_PORTS)
116
+       printf("%c\toutput ports: %u/%u\n", '*', info->n_output_ports, info->max_output_ports);
117
+   if (info->change_mask & PW_NODE_CHANGE_MASK_STATE)
118
+       printf("%c\tstate: \"%s\"", '*', pw_node_state_as_string(info->state));
119
    if (info->state == PW_NODE_STATE_ERROR && info->error)
120
        printf(" \"%s\"\n", info->error);
121
    else
122
        printf("\n");
123
-   print_properties(info->props, MARK_CHANGE(PW_NODE_CHANGE_MASK_PROPS), true);
124
-   print_params(info->params, info->n_params, MARK_CHANGE(PW_NODE_CHANGE_MASK_PARAMS), true);
125
+   if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)
126
+       print_properties(info->props, '*', true);
127
+   if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS)
128
+       print_params(info->params, info->n_params, '*', true);
129
    info->change_mask = 0;
130
 }
131
 
132
@@ -674,8 +682,10 @@
133
    if (info == NULL)
134
        return;
135
    printf("\tdirection: \"%s\"\n", pw_direction_as_string(info->direction));
136
-   print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS), true);
137
-   print_params(info->params, info->n_params, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS), true);
138
+   if (info->change_mask & PW_PORT_CHANGE_MASK_PROPS)
139
+       print_properties(info->props, '*', true);
140
+   if (info->change_mask & PW_PORT_CHANGE_MASK_PARAMS)
141
+       print_params(info->params, info->n_params, '*', true);
142
    info->change_mask = 0;
143
 }
144
 
145
@@ -715,18 +725,20 @@
146
    printf("\tinput-node-id: %u\n", info->input_node_id);
147
    printf("\tinput-port-id: %u\n", info->input_port_id);
148
 
149
-   printf("%c\tstate: \"%s\"", MARK_CHANGE(PW_LINK_CHANGE_MASK_STATE),
150
-           pw_link_state_as_string(info->state));
151
+   if (info->change_mask & PW_LINK_CHANGE_MASK_STATE)
152
+       printf("%c\tstate: \"%s\"", '*', pw_link_state_as_string(info->state));
153
    if (info->state == PW_LINK_STATE_ERROR && info->error)
154
        printf(" \"%s\"\n", info->error);
155
    else
156
        printf("\n");
157
-   printf("%c\tformat:\n", MARK_CHANGE(PW_LINK_CHANGE_MASK_FORMAT));
158
+   if (info->change_mask & PW_LINK_CHANGE_MASK_FORMAT)
159
+       printf("%c\tformat:\n", '*');
160
    if (info->format)
161
        spa_debug_pod(2, NULL, info->format);
162
    else
163
        printf("\t\tnone\n");
164
-   print_properties(info->props, MARK_CHANGE(PW_LINK_CHANGE_MASK_PROPS), true);
165
+   if (info->change_mask & PW_LINK_CHANGE_MASK_STATE)
166
+       print_properties(info->props, '*', true);
167
    info->change_mask = 0;
168
 }
169
 
170
@@ -737,8 +749,10 @@
171
    info_global(pd);
172
    if (info == NULL)
173
        return;
174
-   print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS), true);
175
-   print_params(info->params, info->n_params, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS), true);
176
+   if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS)
177
+       print_properties(info->props, '*', true);
178
+   if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS)
179
+       print_params(info->params, info->n_params, '*', true);
180
    info->change_mask = 0;
181
 }
182
 
183
@@ -810,7 +824,7 @@
184
        pd->global = pw_map_lookup(&rd->globals, info->id);
185
    if (pd->global && pd->global->info_pending) {
186
        info_core(pd);
187
-       pd->global->info_pending = false;
188
+       pd->global->info_pending = rd->data->monitoring_info;
189
    }
190
 }
191
 
192
@@ -831,7 +845,7 @@
193
        pd->global = pw_map_lookup(&rd->globals, info->id);
194
    if (pd->global && pd->global->info_pending) {
195
        info_module(pd);
196
-       pd->global->info_pending = false;
197
+       pd->global->info_pending = rd->data->monitoring_info;
198
    }
199
 }
200
 
201
@@ -851,14 +865,14 @@
202
        pd->global = pw_map_lookup(&rd->globals, info->id);
203
    if (pd->global && pd->global->info_pending) {
204
        info_node(pd);
205
-       pd->global->info_pending = false;
206
+       pd->global->info_pending = rd->data->monitoring_info;
207
    }
208
 }
209
 
210
 static void event_param(void *_data, int seq, uint32_t id,
211
        uint32_t index, uint32_t next, const struct spa_pod *param)
212
 {
213
-        struct proxy_data *data = _data;
214
+   struct proxy_data *data = _data;
215
    struct remote_data *rd = data->rd;
216
 
217
    if (rd->data->interactive)
218
@@ -886,7 +900,7 @@
219
        pd->global = pw_map_lookup(&rd->globals, info->id);
220
    if (pd->global && pd->global->info_pending) {
221
        info_port(pd);
222
-       pd->global->info_pending = false;
223
+       pd->global->info_pending = rd->data->monitoring_info;
224
    }
225
 }
226
 
227
@@ -907,7 +921,7 @@
228
        pd->global = pw_map_lookup(&rd->globals, info->id);
229
    if (pd->global && pd->global->info_pending) {
230
        info_factory(pd);
231
-       pd->global->info_pending = false;
232
+       pd->global->info_pending = rd->data->monitoring_info;
233
    }
234
 }
235
 
236
@@ -927,14 +941,14 @@
237
        pd->global = pw_map_lookup(&rd->globals, info->id);
238
    if (pd->global && pd->global->info_pending) {
239
        info_client(pd);
240
-       pd->global->info_pending = false;
241
+       pd->global->info_pending = rd->data->monitoring_info;
242
    }
243
 }
244
 
245
 static void client_event_permissions(void *_data, uint32_t index,
246
        uint32_t n_permissions, const struct pw_permission *permissions)
247
 {
248
-        struct proxy_data *data = _data;
249
+   struct proxy_data *data = _data;
250
    struct remote_data *rd = data->rd;
251
    uint32_t i;
252
 
253
@@ -968,7 +982,7 @@
254
        pd->global = pw_map_lookup(&rd->globals, info->id);
255
    if (pd->global && pd->global->info_pending) {
256
        info_link(pd);
257
-       pd->global->info_pending = false;
258
+       pd->global->info_pending = rd->data->monitoring_info;
259
    }
260
 }
261
 
262
@@ -989,7 +1003,7 @@
263
        pd->global = pw_map_lookup(&rd->globals, info->id);
264
    if (pd->global && pd->global->info_pending) {
265
        info_device(pd);
266
-       pd->global->info_pending = false;
267
+       pd->global->info_pending = rd->data->monitoring_info;
268
    }
269
 }
270
 
271
@@ -1034,7 +1048,7 @@
272
        pd->global = pw_map_lookup(&rd->globals, update->id);
273
    if (pd->global && pd->global->info_pending) {
274
        info_session(pd);
275
-       pd->global->info_pending = false;
276
+       pd->global->info_pending = rd->data->monitoring_info;
277
    }
278
 }
279
 
280
@@ -1090,7 +1104,7 @@
281
        pd->global = pw_map_lookup(&rd->globals, update->id);
282
    if (pd->global && pd->global->info_pending) {
283
        info_endpoint(pd);
284
-       pd->global->info_pending = false;
285
+       pd->global->info_pending = rd->data->monitoring_info;
286
    }
287
 }
288
 
289
@@ -1139,7 +1153,7 @@
290
        pd->global = pw_map_lookup(&rd->globals, update->id);
291
    if (pd->global && pd->global->info_pending) {
292
        info_endpoint_stream(pd);
293
-       pd->global->info_pending = false;
294
+       pd->global->info_pending = rd->data->monitoring_info;
295
    }
296
 }
297
 
298
@@ -1176,9 +1190,9 @@
299
 }
300
 
301
 static const struct pw_proxy_events proxy_events = {
302
-        PW_VERSION_PROXY_EVENTS,
303
-        .removed = removed_proxy,
304
-        .destroy = destroy_proxy,
305
+   PW_VERSION_PROXY_EVENTS,
306
+   .removed = removed_proxy,
307
+   .destroy = destroy_proxy,
308
 };
309
 
310
 static bool do_list_objects(struct data *data, const char *cmd, char *args, char **error)
311
@@ -1353,6 +1367,7 @@
312
        pd = pw_proxy_get_user_data(global->proxy);
313
        if (pd->class->info)
314
            pd->class->info(pd);
315
+       pd->global->info_pending = rd->data->monitoring_info;
316
    }
317
    return true;
318
 }
319
@@ -1375,7 +1390,7 @@
320
 {
321
    struct remote_data *rd = data->current;
322
    char *a1;
323
-        int n;
324
+   int n;
325
    struct global *global;
326
 
327
    n = pw_split_ip(args, WHITESPACE, 1, a);
328
@@ -1397,6 +1412,19 @@
329
    return true;
330
 }
331
 
332
+static struct pw_properties *properties_new_checked(const char *str, char **error)
333
+{
334
+   struct pw_properties *props;
335
+   struct spa_error_location loc;
336
+
337
+   props = pw_properties_new_string_checked(str, strlen(str), &loc);
338
+   if (!props) {
339
+       *error = spa_aprintf("syntax error in properties, line:%d col:%d: %s",
340
+               loc.line, loc.col, loc.reason);
341
+   }
342
+   return props;
343
+}
344
+
345
 static bool do_create_device(struct data *data, const char *cmd, char *args, char **error)
346
 {
347
    struct remote_data *rd = data->current;
348
@@ -1412,8 +1440,10 @@
349
        *error = spa_aprintf("%s <factory-name> <properties>", cmd);
350
        return false;
351
    }
352
-   if (n == 2)
353
-       props = pw_properties_new_string(a1);
354
+   if (n == 2) {
355
+       if ((props = properties_new_checked(a1, error)) == NULL)
356
+           return false;
357
+   }
358
 
359
    proxy = pw_core_create_object(rd->core, a0,
360
                        PW_TYPE_INTERFACE_Device,
361
@@ -1441,7 +1471,7 @@
362
 {
363
    struct remote_data *rd = data->current;
364
    char *a2;
365
-        int n;
366
+   int n;
367
    uint32_t id;
368
    struct pw_proxy *proxy;
369
    struct pw_properties *props = NULL;
370
@@ -1452,8 +1482,10 @@
371
        *error = spa_aprintf("%s <factory-name> <properties>", cmd);
372
        return false;
373
    }
374
-   if (n == 2)
375
-       props = pw_properties_new_string(a1);
376
+   if (n == 2) {
377
+       if ((props = properties_new_checked(a1, error)) == NULL)
378
+           return false;
379
+   }
380
 
381
    proxy = pw_core_create_object(rd->core, a0,
382
                        PW_TYPE_INTERFACE_Node,
383
@@ -1466,9 +1498,9 @@
384
    pd = pw_proxy_get_user_data(proxy);
385
    pd->rd = rd;
386
    pd->proxy = proxy;
387
-        pd->class = &node_class;
388
-        pw_proxy_add_object_listener(proxy, &pd->object_listener, &node_events, pd);
389
-        pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);
390
+   pd->class = &node_class;
391
+   pw_proxy_add_object_listener(proxy, &pd->object_listener, &node_events, pd);
392
+   pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);
393
 
394
    id = pw_map_insert_new(&data->vars, proxy);
395
    if (rd->data->interactive)
396
@@ -1481,7 +1513,7 @@
397
 {
398
    struct remote_data *rd = data->current;
399
    char *a1;
400
-        int n;
401
+   int n;
402
    struct global *global;
403
 
404
    n = pw_split_ip(args, WHITESPACE, 1, a);
405
@@ -1569,10 +1601,12 @@
406
        *error = spa_aprintf("%s <node-id> <port> <node-id> <port> <properties>", cmd);
407
        return false;
408
    }
409
-   if (n == 5)
410
-       props = pw_properties_new_string(a4);
411
-   else
412
+   if (n == 5) {
413
+       if ((props = properties_new_checked(a4, error)) == NULL)
414
+           return false;
415
+   } else {
416
        props = pw_properties_new(NULL, NULL);
417
+   }
418
 
419
    if (!spa_streq(a0, "-"))
420
        pw_properties_set(props, PW_KEY_LINK_OUTPUT_NODE, a0);
421
@@ -1678,8 +1712,8 @@
422
 
423
    return true;
424
 
425
-      no_remote:
426
-        *error = spa_aprintf("Remote %d does not exist", idx);
427
+no_remote:
428
+   *error = spa_aprintf("Remote %d does not exist", idx);
429
    return false;
430
 }
431
 
432
@@ -1842,7 +1876,7 @@
433
 {
434
    struct remote_data *rd = data->current;
435
    char *a3;
436
-        int n;
437
+   int n;
438
    struct global *global;
439
 
440
    n = pw_split_ip(args, WHITESPACE, 1, a);
441
@@ -2127,7 +2161,7 @@
442
            return c->func(data, cmd, args, error);
443
        }
444
    }
445
-        *error = spa_aprintf("Command \"%s\" does not exist. Type 'help' for usage.", cmd);
446
+   *error = spa_aprintf("Command \"%s\" does not exist. Type 'help' for usage.", cmd);
447
    return false;
448
 }
449
 
450
@@ -2239,8 +2273,7 @@
451
 static void do_quit_on_signal(void *data, int signal_number)
452
 {
453
    struct data *d = data;
454
-   d->quit = true;
455
-   pw_main_loop_quit(d->loop);
456
+   program_quit(d);
457
 }
458
 
459
 static void show_help(struct data *data, const char *name, bool error)
460
@@ -2362,6 +2395,9 @@
461
            p = stpcpy(p, " ");
462
        }
463
 
464
+       // If we're monitoring, surface info changes as well
465
+       data.monitoring_info = monitor;
466
+
467
        pw_main_loop_run(data.loop);
468
 
469
        if (!parse(&data, buf, &error)) {
470
pipewire-1.0.1.tar.bz2/src/tools/pw-config.c -> pipewire-1.2.0.tar.gz/src/tools/pw-config.c Changed
28
 
1
@@ -66,7 +66,7 @@
2
    int l;
3
    const char *value;
4
 
5
-        spa_json_init(&it0, str, len);
6
+   spa_json_init(&it0, str, len);
7
    if ((l = spa_json_next(&it0, &value)) <= 0)
8
        return 0;
9
 
10
@@ -116,7 +116,7 @@
11
 
12
 static void show_help(const char *name, bool error)
13
 {
14
-        fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n"
15
+   fprintf(error ? stderr : stdout, "%1$s : PipeWire config manager.\n"
16
        "Usage:\n"
17
        "  %1$s options paths                  List config paths (default action)\n"
18
        "  %1$s options list SECTION         List config section\n"
19
@@ -154,7 +154,7 @@
20
    d.opt_prefix = NULL;
21
    d.opt_recurse = false;
22
    d.opt_newline = true;
23
-   if (isatty(fileno(stdout)) && getenv("NO_COLOR") == NULL)
24
+   if (getenv("NO_COLOR") == NULL && isatty(fileno(stdout)))
25
        d.opt_colors = true;
26
    d.opt_cmd = "paths";
27
 
28
pipewire-1.2.0.tar.gz/src/tools/pw-container.c Added
305
 
1
@@ -0,0 +1,303 @@
2
+/* PipeWire */
3
+/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */
4
+/* SPDX-License-Identifier: MIT */
5
+
6
+#include <stdio.h>
7
+#include <signal.h>
8
+#include <getopt.h>
9
+#include <unistd.h>
10
+#include <unistd.h>
11
+#include <limits.h>
12
+#include <locale.h>
13
+#include <fcntl.h>
14
+#include <sys/socket.h>
15
+
16
+#include <spa/utils/result.h>
17
+#include <spa/utils/string.h>
18
+#include <spa/utils/ansi.h>
19
+#include <spa/debug/pod.h>
20
+#include <spa/debug/format.h>
21
+#include <spa/debug/types.h>
22
+#include <spa/debug/file.h>
23
+
24
+#include <pipewire/pipewire.h>
25
+#include <pipewire/extensions/security-context.h>
26
+
27
+struct data {
28
+   struct pw_main_loop *loop;
29
+   struct pw_context *context;
30
+
31
+   struct pw_core *core;
32
+   struct spa_hook core_listener;
33
+
34
+   struct pw_registry *registry;
35
+   struct spa_hook registry_listener;
36
+
37
+   struct pw_properties *props;
38
+
39
+   struct pw_security_context *sec;
40
+
41
+   int pending_create;
42
+   int create_result;
43
+   int pending;
44
+   int done;
45
+};
46
+
47
+static void registry_event_global(void *data, uint32_t id,
48
+                 uint32_t permissions, const char *type, uint32_t version,
49
+                 const struct spa_dict *props)
50
+{
51
+   struct data *d = data;
52
+
53
+   if (spa_streq(type, PW_TYPE_INTERFACE_SecurityContext))
54
+       d->sec = pw_registry_bind(d->registry, id, type, version, 0);
55
+}
56
+
57
+static void registry_event_global_remove(void *data, uint32_t id)
58
+{
59
+}
60
+
61
+static const struct pw_registry_events registry_events = {
62
+   PW_VERSION_REGISTRY_EVENTS,
63
+   .global = registry_event_global,
64
+   .global_remove = registry_event_global_remove,
65
+};
66
+
67
+static void on_core_error(void *_data, uint32_t id, int seq, int res, const char *message)
68
+{
69
+   struct data *data = _data;
70
+
71
+   pw_log_error("error id:%u seq:%d res:%d (%s): %s",
72
+           id, seq, res, spa_strerror(res), message);
73
+
74
+   if (seq == SPA_RESULT_ASYNC_SEQ(data->pending_create))
75
+       data->create_result = res;
76
+
77
+   if (id == PW_ID_CORE && res == -EPIPE) {
78
+       data->done = true;
79
+       pw_main_loop_quit(data->loop);
80
+   }
81
+}
82
+
83
+static const struct pw_core_events core_events = {
84
+   PW_VERSION_CORE_EVENTS,
85
+   .error = on_core_error,
86
+};
87
+
88
+static void core_event_done(void *object, uint32_t id, int seq)
89
+{
90
+   struct data *data = object;
91
+   if (id == PW_ID_CORE && seq == data->pending) {
92
+       data->done = true;
93
+       pw_main_loop_quit(data->loop);
94
+   }
95
+}
96
+
97
+static int roundtrip(struct data *data)
98
+{
99
+   struct spa_hook core_listener;
100
+   const struct pw_core_events core_events = {
101
+   PW_VERSION_CORE_EVENTS,
102
+       .done = core_event_done,
103
+   };
104
+   spa_zero(core_listener);
105
+   pw_core_add_listener(data->core, &core_listener,
106
+           &core_events, data);
107
+
108
+   data->done = false;
109
+   data->pending = pw_core_sync(data->core, PW_ID_CORE, 0);
110
+
111
+   while (!data->done)
112
+       pw_main_loop_run(data->loop);
113
+
114
+   spa_hook_remove(&core_listener);
115
+   return 0;
116
+}
117
+
118
+
119
+static void do_quit(void *data, int signal_number)
120
+{
121
+   struct data *d = data;
122
+   pw_main_loop_quit(d->loop);
123
+}
124
+
125
+static void show_help(struct data *d, const char *name, bool error)
126
+{
127
+   FILE *out = error ? stderr : stdout;
128
+   fprintf(out, "%s options application\n"
129
+       "  -h, --help                            Show this help\n"
130
+       "      --version                         Show version\n"
131
+       "  -r, --remote                          Remote daemon name\n"
132
+       "  -P, --properties                      Context properties\n",
133
+       name);
134
+   fprintf(out, "\nDefault Context properties:\n");
135
+   pw_properties_serialize_dict(out, &d->props->dict,
136
+           PW_PROPERTIES_FLAG_NL | PW_PROPERTIES_FLAG_ENCLOSE);
137
+   fprintf(out, "\n");
138
+}
139
+
140
+int main(int argc, char *argv)
141
+{
142
+   struct data data = { 0 };
143
+   struct pw_loop *l;
144
+   const char *opt_remote = NULL;
145
+   static const struct option long_options = {
146
+       { "help",       no_argument,        NULL, 'h' },
147
+       { "version",        no_argument,        NULL, 'V' },
148
+       { "remote",     required_argument,  NULL, 'r' },
149
+       { "properties",     required_argument,  NULL, 'P' },
150
+       { NULL, 0, NULL, 0}
151
+   };
152
+   struct spa_error_location loc;
153
+   int c, res, listen_fd, close_fd2;
154
+   char tempPATH_MAX = "/tmp/pipewire-XXXXXX";
155
+   struct sockaddr_un sockaddr = {0};
156
+
157
+   data.props = pw_properties_new(
158
+           PW_KEY_SEC_ENGINE, "org.flatpak",
159
+           PW_KEY_ACCESS, "restricted",
160
+           NULL);
161
+
162
+   setlocale(LC_ALL, "");
163
+   pw_init(&argc, &argv);
164
+
165
+   while ((c = getopt_long(argc, argv, "hVr:P:", long_options, NULL)) != -1) {
166
+       switch (c) {
167
+       case 'h':
168
+           show_help(&data, argv0, false);
169
+           return 0;
170
+       case 'V':
171
+           printf("%s\n"
172
+               "Compiled with libpipewire %s\n"
173
+               "Linked with libpipewire %s\n",
174
+               argv0,
175
+               pw_get_headers_version(),
176
+               pw_get_library_version());
177
+           return 0;
178
+       case 'r':
179
+           opt_remote = optarg;
180
+           break;
181
+       case 'P':
182
+           if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
183
+               spa_debug_file_error_location(stderr, &loc,
184
+                       "error: syntax error in --properties: %s",
185
+                       loc.reason);
186
+               return -1;
187
+           }
188
+           break;
189
+       default:
190
+           show_help(&data, argv0, true);
191
+           return -1;
192
+       }
193
+   }
194
+
195
+   data.loop = pw_main_loop_new(NULL);
196
+   if (data.loop == NULL) {
197
+       fprintf(stderr, "can't create main loop: %m\n");
198
+       return -1;
199
+   }
200
+
201
+   l = pw_main_loop_get_loop(data.loop);
202
+   pw_loop_add_signal(l, SIGINT, do_quit, &data);
203
+   pw_loop_add_signal(l, SIGTERM, do_quit, &data);
204
+
205
+   data.context = pw_context_new(l, NULL, 0);
206
+   if (data.context == NULL) {
207
+       fprintf(stderr, "can't create context: %m\n");
208
+       return -1;
209
+   }
210
+
211
+   data.core = pw_context_connect(data.context,
212
+           pw_properties_new(
213
+               PW_KEY_REMOTE_NAME, opt_remote ? opt_remote :
214
+                   ("" PW_DEFAULT_REMOTE "-manager," PW_DEFAULT_REMOTE ""),
215
+               NULL),
216
+           0);
217
+   if (data.core == NULL) {
218
+       fprintf(stderr, "can't connect: %m\n");
219
+       return -1;
220
+   }
221
+
222
+   pw_core_add_listener(data.core,
223
+                  &data.core_listener,
224
+                  &core_events, &data);
225
+   data.registry = pw_core_get_registry(data.core,
226
+                     PW_VERSION_REGISTRY, 0);
227
+   pw_registry_add_listener(data.registry,
228
+                      &data.registry_listener,
229
+                      &registry_events, &data);
230
+
231
+   roundtrip(&data);
232
+
233
+   if (data.sec == NULL) {
234
+       fprintf(stderr, "no security context object found\n");
235
+       return -1;
236
+   }
237
+
238
+   res = mkstemp(temp);
239
+   if (res < 0) {
240
+       fprintf(stderr, "can't make temp file with template %s: %m\n", temp);
241
+       return -1;
242
+   }
243
+   close(res);
244
+   unlink(temp);
245
+
246
+   listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
247
+   if (listen_fd < 0) {
248
+       fprintf(stderr, "can't make unix socket: %m\n");
249
+       return -1;
250
+   }
251
+
252
+   sockaddr.sun_family = AF_UNIX;
253
+   snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", temp);
254
+   if (bind(listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) != 0) {
255
+       fprintf(stderr, "can't bind unix socket to %s: %m\n", temp);
256
+       return -1;
257
+   }
258
+   if (listen(listen_fd, 0) != 0) {
259
+       fprintf(stderr, "can't listen unix socket: %m\n");
260
+       return -1;
261
+   }
262
+
263
+   res = pipe2(close_fd, O_CLOEXEC);
264
+   if (res < 0) {
265
+       fprintf(stderr, "can't create pipe: %m\n");
266
+       return -1;
267
+   }
268
+   setenv("PIPEWIRE_REMOTE", temp, 1);
269
+
270
+   data.create_result = 0;
271
+   data.pending_create = pw_security_context_create(data.sec,
272
+           listen_fd, close_fd1, &data.props->dict);
273
+
274
+   if (SPA_RESULT_IS_ASYNC(data.pending_create)) {
275
+       pw_log_debug("create: %d", data.pending_create);
276
+       roundtrip(&data);
277
+   }
278
+   pw_log_debug("create result: %d", data.create_result);
279
+
280
+   if (data.create_result < 0) {
281
+       fprintf(stderr, "can't create security context: %s\n",
282
+               spa_strerror(data.create_result));
283
+       return -1;
284
+   }
285
+
286
+   if (optind < argc) {
287
+       system(argvoptind++);
288
+   } else {
289
+       fprintf(stdout, "new socket: %s\n", temp);
290
+       pw_main_loop_run(data.loop);
291
+   }
292
+   unlink(temp);
293
+
294
+   spa_hook_remove(&data.registry_listener);
295
+   pw_proxy_destroy((struct pw_proxy*)data.sec);
296
+   pw_proxy_destroy((struct pw_proxy*)data.registry);
297
+   spa_hook_remove(&data.core_listener);
298
+   pw_context_destroy(data.context);
299
+   pw_main_loop_destroy(data.loop);
300
+   pw_properties_free(data.props);
301
+   pw_deinit();
302
+
303
+   return 0;
304
+}
305
pipewire-1.0.1.tar.bz2/src/tools/pw-dot.c -> pipewire-1.2.0.tar.gz/src/tools/pw-dot.c Changed
1017
 
1
@@ -16,17 +16,26 @@
2
 #include <spa/utils/string.h>
3
 #include <spa/utils/json.h>
4
 #include <spa/debug/types.h>
5
+#include <spa/debug/file.h>
6
 
7
 #include <pipewire/pipewire.h>
8
 
9
 #define GLOBAL_ID_NONE UINT32_MAX
10
 #define DEFAULT_DOT_PATH "pw.dot"
11
+#define DEFAULT_DOT_DATA_SIZE 2048
12
+#define MAX_ESCAPED_LEN 128
13
 
14
 struct global;
15
 
16
 typedef void (*draw_t)(struct global *g);
17
 typedef void *(*info_update_t) (void *info, const void *update);
18
 
19
+struct dot_data {
20
+   char *data;
21
+   size_t size;
22
+   size_t max_size;
23
+};
24
+
25
 struct data {
26
    struct pw_main_loop *loop;
27
    struct pw_context *context;
28
@@ -38,7 +47,7 @@
29
    struct spa_hook registry_listener;
30
 
31
    struct spa_list globals;
32
-   char *dot_str;
33
+   struct dot_data dot_data;
34
    const char *dot_rankdir;
35
    bool dot_orthoedges;
36
 
37
@@ -67,126 +76,194 @@
38
    pw_destroy_t info_destroy;
39
    info_update_t info_update;
40
    draw_t draw;
41
+   bool drawn;
42
 
43
    struct spa_hook proxy_listener;
44
    struct spa_hook object_listener;
45
 };
46
 
47
-static char *dot_str_new(void)
48
+static bool dot_data_init(struct dot_data * dd, size_t size)
49
 {
50
-        return strdup("");
51
+   if (size <= 0)
52
+       return false;
53
+
54
+   dd->data = malloc(sizeof (char) * size);
55
+   dd->data0 = '\0';
56
+   dd->size = 0;
57
+   dd->max_size = size;
58
+   return true;
59
 }
60
 
61
-static void dot_str_clear(char **str)
62
+static void dot_data_clear(struct dot_data * dd)
63
 {
64
-   if (str && *str) {
65
-         free(*str);
66
-         *str = NULL;
67
+   if (dd->data) {
68
+       free(dd->data);
69
+       dd->data = NULL;
70
    }
71
+   dd->size = 0;
72
 }
73
 
74
-static SPA_PRINTF_FUNC(2,0) void dot_str_vadd(char **str, const char *fmt, va_list varargs)
75
+static void dot_data_ensure_max_size (struct dot_data * dd, size_t size)
76
 {
77
-   char *res = NULL;
78
-   char *fmt2 = NULL;
79
+   size_t new_size = dd->size + size + 1;
80
+   if (new_size > dd->max_size) {
81
+       size_t next_size = new_size * 2;
82
+       dd->data = realloc (dd->data, next_size);
83
+       dd->max_size = next_size;
84
+   }
85
+}
86
 
87
-   spa_return_if_fail(str != NULL);
88
-   spa_return_if_fail(fmt != NULL);
89
+static void dot_data_add_uint32 (struct dot_data * dd, uint32_t value)
90
+{
91
+   int size;
92
+   dot_data_ensure_max_size (dd, 16);
93
+   size = snprintf (dd->data + dd->size, dd->max_size - dd->size, "%u", value);
94
+   dd->size += size;
95
+}
96
 
97
-   if (asprintf(&fmt2, "%s%s", *str, fmt) < 0) {
98
-       spa_assert_not_reached();
99
-       return;
100
-   }
101
+static void dot_data_add_string (struct dot_data * dd, const char *value)
102
+{
103
+   int size;
104
+   dot_data_ensure_max_size (dd, strlen (value));
105
+   size = snprintf (dd->data + dd->size, dd->max_size - dd->size, "%s", value);
106
+   dd->size += size;
107
+}
108
 
109
-   if (vasprintf(&res, fmt2, varargs) < 0) {
110
-       free (fmt2);
111
-       spa_assert_not_reached();
112
-       return;
113
-   }
114
-   free (fmt2);
115
+static int escape_quotes(char *str, int size, const char *val)
116
+{
117
+   int len = 0;
118
+#define __PUT(c) { if (len < size) *str++ = c; len++; }
119
+   while (*val) {
120
+       switch (*val) {
121
+       case '"':
122
+           __PUT('\\'); __PUT(*val);
123
+           break;
124
+       default:
125
+           __PUT(*val);
126
+           break;
127
+       }
128
+       val++;
129
 
130
-   free(*str);
131
-   *str = res;
132
+       /* Truncate with "..." if string has more than escaped len characters. */
133
+       if (len >= MAX_ESCAPED_LEN) {
134
+           __PUT('.'); __PUT('.'); __PUT('.');
135
+           break;
136
+       }
137
+   }
138
+   __PUT('\0');
139
+#undef __PUT
140
+   return len-1;
141
 }
142
 
143
-static SPA_PRINTF_FUNC(2,3) void dot_str_add(char **str, const char *fmt, ...)
144
+static void dot_data_add_string_escaped (struct dot_data * dd, const char *value)
145
 {
146
-   va_list varargs;
147
-   va_start(varargs, fmt);
148
-   dot_str_vadd(str, fmt, varargs);
149
-   va_end(varargs);
150
+   size_t escaped_size = escape_quotes (dd->data + dd->size,
151
+           dd->max_size - dd->size, value);
152
+   if (escaped_size + 1 > dd->max_size - dd->size) {
153
+       dot_data_ensure_max_size (dd, escaped_size);
154
+       escaped_size = escape_quotes (dd->data + dd->size,
155
+               dd->max_size - dd->size, value);
156
+   }
157
+   dd->size += escaped_size;
158
 }
159
 
160
-static void draw_dict(char **str, const char *title,
161
-             const struct spa_dict *props)
162
+static void draw_dict(struct dot_data * dd, const char *title,
163
+               const struct spa_dict *props)
164
 {
165
    const struct spa_dict_item *item;
166
 
167
-   dot_str_add(str, "%s:\\l", title);
168
+   dot_data_add_string(dd, title);
169
+   dot_data_add_string(dd, ":\\l");
170
    if (props == NULL || props->n_items == 0) {
171
-       dot_str_add(str, "- none\\l");
172
+       dot_data_add_string(dd, "- none\\l");
173
        return;
174
    }
175
 
176
    spa_dict_for_each(item, props) {
177
-       if (item->value)
178
-           dot_str_add(str, "- %s: %s\\l", item->key, item->value);
179
-       else
180
-           dot_str_add(str, "- %s: (null)\\l", item->key);
181
+       if (item->value) {
182
+           dot_data_add_string(dd, "- ");
183
+           dot_data_add_string_escaped(dd, item->key);
184
+           dot_data_add_string(dd, ": ");
185
+           dot_data_add_string_escaped(dd, item->value);
186
+           dot_data_add_string(dd, "\\l");
187
+       } else {
188
+           dot_data_add_string(dd, "- ");
189
+           dot_data_add_string_escaped(dd, item->key);
190
+           dot_data_add_string(dd, ": (null)\\l");
191
+       }
192
    }
193
 }
194
 
195
-static SPA_PRINTF_FUNC(6,0) void draw_vlabel(char **str, const char *name, uint32_t id, bool detail,
196
-              const struct spa_dict *props, const char *fmt, va_list varargs)
197
+static void draw_port(struct global *g)
198
 {
199
-   /* draw the label header */
200
-   dot_str_add(str, "%s_%u label=\"", name, id);
201
+   spa_assert(g != NULL);
202
+   spa_assert(g->info != NULL);
203
+   spa_assert(g->type == INTERFACE_Port);
204
 
205
-   /* draw the label body */
206
-   dot_str_vadd(str, fmt, varargs);
207
+   struct pw_port_info *info = g->info;
208
+   struct dot_data *dd = &g->data->dot_data;
209
 
210
-   if (detail)
211
-       draw_dict(str, "properties", props);
212
+   const char *port_name = spa_dict_lookup(info->props, PW_KEY_PORT_NAME);
213
 
214
-   /*draw the label footer */
215
-   dot_str_add(str, "%s", "\";\n");
216
-}
217
+   /* draw the box */
218
+   dot_data_add_string(dd, "port_");
219
+   dot_data_add_uint32(dd, g->id);
220
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=");
221
+   dot_data_add_string(dd, info->direction == PW_DIRECTION_INPUT ? "lightslateblue" : "lightcoral");
222
+   dot_data_add_string(dd, ";");
223
 
224
-static SPA_PRINTF_FUNC(6,7) void draw_label(char **str, const char *name, uint32_t id, bool detail,
225
-              const struct spa_dict *props, const char *fmt, ...)
226
-{
227
-   va_list varargs;
228
-   va_start(varargs, fmt);
229
-   draw_vlabel(str, name, id, detail, props, fmt, varargs);
230
-   va_end(varargs);
231
+   /* draw the label header */
232
+   dot_data_add_string(dd, "port_");
233
+   dot_data_add_uint32(dd, g->id);
234
+   dot_data_add_string(dd, " label=\"");
235
+
236
+   /* draw the label body */
237
+   dot_data_add_string(dd, "port_id: ");
238
+   dot_data_add_uint32(dd, g->id);
239
+   dot_data_add_string(dd, "\\lname: ");
240
+   dot_data_add_string_escaped(dd, port_name ? port_name : "(null)");
241
+   dot_data_add_string(dd, "\\ldirection: ");
242
+   dot_data_add_string_escaped(dd, pw_direction_as_string(info->direction));
243
+   dot_data_add_string(dd, "\\l");
244
+   if (g->data->show_detail)
245
+       draw_dict(dd, "properties", info->props);
246
+
247
+   /* draw the label footer */
248
+   dot_data_add_string(dd, "\";\n");
249
 }
250
 
251
-static void draw_port(struct global *g)
252
+static void draw_node_arrows(struct global *g)
253
 {
254
    spa_assert(g != NULL);
255
    spa_assert(g->info != NULL);
256
-   spa_assert(g->type == INTERFACE_Port);
257
+   spa_assert(g->type == INTERFACE_Node);
258
 
259
-   struct pw_port_info *info = g->info;
260
-   char **dot_str = &g->data->dot_str;
261
+   struct pw_node_info *info = g->info;
262
+   struct dot_data *dd = &g->data->dot_data;
263
 
264
-   /* draw the box */
265
-   dot_str_add(dot_str,
266
-       "port_%u shape=box style=filled fillcolor=%s;\n",
267
-       g->id,
268
-       info->direction == PW_DIRECTION_INPUT ? "lightslateblue" : "lightcoral"
269
-   );
270
-
271
-   /* draw the label */
272
-   draw_label(dot_str,
273
-       "port", g->id, g->data->show_detail, info->props,
274
-       "port_id: %u\\lname: %s\\ldirection: %s\\l",
275
-       g->id,
276
-       spa_dict_lookup(info->props, PW_KEY_PORT_NAME),
277
-       pw_direction_as_string(info->direction)
278
-   );
279
-}
280
+   const char *client_id_str, *factory_id_str;
281
+   uint32_t client_id = GLOBAL_ID_NONE, factory_id = GLOBAL_ID_NONE;
282
 
283
+   client_id_str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID);
284
+   factory_id_str = spa_dict_lookup(info->props, PW_KEY_FACTORY_ID);
285
+   spa_atou32(client_id_str, &client_id, 10);
286
+   spa_atou32(factory_id_str, &factory_id, 10);
287
+
288
+   if (client_id != GLOBAL_ID_NONE) {
289
+       dot_data_add_string(dd, "node_");
290
+       dot_data_add_uint32(dd, g->id);
291
+       dot_data_add_string(dd, " -> client_");
292
+       dot_data_add_uint32(dd, client_id);
293
+       dot_data_add_string(dd, " style=dashed;\n");
294
+   }
295
+   if (factory_id != GLOBAL_ID_NONE) {
296
+       dot_data_add_string(dd, "node_");
297
+       dot_data_add_uint32(dd, g->id);
298
+       dot_data_add_string(dd, " -> factory_");
299
+       dot_data_add_uint32(dd, factory_id);
300
+       dot_data_add_string(dd, " style=dashed;\n");
301
+   }
302
+}
303
 
304
 static void draw_node(struct global *g)
305
 {
306
@@ -195,47 +272,55 @@
307
    spa_assert(g->type == INTERFACE_Node);
308
 
309
    struct pw_node_info *info = g->info;
310
-   char **dot_str = &g->data->dot_str;
311
+   struct dot_data *dd = &g->data->dot_data;
312
 
313
-   const char *client_id_str, *factory_id_str;
314
-   uint32_t client_id, factory_id;
315
+   const char *node_name, *media_class, *client_id_str, *factory_id_str;
316
+   uint32_t client_id = GLOBAL_ID_NONE, factory_id = GLOBAL_ID_NONE;
317
 
318
+   node_name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME);
319
+   media_class = spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS);
320
    client_id_str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID);
321
    factory_id_str = spa_dict_lookup(info->props, PW_KEY_FACTORY_ID);
322
-   client_id = client_id_str ? (uint32_t)atoi(client_id_str) : GLOBAL_ID_NONE;
323
-   factory_id = factory_id_str ? (uint32_t)atoi(factory_id_str) : GLOBAL_ID_NONE;
324
+   spa_atou32(client_id_str, &client_id, 10);
325
+   spa_atou32(factory_id_str, &factory_id, 10);
326
 
327
    /* draw the node header */
328
-   dot_str_add(dot_str, "subgraph cluster_node_%u {\n", g->id);
329
-   dot_str_add(dot_str, "bgcolor=palegreen;\n");
330
+   dot_data_add_string(dd, "subgraph cluster_node_");
331
+   dot_data_add_uint32(dd, g->id);
332
+   dot_data_add_string(dd, "{\n");
333
+   dot_data_add_string(dd, "bgcolor=palegreen;\n");
334
 
335
    /* draw the label header */
336
-   dot_str_add(dot_str, "label=\"");
337
+   dot_data_add_string(dd, "label=\"");
338
 
339
    /* draw the label body */
340
-   dot_str_add(dot_str, "node_id: %u\\lname: %s\\lmedia_class: %s\\l",
341
-       g->id,
342
-       spa_dict_lookup(info->props, PW_KEY_NODE_NAME),
343
-       spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS));
344
-
345
+   dot_data_add_string(dd, "node_id: ");
346
+   dot_data_add_uint32(dd, g->id);
347
+   dot_data_add_string(dd, "\\lname: ");
348
+   dot_data_add_string_escaped(dd, node_name ? node_name : "(null)");
349
+   dot_data_add_string(dd, "\\lmedia_class: ");
350
+   dot_data_add_string_escaped(dd, media_class ? media_class : "(null)");
351
+   dot_data_add_string(dd, "\\l");
352
    if (g->data->show_detail)
353
-       draw_dict(dot_str, "properties", info->props);
354
+       draw_dict(dd, "properties", info->props);
355
 
356
-   /*draw the label footer */
357
-   dot_str_add(dot_str, "%s", "\"\n");
358
+   /* draw the label footer */
359
+   dot_data_add_string(dd, "\"\n");
360
 
361
    /* draw all node ports */
362
    struct global *p;
363
    const char *prop_node_id;
364
    spa_list_for_each(p, &g->data->globals, link) {
365
        struct pw_port_info *pinfo;
366
+       uint32_t node_id = GLOBAL_ID_NONE;
367
        if (p->info == NULL)
368
            continue;
369
        if (p->type != INTERFACE_Port)
370
            continue;
371
        pinfo = p->info;
372
        prop_node_id = spa_dict_lookup(pinfo->props, PW_KEY_NODE_ID);
373
-       if (!prop_node_id || (uint32_t)atoi(prop_node_id) != g->id)
374
+       spa_atou32(prop_node_id, &node_id, 10);
375
+       if (node_id == GLOBAL_ID_NONE || node_id != g->id)
376
            continue;
377
        if (p->draw)
378
            p->draw(p);
379
@@ -243,18 +328,27 @@
380
 
381
    /* draw the client/factory box if all option is enabled */
382
    if (g->data->show_all) {
383
-       dot_str_add(dot_str, "node_%u shape=box style=filled fillcolor=white;\n", g->id);
384
-       dot_str_add(dot_str, "node_%u label=\"client_id: %u\\lfactory_id: %u\\l\";\n", g->id, client_id, factory_id);
385
+       dot_data_add_string(dd, "node_");
386
+       dot_data_add_uint32(dd, g->id);
387
+       dot_data_add_string(dd, " shape=box style=filled fillcolor=white;\n");
388
+       dot_data_add_string(dd, "node_");
389
+       dot_data_add_uint32(dd, g->id);
390
+       dot_data_add_string(dd, " label=\"");
391
+       if (client_id != GLOBAL_ID_NONE) {
392
+           dot_data_add_string(dd, "client_id: ");
393
+           dot_data_add_uint32(dd, client_id);
394
+           dot_data_add_string(dd, "\\l");
395
+       }
396
+       if (factory_id != GLOBAL_ID_NONE) {
397
+           dot_data_add_string(dd, "factory_id: ");
398
+           dot_data_add_uint32(dd, factory_id);
399
+           dot_data_add_string(dd, "\\l");
400
+       }
401
+       dot_data_add_string(dd, "\";\n");
402
    }
403
 
404
    /* draw the node footer */
405
-   dot_str_add(dot_str, "}\n");
406
-
407
-   /* draw the client/factory arrows if all option is enabled */
408
-   if (g->data->show_all) {
409
-       dot_str_add(dot_str, "node_%u -> client_%u style=dashed;\n", g->id, client_id);
410
-       dot_str_add(dot_str, "node_%u -> factory_%u style=dashed;\n", g->id, factory_id);
411
-   }
412
+   dot_data_add_string(dd, "}\n");
413
 }
414
 
415
 static void draw_link(struct global *g)
416
@@ -264,25 +358,46 @@
417
    spa_assert(g->type == INTERFACE_Link);
418
 
419
    struct pw_link_info *info = g->info;
420
-   char **dot_str = &g->data->dot_str;
421
+   struct dot_data *dd = &g->data->dot_data;
422
 
423
    /* draw the box */
424
-   dot_str_add(dot_str, "link_%u shape=box style=filled fillcolor=lightblue;\n", g->id);
425
-
426
-   /* draw the label */
427
-   draw_label(dot_str,
428
-       "link", g->id, g->data->show_detail, info->props,
429
-       "link_id: %u\\loutput_node_id: %u\\linput_node_id: %u\\loutput_port_id: %u\\linput_port_id: %u\\lstate: %s\\l",
430
-       g->id,
431
-       info->output_node_id,
432
-       info->input_node_id,
433
-       info->output_port_id,
434
-       info->input_port_id,
435
-       pw_link_state_as_string(info->state)
436
-   );
437
+   dot_data_add_string(dd, "link_");
438
+   dot_data_add_uint32(dd, g->id);
439
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=lightblue;\n");
440
+
441
+   /* draw the label header */
442
+   dot_data_add_string(dd, "link_");
443
+   dot_data_add_uint32(dd, g->id);
444
+   dot_data_add_string(dd, " label=\"");
445
+
446
+   /* draw the label body */
447
+   dot_data_add_string(dd, "link_id: ");
448
+   dot_data_add_uint32(dd, g->id);
449
+   dot_data_add_string(dd, "\\loutput_node_id: ");
450
+   dot_data_add_uint32(dd, info->output_node_id);
451
+   dot_data_add_string(dd, "\\linput_node_id: ");
452
+   dot_data_add_uint32(dd, info->output_node_id);
453
+   dot_data_add_string(dd, "\\loutput_port_id: ");
454
+   dot_data_add_uint32(dd, info->output_port_id);
455
+   dot_data_add_string(dd, "\\linput_node_id: ");
456
+   dot_data_add_uint32(dd, info->input_port_id);
457
+   dot_data_add_string(dd, "\\lstate: ");
458
+   dot_data_add_string_escaped(dd, pw_link_state_as_string(info->state));
459
+   dot_data_add_string(dd, "\\l");
460
+   if (g->data->show_detail)
461
+       draw_dict(dd, "properties", info->props);
462
+
463
+   /* draw the label footer */
464
+   dot_data_add_string(dd, "\";\n");
465
 
466
    /* draw the arrows */
467
-   dot_str_add(dot_str, "port_%u -> link_%u -> port_%u;\n", info->output_port_id, g->id, info->input_port_id);
468
+   dot_data_add_string(dd, "port_");
469
+   dot_data_add_uint32(dd, info->output_port_id);
470
+   dot_data_add_string(dd, " -> link_");
471
+   dot_data_add_uint32(dd, g->id);
472
+   dot_data_add_string(dd, " -> port_");
473
+   dot_data_add_uint32(dd, info->input_port_id);
474
+   dot_data_add_string(dd, ";\n");
475
 }
476
 
477
 static void draw_client(struct global *g)
478
@@ -292,19 +407,34 @@
479
    spa_assert(g->type == INTERFACE_Client);
480
 
481
    struct pw_client_info *info = g->info;
482
-   char **dot_str = &g->data->dot_str;
483
+   struct dot_data *dd = &g->data->dot_data;
484
+
485
+   const char *app_name = spa_dict_lookup(info->props, PW_KEY_APP_NAME);
486
+   const char *app_process_id = spa_dict_lookup(info->props, PW_KEY_APP_PROCESS_ID);
487
 
488
    /* draw the box */
489
-   dot_str_add(dot_str, "client_%u shape=box style=filled fillcolor=tan1;\n", g->id);
490
-
491
-   /* draw the label */
492
-   draw_label(dot_str,
493
-       "client", g->id, g->data->show_detail, info->props,
494
-       "client_id: %u\\lname: %s\\lpid: %s\\l",
495
-       g->id,
496
-       spa_dict_lookup(info->props, PW_KEY_APP_NAME),
497
-       spa_dict_lookup(info->props, PW_KEY_APP_PROCESS_ID)
498
-   );
499
+   dot_data_add_string(dd, "client_");
500
+   dot_data_add_uint32(dd, g->id);
501
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=tan1;\n");
502
+
503
+   /* draw the label header */
504
+   dot_data_add_string(dd, "client_");
505
+   dot_data_add_uint32(dd, g->id);
506
+   dot_data_add_string(dd, " label=\"");
507
+
508
+   /* draw the label body */
509
+   dot_data_add_string(dd, "client_id: ");
510
+   dot_data_add_uint32(dd, g->id);
511
+   dot_data_add_string(dd, "\\lname: ");
512
+   dot_data_add_string_escaped(dd, app_name ? app_name : "(null)");
513
+   dot_data_add_string(dd, "\\lpid: ");
514
+   dot_data_add_string(dd, app_process_id ? app_process_id : "(null)");
515
+   dot_data_add_string(dd, "\\l");
516
+   if (g->data->show_detail)
517
+       draw_dict(dd, "properties", info->props);
518
+
519
+   /* draw the label footer */
520
+   dot_data_add_string(dd, "\";\n");
521
 }
522
 
523
 static void draw_device(struct global *g)
524
@@ -314,30 +444,62 @@
525
    spa_assert(g->type == INTERFACE_Device);
526
 
527
    struct pw_device_info *info = g->info;
528
-   char **dot_str = &g->data->dot_str;
529
+   struct dot_data *dd = &g->data->dot_data;
530
 
531
+   const char *app_name = spa_dict_lookup(info->props, PW_KEY_APP_NAME);
532
+   const char *media_class = spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS);
533
+   const char *device_api = spa_dict_lookup(info->props, PW_KEY_DEVICE_API);
534
+   const char *path = spa_dict_lookup(info->props, PW_KEY_OBJECT_PATH);
535
    const char *client_id_str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID);
536
    const char *factory_id_str = spa_dict_lookup(info->props, PW_KEY_FACTORY_ID);
537
-   uint32_t client_id = client_id_str ? (uint32_t)atoi(client_id_str) : GLOBAL_ID_NONE;
538
-   uint32_t factory_id = factory_id_str ? (uint32_t)atoi(factory_id_str) : GLOBAL_ID_NONE;
539
+   uint32_t client_id = GLOBAL_ID_NONE, factory_id = GLOBAL_ID_NONE;
540
+
541
+   spa_atou32(client_id_str, &client_id, 10);
542
+   spa_atou32(factory_id_str, &factory_id, 10);
543
 
544
    /* draw the box */
545
-   dot_str_add(dot_str, "device_%u shape=box style=filled fillcolor=lightpink;\n", g->id);
546
-
547
-   /* draw the label */
548
-   draw_label(dot_str,
549
-       "device", g->id, g->data->show_detail, info->props,
550
-       "device_id: %u\\lname: %s\\lmedia_class: %s\\lapi: %s\\lpath: %s\\l",
551
-       g->id,
552
-       spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME),
553
-       spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS),
554
-       spa_dict_lookup(info->props, PW_KEY_DEVICE_API),
555
-       spa_dict_lookup(info->props, PW_KEY_OBJECT_PATH)
556
-   );
557
+   dot_data_add_string(dd, "device_");
558
+   dot_data_add_uint32(dd, g->id);
559
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=lightpink;\n");
560
+
561
+   /* draw the label header */
562
+   dot_data_add_string(dd, "device_");
563
+   dot_data_add_uint32(dd, g->id);
564
+   dot_data_add_string(dd, " label=\"");
565
+
566
+   /* draw the label body */
567
+   dot_data_add_string(dd, "device_id: ");
568
+   dot_data_add_uint32(dd, g->id);
569
+   dot_data_add_string(dd, "\\lname: ");
570
+   dot_data_add_string_escaped(dd, app_name ? app_name : "(null)");
571
+   dot_data_add_string(dd, "\\lmedia_class: ");
572
+   dot_data_add_string_escaped(dd, media_class ? media_class : "(null)");
573
+   dot_data_add_string(dd, "\\lapi: ");
574
+   dot_data_add_string_escaped(dd, device_api ? device_api : "(null)");
575
+   dot_data_add_string(dd, "\\lpath: ");
576
+   dot_data_add_string_escaped(dd, path ? path : "(null)");
577
+   dot_data_add_string(dd, "\\l");
578
+   if (g->data->show_detail)
579
+       draw_dict(dd, "properties", info->props);
580
+
581
+   /* draw the label footer */
582
+   dot_data_add_string(dd, "\";\n");
583
 
584
    /* draw the arrows */
585
-   dot_str_add(dot_str, "device_%u -> client_%u style=dashed;\n", g->id, client_id);
586
-   dot_str_add(dot_str, "device_%u -> factory_%u style=dashed;\n", g->id, factory_id);
587
+   if (client_id != GLOBAL_ID_NONE) {
588
+       dot_data_add_string(dd, "device_");
589
+       dot_data_add_uint32(dd, g->id);
590
+       dot_data_add_string(dd, " -> client_");
591
+       dot_data_add_uint32(dd, client_id);
592
+       dot_data_add_string(dd, " style=dashed;\n");
593
+   }
594
+   if (factory_id != GLOBAL_ID_NONE) {
595
+       dot_data_add_string(dd, "device_");
596
+       dot_data_add_uint32(dd, g->id);
597
+       dot_data_add_string(dd, " -> factory_");
598
+       dot_data_add_uint32(dd, factory_id);
599
+       dot_data_add_string(dd, " style=dashed;\n");
600
+   }
601
 }
602
 
603
 static void draw_factory(struct global *g)
604
@@ -347,23 +509,46 @@
605
    spa_assert(g->type == INTERFACE_Factory);
606
 
607
    struct pw_factory_info *info = g->info;
608
-   char **dot_str = &g->data->dot_str;
609
-
610
+   struct dot_data *dd = &g->data->dot_data;
611
    const char *module_id_str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID);
612
-   uint32_t module_id = module_id_str ? (uint32_t)atoi(module_id_str) : GLOBAL_ID_NONE;
613
+   uint32_t module_id = GLOBAL_ID_NONE;
614
+
615
+   spa_atou32(module_id_str, &module_id, 10);
616
 
617
    /* draw the box */
618
-   dot_str_add(dot_str, "factory_%u shape=box style=filled fillcolor=lightyellow;\n", g->id);
619
+   dot_data_add_string(dd, "factory_");
620
+   dot_data_add_uint32(dd, g->id);
621
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=lightyellow;\n");
622
 
623
-   /* draw the label */
624
-   draw_label(dot_str,
625
-       "factory", g->id, g->data->show_detail, info->props,
626
-       "factory_id: %u\\lname: %s\\lmodule_id: %u\\l",
627
-       g->id, info->name, module_id
628
-   );
629
+   /* draw the label header */
630
+   dot_data_add_string(dd, "factory_");
631
+   dot_data_add_uint32(dd, g->id);
632
+   dot_data_add_string(dd, " label=\"");
633
+
634
+   /* draw the label body */
635
+   dot_data_add_string(dd, "factory_id: ");
636
+   dot_data_add_uint32(dd, g->id);
637
+   dot_data_add_string(dd, "\\lname: ");
638
+   dot_data_add_string_escaped(dd, info->name);
639
+   if (module_id != GLOBAL_ID_NONE) {
640
+       dot_data_add_string(dd, "\\lmodule_id: ");
641
+       dot_data_add_uint32(dd, module_id);
642
+       dot_data_add_string(dd, "\\l");
643
+   }
644
+   if (g->data->show_detail)
645
+       draw_dict(dd, "properties", info->props);
646
+
647
+   /* draw the label footer */
648
+   dot_data_add_string(dd, "\";\n");
649
 
650
    /* draw the arrow */
651
-   dot_str_add(dot_str, "factory_%u -> module_%u style=dashed;\n", g->id, module_id);
652
+   if (module_id != GLOBAL_ID_NONE) {
653
+       dot_data_add_string(dd, "factory_");
654
+       dot_data_add_uint32(dd, g->id);
655
+       dot_data_add_string(dd, " -> module_");
656
+       dot_data_add_uint32(dd, module_id);
657
+       dot_data_add_string(dd, " style=dashed;\n");
658
+   }
659
 }
660
 
661
 static void draw_module(struct global *g)
662
@@ -373,51 +558,128 @@
663
    spa_assert(g->type == INTERFACE_Module);
664
 
665
    struct pw_module_info *info = g->info;
666
-   char **dot_str = &g->data->dot_str;
667
+   struct dot_data *dd = &g->data->dot_data;
668
 
669
    /* draw the box */
670
-   dot_str_add(dot_str, "module_%u shape=box style=filled fillcolor=lightgrey;\n", g->id);
671
-
672
-   /* draw the label */
673
-   draw_label(dot_str,
674
-       "module", g->id, g->data->show_detail, info->props,
675
-       "module_id: %u\\lname: %s\\l",
676
-       g->id, info->name
677
-   );
678
+   dot_data_add_string(dd, "module_");
679
+   dot_data_add_uint32(dd, g->id);
680
+   dot_data_add_string(dd, " shape=box style=filled fillcolor=lightgrey;\n");
681
+
682
+   /* draw the label header */
683
+   dot_data_add_string(dd, "module_");
684
+   dot_data_add_uint32(dd, g->id);
685
+   dot_data_add_string(dd, " label=\"");
686
+
687
+   /* draw the label body */
688
+   dot_data_add_string(dd, "module_id: ");
689
+   dot_data_add_uint32(dd, g->id);
690
+   dot_data_add_string(dd, "\\lname: ");
691
+   dot_data_add_string_escaped(dd, info->name);
692
+   dot_data_add_string(dd, "\\l");
693
+   if (g->data->show_detail)
694
+       draw_dict(dd, "properties", info->props);
695
+
696
+   /* draw the label footer */
697
+   dot_data_add_string(dd, "\";\n");
698
+}
699
+
700
+static void draw_group(struct data *d, const char *group)
701
+{
702
+   struct spa_list *globals = &d->globals;
703
+   struct dot_data *dd = &d->dot_data;
704
+   static uint32_t group_id = 0;
705
+
706
+   /* draw the group header */
707
+   dot_data_add_string(dd, "subgraph cluster_group_");
708
+   dot_data_add_uint32(dd, group_id++);
709
+   dot_data_add_string(dd, "{\n");
710
+   dot_data_add_string(dd, "bgcolor=lavender;\n");
711
+
712
+   /* draw the label header */
713
+   dot_data_add_string(dd, "label=\"");
714
+
715
+   /* draw the label body */
716
+   dot_data_add_string(dd, group);
717
+   dot_data_add_string(dd, "\\l");
718
+
719
+   /* draw the label footer */
720
+   dot_data_add_string(dd, "\"\n");
721
+
722
+   /* draw all nodes for this group */
723
+   struct global *n;
724
+   const char *node_link_group;
725
+   spa_list_for_each(n, globals, link) {
726
+       struct pw_node_info *info;
727
+       if (n->info == NULL)
728
+           continue;
729
+       if (n->type != INTERFACE_Node)
730
+           continue;
731
+       if (n->drawn)
732
+           continue;
733
+       info = n->info;
734
+       node_link_group = spa_dict_lookup(info->props, PW_KEY_NODE_LINK_GROUP);
735
+       if (node_link_group == NULL || !spa_streq (node_link_group, group))
736
+           continue;
737
+       if (n->draw) {
738
+           n->draw(n);
739
+           n->drawn = true;
740
+       }
741
+   }
742
+
743
+   /* draw the group footer */
744
+   dot_data_add_string(dd, "}\n");
745
+
746
+   /* draw the client/factory arrows if all option is enabled */
747
+   if (d->show_all) {
748
+       spa_list_for_each(n, globals, link) {
749
+           struct pw_node_info *info;
750
+           if (n->info == NULL)
751
+               continue;
752
+           if (n->type != INTERFACE_Node)
753
+               continue;
754
+           info = n->info;
755
+           node_link_group = spa_dict_lookup(info->props, PW_KEY_NODE_LINK_GROUP);
756
+           if (node_link_group == NULL || !spa_streq (node_link_group, group))
757
+               continue;
758
+           draw_node_arrows(n);
759
+       }
760
+   }
761
 }
762
 
763
 static bool is_node_id_link_referenced(uint32_t id, struct spa_list *globals)
764
 {
765
-        struct global *g;
766
-        struct pw_link_info *info;
767
-        spa_list_for_each(g, globals, link) {
768
-                if (g->info == NULL)
769
-                        continue;
770
-                if (g->type != INTERFACE_Link)
771
-                        continue;
772
-                info = g->info;
773
-                if (info->input_node_id == id || info->output_node_id == id)
774
-                        return true;
775
-        }
776
-        return false;
777
+   struct global *g;
778
+   struct pw_link_info *info;
779
+   spa_list_for_each(g, globals, link) {
780
+       if (g->info == NULL)
781
+           continue;
782
+       if (g->type != INTERFACE_Link)
783
+           continue;
784
+       info = g->info;
785
+       if (info->input_node_id == id || info->output_node_id == id)
786
+           return true;
787
+   }
788
+   return false;
789
 }
790
 
791
 static bool is_module_id_factory_referenced(uint32_t id, struct spa_list *globals)
792
 {
793
-        struct global *g;
794
-        struct pw_factory_info *info;
795
-        const char *module_id_str;
796
-        spa_list_for_each(g, globals, link) {
797
-                if (g->info == NULL)
798
-                        continue;
799
-                if (g->type != INTERFACE_Factory)
800
-                        continue;
801
-                info = g->info;
802
-                module_id_str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID);
803
-                if (module_id_str && (uint32_t)atoi(module_id_str) == id)
804
-                        return true;
805
-        }
806
-        return false;
807
+   struct global *g;
808
+   struct pw_factory_info *info;
809
+   const char *module_id_str;
810
+   spa_list_for_each(g, globals, link) {
811
+       uint32_t module_id = GLOBAL_ID_NONE;
812
+       if (g->info == NULL)
813
+           continue;
814
+       if (g->type != INTERFACE_Factory)
815
+           continue;
816
+       info = g->info;
817
+       module_id_str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID);
818
+       spa_atou32(module_id_str, &module_id, 10);
819
+       if (module_id != GLOBAL_ID_NONE && module_id == id)
820
+           return true;
821
+   }
822
+   return false;
823
 }
824
 
825
 static bool is_global_referenced(struct global *g)
826
@@ -440,16 +702,18 @@
827
    struct global *g;
828
 
829
    /* draw the header */
830
-   dot_str_add(&d->dot_str, "digraph pipewire {\n");
831
+   dot_data_add_string(&d->dot_data, "digraph pipewire {\n");
832
 
833
    if (d->dot_rankdir) {
834
        /* set rank direction, if provided */
835
-       dot_str_add(&d->dot_str, "rankdir = \"%s\";\n", d->dot_rankdir);
836
+       dot_data_add_string(&d->dot_data, "rankdir = \"");
837
+       dot_data_add_string(&d->dot_data, d->dot_rankdir);
838
+       dot_data_add_string(&d->dot_data, "\";\n");
839
    }
840
 
841
    if (d->dot_orthoedges) {
842
        /* enable orthogonal edges */
843
-       dot_str_add(&d->dot_str, "splines = ortho;\n");
844
+       dot_data_add_string(&d->dot_data, "splines = ortho;\n");
845
    }
846
 
847
    /* iterate the globals */
848
@@ -479,17 +743,37 @@
849
        if (d->show_smart && !is_global_referenced(g))
850
            continue;
851
 
852
+       /* skip already drawn globals */
853
+       if (g->drawn)
854
+           continue;
855
+
856
+       /* Draw groups (nodes with node.link-group property) */
857
+       if (g->type == INTERFACE_Node) {
858
+           struct pw_node_info *info = g->info;
859
+           const char *group = spa_dict_lookup(info->props, PW_KEY_NODE_LINK_GROUP);
860
+           if (group != NULL) {
861
+               draw_group (d, group);
862
+               continue;
863
+           }
864
+       }
865
+
866
        /* draw the global */
867
-       if (g->draw)
868
+       if (g->draw) {
869
            g->draw(g);
870
+           g->drawn = true;
871
+       }
872
+
873
+       /* Draw the node arrows */
874
+       if (d->show_all && g->type == INTERFACE_Node)
875
+           draw_node_arrows (g);
876
    }
877
 
878
    /* draw the footer */
879
-   dot_str_add(&d->dot_str, "}\n");
880
+   dot_data_add_string(&d->dot_data, "}\n");
881
 
882
    if (spa_streq(path, "-")) {
883
        /* wire the dot graph into to stdout */
884
-       fputs(d->dot_str, stdout);
885
+       fputs(d->dot_data.data, stdout);
886
    } else {
887
        /* open the file */
888
        fp = fopen(path, "we");
889
@@ -499,7 +783,7 @@
890
        }
891
 
892
        /* wire the dot graph into the file */
893
-       fputs(d->dot_str, fp);
894
+       fputs(d->dot_data.data, fp);
895
        fclose(fp);
896
    }
897
    return 0;
898
@@ -507,18 +791,18 @@
899
 
900
 static void global_event_info(struct global *g, const void *info)
901
 {
902
-        if (g->info_update)
903
-                g->info = g->info_update(g->info, info);
904
+   if (g->info_update)
905
+       g->info = g->info_update(g->info, info);
906
 }
907
 
908
 static void port_event_info(void *data, const struct pw_port_info *info)
909
 {
910
-        global_event_info(data, info);
911
+   global_event_info(data, info);
912
 }
913
 
914
 static const struct pw_port_events port_events = {
915
-        PW_VERSION_PORT_EVENTS,
916
-        .info = port_event_info,
917
+   PW_VERSION_PORT_EVENTS,
918
+   .info = port_event_info,
919
 };
920
 
921
 static void node_event_info(void *data, const struct pw_node_info *info)
922
@@ -604,15 +888,15 @@
923
                  const char *type, uint32_t version,
924
                  const struct spa_dict *props)
925
 {
926
-        struct data *d = data;
927
-        struct pw_proxy *proxy;
928
-        uint32_t client_version;
929
+   struct data *d = data;
930
+   struct pw_proxy *proxy;
931
+   uint32_t client_version;
932
    uint32_t object_type;
933
-        const void *events;
934
-        pw_destroy_t info_destroy;
935
-        info_update_t info_update;
936
-        draw_t draw;
937
-        struct global *g;
938
+   const void *events;
939
+   pw_destroy_t info_destroy;
940
+   info_update_t info_update;
941
+   draw_t draw;
942
+   struct global *g;
943
 
944
    if (spa_streq(type, PW_TYPE_INTERFACE_Port)) {
945
        events = &port_events;
946
@@ -696,11 +980,11 @@
947
    g->info_update = info_update;
948
    g->draw = draw;
949
 
950
-        pw_proxy_add_object_listener(proxy, &g->object_listener, events, g);
951
-        pw_proxy_add_listener(proxy, &g->proxy_listener, &proxy_events, g);
952
+   pw_proxy_add_object_listener(proxy, &g->object_listener, events, g);
953
+   pw_proxy_add_listener(proxy, &g->proxy_listener, &proxy_events, g);
954
 
955
-        /* add the global to the list */
956
-        spa_list_insert(&d->globals, &g->link);
957
+   /* add the global to the list */
958
+   spa_list_insert(&d->globals, &g->link);
959
 }
960
 
961
 static const struct pw_registry_events registry_events = {
962
@@ -808,6 +1092,7 @@
963
 
964
    g = calloc(1, sizeof (struct global));
965
    g->data = data;
966
+   g->draw = false;
967
 
968
    if (spa_streq(str, PW_TYPE_INTERFACE_Port)) {
969
        g->info_destroy = (pw_destroy_t)pw_port_info_free;
970
@@ -993,6 +1278,7 @@
971
    struct stat sbuf;
972
    struct spa_json it2;
973
    const char *value;
974
+   struct spa_error_location loc;
975
 
976
    if ((fd = open(json_path,  O_CLOEXEC | O_RDONLY)) < 0) {
977
        fprintf(stderr, "error opening file '%s': %m\n", json_path);
978
@@ -1028,12 +1314,19 @@
979
    }
980
 
981
    munmap(json, sbuf.st_size);
982
+
983
+   if (spa_json_get_error(&it0, json, &loc)) {
984
+       spa_debug_file_error_location(stderr, &loc,
985
+               "JSON syntax error: %s\n", loc.reason);
986
+       return -1;
987
+   }
988
+
989
    return 0;
990
 }
991
 
992
 static void show_help(const char *name, bool error)
993
 {
994
-        fprintf(error ? stderr : stdout, "%s options\n"
995
+   fprintf(error ? stderr : stdout, "%s options\n"
996
        "  -h, --help                            Show this help\n"
997
        "      --version                         Show version\n"
998
        "  -a, --all                             Show all object types\n"
999
@@ -1124,7 +1417,7 @@
1000
        }
1001
    }
1002
 
1003
-   if (!(data.dot_str = dot_str_new()))
1004
+   if (!dot_data_init(&data.dot_data, DEFAULT_DOT_DATA_SIZE))
1005
        return -1;
1006
 
1007
    spa_list_init(&data.globals);
1008
@@ -1136,7 +1429,7 @@
1009
 
1010
    draw_graph(&data, dot_path);
1011
 
1012
-   dot_str_clear(&data.dot_str);
1013
+   dot_data_clear(&data.dot_data);
1014
    spa_list_consume(g, &data.globals, link) {
1015
        if (g->info && g->info_destroy)
1016
            g->info_destroy(g->info);
1017
pipewire-1.0.1.tar.bz2/src/tools/pw-dump.c -> pipewire-1.2.0.tar.gz/src/tools/pw-dump.c Changed
132
 
1
@@ -279,7 +279,7 @@
2
 {
3
    char buf128;
4
    put_fmt(d, key, "%s%s%s", NUMBER,
5
-           spa_json_format_float(buf, sizeof(buf), val), NORMAL);
6
+           spa_json_format_float(buf, sizeof(buf), (float)val), NORMAL);
7
 }
8
 
9
 static void put_value(struct data *d, const char *key, const char *val)
10
@@ -349,7 +349,7 @@
11
        break;
12
    case SPA_TYPE_Rectangle:
13
    {
14
-                struct spa_rectangle *r = (struct spa_rectangle *)body;
15
+       struct spa_rectangle *r = (struct spa_rectangle *)body;
16
        put_begin(d, NULL, "{", STATE_SIMPLE);
17
        put_int(d, "width", r->width);
18
        put_int(d, "height", r->height);
19
@@ -358,7 +358,7 @@
20
    }
21
    case SPA_TYPE_Fraction:
22
    {
23
-                struct spa_fraction *f = (struct spa_fraction *)body;
24
+       struct spa_fraction *f = (struct spa_fraction *)body;
25
        put_begin(d, NULL, "{", STATE_SIMPLE);
26
        put_int(d, "num", f->num);
27
        put_int(d, "denom", f->denom);
28
@@ -436,7 +436,7 @@
29
        break;
30
    }
31
    case SPA_TYPE_Object:
32
-        {
33
+   {
34
        put_begin(d, NULL, "{", 0);
35
        struct spa_pod_object_body *b = (struct spa_pod_object_body *)body;
36
        struct spa_pod_prop *p;
37
@@ -1231,6 +1231,7 @@
38
        e->changed++;
39
    }
40
    o->changed++;
41
+   core_sync(o->data);
42
    return 0;
43
 }
44
 
45
@@ -1299,13 +1300,13 @@
46
        if (o->class->destroy)
47
                    o->class->destroy(o);
48
    }
49
-        o->proxy = NULL;
50
+   o->proxy = NULL;
51
 }
52
 
53
 static const struct pw_proxy_events proxy_events = {
54
-        PW_VERSION_PROXY_EVENTS,
55
-        .removed = destroy_removed,
56
-        .destroy = destroy_proxy,
57
+   PW_VERSION_PROXY_EVENTS,
58
+   .removed = destroy_removed,
59
+   .destroy = destroy_proxy,
60
 };
61
 
62
 static void registry_event_global(void *data, uint32_t id,
63
@@ -1329,6 +1330,7 @@
64
    spa_list_init(&o->param_list);
65
    spa_list_init(&o->pending_list);
66
    spa_list_init(&o->data_list);
67
+   spa_list_append(&d->object_list, &o->link);
68
 
69
    o->class = find_class(type, version);
70
    if (o->class != NULL) {
71
@@ -1350,15 +1352,13 @@
72
    } else {
73
        o->changed++;
74
    }
75
-   spa_list_append(&d->object_list, &o->link);
76
 
77
    core_sync(d);
78
    return;
79
 
80
 bind_failed:
81
    pw_log_error("can't bind object for %u %s/%d: %m", id, type, version);
82
-   pw_properties_free(o->props);
83
-   free(o);
84
+   object_destroy(o);
85
    return;
86
 }
87
 
88
@@ -1396,20 +1396,20 @@
89
    if ((o = find_object(d, id)) == NULL)
90
        return;
91
 
92
-   d->state = STATE_FIRST;
93
-   if (d->pattern != NULL && !object_matches(o, d->pattern))
94
-       return;
95
-   if (d->state == STATE_FIRST)
96
-       put_begin(d, NULL, "", 0);
97
-   put_begin(d, NULL, "{", 0);
98
-   put_int(d, "id", o->id);
99
-   if (o->class && o->class->dump)
100
-       put_value(d, "info", NULL);
101
-   else if (o->props)
102
-       put_value(d, "props", NULL);
103
-   put_end(d, "}", 0);
104
-   if (d->state != STATE_FIRST)
105
-       put_end(d, "\n", 0);
106
+   if (!d->pattern || object_matches(o, d->pattern)) {
107
+       d->state = STATE_FIRST;
108
+       if (d->state == STATE_FIRST)
109
+           put_begin(d, NULL, "", 0);
110
+       put_begin(d, NULL, "{", 0);
111
+       put_int(d, "id", o->id);
112
+       if (o->class && o->class->dump)
113
+           put_value(d, "info", NULL);
114
+       else if (o->props)
115
+           put_value(d, "props", NULL);
116
+       put_end(d, "}", 0);
117
+       if (d->state != STATE_FIRST)
118
+           put_end(d, "\n", 0);
119
+   }
120
 
121
    object_destroy(o);
122
 }
123
@@ -1541,7 +1541,7 @@
124
    pw_init(&argc, &argv);
125
 
126
    data.out = stdout;
127
-   if (isatty(fileno(data.out)) && getenv("NO_COLOR") == NULL)
128
+   if (getenv("NO_COLOR") == NULL && isatty(fileno(data.out)))
129
        colors = true;
130
    setlinebuf(data.out);
131
 
132
pipewire-1.0.1.tar.bz2/src/tools/pw-link.c -> pipewire-1.2.0.tar.gz/src/tools/pw-link.c Changed
1004
 
1
@@ -9,40 +9,74 @@
2
 #include <regex.h>
3
 #include <locale.h>
4
 
5
+#include <spa/utils/cleanup.h>
6
 #include <spa/utils/result.h>
7
 #include <spa/utils/string.h>
8
 #include <spa/utils/defs.h>
9
+#include <spa/debug/file.h>
10
 
11
 #include <pipewire/pipewire.h>
12
 #include <pipewire/filter.h>
13
 
14
+enum object_type {
15
+   OBJECT_ANY,
16
+   OBJECT_NODE,
17
+   OBJECT_PORT,
18
+   OBJECT_LINK,
19
+};
20
+
21
+union object_data {
22
+   struct {
23
+       enum pw_direction direction;
24
+       uint32_t node;
25
+   } port;
26
+   struct {
27
+       uint32_t output_port;
28
+       uint32_t input_port;
29
+   } link;
30
+};
31
+
32
 struct object {
33
    struct spa_list link;
34
 
35
    uint32_t id;
36
-#define OBJECT_ANY 0
37
-#define OBJECT_NODE    1
38
-#define OBJECT_PORT    2
39
-#define OBJECT_LINK    3
40
-   uint32_t type;
41
+   enum object_type type;
42
    struct pw_properties *props;
43
-   uint32_t extra2;
44
+   union object_data data;
45
+};
46
+
47
+struct target_link {
48
+   struct spa_list link;
49
+   struct data *data;
50
+   struct pw_proxy *proxy;
51
+   struct spa_hook listener, link_listener;
52
+   enum pw_link_state state;
53
+   int result;
54
+};
55
+
56
+enum mode {
57
+   MODE_CONNECT,
58
+   MODE_DISCONNECT,
59
+   MODE_LIST,
60
+};
61
+
62
+enum list_target {
63
+   LIST_OUTPUT = 1 << 0,
64
+   LIST_INPUT = 1 << 1,
65
+   LIST_PORTS = LIST_OUTPUT | LIST_INPUT,
66
+   LIST_LINKS = 1 << 2,
67
 };
68
 
69
 struct data {
70
    struct pw_main_loop *loop;
71
 
72
    const char *opt_remote;
73
-#define MODE_LIST_OUTPUT   (1<<0)
74
-#define MODE_LIST_INPUT        (1<<1)
75
-#define MODE_LIST_PORTS        (MODE_LIST_OUTPUT|MODE_LIST_INPUT)
76
-#define MODE_LIST_LINKS        (1<<2)
77
-#define MODE_LIST      (MODE_LIST_PORTS|MODE_LIST_LINKS)
78
-#define MODE_MONITOR       (1<<3)
79
-#define MODE_DISCONNECT        (1<<4)
80
-   uint32_t opt_mode;
81
+   enum mode opt_mode;
82
+   enum list_target opt_list; /* for `MODE_LIST` */
83
    bool opt_id;
84
    bool opt_verbose;
85
+   bool opt_wait;
86
+   bool opt_monitor;
87
    const char *opt_output;
88
    const char *opt_input;
89
    struct pw_properties *props;
90
@@ -56,9 +90,11 @@
91
    struct spa_hook registry_listener;
92
 
93
    struct spa_list objects;
94
+   struct spa_list target_links;
95
 
96
    int sync;
97
-   int link_res;
98
+   int nb_links;
99
+   bool new_object;
100
    bool monitoring;
101
    bool list_inputs;
102
    bool list_outputs;
103
@@ -68,51 +104,86 @@
104
    regex_t in_port_regex, *in_regex;
105
 };
106
 
107
-static void link_proxy_error(void *data, int seq, int res, const char *message)
108
+static void link_event(struct target_link *tl, enum pw_link_state state, int result)
109
 {
110
-   struct data *d = data;
111
-   d->link_res = res;
112
-}
113
+   /* Ignore non definitive states (negotiating, allocating, etc). */
114
+   if (state != PW_LINK_STATE_ERROR &&
115
+       state != PW_LINK_STATE_PAUSED &&
116
+       state != PW_LINK_STATE_ACTIVE)
117
+       return;
118
 
119
-static const struct pw_proxy_events link_proxy_events = {
120
-   PW_VERSION_PROXY_EVENTS,
121
-   .error = link_proxy_error,
122
-};
123
+   /* Keep the first definitive state. For example, once a link is marked
124
+    * as paused, we start ignoring all errors. */
125
+   if (tl->state == PW_LINK_STATE_INIT) {
126
+       tl->state = state;
127
+       tl->result = result;
128
+   }
129
 
130
-static void core_sync(struct data *data)
131
+   /* End if all links have a definitive state. */
132
+   struct target_link *m;
133
+   spa_list_for_each(m, &tl->data->target_links, link) {
134
+       if (m->state == PW_LINK_STATE_INIT)
135
+           return;
136
+   }
137
+   pw_main_loop_quit(tl->data->loop);
138
+}
139
+
140
+static void link_proxy_destroy(void *data)
141
 {
142
-   data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync);
143
+   struct target_link *tl = data;
144
+
145
+   spa_hook_remove(&tl->listener);
146
+   spa_hook_remove(&tl->link_listener);
147
+   tl->proxy = NULL;
148
+
149
+   link_event(tl, PW_LINK_STATE_ERROR, -EINVAL);
150
 }
151
 
152
-static int create_link(struct data *data)
153
+static void link_proxy_removed(void *data)
154
 {
155
-   struct pw_proxy *proxy;
156
-   struct spa_hook listener;
157
+   struct target_link *tl = data;
158
+   pw_proxy_destroy(tl->proxy);
159
+}
160
 
161
-   data->link_res = 0;
162
+static void link_proxy_error(void *data, int seq, int res, const char *message)
163
+{
164
+   struct target_link *tl = data;
165
+   link_event(tl, PW_LINK_STATE_ERROR, res);
166
+}
167
 
168
-   proxy = pw_core_create_object(data->core,
169
-           "link-factory",
170
-           PW_TYPE_INTERFACE_Link,
171
-           PW_VERSION_LINK,
172
-           &data->props->dict, 0);
173
-   if (proxy == NULL)
174
-       return -errno;
175
+static const struct pw_proxy_events link_proxy_events = {
176
+   PW_VERSION_PROXY_EVENTS,
177
+   .destroy = link_proxy_destroy,
178
+   .removed = link_proxy_removed,
179
+   .error = link_proxy_error,
180
+};
181
 
182
-   spa_zero(listener);
183
-   pw_proxy_add_listener(proxy, &listener, &link_proxy_events, data);
184
+static void link_event_info(void *data, const struct pw_link_info *info)
185
+{
186
+   struct target_link *tl = data;
187
+   int result = 0;
188
 
189
-   core_sync(data);
190
-   pw_main_loop_run(data->loop);
191
+   /*
192
+    * Invent an error code to always have one if state == error. That does
193
+    * not occur currently; on link error a proxy error is raised first.
194
+    */
195
+   if (tl->state == PW_LINK_STATE_ERROR)
196
+       result = -EINVAL;
197
 
198
-        spa_hook_remove(&listener);
199
+   link_event(tl, info->state, result);
200
+}
201
 
202
-        pw_proxy_destroy(proxy);
203
+static const struct pw_link_events link_events = {
204
+   PW_VERSION_LINK_EVENTS,
205
+   .info = link_event_info,
206
+};
207
 
208
-   return data->link_res;
209
+static void core_sync(struct data *data)
210
+{
211
+   data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync);
212
 }
213
 
214
-static struct object *find_object(struct data *data, uint32_t type, uint32_t id)
215
+static struct object *find_object(struct data *data, enum object_type type, uint32_t id)
216
 {
217
    struct object *o;
218
    spa_list_for_each(o, &data->objects, link)
219
@@ -129,9 +200,9 @@
220
        const char *o_port_id;
221
        if (o->type != OBJECT_PORT)
222
            continue;
223
-       if (o->extra1 != node->id)
224
+       if (o->data.port.node != node->id)
225
            continue;
226
-       if (o->extra0 != direction)
227
+       if (o->data.port.direction != direction)
228
            continue;
229
        if ((o_port_id = pw_properties_get(o->props, PW_KEY_PORT_ID)) == NULL)
230
            continue;
231
@@ -147,8 +218,9 @@
232
    const char *name;
233
    buffer0 = '\0';
234
    if ((name = pw_properties_get(n->props, PW_KEY_NODE_NAME)) == NULL)
235
-       return buffer;
236
-   snprintf(buffer, size, "%s", name);
237
+       snprintf(buffer, size, "node.id.%d", n->id);
238
+   else
239
+       snprintf(buffer, size, "%s", name);
240
    return buffer;
241
 }
242
 
243
@@ -157,8 +229,9 @@
244
    const char *name;
245
    buffer0 = '\0';
246
    if ((name = pw_properties_get(n->props, PW_KEY_OBJECT_PATH)) == NULL)
247
-       return buffer;
248
-   snprintf(buffer, size, "%s", name);
249
+       snprintf(buffer, size, "node.path.%d", n->id);
250
+   else
251
+       snprintf(buffer, size, "%s", name);
252
    return buffer;
253
 }
254
 
255
@@ -166,11 +239,16 @@
256
 {
257
    const char *name1, *name2;
258
    buffer0 = '\0';
259
-   if ((name1 = pw_properties_get(n->props, PW_KEY_NODE_NAME)) == NULL)
260
-       return buffer;
261
-   if ((name2 = pw_properties_get(p->props, PW_KEY_PORT_NAME)) == NULL)
262
-       return buffer;
263
-   snprintf(buffer, size, "%s:%s", name1, name2);
264
+   name1 = pw_properties_get(n->props, PW_KEY_NODE_NAME);
265
+   name2 = pw_properties_get(p->props, PW_KEY_PORT_NAME);
266
+   if (name1 && name2)
267
+       snprintf(buffer, size, "%s:%s", name1, name2);
268
+   else if (name1)
269
+       snprintf(buffer, size, "%s:port.id.%d", name1, p->id);
270
+   else if (name2)
271
+       snprintf(buffer, size, "node.id.%d:%s", n->id, name2);
272
+   else
273
+       snprintf(buffer, size, "node.id.%d:port.id.%d", n->id, p->id);
274
    return buffer;
275
 }
276
 
277
@@ -179,8 +257,9 @@
278
    const char *name;
279
    buffer0 = '\0';
280
    if ((name = pw_properties_get(p->props, PW_KEY_OBJECT_PATH)) == NULL)
281
-       return buffer;
282
-   snprintf(buffer, size, "%s", name);
283
+       snprintf(buffer, size, "port.path.%d", p->id);
284
+   else
285
+       snprintf(buffer, size, "%s", name);
286
    return buffer;
287
 }
288
 
289
@@ -189,8 +268,9 @@
290
    const char *name;
291
    buffer0 = '\0';
292
    if ((name = pw_properties_get(p->props, PW_KEY_PORT_ALIAS)) == NULL)
293
-       return buffer;
294
-   snprintf(buffer, size, "%s", name);
295
+       snprintf(buffer, size, "port_alias.%d", p->id);
296
+   else
297
+       snprintf(buffer, size, "%s", name);
298
    return buffer;
299
 }
300
 
301
@@ -222,7 +302,7 @@
302
    struct object *n, *p;
303
    if ((p = find_object(data, OBJECT_PORT, peer)) == NULL)
304
        return;
305
-   if ((n = find_object(data, OBJECT_NODE, p->extra1)) == NULL)
306
+   if ((n = find_object(data, OBJECT_NODE, p->data.port.node)) == NULL)
307
        return;
308
    print_port(data, prefix, n, p, false);
309
 }
310
@@ -232,7 +312,7 @@
311
    struct object *o;
312
    bool first = false;
313
 
314
-   if ((data->opt_mode & MODE_LIST_PORTS) == 0)
315
+   if (!(data->opt_list & LIST_PORTS))
316
        first = true;
317
 
318
    spa_list_for_each(o, &data->objects, link) {
319
@@ -245,14 +325,14 @@
320
        if (o->type != OBJECT_LINK)
321
            continue;
322
 
323
-       if (port->extra0 == PW_DIRECTION_OUTPUT &&
324
-           o->extra0 == port->id) {
325
-           peer = o->extra1;
326
+       if (port->data.port.direction == PW_DIRECTION_OUTPUT &&
327
+           o->data.link.output_port == port->id) {
328
+           peer = o->data.link.input_port;
329
            snprintf(prefix, sizeof(prefix), "%s  |-> ", id);
330
        }
331
-       else if (port->extra0 == PW_DIRECTION_INPUT &&
332
-           o->extra1 == port->id) {
333
-           peer = o->extra0;
334
+       else if (port->data.port.direction == PW_DIRECTION_INPUT &&
335
+           o->data.link.input_port == port->id) {
336
+           peer = o->data.link.output_port;
337
            snprintf(prefix, sizeof(prefix), "%s  |<- ", id);
338
        }
339
        else
340
@@ -309,17 +389,17 @@
341
    spa_list_for_each(o, &data->objects, link) {
342
        if (o->type != OBJECT_PORT)
343
            continue;
344
-       if (o->extra1 != node->id)
345
+       if (o->data.port.node != node->id)
346
            continue;
347
-       if (o->extra0 != direction)
348
+       if (o->data.port.direction != direction)
349
            continue;
350
 
351
        if (regex && !port_regex(data, node, o, regex))
352
            continue;
353
 
354
-       if (data->opt_mode & MODE_LIST_PORTS)
355
+       if (data->opt_list & LIST_PORTS)
356
            print_port(data, "", node, o, data->opt_verbose);
357
-       if (data->opt_mode & MODE_LIST_LINKS)
358
+       if (data->opt_list & LIST_LINKS)
359
            do_list_port_links(data, node, o);
360
    }
361
 }
362
@@ -338,12 +418,47 @@
363
    }
364
 }
365
 
366
-static int do_link_ports(struct data *data)
367
+static int create_link_target(struct data *data)
368
+{
369
+   struct target_link *tl = calloc(1, sizeof(*tl));
370
+   if (!tl)
371
+       return -ENOMEM;
372
+
373
+   tl->proxy = pw_core_create_object(data->core,
374
+           "link-factory", PW_TYPE_INTERFACE_Link,
375
+           PW_VERSION_LINK, &data->props->dict, 0);
376
+   if (tl->proxy == NULL) {
377
+       free(tl);
378
+       return -errno;
379
+   }
380
+
381
+   tl->data = data;
382
+   tl->state = PW_LINK_STATE_INIT;
383
+   pw_proxy_add_listener(tl->proxy, &tl->listener, &link_proxy_events, tl);
384
+   pw_proxy_add_object_listener(tl->proxy, &tl->link_listener, &link_events, tl);
385
+   spa_list_append(&data->target_links, &tl->link);
386
+   return 0;
387
+}
388
+
389
+/*
390
+ * create_link_proxies() looks at the current objects and tries to find the
391
+ * matching output and input nodes (multiple links) or the matching output and
392
+ * input ports.
393
+ *
394
+ * If successful, it fills data->target_links with proxies for all links and
395
+ * returns the number of links. This can be zero (two nodes with no ports). It
396
+ * might return (negative) errors. -ENOENT means no matching nodes or ports
397
+ * were found.
398
+ */
399
+static int create_link_proxies(struct data *data)
400
 {
401
    uint32_t in_port = 0, out_port = 0;
402
    struct object *n, *p;
403
    struct object *in_node = NULL, *out_node = NULL;
404
 
405
+   spa_assert(data->opt_output);
406
+   spa_assert(data->opt_input);
407
+
408
    spa_list_for_each(n, &data->objects, link) {
409
        if (n->type != OBJECT_NODE)
410
            continue;
411
@@ -359,22 +474,21 @@
412
        spa_list_for_each(p, &data->objects, link) {
413
            if (p->type != OBJECT_PORT)
414
                continue;
415
-           if (p->extra1 != n->id)
416
+           if (p->data.port.node != n->id)
417
                continue;
418
 
419
-           if (out_port == 0 && p->extra0 == PW_DIRECTION_OUTPUT &&
420
+           if (out_port == 0 && p->data.port.direction == PW_DIRECTION_OUTPUT &&
421
                port_matches(data, n, p, data->opt_output))
422
                out_port = p->id;
423
-           else if (in_port == 0 && p->extra0 == PW_DIRECTION_INPUT &&
424
+           else if (in_port == 0 && p->data.port.direction == PW_DIRECTION_INPUT &&
425
                port_matches(data, n, p, data->opt_input))
426
                in_port = p->id;
427
        }
428
    }
429
 
430
    if (in_node && out_node) {
431
-       int i, ret;
432
+       int i;
433
        char port_id32;
434
-       bool all_links_exist = true;
435
 
436
        for (i=0;; i++) {
437
            snprintf(port_id, sizeof(port_id), "%d", i);
438
@@ -382,27 +496,17 @@
439
            struct object *port_out = find_node_port(data, out_node, PW_DIRECTION_OUTPUT, port_id);
440
            struct object *port_in = find_node_port(data, in_node, PW_DIRECTION_INPUT, port_id);
441
 
442
-           if (!port_out && !port_in) {
443
-               fprintf(stderr, "Input & output port do not exist\n");
444
-               goto no_port;
445
-           } else if (!port_in) {
446
-               fprintf(stderr, "Input port does not exist\n");
447
-               goto no_port;
448
-           } else if (!port_out) {
449
-               fprintf(stderr, "Output port does not exist\n");
450
-               goto no_port;
451
-           }
452
+           if (!port_out || !port_in)
453
+               return i;
454
 
455
            pw_properties_setf(data->props, PW_KEY_LINK_OUTPUT_PORT, "%u", port_out->id);
456
            pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", port_in->id);
457
 
458
-           if ((ret = create_link(data)) < 0 && ret != -EEXIST)
459
+           int ret = create_link_target(data);
460
+           if (ret)
461
                return ret;
462
-
463
-           if (ret >= 0)
464
-               all_links_exist = false;
465
        }
466
-       return (all_links_exist ? -EEXIST : 0);
467
+       return i;
468
    }
469
 
470
    if (in_port == 0 || out_port == 0)
471
@@ -411,10 +515,12 @@
472
    pw_properties_setf(data->props, PW_KEY_LINK_OUTPUT_PORT, "%u", out_port);
473
    pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", in_port);
474
 
475
-   return create_link(data);
476
-
477
-no_port:
478
-   return -ENOENT;
479
+   /*
480
+    * create_link_target() returns zero on success. We return the number of
481
+    * links on success, meaning 1.
482
+    */
483
+   int ret = create_link_target(data);
484
+   return !ret ? 1 : ret;
485
 }
486
 
487
 static int do_unlink_ports(struct data *data)
488
@@ -423,6 +529,8 @@
489
    bool found_any = false;
490
    struct object *in_node = NULL, *out_node = NULL;
491
 
492
+   spa_assert(data->opt_output);
493
+
494
    if (data->opt_input != NULL) {
495
        /* 2 args, check if they are node names */
496
        spa_list_for_each(n, &data->objects, link) {
497
@@ -449,31 +557,31 @@
498
                continue;
499
        } else if (out_node && in_node) {
500
            /* 2 args, check nodes */
501
-           if ((p = find_object(data, OBJECT_PORT, l->extra0)) == NULL)
502
+           if ((p = find_object(data, OBJECT_PORT, l->data.link.output_port)) == NULL)
503
                continue;
504
-           if ((n = find_object(data, OBJECT_NODE, p->extra1)) == NULL)
505
+           if ((n = find_object(data, OBJECT_NODE, p->data.port.node)) == NULL)
506
                continue;
507
            if (n->id != out_node->id)
508
                continue;
509
 
510
-           if ((p = find_object(data, OBJECT_PORT, l->extra1)) == NULL)
511
+           if ((p = find_object(data, OBJECT_PORT, l->data.link.input_port)) == NULL)
512
                continue;
513
-           if ((n = find_object(data, OBJECT_NODE, p->extra1)) == NULL)
514
+           if ((n = find_object(data, OBJECT_NODE, p->data.port.node)) == NULL)
515
                continue;
516
            if (n->id != in_node->id)
517
                continue;
518
        } else {
519
            /* 2 args, check port names */
520
-           if ((p = find_object(data, OBJECT_PORT, l->extra0)) == NULL)
521
+           if ((p = find_object(data, OBJECT_PORT, l->data.link.output_port)) == NULL)
522
                continue;
523
-           if ((n = find_object(data, OBJECT_NODE, p->extra1)) == NULL)
524
+           if ((n = find_object(data, OBJECT_NODE, p->data.port.node)) == NULL)
525
                continue;
526
            if (!port_matches(data, n, p, data->opt_output))
527
                continue;
528
 
529
-           if ((p = find_object(data, OBJECT_PORT, l->extra1)) == NULL)
530
+           if ((p = find_object(data, OBJECT_PORT, l->data.link.input_port)) == NULL)
531
                continue;
532
-           if ((n = find_object(data, OBJECT_NODE, p->extra1)) == NULL)
533
+           if ((n = find_object(data, OBJECT_NODE, p->data.port.node)) == NULL)
534
                continue;
535
            if (!port_matches(data, n, p, data->opt_input))
536
                continue;
537
@@ -496,18 +604,18 @@
538
    bool do_print = false;
539
    struct object *node;
540
 
541
-   if (port->extra0 == PW_DIRECTION_OUTPUT && data->list_outputs) {
542
+   if (port->data.port.direction == PW_DIRECTION_OUTPUT && data->list_outputs) {
543
        regex = data->out_regex;
544
        do_print = true;
545
    }
546
-   if (port->extra0 == PW_DIRECTION_INPUT && data->list_inputs) {
547
+   if (port->data.port.direction == PW_DIRECTION_INPUT && data->list_inputs) {
548
        regex = data->in_regex;
549
        do_print = true;
550
    }
551
    if (!do_print)
552
        return 0;
553
 
554
-   if ((node = find_object(data, OBJECT_NODE, port->extra1)) == NULL)
555
+   if ((node = find_object(data, OBJECT_NODE, port->data.port.node)) == NULL)
556
        return -ENOENT;
557
 
558
    if (regex && !port_regex(data, node, port, regex))
559
@@ -522,19 +630,19 @@
560
    char buffer11024, buffer21024, id64 = "";
561
    struct object *n1, *n2, *p1, *p2;
562
 
563
-   if (!(data->opt_mode & MODE_LIST_LINKS))
564
+   if (!(data->opt_list & LIST_LINKS))
565
        return 0;
566
 
567
-   if ((p1 = find_object(data, OBJECT_PORT, link->extra0)) == NULL)
568
+   if ((p1 = find_object(data, OBJECT_PORT, link->data.link.output_port)) == NULL)
569
        return -ENOENT;
570
-   if ((n1 = find_object(data, OBJECT_NODE, p1->extra1)) == NULL)
571
+   if ((n1 = find_object(data, OBJECT_NODE, p1->data.port.node)) == NULL)
572
        return -ENOENT;
573
    if (data->out_regex && !port_regex(data, n1, p1, data->out_regex))
574
        return 0;
575
 
576
-   if ((p2 = find_object(data, OBJECT_PORT, link->extra1)) == NULL)
577
+   if ((p2 = find_object(data, OBJECT_PORT, link->data.link.input_port)) == NULL)
578
        return -ENOENT;
579
-   if ((n2 = find_object(data, OBJECT_NODE, p2->extra1)) == NULL)
580
+   if ((n2 = find_object(data, OBJECT_NODE, p2->data.port.node)) == NULL)
581
        return -ENOENT;
582
    if (data->in_regex && !port_regex(data, n2, p2, data->in_regex))
583
        return 0;
584
@@ -553,7 +661,8 @@
585
                  const struct spa_dict *props)
586
 {
587
    struct data *d = data;
588
-   uint32_t t, extra2;
589
+   enum object_type t;
590
+   union object_data extra = {0};
591
    struct object *obj;
592
    const char *str;
593
 
594
@@ -564,26 +673,31 @@
595
    if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
596
        t = OBJECT_NODE;
597
    } else if (spa_streq(type, PW_TYPE_INTERFACE_Port)) {
598
+       if (!d->new_object && d->opt_wait && spa_list_is_empty(&d->target_links)) {
599
+           d->new_object = true;
600
+           core_sync(d);
601
+       }
602
+
603
        t = OBJECT_PORT;
604
        if ((str = spa_dict_lookup(props, PW_KEY_PORT_DIRECTION)) == NULL)
605
            return;
606
        if (spa_streq(str, "in"))
607
-           extra0 = PW_DIRECTION_INPUT;
608
+           extra.port.direction = PW_DIRECTION_INPUT;
609
        else if (spa_streq(str, "out"))
610
-           extra0 = PW_DIRECTION_OUTPUT;
611
+           extra.port.direction = PW_DIRECTION_OUTPUT;
612
        else
613
            return;
614
        if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL)
615
            return;
616
-       extra1 = atoi(str);
617
+       extra.port.node = atoi(str);
618
    } else if (spa_streq(type, PW_TYPE_INTERFACE_Link)) {
619
        t = OBJECT_LINK;
620
        if ((str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_PORT)) == NULL)
621
            return;
622
-       extra0 = atoi(str);
623
+       extra.link.output_port = atoi(str);
624
        if ((str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_PORT)) == NULL)
625
            return;
626
-       extra1 = atoi(str);
627
+       extra.link.input_port = atoi(str);
628
    } else
629
        return;
630
 
631
@@ -591,12 +705,16 @@
632
    obj->type = t;
633
    obj->id = id;
634
    obj->props = pw_properties_new_dict(props);
635
-   memcpy(obj->extra, extra, sizeof(extra));
636
+   obj->data = extra;
637
    spa_list_append(&d->objects, &obj->link);
638
 
639
    if (d->monitoring) {
640
        d->prefix = "+ ";
641
        switch (obj->type) {
642
+       case OBJECT_ANY:
643
+           spa_assert_not_reached();
644
+       case OBJECT_NODE:
645
+           break;
646
        case OBJECT_PORT:
647
            do_monitor_port(d, obj);
648
            break;
649
@@ -607,6 +725,13 @@
650
    }
651
 }
652
 
653
+static void destroy_object(struct object *obj)
654
+{
655
+   spa_list_remove(&obj->link);
656
+   pw_properties_free(obj->props);
657
+   free(obj);
658
+}
659
+
660
 static void registry_event_global_remove(void *data, uint32_t id)
661
 {
662
    struct data *d = data;
663
@@ -618,6 +743,10 @@
664
    if (d->monitoring) {
665
        d->prefix = "- ";
666
        switch (obj->type) {
667
+       case OBJECT_ANY:
668
+           spa_assert_not_reached();
669
+       case OBJECT_NODE:
670
+           break;
671
        case OBJECT_PORT:
672
            do_monitor_port(d, obj);
673
            break;
674
@@ -627,9 +756,7 @@
675
        }
676
    }
677
 
678
-   spa_list_remove(&obj->link);
679
-   pw_properties_free(obj->props);
680
-   free(obj);
681
+   destroy_object(obj);
682
 }
683
 
684
 static const struct pw_registry_events registry_events = {
685
@@ -641,8 +768,22 @@
686
 static void on_core_done(void *data, uint32_t id, int seq)
687
 {
688
    struct data *d = data;
689
-   if (d->sync == seq)
690
-       pw_main_loop_quit(d->loop);
691
+
692
+   if (d->sync != seq)
693
+       return;
694
+
695
+   /* Connect mode, look for our targets. */
696
+   if (d->opt_mode == MODE_CONNECT) {
697
+       d->nb_links = create_link_proxies(d);
698
+       /* In wait mode, if none exist, keep running. */
699
+       if (d->opt_wait && d->nb_links == -ENOENT) {
700
+           d->new_object = false;
701
+           return;
702
+       }
703
+   }
704
+
705
+   pw_main_loop_quit(d->loop);
706
+
707
 }
708
 
709
 static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
710
@@ -670,7 +811,7 @@
711
 
712
 static void show_help(struct data *data, const char *name, bool error)
713
 {
714
-        fprintf(error ? stderr : stdout, "%1$s : PipeWire port and link manager.\n"
715
+   fprintf(error ? stderr : stdout, "%1$s : PipeWire port and link manager.\n"
716
        "Generic: %1$s options\n"
717
        "  -h, --help                            Show this help\n"
718
        "      --version                         Show version\n"
719
@@ -686,18 +827,60 @@
720
        "  -L, --linger                          Linger (default, unless -m is used)\n"
721
        "  -P, --passive                         Passive link\n"
722
        "  -p, --props=PROPS                     Properties as JSON object\n"
723
+       "  -w, --wait                            Wait until link creation attempt\n"
724
        "Disconnect: %1$s -d options output input\n"
725
        "            %1$s -d options link-id\n"
726
        "  -d, --disconnect                      Disconnect ports\n",
727
        name);
728
 }
729
 
730
-int main(int argc, char *argv)
731
+static void data_clear(struct data *data)
732
 {
733
-   struct data data = { 0, };
734
+   struct object *o;
735
+   spa_list_consume(o, &data->objects, link)
736
+       destroy_object(o);
737
+
738
+   struct target_link *tl;
739
+   spa_list_consume(tl, &data->target_links, link) {
740
+       if (tl->proxy != NULL) {
741
+           spa_hook_remove(&tl->listener);
742
+           spa_hook_remove(&tl->link_listener);
743
+           pw_proxy_destroy(tl->proxy);
744
+       }
745
+       spa_list_remove(&tl->link);
746
+       free(tl);
747
+   }
748
+
749
+   if (data->out_regex)
750
+       regfree(data->out_regex);
751
+   if (data->in_regex)
752
+       regfree(data->in_regex);
753
+
754
+   if (data->registry) {
755
+       spa_hook_remove(&data->registry_listener);
756
+       pw_proxy_destroy((struct pw_proxy *) data->registry);
757
+   }
758
+
759
+   if (data->core) {
760
+       spa_hook_remove(&data->core_listener);
761
+       pw_core_disconnect(data->core);
762
+   }
763
+
764
+   spa_clear_ptr(data->context, pw_context_destroy);
765
+   spa_clear_ptr(data->loop, pw_main_loop_destroy);
766
+
767
+   pw_properties_free(data->props);
768
+}
769
+
770
+static int run(int argc, char *argv)
771
+{
772
+   spa_cleanup(data_clear) struct data data = {
773
+       .opt_mode = MODE_CONNECT,
774
+       .objects = SPA_LIST_INIT(&data.objects),
775
+       .target_links = SPA_LIST_INIT(&data.target_links),
776
+   };
777
    int res = 0, c;
778
-   regex_t out_port_regex;
779
-   regex_t in_port_regex;
780
+   struct spa_error_location loc;
781
    static const struct option long_options = {
782
        { "help",   no_argument,        NULL, 'h' },
783
        { "version",    no_argument,        NULL, 'V' },
784
@@ -711,26 +894,21 @@
785
        { "linger", no_argument,        NULL, 'L' },
786
        { "passive",    no_argument,        NULL, 'P' },
787
        { "props",  required_argument,  NULL, 'p' },
788
+       { "wait",   no_argument,        NULL, 'w' },
789
        { "disconnect", no_argument,        NULL, 'd' },
790
        { NULL, 0, NULL, 0}
791
    };
792
 
793
-   setlocale(LC_ALL, "");
794
-   pw_init(&argc, &argv);
795
-   spa_list_init(&data.objects);
796
-
797
-   setlinebuf(stdout);
798
-
799
    data.props = pw_properties_new(NULL, NULL);
800
    if (data.props == NULL) {
801
        fprintf(stderr, "can't create properties: %m\n");
802
        return -1;
803
    }
804
 
805
-   while ((c = getopt_long(argc, argv, "hVr:oilmIvLPp:d", long_options, NULL)) != -1) {
806
+   while ((c = getopt_long(argc, argv, "hVr:oilmIvLPp:wd", long_options, NULL)) != -1) {
807
        switch (c) {
808
        case 'h':
809
-           show_help(&data, argv0, NULL);
810
+           show_help(&data, argv0, false);
811
            return 0;
812
        case 'V':
813
            printf("%s\n"
814
@@ -744,16 +922,19 @@
815
            data.opt_remote = optarg;
816
            break;
817
        case 'o':
818
-           data.opt_mode |= MODE_LIST_OUTPUT;
819
+           data.opt_mode = MODE_LIST;
820
+           data.opt_list |= LIST_OUTPUT;
821
            break;
822
        case 'i':
823
-           data.opt_mode |= MODE_LIST_INPUT;
824
+           data.opt_mode = MODE_LIST;
825
+           data.opt_list |= LIST_INPUT;
826
            break;
827
        case 'l':
828
-           data.opt_mode |= MODE_LIST_LINKS;
829
+           data.opt_mode = MODE_LIST;
830
+           data.opt_list |= LIST_LINKS;
831
            break;
832
        case 'm':
833
-           data.opt_mode |= MODE_MONITOR;
834
+           data.opt_monitor = true;
835
            break;
836
        case 'I':
837
            data.opt_id = true;
838
@@ -768,10 +949,17 @@
839
            pw_properties_set(data.props, PW_KEY_LINK_PASSIVE, "true");
840
            break;
841
        case 'p':
842
-           pw_properties_update_string(data.props, optarg, strlen(optarg));
843
+           if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
844
+               spa_debug_file_error_location(stderr, &loc,
845
+                       "error: syntax error in --props: %s", loc.reason);
846
+               return -1;
847
+           }
848
            break;
849
        case 'd':
850
-           data.opt_mode |= MODE_DISCONNECT;
851
+           data.opt_mode = MODE_DISCONNECT;
852
+           break;
853
+       case 'w':
854
+           data.opt_wait = true;
855
            break;
856
        default:
857
            show_help(&data, argv0, true);
858
@@ -781,12 +969,12 @@
859
    if (argc == 1)
860
        show_help(&data, argv0, true);
861
 
862
-   if (data.opt_id && (data.opt_mode & MODE_LIST) == 0) {
863
+   if (data.opt_id && data.opt_mode != MODE_LIST) {
864
        fprintf(stderr, "-I option needs one or more of -l, -i or -o\n");
865
        return -1;
866
    }
867
 
868
-   if ((data.opt_mode & MODE_MONITOR) == 0)
869
+   if (!data.opt_monitor)
870
        pw_properties_set(data.props, PW_KEY_OBJECT_LINGER, "true");
871
 
872
    if (optind < argc)
873
@@ -794,6 +982,23 @@
874
    if (optind < argc)
875
        data.opt_input = argvoptind++;
876
 
877
+   switch (data.opt_mode) {
878
+   case MODE_LIST:
879
+       break;
880
+   case MODE_DISCONNECT:
881
+       if (data.opt_output == NULL) {
882
+           fprintf(stderr, "missing link-id or output and input port names to disconnect\n");
883
+           return -1;
884
+       }
885
+       break;
886
+   case MODE_CONNECT:
887
+       if (data.opt_output == NULL || data.opt_input == NULL) {
888
+           fprintf(stderr, "missing output and input port names to connect\n");
889
+           return -1;
890
+       }
891
+       break;
892
+   }
893
+
894
    data.loop = pw_main_loop_new(NULL);
895
    if (data.loop == NULL) {
896
        fprintf(stderr, "can't create mainloop: %m\n");
897
@@ -828,66 +1033,74 @@
898
            &data.registry_listener,
899
            &registry_events, &data);
900
 
901
-   data.prefix = (data.opt_mode & MODE_MONITOR) ? "= " : "";
902
+   data.prefix = data.opt_monitor ? "= " : "";
903
 
904
    core_sync(&data);
905
    pw_main_loop_run(data.loop);
906
 
907
-   if ((data.opt_mode & (MODE_LIST_PORTS|MODE_LIST_LINKS)) == MODE_LIST_LINKS)
908
+   if ((data.opt_list & (LIST_PORTS|LIST_LINKS)) == LIST_LINKS)
909
        data.list_inputs = data.list_outputs = true;
910
-   if ((data.opt_mode & MODE_LIST_INPUT) == MODE_LIST_INPUT)
911
+   if ((data.opt_list & LIST_INPUT) == LIST_INPUT)
912
        data.list_inputs = true;
913
-   if ((data.opt_mode & MODE_LIST_OUTPUT) == MODE_LIST_OUTPUT)
914
+   if ((data.opt_list & LIST_OUTPUT) == LIST_OUTPUT)
915
        data.list_outputs = true;
916
 
917
    if (data.opt_output) {
918
-       if (regcomp(&out_port_regex, data.opt_output, REG_EXTENDED | REG_NOSUB) == 0)
919
-           data.out_regex = &out_port_regex;
920
+       if (regcomp(&data.out_port_regex, data.opt_output, REG_EXTENDED | REG_NOSUB) == 0)
921
+           data.out_regex = &data.out_port_regex;
922
    }
923
    if (data.opt_input) {
924
-       if (regcomp(&in_port_regex, data.opt_input, REG_EXTENDED | REG_NOSUB) == 0)
925
-           data.in_regex = &in_port_regex;
926
+       if (regcomp(&data.in_port_regex, data.opt_input, REG_EXTENDED | REG_NOSUB) == 0)
927
+           data.in_regex = &data.in_port_regex;
928
    }
929
 
930
-   if (data.opt_mode & (MODE_LIST)) {
931
+   switch (data.opt_mode) {
932
+   case MODE_LIST:
933
        do_list(&data);
934
-   } else if (data.opt_mode & MODE_DISCONNECT) {
935
-       if (data.opt_output == NULL) {
936
-           fprintf(stderr, "missing link-id or output and input port names to disconnect\n");
937
-           return -1;
938
-       }
939
+       break;
940
+   case MODE_DISCONNECT:
941
        if ((res = do_unlink_ports(&data)) < 0) {
942
            fprintf(stderr, "failed to unlink ports: %s\n", spa_strerror(res));
943
            return -1;
944
        }
945
-   } else {
946
-       if (data.opt_output == NULL ||
947
-           data.opt_input == NULL) {
948
-           fprintf(stderr, "missing output and input port names to connect\n");
949
+       break;
950
+   case MODE_CONNECT:
951
+       if (data.nb_links < 0) {
952
+           fprintf(stderr, "failed to link ports: %s\n", spa_strerror(data.nb_links));
953
            return -1;
954
        }
955
-       if ((res = do_link_ports(&data)) < 0) {
956
-           fprintf(stderr, "failed to link ports: %s\n", spa_strerror(res));
957
-           return -1;
958
+
959
+       if (data.nb_links > 0) {
960
+           pw_main_loop_run(data.loop);
961
+
962
+           struct target_link *tl;
963
+           spa_list_for_each(tl, &data.target_links, link) {
964
+               if (tl->state == PW_LINK_STATE_ERROR) {
965
+                   fprintf(stderr, "failed to link ports: %s\n",
966
+                       spa_strerror(tl->result));
967
+                   return -1;
968
+               }
969
+           }
970
        }
971
+       break;
972
    }
973
 
974
-   if (data.opt_mode & MODE_MONITOR) {
975
+   if (data.opt_monitor) {
976
        data.monitoring = true;
977
        pw_main_loop_run(data.loop);
978
        data.monitoring = false;
979
    }
980
 
981
-   if (data.out_regex)
982
-       regfree(data.out_regex);
983
-   if (data.in_regex)
984
-       regfree(data.in_regex);
985
-   spa_hook_remove(&data.registry_listener);
986
-   pw_proxy_destroy((struct pw_proxy*)data.registry);
987
-   spa_hook_remove(&data.core_listener);
988
-   pw_core_disconnect(data.core);
989
-   pw_context_destroy(data.context);
990
-   pw_main_loop_destroy(data.loop);
991
+   return 0;
992
+}
993
+
994
+int main(int argc, char *argv)
995
+{
996
+   setlocale(LC_ALL, "");
997
+   setlinebuf(stdout);
998
+
999
+   pw_init(&argc, &argv);
1000
+   int res = run(argc, argv);
1001
    pw_deinit();
1002
 
1003
    return res;
1004
pipewire-1.0.1.tar.bz2/src/tools/pw-loopback.c -> pipewire-1.2.0.tar.gz/src/tools/pw-loopback.c Changed
69
 
1
@@ -16,6 +16,7 @@
2
 #include <spa/param/audio/format-utils.h>
3
 #include <spa/param/audio/raw.h>
4
 #include <spa/utils/json.h>
5
+#include <spa/debug/file.h>
6
 
7
 #include <pipewire/pipewire.h>
8
 #include <pipewire/impl.h>
9
@@ -65,7 +66,7 @@
10
 
11
 static void show_help(struct data *data, const char *name, bool error)
12
 {
13
-        fprintf(error ? stderr : stdout, "%s options\n"
14
+   fprintf(error ? stderr : stdout, "%s options\n"
15
        "  -h, --help                            Show this help\n"
16
        "      --version                         Show version\n"
17
        "  -r, --remote                          Remote daemon name\n"
18
@@ -111,6 +112,7 @@
19
        { NULL, 0, NULL, 0}
20
    };
21
    int c, res = -1;
22
+   struct spa_error_location loc;
23
 
24
    setlocale(LC_ALL, "");
25
    pw_init(&argc, &argv);
26
@@ -161,7 +163,7 @@
27
            data.latency = atoi(optarg) * DEFAULT_RATE / SPA_MSEC_PER_SEC;
28
            break;
29
        case 'd':
30
-           data.delay = atof(optarg);
31
+           data.delay = (float)atof(optarg);
32
            break;
33
        case 'C':
34
            pw_properties_set(data.capture_props, PW_KEY_TARGET_OBJECT, optarg);
35
@@ -170,10 +172,22 @@
36
            pw_properties_set(data.playback_props, PW_KEY_TARGET_OBJECT, optarg);
37
            break;
38
        case 'i':
39
-           pw_properties_update_string(data.capture_props, optarg, strlen(optarg));
40
+           if (pw_properties_update_string_checked(data.capture_props,
41
+                       optarg, strlen(optarg), &loc) < 0) {
42
+               spa_debug_file_error_location(stderr, &loc,
43
+                       "error: syntax error in --capture-props: %s",
44
+                       loc.reason);
45
+               return -1;
46
+           }
47
            break;
48
        case 'o':
49
-           pw_properties_update_string(data.playback_props, optarg, strlen(optarg));
50
+           if (pw_properties_update_string_checked(data.playback_props,
51
+                       optarg, strlen(optarg), &loc) < 0) {
52
+               spa_debug_file_error_location(stderr, &loc,
53
+                       "error: syntax error in --playback-props: %s",
54
+                       loc.reason);
55
+               return -1;
56
+           }
57
            break;
58
        default:
59
            show_help(&data, argv0, true);
60
@@ -198,7 +212,7 @@
61
    }
62
 
63
 
64
-        if ((f = open_memstream(&args, &size)) == NULL) {
65
+   if ((f = open_memstream(&args, &size)) == NULL) {
66
        fprintf(stderr, "can't open memstream: %m\n");
67
        goto exit;
68
    }
69
pipewire-1.0.1.tar.bz2/src/tools/pw-metadata.c -> pipewire-1.2.0.tar.gz/src/tools/pw-metadata.c Changed
10
 
1
@@ -165,7 +165,7 @@
2
 
3
 static void show_help(struct data *data, const char *name, bool error)
4
 {
5
-        fprintf(error ? stderr : stdout, "%s options  id  key  value  type    \n"
6
+   fprintf(error ? stderr : stdout, "%s options  id  key  value  type    \n"
7
        "  -h, --help                            Show this help\n"
8
        "      --version                         Show version\n"
9
        "  -r, --remote                          Remote daemon name\n"
10
pipewire-1.0.1.tar.bz2/src/tools/pw-mididump.c -> pipewire-1.2.0.tar.gz/src/tools/pw-mididump.c Changed
34
 
1
@@ -76,12 +76,12 @@
2
    d = &buf->datas0;
3
 
4
    if (d->data == NULL)
5
-       return;
6
+       goto done;
7
 
8
    if ((pod = spa_pod_from_data(d->data, d->maxsize, d->chunk->offset, d->chunk->size)) == NULL)
9
-       return;
10
+       goto done;
11
    if (!spa_pod_is_sequence(pod))
12
-       return;
13
+       goto done;
14
 
15
    SPA_POD_SEQUENCE_FOREACH((struct spa_pod_sequence*)pod, c) {
16
        struct midi_event ev;
17
@@ -98,6 +98,7 @@
18
        midi_file_dump_event(stdout, &ev);
19
    }
20
 
21
+done:
22
    pw_filter_queue_buffer(data->in_port, b);
23
 }
24
 
25
@@ -158,7 +159,7 @@
26
 
27
 static void show_help(const char *name, bool error)
28
 {
29
-        fprintf(error ? stderr : stdout, "%s options FILE\n"
30
+   fprintf(error ? stderr : stdout, "%s options FILE\n"
31
        "  -h, --help                            Show this help\n"
32
        "      --version                         Show version\n"
33
        "  -r, --remote                          Remote daemon name\n",
34
pipewire-1.0.1.tar.bz2/src/tools/pw-mon.c -> pipewire-1.2.0.tar.gz/src/tools/pw-mon.c Changed
303
 
1
@@ -30,9 +30,9 @@
2
 };
3
 
4
 #define with_prefix(use_prefix_) \
5
-   for (bool once_ = !!printf("%s", (pprefix!!(use_prefix_)).prefix); \
6
-   once_; \
7
-   once_ = false, printf("%s", (pprefix!!(use_prefix_)).suffix))
8
+   for (bool once_ = !!printf("%s", (pprefix!!(use_prefix_)).prefix); \
9
+       once_; \
10
+       once_ = false, printf("%s", (pprefix!!(use_prefix_)).suffix))
11
 
12
 
13
 struct param {
14
@@ -58,6 +58,7 @@
15
 
16
    bool hide_params;
17
    bool hide_props;
18
+   bool print_separator;
19
 };
20
 
21
 struct proxy_data {
22
@@ -134,7 +135,7 @@
23
 static void event_param(void *_data, int seq, uint32_t id,
24
        uint32_t index, uint32_t next, const struct spa_pod *param)
25
 {
26
-        struct proxy_data *data = _data;
27
+   struct proxy_data *data = _data;
28
    struct param *p;
29
 
30
    /* remove all params with the same id and older seq */
31
@@ -203,10 +204,10 @@
32
 
33
 static void on_core_info(void *_data, const struct pw_core_info *info)
34
 {
35
-   struct proxy_data *data = _data;
36
+   struct data *data = _data;
37
    bool hide_props, print_mark = true;
38
 
39
-   hide_props = data->data->hide_props;
40
+   hide_props = data->hide_props;
41
 
42
    printf("\ttype: %s\n", PW_TYPE_INTERFACE_Core);
43
    printf("\tcookie: %u\n", info->cookie);
44
@@ -217,6 +218,8 @@
45
    if (!hide_props) {
46
        print_properties(info->props, MARK_CHANGE(PW_CORE_CHANGE_MASK_PROPS));
47
    }
48
+   if (data->print_separator)
49
+       printf("\n");
50
 }
51
 
52
 static void module_event_info(void *_data, const struct pw_module_info *info)
53
@@ -246,11 +249,13 @@
54
    if (!hide_props) {
55
        print_properties(info->props, MARK_CHANGE(PW_MODULE_CHANGE_MASK_PROPS));
56
    }
57
+   if (data->data->print_separator)
58
+       printf("\n");
59
 }
60
 
61
 static const struct pw_module_events module_events = {
62
    PW_VERSION_MODULE_EVENTS,
63
-        .info = module_event_info,
64
+   .info = module_event_info,
65
 };
66
 
67
 static void print_node(struct proxy_data *data)
68
@@ -298,11 +303,13 @@
69
    if (!hide_props) {
70
        print_properties(info->props, MARK_CHANGE(PW_NODE_CHANGE_MASK_PROPS));
71
    }
72
+   if (data->data->print_separator)
73
+       printf("\n");
74
 }
75
 
76
 static void node_event_info(void *_data, const struct pw_node_info *info)
77
 {
78
-        struct proxy_data *data = _data;
79
+   struct proxy_data *data = _data;
80
    uint32_t i;
81
 
82
    info = data->info = pw_node_info_update(data->info, info);
83
@@ -327,8 +334,8 @@
84
 
85
 static const struct pw_node_events node_events = {
86
    PW_VERSION_NODE_EVENTS,
87
-        .info = node_event_info,
88
-        .param = event_param
89
+   .info = node_event_info,
90
+   .param = event_param
91
 };
92
 
93
 static void print_port(struct proxy_data *data)
94
@@ -363,11 +370,13 @@
95
    if (!hide_props) {
96
        print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS));
97
    }
98
+   if (data->data->print_separator)
99
+       printf("\n");
100
 }
101
 
102
 static void port_event_info(void *_data, const struct pw_port_info *info)
103
 {
104
-        struct proxy_data *data = _data;
105
+   struct proxy_data *data = _data;
106
    uint32_t i;
107
 
108
    info = data->info = pw_port_info_update(data->info, info);
109
@@ -392,13 +401,13 @@
110
 
111
 static const struct pw_port_events port_events = {
112
    PW_VERSION_PORT_EVENTS,
113
-        .info = port_event_info,
114
-        .param = event_param
115
+   .info = port_event_info,
116
+   .param = event_param
117
 };
118
 
119
 static void factory_event_info(void *_data, const struct pw_factory_info *info)
120
 {
121
-        struct proxy_data *data = _data;
122
+   struct proxy_data *data = _data;
123
    bool hide_props, print_mark;
124
 
125
    hide_props = data->data->hide_props;
126
@@ -412,7 +421,7 @@
127
        print_mark = true;
128
    }
129
 
130
-        info = data->info = pw_factory_info_update(data->info, info);
131
+   info = data->info = pw_factory_info_update(data->info, info);
132
 
133
    printf("\tid: %d\n", data->id);
134
    printf("\tpermissions: "PW_PERMISSION_FORMAT"\n",
135
@@ -424,16 +433,18 @@
136
    if (!hide_props) {
137
        print_properties(info->props, MARK_CHANGE(PW_FACTORY_CHANGE_MASK_PROPS));
138
    }
139
+   if (data->data->print_separator)
140
+       printf("\n");
141
 }
142
 
143
 static const struct pw_factory_events factory_events = {
144
    PW_VERSION_FACTORY_EVENTS,
145
-        .info = factory_event_info
146
+   .info = factory_event_info
147
 };
148
 
149
 static void client_event_info(void *_data, const struct pw_client_info *info)
150
 {
151
-        struct proxy_data *data = _data;
152
+   struct proxy_data *data = _data;
153
    bool hide_props, print_mark;
154
 
155
    hide_props = data->data->hide_props;
156
@@ -447,7 +458,7 @@
157
        print_mark = true;
158
    }
159
 
160
-        info = data->info = pw_client_info_update(data->info, info);
161
+   info = data->info = pw_client_info_update(data->info, info);
162
 
163
    printf("\tid: %d\n", data->id);
164
    printf("\tpermissions: "PW_PERMISSION_FORMAT"\n",
165
@@ -457,16 +468,18 @@
166
    if (!hide_props) {
167
        print_properties(info->props, MARK_CHANGE(PW_CLIENT_CHANGE_MASK_PROPS));
168
    }
169
+   if (data->data->print_separator)
170
+       printf("\n");
171
 }
172
 
173
 static const struct pw_client_events client_events = {
174
    PW_VERSION_CLIENT_EVENTS,
175
-        .info = client_event_info
176
+   .info = client_event_info
177
 };
178
 
179
 static void link_event_info(void *_data, const struct pw_link_info *info)
180
 {
181
-        struct proxy_data *data = _data;
182
+   struct proxy_data *data = _data;
183
    bool hide_props, print_mark;
184
 
185
    hide_props = data->data->hide_props;
186
@@ -480,7 +493,7 @@
187
        print_mark = true;
188
    }
189
 
190
-        info = data->info = pw_link_info_update(data->info, info);
191
+   info = data->info = pw_link_info_update(data->info, info);
192
 
193
    printf("\tid: %d\n", data->id);
194
    printf("\tpermissions: "PW_PERMISSION_FORMAT"\n",
195
@@ -509,6 +522,8 @@
196
        }
197
        print_properties(info->props, MARK_CHANGE(PW_LINK_CHANGE_MASK_PROPS));
198
    }
199
+   if (data->data->print_separator)
200
+       printf("\n");
201
 }
202
 
203
 static const struct pw_link_events link_events = {
204
@@ -546,12 +561,14 @@
205
    if (!hide_props) {
206
        print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS));
207
    }
208
+   if (data->data->print_separator)
209
+       printf("\n");
210
 }
211
 
212
 
213
 static void device_event_info(void *_data, const struct pw_device_info *info)
214
 {
215
-        struct proxy_data *data = _data;
216
+   struct proxy_data *data = _data;
217
    uint32_t i;
218
 
219
    info = data->info = pw_device_info_update(data->info, info);
220
@@ -576,13 +593,13 @@
221
 static const struct pw_device_events device_events = {
222
    PW_VERSION_DEVICE_EVENTS,
223
    .info = device_event_info,
224
-        .param = event_param
225
+   .param = event_param
226
 };
227
 
228
 static void
229
 removed_proxy (void *data)
230
 {
231
-        struct proxy_data *pd = data;
232
+   struct proxy_data *pd = data;
233
    pw_proxy_destroy(pd->proxy);
234
 }
235
 
236
@@ -663,6 +680,8 @@
237
                PW_PERMISSION_ARGS(permissions));
238
        printf("\ttype: %s (version %d)\n", type, version);
239
        print_properties(props, false);
240
+       if (d->print_separator)
241
+           printf("\n");
242
        return;
243
    }
244
 
245
@@ -712,6 +731,8 @@
246
 
247
    printf("removed:\n");
248
    printf("\tid: %u\n", id);
249
+   if (d->print_separator)
250
+       printf("\n");
251
 
252
    pd = find_proxy(d, id);
253
    if (pd == NULL)
254
@@ -752,14 +773,15 @@
255
 
256
 static void show_help(const char *name, bool error)
257
 {
258
-        fprintf(error ? stderr : stdout, "%s options\n"
259
+   fprintf(error ? stderr : stdout, "%s options\n"
260
        "  -h, --help                            Show this help\n"
261
        "      --version                         Show version\n"
262
        "  -r, --remote                          Remote daemon name\n"
263
        "  -N, --no-colors                       disable color output\n"
264
        "  -C, --color=WHEN                    whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
265
        "  -o, --hide-props                      hide node properties\n"
266
-       "  -a, --hide-params                     hide node properties\n",
267
+       "  -a, --hide-params                     hide node properties\n"
268
+       "  -p, --print-separator                 print empty line after every event to help streaming parser\n",
269
        name);
270
 }
271
 
272
@@ -776,6 +798,7 @@
273
        { "color",      optional_argument,  NULL, 'C' },
274
        { "hide-props",     no_argument,        NULL, 'o' },
275
        { "hide-params",    no_argument,        NULL, 'a' },
276
+       { "print-separator",    no_argument,        NULL, 'p' },
277
        { NULL, 0, NULL, 0}
278
    };
279
    int c;
280
@@ -786,10 +809,10 @@
281
 
282
    setlinebuf(stdout);
283
 
284
-   if (isatty(STDOUT_FILENO) && getenv("NO_COLOR") == NULL)
285
+   if (getenv("NO_COLOR") == NULL && isatty(STDOUT_FILENO))
286
        colors = true;
287
 
288
-   while ((c = getopt_long(argc, argv, "hVr:NCoa", long_options, NULL)) != -1) {
289
+   while ((c = getopt_long(argc, argv, "hVr:NCoap", long_options, NULL)) != -1) {
290
        switch (c) {
291
        case 'h':
292
            show_help(argv0, false);
293
@@ -828,6 +851,9 @@
294
        case 'a':
295
            data.hide_params = true;
296
            break;
297
+       case 'p':
298
+           data.print_separator = true;
299
+           break;
300
        default:
301
            show_help(argv0, true);
302
            return -1;
303
pipewire-1.0.1.tar.bz2/src/tools/pw-profiler.c -> pipewire-1.2.0.tar.gz/src/tools/pw-profiler.c Changed
48
 
1
@@ -182,8 +182,8 @@
2
    int64_t d1, d2;
3
    int64_t delay, period_usecs;
4
 
5
-#define CLOCK_AS_USEC(cl,val) (val * (float)SPA_USEC_PER_SEC / (cl)->rate.denom)
6
-#define CLOCK_AS_SUSEC(cl,val) (val * (float)SPA_USEC_PER_SEC / ((cl)->rate.denom * (cl)->rate_diff))
7
+#define CLOCK_AS_USEC(cl,val) (int64_t)(val * (float)SPA_USEC_PER_SEC / (cl)->rate.denom)
8
+#define CLOCK_AS_SUSEC(cl,val) (int64_t)(val * (float)SPA_USEC_PER_SEC / ((cl)->rate.denom * (cl)->rate_diff))
9
 
10
    delay = CLOCK_AS_USEC(&point->clock, point->clock.delay);
11
    period_usecs = CLOCK_AS_SUSEC(&point->clock, point->clock.duration);
12
@@ -193,7 +193,7 @@
13
 
14
    if (d1 > period_usecs * 1.3 ||
15
        d2 > period_usecs * 1.3)
16
-       d1 = d2 = period_usecs * 1.4;
17
+       d1 = d2 = (int64_t)(period_usecs * 1.4);
18
 
19
    /* 4 columns for the driver */
20
    fprintf(d->output, "%"PRIi64"\t%"PRIi64"\t%"PRIi64"\t%"PRIi64"\t",
21
@@ -406,7 +406,7 @@
22
 
23
 static void profiler_profile(void *data, const struct spa_pod *pod)
24
 {
25
-        struct data *d = data;
26
+   struct data *d = data;
27
    struct spa_pod *o;
28
    struct spa_pod_prop *p;
29
    struct point point;
30
@@ -446,7 +446,7 @@
31
 
32
 static const struct pw_profiler_events profiler_events = {
33
    PW_VERSION_PROFILER_EVENTS,
34
-        .profile = profiler_profile,
35
+   .profile = profiler_profile,
36
 };
37
 
38
 static void registry_event_global(void *data, uint32_t id,
39
@@ -522,7 +522,7 @@
40
 
41
 static void show_help(const char *name, bool error)
42
 {
43
-        fprintf(error ? stderr : stdout, "%s options\n"
44
+   fprintf(error ? stderr : stdout, "%s options\n"
45
        "  -h, --help                            Show this help\n"
46
        "      --version                         Show version\n"
47
        "  -r, --remote                          Remote daemon name\n"
48
pipewire-1.0.1.tar.bz2/src/tools/pw-reserve.c -> pipewire-1.2.0.tar.gz/src/tools/pw-reserve.c Changed
49
 
1
@@ -70,15 +70,15 @@
2
 
3
 static void show_help(const char *name, bool error)
4
 {
5
-        fprintf(error ? stderr : stdout, "%s options\n"
6
-             "  -h, --help                            Show this help\n"
7
-             "      --version                         Show version\n"
8
-             "  -n, --name                            Name to reserve (Audio0, Midi0, Video0, ..)\n"
9
-             "  -a, --appname                         Application Name (default %s)\n"
10
-             "  -p, --priority                        Priority (default %d)\n"
11
-             "  -m, --monitor                         Monitor only, don't try to acquire\n"
12
-             "  -r, --release                         Request release when busy\n",
13
-        name, DEFAULT_APPNAME, DEFAULT_PRIORITY);
14
+   fprintf(error ? stderr : stdout, "%s options\n"
15
+       "  -h, --help                            Show this help\n"
16
+       "      --version                         Show version\n"
17
+       "  -n, --name                            Name to reserve (Audio0, Midi0, Video0, ..)\n"
18
+       "  -a, --appname                         Application Name (default %s)\n"
19
+       "  -p, --priority                        Priority (default %d)\n"
20
+       "  -m, --monitor                         Monitor only, don't try to acquire\n"
21
+       "  -r, --release                         Request release when busy\n",
22
+       name, DEFAULT_APPNAME, DEFAULT_PRIORITY);
23
 }
24
 
25
 int main(int argc, char *argv)
26
@@ -142,8 +142,8 @@
27
            return -1;
28
        }
29
    }
30
-   if (opt_name == NULL) {
31
-       fprintf(stderr, "name must be given\n");
32
+   if (opt_name == NULL || !rd_device_valid_device_name(opt_name)) {
33
+       fprintf(stderr, "valid name must be given\n");
34
        return -1;
35
    }
36
 
37
@@ -190,6 +190,11 @@
38
            opt_appname,
39
            opt_priority,
40
            &reserve_callbacks, &impl);
41
+   if (!impl.device) {
42
+       res = -errno;
43
+       fprintf(stderr, "dbus connection failed: %m\n");
44
+       goto exit;
45
+   }
46
 
47
    if (!opt_monitor) {
48
        res = rd_device_acquire(impl.device);
49
pipewire-1.0.1.tar.bz2/src/tools/pw-top.c -> pipewire-1.2.0.tar.gz/src/tools/pw-top.c Changed
193
 
1
@@ -29,6 +29,7 @@
2
    float cpu_load3;
3
    struct spa_io_clock clock;
4
    uint32_t xrun_count;
5
+   uint32_t transport_state;
6
 };
7
 
8
 struct measurement {
9
@@ -130,7 +131,8 @@
10
            SPA_POD_Long(&info->clock.duration),
11
            SPA_POD_Long(&info->clock.delay),
12
            SPA_POD_Double(&info->clock.rate_diff),
13
-           SPA_POD_Long(&info->clock.next_nsec));
14
+           SPA_POD_Long(&info->clock.next_nsec),
15
+           SPA_POD_OPT_Int(&info->transport_state));
16
 }
17
 
18
 static struct node *find_node(struct data *d, uint32_t id)
19
@@ -165,6 +167,32 @@
20
 
21
 static void do_refresh(struct data *d, bool force_refresh);
22
 
23
+static const char *find_node_name(const struct spa_dict *props)
24
+{
25
+   static const char * const name_keys = {
26
+       PW_KEY_NODE_NAME,
27
+       PW_KEY_NODE_DESCRIPTION,
28
+       PW_KEY_APP_NAME,
29
+       PW_KEY_MEDIA_NAME,
30
+   };
31
+
32
+   SPA_FOR_EACH_ELEMENT_VAR(name_keys, key) {
33
+       const char *name = spa_dict_lookup(props, *key);
34
+       if (name)
35
+           return name;
36
+   }
37
+
38
+   return NULL;
39
+}
40
+
41
+static void set_node_name(struct node *n, const char *name)
42
+{
43
+   if (name)
44
+       snprintf(n->name, sizeof(n->name), "%s", name);
45
+   else
46
+       snprintf(n->name, sizeof(n->name), "%u", n->id);
47
+}
48
+
49
 static void node_info(void *data, const struct pw_node_info *info)
50
 {
51
    struct node *n = data;
52
@@ -173,6 +201,9 @@
53
        n->state = info->state;
54
        do_refresh(n->data, !n->data->batch_mode);
55
    }
56
+
57
+   if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)
58
+       set_node_name(n, find_node_name(info->props));
59
 }
60
 
61
 static void node_param(void *data, int seq,
62
@@ -294,13 +325,10 @@
63
    if ((n = calloc(1, sizeof(*n))) == NULL)
64
        return NULL;
65
 
66
-   if (name)
67
-       strncpy(n->name, name, MAX_NAME);
68
-   else
69
-       snprintf(n->name, sizeof(n->name), "%u", id);
70
    n->data = d;
71
    n->id = id;
72
    n->driver = n;
73
+
74
    n->proxy = pw_registry_bind(d->registry, id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
75
    if (n->proxy) {
76
        uint32_t ids1 = { SPA_PARAM_Format };
77
@@ -318,6 +346,8 @@
78
    if (!d->batch_mode)
79
        d->pending_refresh = true;
80
 
81
+   set_node_name(n, name);
82
+
83
    return n;
84
 }
85
 
86
@@ -420,14 +450,16 @@
87
        snprintf(buf, len, " --- ");
88
    } else if (val == (uint64_t)-2) {
89
        snprintf(buf, len, " +++ ");
90
+   } else if (quantum == 0.0f) {
91
+       snprintf(buf, len, " ??? ");
92
    } else {
93
        float frac = val / 1000000000.f;
94
-       snprintf(buf, len, "%5.2f", quantum == 0.0f ? 0.0f : frac/quantum);
95
+       snprintf(buf, len, "%5.2f", frac/quantum);
96
    }
97
    return buf;
98
 }
99
 
100
-static const char *state_as_string(enum pw_node_state state)
101
+static const char *state_as_string(enum pw_node_state state, uint32_t transport)
102
 {
103
    switch (state) {
104
    case PW_NODE_STATE_ERROR:
105
@@ -439,7 +471,14 @@
106
    case PW_NODE_STATE_IDLE:
107
        return "I";
108
    case PW_NODE_STATE_RUNNING:
109
-       return "R";
110
+       switch (transport) {
111
+       case SPA_IO_POSITION_STATE_STARTING:
112
+           return "t";
113
+       case SPA_IO_POSITION_STATE_RUNNING:
114
+           return "T";
115
+       default:
116
+           return "R";
117
+       }
118
    }
119
    return "!";
120
 }
121
@@ -467,7 +506,7 @@
122
    if (i->clock.rate.denom)
123
        quantum = (float)i->clock.duration * i->clock.rate.num / (float)i->clock.rate.denom;
124
    else
125
-       quantum = 0.0;
126
+       quantum = 0.0f;
127
 
128
    if (n->measurement.awake >= n->measurement.signal)
129
        waiting = n->measurement.awake - n->measurement.signal;
130
@@ -484,7 +523,7 @@
131
        busy = -1;
132
 
133
    print_mode_dependent(d, y, 0, "%s %4.1u %6.1u %6.1u %s %s %s %s  %3.1u %16.16s %s%s",
134
-           state_as_string(n->state),
135
+           state_as_string(n->state, i->transport_state),
136
            n->id,
137
            frac.num, frac.denom,
138
            print_time(buf1, active, 64, waiting),
139
@@ -540,7 +579,7 @@
140
                continue;
141
 
142
            print_node(d, &n->info, f, y++);
143
-           if(y > LINES)
144
+           if(!d->batch_mode && y > LINES)
145
                break;
146
 
147
        }
148
@@ -572,7 +611,7 @@
149
 
150
 static void profiler_profile(void *data, const struct spa_pod *pod)
151
 {
152
-        struct data *d = data;
153
+   struct data *d = data;
154
    struct spa_pod *o;
155
    struct spa_pod_prop *p;
156
    struct point point;
157
@@ -612,7 +651,7 @@
158
 
159
 static const struct pw_profiler_events profiler_events = {
160
    PW_VERSION_PROFILER_EVENTS,
161
-        .profile = profiler_profile,
162
+   .profile = profiler_profile,
163
 };
164
 
165
 static void registry_event_global(void *data, uint32_t id,
166
@@ -623,16 +662,8 @@
167
    struct pw_proxy *proxy;
168
 
169
    if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
170
-       const char *str;
171
-
172
-       if ((str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL &&
173
-           (str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION)) == NULL) {
174
-               str = spa_dict_lookup(props, PW_KEY_APP_NAME);
175
-       }
176
-
177
-       if (add_node(d, id, str) == NULL) {
178
+       if (add_node(d, id, find_node_name(props)) == NULL)
179
            pw_log_warn("can add node %u: %m", id);
180
-       }
181
    } else if (spa_streq(type, PW_TYPE_INTERFACE_Profiler)) {
182
        if (d->profiler != NULL) {
183
            printf("Ignoring profiler %d: already attached\n", id);
184
@@ -719,7 +750,7 @@
185
 
186
 static void show_help(const char *name, bool error)
187
 {
188
-        fprintf(error ? stderr : stdout, "Usage:\n%s options\n\n"
189
+   fprintf(error ? stderr : stdout, "Usage:\n%s options\n\n"
190
        "Options:\n"
191
        "  -b, --batch-mode              run in non-interactive batch mode\n"
192
        "  -n, --iterations = NUMBER             exit after NUMBER batch iterations\n"
193
pipewire-1.0.1.tar.bz2/src/tools/reserve.c -> pipewire-1.2.0.tar.gz/src/tools/reserve.c Changed
112
 
1
@@ -11,7 +11,7 @@
2
 #include <spa/utils/string.h>
3
 #include <pipewire/log.h>
4
 
5
-#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
6
+#define SERVICE_NAMESPACE "org.freedesktop.ReserveDevice1"
7
 #define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
8
 
9
 static const char introspection =
10
@@ -307,6 +307,34 @@
11
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
12
 }
13
 
14
+bool rd_device_valid_device_name(const char *name)
15
+{
16
+   const char *p = name;
17
+
18
+   if (!p || !*p)
19
+       return false;
20
+
21
+   /* cf. dbus-marshal-validate.c:_dbus_validate_bus_name_full */
22
+   while (*p) {
23
+       switch (*p) {
24
+       case 'A' ... 'Z':
25
+       case 'a' ... 'z':
26
+       case '-':
27
+       case '_':
28
+           break;
29
+       case '0' ... '9':
30
+           if (p == name)
31
+               return false;
32
+           break;
33
+       default:
34
+           return false;
35
+       }
36
+       ++p;
37
+   }
38
+
39
+   return true;
40
+}
41
+
42
 struct rd_device *
43
 rd_device_new(DBusConnection *connection, const char *device_name, const char *application_name,
44
        int32_t priority, const struct rd_device_callbacks *callbacks, void *data)
45
@@ -314,6 +342,11 @@
46
    struct rd_device *d;
47
    int res;
48
 
49
+   if (!rd_device_valid_device_name(device_name)) {
50
+       errno = EINVAL;
51
+       return NULL;
52
+   }
53
+
54
    d = calloc(1, sizeof(struct rd_device));
55
    if (d == NULL)
56
        return NULL;
57
@@ -330,7 +363,7 @@
58
        res = -errno;
59
        goto error_free;
60
    }
61
-   d->service_name = spa_aprintf(SERVICE_PREFIX "%s", device_name);
62
+   d->service_name = spa_aprintf(SERVICE_NAMESPACE ".%s", device_name);
63
    if (d->service_name == NULL) {
64
        res = -errno;
65
        goto error_free;
66
@@ -351,7 +384,7 @@
67
                         "interface='org.freedesktop.DBus',member='NameAcquired'", NULL);
68
    dbus_bus_add_match(d->connection,
69
                         "type='signal',sender='org.freedesktop.DBus',"
70
-                        "interface='org.freedesktop.DBus',member='NameOwnerChanged'", NULL);
71
+                        "interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0namespace='" SERVICE_NAMESPACE "'", NULL);
72
 
73
    dbus_connection_ref(d->connection);
74
 
75
@@ -403,6 +436,7 @@
76
 int rd_device_request_release(struct rd_device *d)
77
 {
78
    DBusMessage *m = NULL;
79
+   int res = 0;
80
 
81
    if (d->priority <= INT32_MIN)
82
        return -EBUSY;
83
@@ -413,16 +447,22 @@
84
                    "RequestRelease")) == NULL) {
85
        return -ENOMEM;
86
    }
87
-        if (!dbus_message_append_args(m,
88
+
89
+   if (!dbus_message_append_args(m,
90
                DBUS_TYPE_INT32, &d->priority,
91
                DBUS_TYPE_INVALID)) {
92
-       dbus_message_unref(m);
93
-       return -ENOMEM;
94
-        }
95
+       res = -ENOMEM;
96
+       goto exit;
97
+   }
98
+
99
    if (!dbus_connection_send(d->connection, m, NULL)) {
100
-       return -EIO;
101
+       res = -EIO;
102
+       goto exit;
103
    }
104
-   return 0;
105
+
106
+exit:
107
+   dbus_message_unref(m);
108
+   return res;
109
 }
110
 
111
 int rd_device_complete_release(struct rd_device *d, int res)
112
pipewire-1.0.1.tar.bz2/src/tools/reserve.h -> pipewire-1.2.0.tar.gz/src/tools/reserve.h Changed
19
 
1
@@ -7,6 +7,7 @@
2
 
3
 #include <dbus/dbus.h>
4
 #include <inttypes.h>
5
+#include <stdbool.h>
6
 
7
 #ifdef __cplusplus
8
 extern "C" {
9
@@ -51,6 +52,9 @@
10
 /** destroy a device */
11
 void rd_device_destroy(struct rd_device *d);
12
 
13
+/** check if device name is a valid name */
14
+bool rd_device_valid_device_name(const char *name);
15
+
16
 /* Set the application device name for an rd_device object. Returns 0
17
  * on success, a negative errno style return value on error. */
18
 int rd_device_set_application_device_name(struct rd_device *d, const char *name);
19
pipewire-1.2.0.tar.gz/test/data Added
2
 
1
+(directory)
2
pipewire-1.2.0.tar.gz/test/data/test-spa-json.txt Added
pipewire-1.0.1.tar.bz2/test/meson.build -> pipewire-1.2.0.tar.gz/test/meson.build Changed
17
 
1
@@ -83,6 +83,7 @@
2
                link_with: pwtest_lib)
3
 )
4
 
5
+if get_option('spa-plugins').allowed()
6
 test('test-context',
7
     executable('test-context',
8
                'test-context.c',
9
@@ -106,6 +107,7 @@
10
                dependencies: spa_dep, systemd_dep, spa_support_dep, spa_journal_dep,
11
                link_with: pwtest_lib)
12
 )
13
+endif
14
 test('test-spa',
15
     executable('test-spa',
16
                'test-spa-buffer.c',
17
pipewire-1.0.1.tar.bz2/test/pwtest.c -> pipewire-1.2.0.tar.gz/test/pwtest.c Changed
9
 
1
@@ -787,6 +787,7 @@
2
    replace_env(t, "ACP_PATHS_DIR", SOURCE_ROOT "/spa/plugins/alsa/mixer/paths");
3
    replace_env(t, "ACP_PROFILES_DIR", SOURCE_ROOT "/spa/plugins/alsa/mixer/profile-sets");
4
    replace_env(t, "PIPEWIRE_LOG_SYSTEMD", "false");
5
+   replace_env(t, "PWTEST_DATA_DIR", SOURCE_ROOT "/test/data");
6
 }
7
 
8
 static void close_pipes(int fds_FD_LAST)
9
pipewire-1.0.1.tar.bz2/test/test-logger.c -> pipewire-1.2.0.tar.gz/test/test-logger.c Changed
10
 
1
@@ -356,7 +356,7 @@
2
    fsync(STDERR_FILENO);
3
    lseek(fd, SEEK_SET, 0);
4
    while ((rc = read(fd, buf, sizeof(buf) - 1) > 0)) {
5
-       if (strstr(buf, "Ignoring invalid format in PIPEWIRE_DEBUG")) {
6
+       if (strstr(buf, "Ignoring invalid format in log level")) {
7
            error_message_found = true;
8
            break;
9
        }
10
pipewire-1.0.1.tar.bz2/test/test-loop.c -> pipewire-1.2.0.tar.gz/test/test-loop.c Changed
10
 
1
@@ -387,7 +387,7 @@
2
 PWTEST(cancel_thread_while_dispatching)
3
 {
4
    static const struct spa_dict_item data_loop_props_items = {
5
-       { "loop.cancel", "true" },
6
+       { PW_KEY_LOOP_CANCEL, "true" },
7
    };
8
    static const struct spa_dict data_loop_props = SPA_DICT_INIT_ARRAY(data_loop_props_items);
9
 
10
pipewire-1.0.1.tar.bz2/test/test-properties.c -> pipewire-1.2.0.tar.gz/test/test-properties.c Changed
19
 
1
@@ -162,7 +162,7 @@
2
    p = pw_properties_new(NULL, NULL);
3
    pwtest_ptr_notnull(p);
4
    pwtest_ptr_null(pw_properties_get(p, "k1"));
5
-   pw_properties_free(p); /* sefault/valgrind only check */
6
+   pw_properties_free(p); /* segfault/valgrind only check */
7
 
8
    p = pw_properties_new_string("k1=v1 k2 = v2\tk3\t=\tv3\nk4\n=\nv4");
9
    pwtest_str_eq(pw_properties_get(p, "k1"), "v1");
10
@@ -207,7 +207,7 @@
11
 
12
    pw_properties_free(props);
13
 
14
-   props = pw_properties_new_string("foo=bar bar=\"baz");
15
+   props = pw_properties_new_string("foo=bar bar=\"baz\"");
16
    pwtest_ptr_notnull(props);
17
    pwtest_int_eq(props->flags, 0U);
18
    pwtest_int_eq(props->dict.n_items, 2U);
19
pipewire-1.0.1.tar.bz2/test/test-spa-buffer.c -> pipewire-1.2.0.tar.gz/test/test-spa-buffer.c Changed
21
 
1
@@ -17,7 +17,8 @@
2
    pwtest_int_eq(SPA_DATA_MemFd, 2);
3
    pwtest_int_eq(SPA_DATA_DmaBuf, 3);
4
    pwtest_int_eq(SPA_DATA_MemId, 4);
5
-   pwtest_int_eq(_SPA_DATA_LAST, 5);
6
+   pwtest_int_eq(SPA_DATA_SyncObj, 5);
7
+   pwtest_int_eq(_SPA_DATA_LAST, 6);
8
 
9
    /* meta */
10
    pwtest_int_eq(SPA_META_Invalid, 0);
11
@@ -29,7 +30,8 @@
12
    pwtest_int_eq(SPA_META_Control, 6);
13
    pwtest_int_eq(SPA_META_Busy, 7);
14
    pwtest_int_eq(SPA_META_VideoTransform, 8);
15
-   pwtest_int_eq(_SPA_META_LAST, 9);
16
+   pwtest_int_eq(SPA_META_SyncTimeline, 9);
17
+   pwtest_int_eq(_SPA_META_LAST, 10);
18
 
19
    return PWTEST_PASS;
20
 }
21
pipewire-1.0.1.tar.bz2/test/test-spa-json.c -> pipewire-1.2.0.tar.gz/test/test-spa-json.c Changed
874
 
1
@@ -3,12 +3,14 @@
2
 /* SPDX-License-Identifier: MIT */
3
 
4
 #include <locale.h>
5
+#include <stdio.h>
6
 
7
 #include "pwtest.h"
8
 
9
 #include <spa/utils/defs.h>
10
 #include <spa/utils/json.h>
11
 #include <spa/utils/string.h>
12
+#include <spa/utils/cleanup.h>
13
 
14
 PWTEST(json_abi)
15
 {
16
@@ -29,6 +31,7 @@
17
 #define TYPE_TRUE  5
18
 #define TYPE_FALSE 6
19
 #define TYPE_FLOAT 7
20
+#define TYPE_INT   8
21
 
22
 static void check_type(int type, const char *value, int len)
23
 {
24
@@ -38,9 +41,25 @@
25
    pwtest_bool_eq(spa_json_is_bool(value, len),
26
            (type == TYPE_BOOL || type == TYPE_TRUE || type == TYPE_FALSE));
27
    pwtest_bool_eq(spa_json_is_null(value, len), (type == TYPE_NULL));
28
-   pwtest_bool_eq(spa_json_is_true(value, len), (type == TYPE_TRUE || type == TYPE_BOOL));
29
-   pwtest_bool_eq(spa_json_is_false(value, len), (type == TYPE_FALSE || type == TYPE_BOOL));
30
-   pwtest_bool_eq(spa_json_is_float(value, len), (type == TYPE_FLOAT));
31
+   if (type == TYPE_BOOL) {
32
+       pwtest_bool_true(spa_json_is_true(value, len) || spa_json_is_false(value, len));
33
+   } else {
34
+       pwtest_bool_eq(spa_json_is_true(value, len), type == TYPE_TRUE);
35
+       pwtest_bool_eq(spa_json_is_false(value, len), type == TYPE_FALSE);
36
+   }
37
+
38
+   switch (type) {
39
+   case TYPE_FLOAT:
40
+       pwtest_bool_true(spa_json_is_float(value, len));
41
+       break;
42
+   case TYPE_INT:
43
+       pwtest_bool_true(spa_json_is_int(value, len));
44
+       break;
45
+   default:
46
+       pwtest_bool_false(spa_json_is_float(value, len));
47
+       pwtest_bool_false(spa_json_is_int(value, len));
48
+       break;
49
+   }
50
 }
51
 
52
 static void expect_type(struct spa_json *it, int type)
53
@@ -51,6 +70,49 @@
54
    check_type(type, value, len);
55
 }
56
 
57
+static void expect_end(struct spa_json *it)
58
+{
59
+   const char *value;
60
+   struct spa_json it2;
61
+
62
+   pwtest_int_eq(spa_json_next(it, &value), 0);
63
+
64
+   /* end is idempotent */
65
+   memcpy(&it2, it, sizeof(*it));
66
+   pwtest_int_eq(spa_json_next(it, &value), 0);
67
+   pwtest_int_eq(memcmp(&it2, it, sizeof(*it)), 0);
68
+}
69
+
70
+static void expect_parse_error(struct spa_json *it, const char *str, int line, int col)
71
+{
72
+   const char *value;
73
+   struct spa_json it2;
74
+   struct spa_error_location loc = { 0 };
75
+
76
+   pwtest_int_eq(spa_json_next(it, &value), -1);
77
+   pwtest_bool_true(spa_json_get_error(it, str, &loc));
78
+   pwtest_int_eq(loc.line, line);
79
+   pwtest_int_eq(loc.col, col);
80
+
81
+   /* parse error is idempotent also for parents */
82
+   while (it) {
83
+       memcpy(&it2, it, sizeof(*it));
84
+       pwtest_int_eq(spa_json_next(it, &value), -1);
85
+       pwtest_int_eq(memcmp(&it2, it, sizeof(*it)), 0);
86
+       it = it->parent;
87
+   }
88
+}
89
+
90
+static void expect_array(struct spa_json *it, struct spa_json *sub)
91
+{
92
+   pwtest_int_eq(spa_json_enter_array(it, sub), 1);
93
+}
94
+
95
+static void expect_object(struct spa_json *it, struct spa_json *sub)
96
+{
97
+   pwtest_int_eq(spa_json_enter_object(it, sub), 1);
98
+}
99
+
100
 static void expect_string(struct spa_json *it, const char *str)
101
 {
102
    const char *value;
103
@@ -59,9 +121,21 @@
104
    pwtest_int_gt((len = spa_json_next(it, &value)), 0);
105
    check_type(TYPE_STRING, value, len);
106
    s = alloca(len+1);
107
-   spa_json_parse_stringn(value, len, s, len+1);
108
+   pwtest_int_eq(spa_json_parse_stringn(value, len, s, len+1), 1);
109
    pwtest_str_eq(s, str);
110
 }
111
+
112
+static void expect_string_or_bare(struct spa_json *it, const char *str)
113
+{
114
+   const char *value;
115
+   int len;
116
+   char *s;
117
+   pwtest_int_gt((len = spa_json_next(it, &value)), 0);
118
+   s = alloca(len+1);
119
+   pwtest_int_eq(spa_json_parse_stringn(value, len, s, len+1), 1);
120
+   pwtest_str_eq(s, str);
121
+}
122
+
123
 static void expect_float(struct spa_json *it, float val)
124
 {
125
    const char *value;
126
@@ -73,11 +147,42 @@
127
    pwtest_double_eq(f, val);
128
 }
129
 
130
+static void expect_int(struct spa_json *it, int val)
131
+{
132
+   const char *value;
133
+   int len;
134
+   int f = 0;
135
+   pwtest_int_gt((len = spa_json_next(it, &value)), 0);
136
+   check_type(TYPE_INT, value, len);
137
+   pwtest_int_gt(spa_json_parse_int(value, len, &f), 0);
138
+   pwtest_int_eq(f, val);
139
+}
140
+
141
+static void expect_bool(struct spa_json *it, bool val)
142
+{
143
+   const char *value;
144
+   int len;
145
+   bool f = false;
146
+   pwtest_int_gt((len = spa_json_next(it, &value)), 0);
147
+   check_type(TYPE_BOOL, value, len);
148
+   check_type(val ? TYPE_TRUE : TYPE_FALSE, value, len);
149
+   pwtest_int_gt(spa_json_parse_bool(value, len, &f), 0);
150
+   pwtest_int_eq(f, val);
151
+}
152
+
153
+static void expect_null(struct spa_json *it)
154
+{
155
+   const char *value;
156
+   int len;
157
+   pwtest_int_gt((len = spa_json_next(it, &value)), 0);
158
+   check_type(TYPE_NULL, value, len);
159
+}
160
+
161
 PWTEST(json_parse)
162
 {
163
    struct spa_json it5;
164
    const char *json = " { "
165
-           "\"foo\": \"bar\","
166
+           "\"foo\": \"bar\", # comment\n"
167
            "\"foo\\\"  \":   true,       "
168
            "\"foo \\n\\r\\t\": false,"
169
            "  \"  arr\":  true, false, null, 5, 5.7, \"str\","
170
@@ -115,6 +220,8 @@
171
    expect_float(&it1, -1.8f);
172
    expect_string(&it1, "foo 6");
173
    expect_float(&it1, +2.8f);
174
+   expect_end(&it1);
175
+   expect_end(&it0);
176
    /* in the array */
177
    expect_type(&it2, TYPE_TRUE);
178
    expect_type(&it2, TYPE_FALSE);
179
@@ -135,6 +242,334 @@
180
    expect_string(&it3, "1.9");
181
    expect_float(&it3, 1.9f);
182
 
183
+   expect_end(&it3);
184
+   expect_end(&it2);
185
+
186
+   pwtest_bool_false(spa_json_get_error(&it0, NULL, NULL));
187
+   pwtest_bool_false(spa_json_get_error(&it1, NULL, NULL));
188
+   pwtest_bool_false(spa_json_get_error(&it2, NULL, NULL));
189
+   pwtest_bool_false(spa_json_get_error(&it3, NULL, NULL));
190
+
191
+   json = "section={\"key\":value}, section2=item1,item2";
192
+
193
+   spa_json_init(&it0, json, strlen(json));
194
+   expect_string_or_bare(&it0, "section");
195
+   expect_object(&it0, &it1);
196
+   expect_string_or_bare(&it0, "section2");
197
+   expect_array(&it0, &it1);
198
+   expect_end(&it0);
199
+
200
+   spa_json_init(&it0, json, strlen(json));
201
+   expect_string_or_bare(&it0, "section");
202
+   expect_object(&it0, &it1);
203
+   expect_string(&it1, "key");
204
+   expect_string_or_bare(&it1, "value");
205
+   expect_string_or_bare(&it0, "section2");
206
+   expect_array(&it0, &it1);
207
+   expect_string_or_bare(&it1, "item1");
208
+   expect_string_or_bare(&it1, "item2");
209
+   expect_end(&it0);
210
+
211
+   /* 2-byte utf8 */
212
+   json = "\"\xc3\xa4\", \"\xc3\xa4\"";
213
+   spa_json_init(&it0, json, strlen(json));
214
+   expect_string(&it0, "\xc3\xa4");
215
+   expect_string(&it0, "\xc3\xa4");
216
+   expect_end(&it0);
217
+
218
+   /* 3-byte utf8 */
219
+   json = "\"\xe6\xad\xa3\", \"\xe6\xad\xa3\"";
220
+   spa_json_init(&it0, json, strlen(json));
221
+   expect_string(&it0, "\xe6\xad\xa3");
222
+   expect_string(&it0, "\xe6\xad\xa3");
223
+   expect_end(&it0);
224
+
225
+   /* 4-byte utf8 */
226
+   json = "\"\xf0\x92\x80\x80\", \"\xf0\x92\x80\x80\"";
227
+   spa_json_init(&it0, json, strlen(json));
228
+   expect_string(&it0, "\xf0\x92\x80\x80");
229
+   expect_string(&it0, "\xf0\x92\x80\x80");
230
+   expect_end(&it0);
231
+
232
+   /* run-in comment in bare */
233
+   json = "foo#comment";
234
+   spa_json_init(&it0, json, strlen(json));
235
+   expect_string_or_bare(&it0, "foo");
236
+   expect_end(&it0);
237
+
238
+   /* end of parsing idempotent */
239
+   json = "{}";
240
+   spa_json_init(&it0, json, strlen(json));
241
+   expect_object(&it0, &it1);
242
+   expect_end(&it0);
243
+   expect_end(&it0);
244
+
245
+   /* non-null terminated strings OK */
246
+   json = "1.234";
247
+   spa_json_init(&it0, json, 4);
248
+   expect_float(&it0, 1.23f);
249
+   expect_end(&it0);
250
+
251
+   json = "1234";
252
+   spa_json_init(&it0, json, 3);
253
+   expect_int(&it0, 123);
254
+   expect_end(&it0);
255
+
256
+   json = "truey";
257
+   spa_json_init(&it0, json, 4);
258
+   expect_bool(&it0, true);
259
+   expect_end(&it0);
260
+
261
+   json = "falsey";
262
+   spa_json_init(&it0, json, 5);
263
+   expect_bool(&it0, false);
264
+   expect_end(&it0);
265
+
266
+   json = "nully";
267
+   spa_json_init(&it0, json, 4);
268
+   expect_null(&it0);
269
+   expect_end(&it0);
270
+
271
+   json = "{}y{";
272
+   spa_json_init(&it0, json, 2);
273
+   expect_object(&it0, &it1);
274
+   expect_end(&it0);
275
+
276
+   json = "y{";
277
+   spa_json_init(&it0, json, 2);
278
+   expect_array(&it0, &it1);
279
+   expect_end(&it0);
280
+
281
+   json = "helloy";
282
+   spa_json_init(&it0, json, 5);
283
+   expect_string_or_bare(&it0, "hello");
284
+   expect_end(&it0);
285
+
286
+   json = "\"hello\"y";
287
+   spa_json_init(&it0, json, 7);
288
+   expect_string(&it0, "hello");
289
+   expect_end(&it0);
290
+
291
+   /* top-level context */
292
+   json = "x y x y";
293
+   spa_json_init(&it0, json, strlen(json));
294
+   expect_string_or_bare(&it0, "x");
295
+   expect_string_or_bare(&it0, "y");
296
+   expect_string_or_bare(&it0, "x");
297
+   expect_string_or_bare(&it0, "y");
298
+   expect_end(&it0);
299
+
300
+   json = "x = y x = y";
301
+   spa_json_init(&it0, json, strlen(json));
302
+   expect_string_or_bare(&it0, "x");
303
+   expect_string_or_bare(&it0, "y");
304
+   expect_string_or_bare(&it0, "x");
305
+   expect_string_or_bare(&it0, "y");
306
+   expect_end(&it0);
307
+
308
+   return PWTEST_PASS;
309
+}
310
+
311
+PWTEST(json_parse_fail)
312
+{
313
+   char buf2048;
314
+   struct spa_json it5;
315
+   const char *json, *value;
316
+   int i;
317
+
318
+   /* = in array */
319
+   json = " foo = bar ";
320
+   spa_json_init(&it0, json, strlen(json));
321
+   expect_array(&it0, &it1);
322
+   expect_string_or_bare(&it1, "foo");
323
+   expect_parse_error(&it1, json, 1, 7);
324
+   expect_parse_error(&it1, json, 1, 7);  /* parse error is idempotent */
325
+   expect_parse_error(&it0, json, 1, 7);  /* parse error visible in parent */
326
+
327
+   /* : in array */
328
+   json = " foo, bar\n : quux ";
329
+   spa_json_init(&it0, json, strlen(json));
330
+   expect_array(&it0, &it1);
331
+   expect_string_or_bare(&it1, "foo");
332
+   expect_string_or_bare(&it1, "bar");
333
+   expect_parse_error(&it1, json, 2, 2);
334
+
335
+   /* missing  */
336
+   json = " foo, bar";
337
+   spa_json_init(&it0, json, strlen(json));
338
+   pwtest_int_eq(spa_json_next(&it0, &value), 1);
339
+   expect_parse_error(&it0, json, 1, 11);
340
+
341
+   /* spurious  */
342
+   json = "foo, bar ";
343
+   spa_json_init(&it0, json, strlen(json));
344
+   pwtest_int_eq(spa_json_next(&it0, &value), 3);
345
+   pwtest_int_eq(spa_json_next(&it0, &value), 3);
346
+   expect_parse_error(&it0, json, 1, 10);
347
+
348
+   /* spurious } */
349
+   json = "{ foo, bar } }";
350
+   spa_json_init(&it0, json, strlen(json));
351
+   expect_object(&it0, &it1);
352
+   expect_parse_error(&it0, json, 1, 14);
353
+
354
+   /* bad nesting */
355
+   json = "{a: {a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a: }}}}}}}}}}}} ";
356
+   spa_json_init(&it0, json, strlen(json));
357
+   pwtest_int_eq(spa_json_next(&it0, &value), 1);
358
+   expect_parse_error(&it0, json, 1, strlen(json));
359
+
360
+   /* bad nesting */
361
+   json = " {a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a: }}}}}}}}}}}} }";
362
+   spa_json_init(&it0, json, strlen(json));
363
+   pwtest_int_eq(spa_json_next(&it0, &value), 1);
364
+   expect_parse_error(&it0, json, 1, strlen(json));
365
+
366
+   /* bad object key-values */
367
+   json = "{ = }";
368
+   spa_json_init(&it0, json, strlen(json));
369
+   expect_object(&it0, &it1);
370
+   expect_parse_error(&it1, json, 1, 3);
371
+
372
+   json = "{ x }";
373
+   spa_json_init(&it0, json, strlen(json));
374
+   expect_object(&it0, &it1);
375
+   expect_string_or_bare(&it1, "x");
376
+   expect_parse_error(&it1, json, 1, 5);
377
+
378
+   json = "{ x : }";
379
+   spa_json_init(&it0, json, strlen(json));
380
+   expect_object(&it0, &it1);
381
+   expect_string_or_bare(&it1, "x");
382
+   expect_parse_error(&it1, json, 1, 7);
383
+
384
+   json = "{ x = y, : }";
385
+   spa_json_init(&it0, json, strlen(json));
386
+   expect_object(&it0, &it1);
387
+   expect_string_or_bare(&it1, "x");
388
+   expect_string_or_bare(&it1, "y");
389
+   expect_parse_error(&it1, json, 1, 10);
390
+
391
+   json = "{ x = {1:3}, z : }";
392
+   spa_json_init(&it0, json, strlen(json));
393
+   expect_object(&it0, &it1);
394
+   expect_string_or_bare(&it1, "x");
395
+   expect_object(&it1, &it2);
396
+   expect_string_or_bare(&it1, "z");
397
+   expect_parse_error(&it1, json, 1, 18);
398
+
399
+   json = "{ x y x }";
400
+   spa_json_init(&it0, json, strlen(json));
401
+   expect_object(&it0, &it1);
402
+   expect_string_or_bare(&it1, "x");
403
+   expect_string_or_bare(&it1, "y");
404
+   expect_string_or_bare(&it1, "x");
405
+   expect_parse_error(&it1, json, 1, 9);
406
+
407
+   json = "x y x";
408
+   spa_json_init(&it0, json, strlen(json));
409
+   expect_string_or_bare(&it0, "x");
410
+   expect_string_or_bare(&it0, "y");
411
+   expect_parse_error(&it0, json, 1, 6);
412
+
413
+   /* unclosed string */
414
+   json = "\"foo";
415
+   spa_json_init(&it0, json, strlen(json));
416
+   expect_parse_error(&it0, json, 1, 5);
417
+
418
+   /* unclosed string */
419
+   json = "foo\"";
420
+   spa_json_init(&it0, json, strlen(json));
421
+   expect_string_or_bare(&it0, "foo");
422
+   expect_parse_error(&it0, json, 1, 5);
423
+
424
+   /* unclosed string */
425
+   json = "foo\"bar";
426
+   spa_json_init(&it0, json, strlen(json));
427
+   expect_string_or_bare(&it0, "foo");
428
+   expect_parse_error(&it0, json, 1, 8);
429
+
430
+   /* unclosed escape */
431
+   json = "\"\\";
432
+   spa_json_init(&it0, json, strlen(json));
433
+   expect_parse_error(&it0, json, 1, 3);
434
+
435
+   /* bare escape */
436
+   json = "foo\\n";
437
+   spa_json_init(&it0, json, strlen(json));
438
+   expect_parse_error(&it0, json, 1, 4);
439
+
440
+   /* bare escape */
441
+   json = "\\nfoo";
442
+   spa_json_init(&it0, json, strlen(json));
443
+   expect_parse_error(&it0, json, 1, 1);
444
+
445
+   /* bad nesting in subparser */
446
+   json = "{a:";
447
+   spa_json_init(&it0, json, strlen(json));
448
+   expect_object(&it0, &it1);
449
+   expect_string_or_bare(&it1, "a");
450
+   expect_array(&it1, &it2);
451
+   expect_parse_error(&it1, json, 1, 6);
452
+
453
+   /* entered parser assumes nesting */
454
+   json = "";
455
+   spa_json_init(&it0, json, strlen(json));
456
+   spa_json_enter(&it0, &it1);
457
+   expect_array(&it1, &it2);
458
+   expect_parse_error(&it1, json, 1, 3);
459
+
460
+   /* overflowing parser nesting stack is an error*/
461
+   for (i = 0; i < 514; ++i)
462
+       bufi = '';
463
+   for (; i < 2*514; ++i)
464
+       bufi = '';
465
+   bufi++ = '\0';
466
+
467
+   spa_json_init(&it0, buf, strlen(buf));
468
+   pwtest_int_eq(spa_json_next(&it0, &value), 1);
469
+   expect_parse_error(&it0, buf, 1, 514);
470
+
471
+   /* bad utf8 */
472
+   json = "\"\xc0\"";
473
+   spa_json_init(&it0, json, strlen(json));
474
+   expect_parse_error(&it0, json, 1, 3);
475
+
476
+   json = "\"\xe6\xad\"";
477
+   spa_json_init(&it0, json, strlen(json));
478
+   expect_parse_error(&it0, json, 1, 4);
479
+
480
+   json = "\"\xf0\x92\x80\"";
481
+   spa_json_init(&it0, json, strlen(json));
482
+   expect_parse_error(&it0, json, 1, 5);
483
+
484
+   /* bad string */
485
+   json = "\"\x01\"";
486
+   spa_json_init(&it0, json, strlen(json));
487
+   expect_parse_error(&it0, json, 1, 2);
488
+
489
+   json = "\"\x0f\"";
490
+   spa_json_init(&it0, json, strlen(json));
491
+   expect_parse_error(&it0, json, 1, 2);
492
+
493
+   /* bad escape */
494
+   json = "\"\\z\"";
495
+   spa_json_init(&it0, json, strlen(json));
496
+   expect_parse_error(&it0, json, 1, 3);
497
+
498
+   /* bad bare */
499
+   json = "\x01x";
500
+   spa_json_init(&it0, json, strlen(json));
501
+   expect_parse_error(&it0, json, 1, 1);
502
+
503
+   json = "x\x01";
504
+   spa_json_init(&it0, json, strlen(json));
505
+   expect_parse_error(&it0, json, 1, 2);
506
+
507
+   json = "\xc3\xa4";
508
+   spa_json_init(&it0, json, strlen(json));
509
+   expect_parse_error(&it0, json, 1, 1);
510
+
511
    return PWTEST_PASS;
512
 }
513
 
514
@@ -231,7 +666,7 @@
515
        { "00.000100", 00.000100f },
516
        { "-0.000100", -0.000100f },
517
    };
518
-   float v;
519
+   float v = 0.0f;
520
    unsigned i;
521
    char buf1128, buf2128, *b1 = buf1, *b2 = buf2;
522
 
523
@@ -298,16 +733,350 @@
524
    return PWTEST_PASS;
525
 }
526
 
527
+static char *read_json_testcase(FILE *f, char **name, size_t *size, char **result, size_t *result_size)
528
+{
529
+   ssize_t res;
530
+   spa_autofree char *data = NULL;
531
+   spa_autofree char *resdata = NULL;
532
+   spa_autofree char *buf = NULL;
533
+   size_t alloc = 0;
534
+   size_t len = 0;
535
+   size_t resdata_len = 0;
536
+   char **dst = &data;
537
+   size_t *dst_len = &len;
538
+
539
+   *name = NULL;
540
+
541
+   do {
542
+       res = getline(&buf, &alloc, f);
543
+       if (res <= 0)
544
+           return NULL;
545
+
546
+       if (spa_strstartswith(buf, "<<< ")) {
547
+           size_t k = strcspn(buf + 4, " \t\n");
548
+           free(*name);
549
+           *name = strndup(buf + 4, k);
550
+           continue;
551
+       } else if (spa_strstartswith(buf, "==")) {
552
+           dst = &resdata;
553
+           dst_len = &resdata_len;
554
+           continue;
555
+       } else if (spa_strstartswith(buf, ">>>")) {
556
+           break;
557
+       } else if (!*name) {
558
+           continue;
559
+       }
560
+
561
+       if (!*dst) {
562
+           *dst = spa_steal_ptr(buf);
563
+           *dst_len = res;
564
+           buf = NULL;
565
+           alloc = 0;
566
+       } else {
567
+           char *p = realloc(*dst, *dst_len + res + 1);
568
+
569
+           pwtest_ptr_notnull(p);
570
+
571
+           *dst = p;
572
+           memcpy(*dst + *dst_len, buf, res);
573
+           *dst_len += res;
574
+           (*dst)*dst_len = '\0';
575
+       }
576
+   } while (1);
577
+
578
+   if (!*name)
579
+       return NULL;
580
+
581
+   *result = spa_steal_ptr(resdata);
582
+   *result_size = resdata_len;
583
+
584
+   *size = len;
585
+   return spa_steal_ptr(data);
586
+}
587
+
588
+static int validate_strict_json(struct spa_json *it, int depth, FILE *f)
589
+{
590
+   struct spa_json sub;
591
+   int res = 0, len;
592
+   char key1024;
593
+   const char *value;
594
+
595
+   len = spa_json_next(it, &value);
596
+   if (len <= 0)
597
+       goto done;
598
+
599
+   if (depth > 50) {
600
+       /* stop descending */
601
+       while ((len = spa_json_next(it, &value)) > 0);
602
+       goto done;
603
+   }
604
+
605
+   if (spa_json_is_array(value, len)) {
606
+       bool empty = true;
607
+
608
+       fputc('', f);
609
+       spa_json_enter(it, &sub);
610
+       while ((res = validate_strict_json(&sub, depth+1, f)) > 0) {
611
+           empty = false;
612
+           fputc(',', f);
613
+       }
614
+       if (res < 0)
615
+           return res;
616
+       if (!empty)
617
+           fseek(f, -1, SEEK_CUR);
618
+       fputc('', f);
619
+   } else if (spa_json_is_object(value, len)) {
620
+       bool empty = true;
621
+
622
+       spa_json_enter(it, &sub);
623
+
624
+       fputc('{', f);
625
+       while (spa_json_get_string(&sub, key, sizeof(key)) > 0) {
626
+           fprintf(f, "\"%s\":", key);
627
+
628
+           res = validate_strict_json(&sub, depth+1, f);
629
+           if (res < 0)
630
+               return res;
631
+           if (res == 0)
632
+               return -2; /* empty object body */
633
+           fputc(',', f);
634
+           empty = false;
635
+       }
636
+       if (!empty)
637
+           fseek(f, -1, SEEK_CUR);
638
+       fputc('}', f);
639
+   } else if (spa_json_is_string(value, len)) {
640
+       char buf1024;
641
+       int i;
642
+
643
+       spa_json_parse_stringn(value, len, buf, sizeof(buf));
644
+
645
+       fputc('"', f);
646
+       for (i = 0; bufi; ++i) {
647
+           uint8_t c = bufi;
648
+           switch (c) {
649
+           case '\n': fputs("\\n", f); break;
650
+           case '\b': fputs("\\b", f); break;
651
+           case '\f': fputs("\\f", f); break;
652
+           case '\t': fputs("\\t", f); break;
653
+           case '\r': fputs("\\r", f); break;
654
+           case '"': fputs("\\\"", f); break;
655
+           case '\\': fputs("\\\\", f); break;
656
+           default:
657
+               if (c < 32 || c == 0x7f) {
658
+                   fprintf(f, "\\u%04x", c);
659
+               } else {
660
+                   fputc(c, f);
661
+               }
662
+               break;
663
+           }
664
+       }
665
+       fputc('"', f);
666
+   } else if (spa_json_is_null(value, len)) {
667
+       fprintf(f, "null");
668
+   } else if (spa_json_is_bool(value, len)) {
669
+       fprintf(f, spa_json_is_true(value, len) ? "true" : "false");
670
+   } else if (spa_json_is_int(value, len)) {
671
+       int v;
672
+       if (spa_json_parse_int(value, len, &v) > 0)
673
+           fprintf(f, "%d", v);
674
+   } else if (spa_json_is_float(value, len)) {
675
+       float v;
676
+       if (spa_json_parse_float(value, len, &v) > 0)
677
+           fprintf(f, "%G", v);
678
+   } else {
679
+       /* bare value: error here, as we want to test
680
+        * int/float/etc parsing */
681
+       return -2;
682
+   }
683
+
684
+done:
685
+   if (spa_json_get_error(it, NULL, NULL))
686
+       return -1;
687
+
688
+   return len;
689
+}
690
+
691
+PWTEST(json_data)
692
+{
693
+   static const char * const extra_success = {
694
+       /* indeterminate cases that succeed */
695
+       "i_number_double_huge_neg_exp.json",
696
+       "i_number_neg_int_huge_exp.json",
697
+       "i_number_pos_double_huge_exp.json",
698
+       "i_number_real_neg_overflow.json",
699
+       "i_number_real_pos_overflow.json",
700
+       "i_number_real_underflow.json",
701
+       "i_number_too_big_neg_int.json",
702
+       "i_number_too_big_pos_int.json",
703
+       "i_number_very_big_negative_int.json",
704
+       "i_object_key_lone_2nd_surrogate.json",
705
+       "i_string_1st_surrogate_but_2nd_missing.json",
706
+       "i_string_1st_valid_surrogate_2nd_invalid.json",
707
+       "i_string_incomplete_surrogate_and_escape_valid.json",
708
+       "i_string_incomplete_surrogate_pair.json",
709
+       "i_string_incomplete_surrogates_escape_valid.json",
710
+       "i_string_invalid_lonely_surrogate.json",
711
+       "i_string_invalid_surrogate.json",
712
+       "i_string_inverted_surrogates_U+1D11E.json",
713
+       "i_string_lone_second_surrogate.json",
714
+       "i_string_not_in_unicode_range.json",
715
+       "i_string_overlong_sequence_2_bytes.json",
716
+       "i_string_UTF8_surrogate_U+D800.json",
717
+       "i_structure_500_nested_arrays.json",
718
+
719
+       /* relaxed JSON parsing */
720
+       "n_array_1_true_without_comma.json",
721
+       "n_array_comma_after_close.json",
722
+       "n_array_comma_and_number.json",
723
+       "n_array_double_comma.json",
724
+       "n_array_double_extra_comma.json",
725
+       "n_array_extra_comma.json",
726
+       "n_array_just_comma.json",
727
+       "n_array_missing_value.json",
728
+       "n_array_number_and_comma.json",
729
+       "n_array_number_and_several_commas.json",
730
+       "n_object_comma_instead_of_colon.json",
731
+       "n_object_double_colon.json",
732
+       "n_object_missing_semicolon.json",
733
+       "n_object_non_string_key_but_huge_number_instead.json",
734
+       "n_object_non_string_key.json",
735
+       "n_object_repeated_null_null.json",
736
+       "n_object_several_trailing_commas.json",
737
+       "n_object_single_quote.json",
738
+       "n_object_trailing_comma.json",
739
+       "n_object_two_commas_in_a_row.json",
740
+       "n_object_unquoted_key.json",
741
+       "n_object_with_trailing_garbage.json",
742
+       "n_single_space.json",
743
+       "n_structure_no_data.json",
744
+       "n_structure_null-byte-outside-string.json",
745
+       "n_structure_trailing_#.json",
746
+       "n_multidigit_number_then_00.json",
747
+
748
+       /* SPA JSON accepts more number formats */
749
+       "n_number_-01.json",
750
+       "n_number_0.e1.json",
751
+       "n_number_1_000.json",
752
+       "n_number_+1.json",
753
+       "n_number_2.e+3.json",
754
+       "n_number_2.e-3.json",
755
+       "n_number_2.e3.json",
756
+       "n_number_.2e-3.json",
757
+       "n_number_-2..json",
758
+       "n_number_hex_1_digit.json",
759
+       "n_number_hex_2_digits.json",
760
+       "n_number_neg_int_starting_with_zero.json",
761
+       "n_number_neg_real_without_int_part.json",
762
+       "n_number_real_without_fractional_part.json",
763
+       "n_number_starting_with_dot.json",
764
+       "n_number_with_leading_zero.json",
765
+
766
+       /* \u escape not validated */
767
+       "n_string_1_surrogate_then_escape_u1.json",
768
+       "n_string_1_surrogate_then_escape_u1x.json",
769
+       "n_string_1_surrogate_then_escape_u.json",
770
+       "n_string_incomplete_escaped_character.json",
771
+       "n_string_incomplete_surrogate.json",
772
+       "n_string_invalid_unicode_escape.json",
773
+   };
774
+
775
+   static const char * const ignore_result = {
776
+       /* Filtering duplicates is for upper layer */
777
+       "y_object_duplicated_key_and_value.json",
778
+       "y_object_duplicated_key.json",
779
+
780
+       /* spa_json_parse_string API doesn't do \0 */
781
+       "y_object_escaped_null_in_key.json",
782
+       "y_string_null_escape.json",
783
+   };
784
+
785
+   const char *basedir = getenv("PWTEST_DATA_DIR");
786
+   char pathPATH_MAX;
787
+   spa_autoptr(FILE) f = NULL;
788
+
789
+   pwtest_ptr_notnull(basedir);
790
+   spa_scnprintf(path, sizeof(path), "%s/test-spa-json.txt", basedir);
791
+   f = fopen(path, "r");
792
+   pwtest_ptr_notnull(f);
793
+
794
+   while (1) {
795
+       spa_autofree char *data = NULL;
796
+       spa_autofree char *result = NULL;
797
+       spa_autofree char *name = NULL;
798
+       size_t size;
799
+       size_t result_size;
800
+       struct spa_json it;
801
+       int expect = -1;
802
+       int res;
803
+       spa_autofree char *f_ptr = NULL;
804
+       size_t f_size;
805
+       FILE *fres;
806
+
807
+       data = read_json_testcase(f, &name, &size, &result, &result_size);
808
+       if (!data)
809
+           break;
810
+
811
+       spa_json_init(&it, data, size);
812
+
813
+       fres = open_memstream(&f_ptr, &f_size);
814
+
815
+       while ((res = validate_strict_json(&it, 0, fres)) > 0);
816
+
817
+       fclose(fres);
818
+
819
+       SPA_FOR_EACH_ELEMENT_VAR(extra_success, suc) {
820
+           if (spa_streq(*suc, name)) {
821
+               expect = false;
822
+               break;
823
+           }
824
+       }
825
+       if (expect < 0) {
826
+           if (spa_strstartswith(name, "y_"))
827
+               expect = false;
828
+           if (spa_strstartswith(name, "t_"))
829
+               expect = false;
830
+       }
831
+
832
+       SPA_FOR_EACH_ELEMENT_VAR(ignore_result, suc) {
833
+           if (spa_streq(*suc, name)) {
834
+               free(result);
835
+               result = NULL;
836
+               break;
837
+           }
838
+       }
839
+
840
+       fprintf(stdout, "%s (expect %s)\n", name, expect ? "fail" : "ok");
841
+       fflush(stdout);
842
+       pwtest_bool_eq(res == -2 || spa_json_get_error(&it, NULL, NULL), expect);
843
+       if (res == -2)
844
+           pwtest_bool_false(spa_json_get_error(&it, NULL, NULL));
845
+
846
+       if (result) {
847
+           while (strlen(result) > 0 && resultstrlen(result) - 1 == '\n')
848
+               resultstrlen(result) - 1 = '\0';
849
+
850
+           fprintf(stdout, "\tgot: >>%s<< expected: >>%s<<\n", f_ptr, result);
851
+           fflush(stdout);
852
+           pwtest_bool_true(spa_streq(f_ptr, result));
853
+       }
854
+   }
855
+
856
+   return PWTEST_PASS;
857
+}
858
+
859
 PWTEST_SUITE(spa_json)
860
 {
861
    pwtest_add(json_abi, PWTEST_NOARG);
862
    pwtest_add(json_parse, PWTEST_NOARG);
863
+   pwtest_add(json_parse_fail, PWTEST_NOARG);
864
    pwtest_add(json_encode, PWTEST_NOARG);
865
    pwtest_add(json_array, PWTEST_NOARG);
866
    pwtest_add(json_overflow, PWTEST_NOARG);
867
    pwtest_add(json_float, PWTEST_NOARG);
868
    pwtest_add(json_float_check, PWTEST_NOARG);
869
    pwtest_add(json_int, PWTEST_NOARG);
870
+   pwtest_add(json_data, PWTEST_NOARG);
871
 
872
    return PWTEST_PASS;
873
 }
874
pipewire-1.0.1.tar.bz2/test/test-spa-node.c -> pipewire-1.2.0.tar.gz/test/test-spa-node.c Changed
9
 
1
@@ -71,6 +71,7 @@
2
    pwtest_int_eq(SPA_IO_Position, 7);
3
    pwtest_int_eq(SPA_IO_RateMatch, 8);
4
    pwtest_int_eq(SPA_IO_Memory, 9);
5
+   pwtest_int_eq(SPA_IO_AsyncBuffers, 10);
6
 
7
    /* position state */
8
    pwtest_int_eq(SPA_IO_POSITION_STATE_STOPPED, 0);
9
pipewire-1.0.1.tar.bz2/test/test-spa-utils.c -> pipewire-1.2.0.tar.gz/test/test-spa-utils.c Changed
69
 
1
@@ -6,6 +6,7 @@
2
 #include <locale.h>
3
 #include <unistd.h>
4
 #include <sys/wait.h>
5
+#include <math.h>
6
 
7
 #include <valgrind/valgrind.h>
8
 
9
@@ -150,6 +151,13 @@
10
    pwtest_int_eq(SPA_CLAMP(-1, 1, 16), 1);
11
    pwtest_int_eq(SPA_CLAMP(8, 1, 16), 8);
12
 
13
+   pwtest_int_eq(SPA_CMP(INT64_MAX, INT64_MIN), 1);
14
+   pwtest_int_eq(SPA_CMP(INT64_MIN, INT64_MAX), -1);
15
+   pwtest_int_eq(SPA_CMP(INT64_MAX, INT64_MAX), 0);
16
+   pwtest_int_eq(SPA_CMP(1, nan("")), -1);
17
+   pwtest_int_eq(SPA_CMP(nan(""), 1), 1);
18
+   pwtest_int_eq(SPA_CMP(nan(""), nan("")), 1);
19
+
20
    /* SPA_MEMBER exists for backwards compatibility but should no
21
     * longer be used, let's make sure it does what we expect it to */
22
    pwtest_ptr_eq(SPA_MEMBER(ptr, 4, void), SPA_PTROFF(ptr, 4, void));
23
@@ -628,14 +636,14 @@
24
    pwtest_bool_true(spa_atof("0x1", &v));  pwtest_double_eq(v, 1.0f);
25
 
26
    v = 0xabcd;
27
-   pwtest_bool_false(spa_atof("0,00", &v));  pwtest_int_eq(v, 0xabcd);
28
-   pwtest_bool_false(spa_atof("fabc", &v));  pwtest_int_eq(v, 0xabcd);
29
-   pwtest_bool_false(spa_atof("1.bogus", &v));pwtest_int_eq(v, 0xabcd);
30
-   pwtest_bool_false(spa_atof("1.0a", &v));  pwtest_int_eq(v, 0xabcd);
31
-   pwtest_bool_false(spa_atof("  ", &v));    pwtest_int_eq(v, 0xabcd);
32
-   pwtest_bool_false(spa_atof(" ", &v));     pwtest_int_eq(v, 0xabcd);
33
-   pwtest_bool_false(spa_atof("", &v));      pwtest_int_eq(v, 0xabcd);
34
-   pwtest_bool_false(spa_atof(NULL, &v));    pwtest_int_eq(v, 0xabcd);
35
+   pwtest_bool_false(spa_atof("0,00", &v));  pwtest_int_eq((int)v, 0xabcd);
36
+   pwtest_bool_false(spa_atof("fabc", &v));  pwtest_int_eq((int)v, 0xabcd);
37
+   pwtest_bool_false(spa_atof("1.bogus", &v));pwtest_int_eq((int)v, 0xabcd);
38
+   pwtest_bool_false(spa_atof("1.0a", &v));  pwtest_int_eq((int)v, 0xabcd);
39
+   pwtest_bool_false(spa_atof("  ", &v));    pwtest_int_eq((int)v, 0xabcd);
40
+   pwtest_bool_false(spa_atof(" ", &v));     pwtest_int_eq((int)v, 0xabcd);
41
+   pwtest_bool_false(spa_atof("", &v));      pwtest_int_eq((int)v, 0xabcd);
42
+   pwtest_bool_false(spa_atof(NULL, &v));    pwtest_int_eq((int)v, 0xabcd);
43
 
44
    return PWTEST_PASS;
45
 }
46
@@ -651,14 +659,14 @@
47
    pwtest_bool_true(spa_atod("0x1", &v));      pwtest_double_eq(v, 1.0);
48
 
49
    v = 0xabcd;
50
-   pwtest_bool_false(spa_atod("0,00", &v));    pwtest_int_eq(v, 0xabcd);
51
-   pwtest_bool_false(spa_atod("fabc", &v));    pwtest_int_eq(v, 0xabcd);
52
-   pwtest_bool_false(spa_atod("1.bogus", &v)); pwtest_int_eq(v, 0xabcd);
53
-   pwtest_bool_false(spa_atod("1.0a", &v));    pwtest_int_eq(v, 0xabcd);
54
-   pwtest_bool_false(spa_atod("  ", &v));      pwtest_int_eq(v, 0xabcd);
55
-   pwtest_bool_false(spa_atod(" ", &v));       pwtest_int_eq(v, 0xabcd);
56
-   pwtest_bool_false(spa_atod("", &v));        pwtest_int_eq(v, 0xabcd);
57
-   pwtest_bool_false(spa_atod(NULL, &v));      pwtest_int_eq(v, 0xabcd);
58
+   pwtest_bool_false(spa_atod("0,00", &v));    pwtest_int_eq((int)v, 0xabcd);
59
+   pwtest_bool_false(spa_atod("fabc", &v));    pwtest_int_eq((int)v, 0xabcd);
60
+   pwtest_bool_false(spa_atod("1.bogus", &v)); pwtest_int_eq((int)v, 0xabcd);
61
+   pwtest_bool_false(spa_atod("1.0a", &v));    pwtest_int_eq((int)v, 0xabcd);
62
+   pwtest_bool_false(spa_atod("  ", &v));      pwtest_int_eq((int)v, 0xabcd);
63
+   pwtest_bool_false(spa_atod(" ", &v));       pwtest_int_eq((int)v, 0xabcd);
64
+   pwtest_bool_false(spa_atod("", &v));        pwtest_int_eq((int)v, 0xabcd);
65
+   pwtest_bool_false(spa_atod(NULL, &v));      pwtest_int_eq((int)v, 0xabcd);
66
 
67
    return PWTEST_PASS;
68
 }
69
pipewire-1.0.1.tar.bz2/test/test-utils.c -> pipewire-1.2.0.tar.gz/test/test-utils.c Changed
37
 
1
@@ -223,11 +223,35 @@
2
    return PWTEST_PASS;
3
 }
4
 
5
+PWTEST(utils_strv_parse)
6
+{
7
+   char **res = NULL;
8
+   int n_tokens = -1;
9
+
10
+   static const char test1 = " x \"y\" \" z \" ";
11
+   res = pw_strv_parse(test1, strlen(test1), INT_MAX, &n_tokens);
12
+   pwtest_ptr_notnull(res);
13
+   pwtest_int_eq(n_tokens, 3);
14
+   pwtest_ptr_eq(resn_tokens, NULL);
15
+   spa_assert_se(spa_streq(res0, "x"));
16
+   spa_assert_se(spa_streq(res1, "y"));
17
+   spa_assert_se(spa_streq(res2, " z "));
18
+   pw_free_strv(res);
19
+
20
+   static const char test2 = " x y {  1 = 2  }";
21
+   res = pw_strv_parse(test2, strlen(test2), INT_MAX, &n_tokens);
22
+   pwtest_ptr_null(res);
23
+   pwtest_int_eq(n_tokens, 0);
24
+
25
+   return PWTEST_PASS;
26
+}
27
+
28
 PWTEST_SUITE(utils)
29
 {
30
    pwtest_add(utils_abi, PWTEST_NOARG);
31
    pwtest_add(utils_split, PWTEST_NOARG);
32
    pwtest_add(utils_strip, PWTEST_NOARG);
33
+   pwtest_add(utils_strv_parse, PWTEST_NOARG);
34
 
35
    return PWTEST_PASS;
36
 }
37
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request 10 months ago

New stable branch release


Bjørn Lie's avatar

zaitor accepted request 10 months ago

Xin