diff --git a/doc/dox/config/pipewire-props.7.md b/doc/dox/config/pipewire-props.7.md
index 9cf3943ed..ed82b2f41 100644
--- a/doc/dox/config/pipewire-props.7.md
+++ b/doc/dox/config/pipewire-props.7.md
@@ -1171,15 +1171,6 @@ in a platform-specific way. See `tests/examples/bt-pinephone.lua` in WirePlumber
Do not enable this setting if you don't know what all this means, as it won't work.
\endparblock
-@PAR@ monitor-prop bluez5.hw-offload-datapath # integer
-\parblock
-HFP/HSP hardware offload data path ID (default: 0).
-
-This feature configures the SCO hardware‑offload data path for HFP/HSP using the Bluetooth
-SIG–specified procedure. It is intended for advanced setups and vendor integrations. Do not
-edit this unless required; incorrect values can disable SCO offload.
-\endparblock
-
@PAR@ monitor-prop bluez5.a2dp.opus.pro.channels = 3 # integer
PipeWire Opus Pro audio profile channel count.
@@ -1211,7 +1202,6 @@ PipeWire Opus Pro audio profile duplex max bitrate.
PipeWire Opus Pro audio profile duplex frame duration (1/10 ms).
@PAR@ monitor-prop bluez5.bcast_source.config = [] # JSON
-For a per-adapter configuration of multiple BIGs use an "adapter" entry in the BIG with the HCI device name (e.g. hci0).
\parblock
Example:
```
diff --git a/doc/dox/internals/dma-buf.dox b/doc/dox/internals/dma-buf.dox
index 5042f285c..273be930e 100644
--- a/doc/dox/internals/dma-buf.dox
+++ b/doc/dox/internals/dma-buf.dox
@@ -313,13 +313,12 @@ performed.
Device ID negotiation needs explicit support by both end points of a stream, thus, the
first step of negotiation is discovering whether other peer has support for it. This is
done by advertising a \ref SPA_PARAM_Capability with the key \ref
-PW_CAPABILITY_DEVICE_ID_NEGOTIATION and value `1` which corresponds to the
-current negotiation API version.
+PW_CAPABILITY_DEVICE_ID_NEGOTIATION and value `true`
```
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
&SPA_DICT_ITEMS(
- SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1")));
+ SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true")));
```
To do this, when connecting to the stream, the \ref PW_STREAM_FLAG_INACTIVE flag must be
@@ -365,15 +364,12 @@ with. This can be used to reduce the amount of devices that are queried for form
metadata, which can be a time consuming task, if devices needs to be woken up.
To achieve this, the consumer adds another \ref SPA_PARAM_PeerCapability item with the key
-\ref PW_CAPABILITY_DEVICE_IDS set to a JSON object describing what device IDs are supported.
-
-This JSON object as of version 1 contains a single key "available-devices" that contain
-a list of hexadecimal encoded `dev_t` device IDs.
+\ref PW_CAPABILITY_DEVICE_IDS set to a string of base 64 encoded `dev_t` device IDs.
```
- char *device_ids = "{\"available-devices\": [\"6464000000000000\",\"c8c8000000000000\"]}";
+ char *device_ids = ...; /* Base 64 encoding of a dev_t. */.
&SPA_DICT_ITEMS(
- SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
+ SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true"),
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)));
```
diff --git a/meson.build b/meson.build
index 2e308cfa5..e8b6f38dd 100644
--- a/meson.build
+++ b/meson.build
@@ -82,7 +82,6 @@ common_flags = [
'-fvisibility=hidden',
'-fno-strict-aliasing',
'-fno-strict-overflow',
- '-DSPA_AUDIO_MAX_CHANNELS=128u',
'-Werror=suggest-attribute=format',
'-Wsign-compare',
'-Wpointer-arith',
@@ -116,6 +115,7 @@ cc_flags = common_flags + [
'-Werror=old-style-definition',
'-Werror=missing-parameter-type',
'-Werror=strict-prototypes',
+ '-DSPA_AUDIO_MAX_CHANNELS=128u',
]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
diff --git a/spa/plugins/alsa/90-pipewire-alsa.rules b/spa/plugins/alsa/90-pipewire-alsa.rules
index 8c986d070..b2e1f6886 100644
--- a/spa/plugins/alsa/90-pipewire-alsa.rules
+++ b/spa/plugins/alsa/90-pipewire-alsa.rules
@@ -187,11 +187,6 @@ ATTRS{idVendor}=="1395", ATTRS{idProduct}=="0300", ENV{ACP_PROFILE_SET}="usb-gam
# Sennheiser GSP 670 USB headset
ATTRS{idVendor}=="1395", ATTRS{idProduct}=="008a", ENV{ACP_PROFILE_SET}="usb-gaming-headset.conf"
-# JBL Quantum One
-ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="203a", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
-# JBL Quantum 810 Wireless
-ATTRS{idVendor}=="0ecb", ATTRS{idProduct}=="2069", ENV{ACP_PROFILE_SET}="usb-gaming-headset-gamefirst.conf"
-
# Audioengine HD3 powered speakers support IEC958 but don't actually
# have any digital outputs.
ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf"
diff --git a/spa/plugins/alsa/mixer/profile-sets/usb-gaming-headset-gamefirst.conf b/spa/plugins/alsa/mixer/profile-sets/usb-gaming-headset-gamefirst.conf
deleted file mode 100644
index 9192c6864..000000000
--- a/spa/plugins/alsa/mixer/profile-sets/usb-gaming-headset-gamefirst.conf
+++ /dev/null
@@ -1,70 +0,0 @@
-# This file is part of PulseAudio.
-#
-# PulseAudio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation; either version 2.1 of the
-# License, or (at your option) any later version.
-#
-# PulseAudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with PulseAudio; if not, see .
-
-; USB gaming headset.
-; These headsets usually have two output devices. The first one is meant
-; for general audio, and the second one is meant for chat. There is also
-; a single input device for chat.
-; The purpose of this unusual design is to provide separate volume
-; controls for voice and other audio, which can be useful in gaming.
-;
-; Works with:
-; JBL Quantum 810 Wireless
-; JBL Quantum One
-;
-; Based on usb-gaming-headset.conf.
-;
-; See default.conf for an explanation on the directives used here.
-
-[General]
-auto-profiles = yes
-
-[Mapping mono-chat-output]
-description-key = gaming-headset-chat
-device-strings = hw:%f,1,0
-channel-map = mono
-paths-output = usb-gaming-headset-output-mono
-intended-roles = phone
-
-[Mapping stereo-chat-output]
-description-key = gaming-headset-chat
-device-strings = hw:%f,1,0
-channel-map = left,right
-paths-output = usb-gaming-headset-output-stereo
-intended-roles = phone
-
-[Mapping mono-chat-input]
-description-key = gaming-headset-chat
-device-strings = hw:%f,0,0
-channel-map = mono
-paths-input = usb-gaming-headset-input
-intended-roles = phone
-
-[Mapping stereo-game-output]
-description-key = gaming-headset-game
-device-strings = hw:%f,0,0
-channel-map = left,right
-paths-output = usb-gaming-headset-output-stereo
-direction = output
-
-[Profile output:mono-chat+output:stereo-game+input:mono-chat]
-output-mappings = mono-chat-output stereo-game-output
-input-mappings = mono-chat-input
-priority = 5100
-
-[Profile output:stereo-game+output:stereo-chat+input:mono-chat]
-output-mappings = stereo-game-output stereo-chat-output
-input-mappings = mono-chat-input
-priority = 5100
diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c
index 4d14183e7..5c4a2b785 100644
--- a/spa/plugins/bluez5/backend-native.c
+++ b/spa/plugins/bluez5/backend-native.c
@@ -63,9 +63,6 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native");
#define RFCOMM_MESSAGE_MAX_LENGTH 256
-#define BT_CODEC_CVSD 0x02
-#define BT_CODEC_MSBC 0x05
-
enum {
HFP_AG_INITIAL_CODEC_SETUP_NONE = 0,
HFP_AG_INITIAL_CODEC_SETUP_SEND,
@@ -115,7 +112,6 @@ struct impl {
int hfp_default_speaker_volume;
struct spa_source sco;
- unsigned int hfphsp_sco_datapath;
const struct spa_bt_quirks *quirks;
@@ -301,33 +297,6 @@ static const struct media_codec *codec_list_best(struct impl *backend, struct sp
return NULL;
}
-static void sco_offload_btcodec(struct impl *backend, int sock, bool msbc)
-{
- int err;
- char buffer[255];
- struct bt_codecs *codecs;
-
- if (backend->hfphsp_sco_datapath == HFP_SCO_DEFAULT_DATAPATH)
- return;
-
- spa_log_info(backend->log, "sock(%d) msbc(%d)", sock, msbc);
-
- memset(buffer, 0, sizeof(buffer));
- codecs = (void *)buffer;
- if (msbc)
- codecs->codecs[0].id = BT_CODEC_MSBC;
- else
- codecs->codecs[0].id = BT_CODEC_CVSD;
- codecs->num_codecs = 1;
- codecs->codecs[0].data_path_id = backend->hfphsp_sco_datapath;
-
- err = setsockopt(sock, SOL_BLUETOOTH, BT_CODEC, codecs, sizeof(buffer));
- if (err < 0)
- spa_log_error(backend->log, "ERROR: %s (%d)", strerror(errno), errno);
- else
- spa_log_info(backend->log, "set offload codec succeeded");
-}
-
static DBusHandlerResult profile_release(DBusConnection *conn, DBusMessage *m, void *userdata)
{
if (!reply_with_error(conn, m, BLUEZ_PROFILE_INTERFACE ".Error.NotImplemented", "Method not implemented"))
@@ -2626,8 +2595,6 @@ static int sco_create_socket(struct impl *backend, struct spa_bt_adapter *adapte
}
}
- sco_offload_btcodec(backend, sock, transparent);
-
return spa_steal_fd(sock);
}
@@ -4134,14 +4101,6 @@ static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dic
backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX;
}
-static void parse_sco_datapath(struct impl *backend, const struct spa_dict *info)
-{
- backend->hfphsp_sco_datapath = HFP_SCO_DEFAULT_DATAPATH;
-
- spa_atou32(spa_dict_lookup(info, "bluez5.hw-offload-datapath"),
- &backend->hfphsp_sco_datapath, 10);
-}
-
static const struct spa_bt_backend_implementation backend_impl = {
SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
.free = backend_native_free,
@@ -4204,7 +4163,6 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
parse_hfp_disable_nrec(backend, info);
parse_hfp_default_volumes(backend, info);
parse_hfp_pts(backend, info);
- parse_sco_datapath(backend, info);
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
if (!dbus_connection_register_object_path(backend->conn,
diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c
index 2581ba5d7..be08fd5a5 100644
--- a/spa/plugins/bluez5/bap-codec-lc3.c
+++ b/spa/plugins/bluez5/bap-codec-lc3.c
@@ -132,14 +132,14 @@ static const struct bap_qos bap_qos_configs[] = {
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3), /* 32_2_2 */
- BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54), /* 441_1_2 */
- BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44), /* 441_2_2 */
- BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55), /* 48_1_2 */
- BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45), /* 48_2_2 */
- BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56), /* 48_3_2 */
- BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46), /* 48_4_2 */
- BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57), /* 48_5_2 */
- BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47), /* 48_6_2 */
+ BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 14), /* 441_1_2 */
+ BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 4), /* 441_2_2 */
+ BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 15), /* 48_1_2 */
+ BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 5), /* 48_2_2 */
+ BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 16), /* 48_3_2 */
+ BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 6), /* 48_4_2 */
+ BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 17), /* 48_5_2 */
+ BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 7), /* 48_6_2 */
};
static const struct bap_qos bap_bcast_qos_configs[] = {
diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c
index a17eedc84..926b1b3c4 100644
--- a/spa/plugins/bluez5/bluez5-dbus.c
+++ b/spa/plugins/bluez5/bluez5-dbus.c
@@ -194,7 +194,6 @@ struct spa_bt_bis {
};
#define BROADCAST_CODE_LEN 16
-#define HCI_DEV_NAME_LEN 8
struct spa_bt_big {
struct spa_list link;
@@ -203,7 +202,6 @@ struct spa_bt_big {
struct spa_list bis_list;
int big_id;
int sync_factor;
- char adapter[HCI_DEV_NAME_LEN];
};
/*
@@ -3104,13 +3102,11 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
spa_log_debug(monitor->log, "remote_endpoint %p: %s=%"PRIu64, remote_endpoint, key, remote_endpoint->hisyncid);
} else if (spa_streq(key, "SupportedFeatures")) {
- DBusMessageIter iter;
-
if (!check_iter_signature(&it[1], "a{sv}"))
goto next;
- dbus_message_iter_recurse(&it[1], &iter);
- parse_supported_features(monitor, &iter, &remote_endpoint->bap_features);
+ dbus_message_iter_recurse(&it[1], &it[2]);
+ parse_supported_features(monitor, &it[2], &remote_endpoint->bap_features);
} else {
unhandled:
spa_log_debug(monitor->log, "remote_endpoint %p: unhandled key %s", remote_endpoint, key);
@@ -6246,20 +6242,8 @@ static void configure_bcast_source(struct spa_bt_monitor *monitor,
{
struct spa_bt_big *big;
struct spa_bt_bis *bis;
- char *pos;
/* Configure each BIS from a BIG */
spa_list_for_each(big, &monitor->bcast_source_config_list, link) {
- /* Apply per adapter configuration if BIG has an adapter value stated,
- * otherwise apply the BIG config angnostically to each adapter
- */
- if (strlen(big->adapter) > 0) {
- pos = strstr(object_path, big->adapter);
- if (pos == NULL)
- continue;
-
- spa_log_debug(monitor->log, "configuring BIG for adapter=%s", big->adapter);
- }
-
spa_list_for_each(bis, &big->bis_list, link) {
configure_bis(monitor, codec, conn, object_path, interface_name,
big, bis, local_endpoint);
@@ -7003,7 +6987,6 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
char bis_key[256];
char qos_key[256];
char bcode[BROADCAST_CODE_LEN + 3];
- char adapter[HCI_DEV_NAME_LEN + 3];
int cursor;
int big_id = 0;
struct spa_json it[3], it_array[4];
@@ -7039,13 +7022,6 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
goto parse_failed;
memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
- } else if (spa_streq(key, "adapter")) {
- if (spa_json_get_string(&it[1], adapter, sizeof(adapter)) <= 0)
- goto parse_failed;
- if (strlen(adapter) > HCI_DEV_NAME_LEN)
- goto parse_failed;
- memcpy(big_entry->adapter, adapter, strlen(adapter));
- spa_log_debug(monitor->log, "big_entry->adapter %s", big_entry->adapter);
} else if (spa_streq(key, "encryption")) {
if (spa_json_get_bool(&it[0], &big_entry->encryption) <= 0)
goto parse_failed;
diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h
index afc56c920..3efec465a 100644
--- a/spa/plugins/bluez5/defs.h
+++ b/spa/plugins/bluez5/defs.h
@@ -135,8 +135,7 @@ extern "C" {
#define PROFILE_HFP_AG "/Profile/HFPAG"
#define PROFILE_HFP_HF "/Profile/HFPHF"
-#define HSP_HS_DEFAULT_CHANNEL 3
-#define HFP_SCO_DEFAULT_DATAPATH 0
+#define HSP_HS_DEFAULT_CHANNEL 3
#define SOURCE_ID_BLUETOOTH 0x1 /* Bluetooth SIG */
#define SOURCE_ID_USB 0x2 /* USB Implementer's Forum */
diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c
index 52609f2c6..9271ace34 100644
--- a/spa/plugins/filter-graph/filter-graph.c
+++ b/spa/plugins/filter-graph/filter-graph.c
@@ -80,6 +80,7 @@ struct descriptor {
unsigned long *output;
unsigned long *control;
unsigned long *notify;
+ float *default_control;
};
struct port {
@@ -93,9 +94,6 @@ struct port {
uint32_t n_links;
uint32_t external;
- bool control_initialized;
-
- float control_current;
float control_data[MAX_HNDL];
float *audio_data[MAX_HNDL];
void *audio_mem[MAX_HNDL];
@@ -195,9 +193,6 @@ struct graph {
struct volume volume[2];
- uint32_t default_inputs;
- uint32_t default_outputs;
-
uint32_t n_inputs;
uint32_t n_outputs;
uint32_t inputs_position[MAX_CHANNELS];
@@ -262,23 +257,16 @@ static void emit_filter_graph_info(struct impl *impl, bool full)
impl->info.change_mask = impl->info_all;
if (impl->info.change_mask || full) {
char n_inputs[64], n_outputs[64], latency[64];
- char n_default_inputs[64], n_default_outputs[64];
struct spa_dict_item items[6];
struct spa_dict dict = SPA_DICT(items, 0);
char in_pos[MAX_CHANNELS * 8];
char out_pos[MAX_CHANNELS * 8];
- /* these are the current graph inputs/outputs */
snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs);
snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs);
- /* these are the default number of graph inputs/outputs */
- snprintf(n_default_inputs, sizeof(n_default_inputs), "%d", impl->graph.default_inputs);
- snprintf(n_default_outputs, sizeof(n_default_outputs), "%d", impl->graph.default_outputs);
items[dict.n_items++] = SPA_DICT_ITEM("n_inputs", n_inputs);
items[dict.n_items++] = SPA_DICT_ITEM("n_outputs", n_outputs);
- items[dict.n_items++] = SPA_DICT_ITEM("n_default_inputs", n_default_inputs);
- items[dict.n_items++] = SPA_DICT_ITEM("n_default_outputs", n_default_outputs);
if (graph->n_inputs_position) {
print_channels(in_pos, sizeof(in_pos),
graph->n_inputs_position, graph->inputs_position);
@@ -351,6 +339,12 @@ static int impl_process(void *object,
return 0;
}
+static float get_default(struct impl *impl, struct descriptor *desc, uint32_t p)
+{
+ struct spa_fga_port *port = &desc->desc->ports[p];
+ return port->def;
+}
+
static struct node *find_node(struct graph *graph, const char *name)
{
struct node *node;
@@ -439,20 +433,6 @@ static struct port *find_port(struct node *node, const char *name, int descripto
return NULL;
}
-static void get_ranges(struct impl *impl, struct spa_fga_port *p,
- float *def, float *min, float *max)
-{
- uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE;
- *def = p->def;
- *min = p->min;
- *max = p->max;
- if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) {
- *def *= rate;
- *min *= rate;
- *max *= rate;
- }
-}
-
static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builder *b,
struct spa_pod **param)
{
@@ -467,6 +447,7 @@ static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builde
struct spa_fga_port *p;
float def, min, max;
char name[512];
+ uint32_t rate = impl->rate ? impl->rate : DEFAULT_RATE;
if (idx >= graph->n_control)
return 0;
@@ -477,7 +458,15 @@ static int impl_enum_prop_info(void *object, uint32_t idx, struct spa_pod_builde
d = desc->desc;
p = &d->ports[port->p];
- get_ranges(impl, p, &def, &min, &max);
+ if (p->hint & SPA_FGA_HINT_SAMPLE_RATE) {
+ def = p->def * rate;
+ min = p->min * rate;
+ max = p->max * rate;
+ } else {
+ def = p->def;
+ min = p->min;
+ max = p->max;
+ }
if (node->name[0] != '\0')
snprintf(name, sizeof(name), "%s:%s", node->name, p->name);
@@ -576,58 +565,41 @@ static int impl_get_props(void *object, struct spa_pod_builder *b, struct spa_po
return 1;
}
-static int port_id_set_control_value(struct port *port, uint32_t id, float value)
+static int port_set_control_value(struct port *port, float *value, uint32_t id)
{
struct node *node = port->node;
struct impl *impl = node->graph->impl;
+
struct descriptor *desc = node->desc;
- struct spa_fga_port *p = &desc->desc->ports[port->p];
float old;
bool changed;
old = port->control_data[id];
- port->control_data[id] = value;
-
+ port->control_data[id] = value ? *value : desc->default_control[port->idx];
spa_log_info(impl->log, "control %d %d ('%s') from %f to %f", port->idx, id,
- p->name, old, value);
-
+ desc->desc->ports[port->p].name, old, port->control_data[id]);
changed = old != port->control_data[id];
node->control_changed |= changed;
-
return changed ? 1 : 0;
}
-static int port_set_control_value(struct port *port, float *value)
-{
- struct node *node = port->node;
- struct impl *impl = node->graph->impl;
- struct spa_fga_port *p;
- float v, def, min, max;
- uint32_t i;
- int count = 0;
-
- p = &node->desc->desc->ports[port->p];
- get_ranges(impl, p, &def, &min, &max);
- v = SPA_CLAMP(value ? *value : def, min, max);
-
- port->control_current = v;
- port->control_initialized = true;
-
- for (i = 0; i < node->n_hndl; i++)
- count += port_id_set_control_value(port, i, v);
-
- return count;
-}
-
static int set_control_value(struct node *node, const char *name, float *value)
{
struct port *port;
+ int count = 0;
+ uint32_t i, n_hndl;
port = find_port(node, name, SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL);
if (port == NULL)
return -ENOENT;
- return port_set_control_value(port, value);
+ /* if we don't have any instances yet, set the first control value, we will
+ * copy to other instances later */
+ n_hndl = SPA_MAX(1u, port->node->n_hndl);
+ for (i = 0; i < n_hndl; i++)
+ count += port_set_control_value(port, value, i);
+
+ return count;
}
static int parse_params(struct graph *graph, const struct spa_pod *pod)
@@ -734,7 +706,7 @@ static int sync_volume(struct graph *graph, struct volume *vol)
v = v * (vol->max[n_port] - vol->min[n_port]) + vol->min[n_port];
n_hndl = SPA_MAX(1u, p->node->n_hndl);
- res += port_id_set_control_value(p, i % n_hndl, v);
+ res += port_set_control_value(p, &v, i % n_hndl);
}
return res;
}
@@ -953,6 +925,7 @@ static void descriptor_unref(struct descriptor *desc)
free(desc->input);
free(desc->output);
free(desc->control);
+ free(desc->default_control);
free(desc->notify);
free(desc);
}
@@ -963,7 +936,7 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
struct plugin *pl;
struct descriptor *desc;
const struct spa_fga_descriptor *d;
- uint32_t n_input, n_output, n_control, n_notify;
+ uint32_t i, n_input, n_output, n_control, n_notify;
unsigned long p;
int res;
@@ -1017,6 +990,7 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
desc->input = calloc(n_input, sizeof(unsigned long));
desc->output = calloc(n_output, sizeof(unsigned long));
desc->control = calloc(n_control, sizeof(unsigned long));
+ desc->default_control = calloc(n_control, sizeof(float));
desc->notify = calloc(n_notify, sizeof(unsigned long));
for (p = 0; p < d->n_ports; p++) {
@@ -1046,6 +1020,17 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
}
}
}
+ if (desc->n_input == 0 && desc->n_output == 0 && desc->n_control == 0 && desc->n_notify == 0) {
+ spa_log_error(impl->log, "plugin has no input and no output ports");
+ res = -ENOTSUP;
+ goto exit;
+ }
+ for (i = 0; i < desc->n_control; i++) {
+ p = desc->control[i];
+ desc->default_control[i] = get_default(impl, desc, p);
+ spa_log_info(impl->log, "control %d ('%s') default to %f", i,
+ d->ports[p].name, desc->default_control[i]);
+ }
spa_list_append(&pl->descriptor_list, &desc->link);
return desc;
@@ -1425,6 +1410,7 @@ static int load_node(struct graph *graph, struct spa_json *json)
port->external = SPA_ID_INVALID;
port->p = desc->control[i];
spa_list_init(&port->link_list);
+ port->control_data[0] = desc->default_control[i];
}
for (i = 0; i < desc->n_notify; i++) {
struct port *port = &node->notify_port[i];
@@ -1798,7 +1784,7 @@ static int setup_graph(struct graph *graph)
struct port *port;
struct graph_port *gp;
struct graph_hndl *gh;
- uint32_t i, j, n, n_input, n_output, n_hndl = 0, n_out_hndl;
+ uint32_t i, j, n, n_input, n_output, n_hndl = 0;
int res;
struct descriptor *desc;
const struct spa_fga_descriptor *d;
@@ -1810,8 +1796,19 @@ static int setup_graph(struct graph *graph)
first = spa_list_first(&graph->node_list, struct node, link);
last = spa_list_last(&graph->node_list, struct node, link);
- n_input = graph->default_inputs;
- n_output = graph->default_outputs;
+ /* calculate the number of inputs and outputs into the graph.
+ * If we have a list of inputs/outputs, just use them. Otherwise
+ * we count all input ports of the first node and all output
+ * ports of the last node */
+ if (graph->n_input_names != 0)
+ n_input = graph->n_input_names;
+ else
+ n_input = first->desc->n_input;
+
+ if (graph->n_output_names != 0)
+ n_output = graph->n_output_names;
+ else
+ n_output = last->desc->n_output;
/* we allow unconnected ports when not explicitly given and the nodes support
* NULL data */
@@ -1819,11 +1816,16 @@ static int setup_graph(struct graph *graph)
SPA_FLAG_IS_SET(first->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA) &&
SPA_FLAG_IS_SET(last->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA);
- if (n_input == 0)
- n_input = n_output;
- if (n_output == 0)
- n_output = n_input;
-
+ if (n_input == 0) {
+ spa_log_error(impl->log, "no inputs");
+ res = -EINVAL;
+ goto error;
+ }
+ if (n_output == 0) {
+ spa_log_error(impl->log, "no outputs");
+ res = -EINVAL;
+ goto error;
+ }
if (graph->n_inputs == 0)
graph->n_inputs = impl->info.n_inputs;
if (graph->n_inputs == 0)
@@ -1834,14 +1836,12 @@ static int setup_graph(struct graph *graph)
/* compare to the requested number of inputs and duplicate the
* graph n_hndl times when needed. */
- n_hndl = n_input ? graph->n_inputs / n_input : 1;
+ n_hndl = graph->n_inputs / n_input;
if (graph->n_outputs == 0)
graph->n_outputs = n_output * n_hndl;
- n_out_hndl = n_output ? graph->n_outputs / n_output : 1;
-
- if (n_hndl != n_out_hndl) {
+ if (n_hndl != graph->n_outputs / n_output) {
spa_log_error(impl->log, "invalid ports. The input stream has %1$d ports and "
"the filter has %2$d inputs. The output stream has %3$d ports "
"and the filter has %4$d outputs. input:%1$d / input:%2$d != "
@@ -2029,9 +2029,11 @@ static int setup_graph(struct graph *graph)
}
}
for (i = 0; i < desc->n_control; i++) {
+ /* any default values for the controls are set in the first instance
+ * of the control data. Duplicate this to the other instances now. */
struct port *port = &node->control_port[i];
- port_set_control_value(port,
- port->control_initialized ? &port->control_current : NULL);
+ for (j = 1; j < n_hndl; j++)
+ port->control_data[j] = port->control_data[0];
}
}
res = 0;
@@ -2081,7 +2083,6 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
struct spa_json inputs, outputs, *pinputs = NULL, *poutputs = NULL;
struct spa_json ivolumes, ovolumes, *pivolumes = NULL, *povolumes = NULL;
struct spa_json nodes, *pnodes = NULL, links, *plinks = NULL;
- struct node *first, *last;
const char *json, *val;
char key[256];
int res, len;
@@ -2231,25 +2232,6 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
}
if ((res = setup_graph_controls(graph)) < 0)
return res;
-
- first = spa_list_first(&graph->node_list, struct node, link);
- last = spa_list_last(&graph->node_list, struct node, link);
-
- /* calculate the number of inputs and outputs into the graph.
- * If we have a list of inputs/outputs, just use them. Otherwise
- * we count all input ports of the first node and all output
- * ports of the last node */
- if (graph->n_input_names != 0)
- graph->default_inputs = graph->n_input_names;
- else
- graph->default_inputs = first->desc->n_input;
-
- if (graph->n_output_names != 0)
- graph->default_outputs = graph->n_output_names;
- else
- graph->default_outputs = last->desc->n_output;
-
-
return 0;
}
diff --git a/spa/plugins/filter-graph/plugin_builtin.c b/spa/plugins/filter-graph/plugin_builtin.c
index 3bcde30c9..8a964ccd7 100644
--- a/spa/plugins/filter-graph/plugin_builtin.c
+++ b/spa/plugins/filter-graph/plugin_builtin.c
@@ -14,7 +14,6 @@
#include
#include
#include
-#include
#include
#include
@@ -3116,136 +3115,6 @@ static const struct spa_fga_descriptor noisegate_desc = {
.cleanup = builtin_cleanup,
};
-/* busy */
-struct busy_impl {
- struct plugin *plugin;
-
- struct spa_fga_dsp *dsp;
- struct spa_log *log;
-
- unsigned long rate;
-
- float wait_scale;
- float cpu_scale;
-};
-
-static void *busy_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
- unsigned long SampleRate, int index, const char *config)
-{
- struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin);
- struct busy_impl *impl;
- struct spa_json it[1];
- const char *val;
- char key[256];
- float wait_percent = 0.0f, cpu_percent = 0.0f;
- int len;
-
- if (config != NULL) {
- if (spa_json_begin_object(&it[0], config, strlen(config)) <= 0) {
- spa_log_error(pl->log, "busy:config must be an object");
- return NULL;
- }
-
- while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
- if (spa_streq(key, "wait-percent")) {
- if (spa_json_parse_float(val, len, &wait_percent) <= 0) {
- spa_log_error(pl->log, "busy:wait-percent requires a number");
- return NULL;
- }
- } else if (spa_streq(key, "cpu-percent")) {
- if (spa_json_parse_float(val, len, &cpu_percent) <= 0) {
- spa_log_error(pl->log, "busy:cpu-percent requires a number");
- return NULL;
- }
- } else {
- spa_log_warn(pl->log, "busy: ignoring config key: '%s'", key);
- }
- }
- if (wait_percent <= 0.0f)
- wait_percent = 0.0f;
- if (cpu_percent <= 0.0f)
- cpu_percent = 0.0f;
- }
-
- impl = calloc(1, sizeof(*impl));
- if (impl == NULL)
- return NULL;
-
- impl->plugin = pl;
- impl->dsp = pl->dsp;
- impl->log = pl->log;
- impl->rate = SampleRate;
- impl->wait_scale = wait_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate);
- impl->cpu_scale = cpu_percent * SPA_NSEC_PER_SEC / (100.0f * SampleRate);
- spa_log_info(impl->log, "wait-percent:%f cpu-percent:%f", wait_percent, cpu_percent);
-
- return impl;
-}
-
-static void busy_run(void * Instance, unsigned long SampleCount)
-{
- struct busy_impl *impl = Instance;
- struct timespec ts;
- uint64_t busy_nsec;
-
- if (impl->wait_scale > 0.0f) {
- busy_nsec = (uint64_t)(impl->wait_scale * SampleCount);
- ts.tv_sec = busy_nsec / SPA_NSEC_PER_SEC;
- ts.tv_nsec = busy_nsec % SPA_NSEC_PER_SEC;
- clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
- }
- if (impl->cpu_scale > 0.0f) {
- clock_gettime(CLOCK_MONOTONIC, &ts);
- busy_nsec = SPA_TIMESPEC_TO_NSEC(&ts);
- busy_nsec += (uint64_t)(impl->cpu_scale * SampleCount);
- do {
- clock_gettime(CLOCK_MONOTONIC, &ts);
- } while ((uint64_t)SPA_TIMESPEC_TO_NSEC(&ts) < busy_nsec);
- }
-}
-
-static const struct spa_fga_descriptor busy_desc = {
- .name = "busy",
- .flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
-
- .n_ports = 0,
- .ports = NULL,
-
- .instantiate = busy_instantiate,
- .connect_port = builtin_connect_port,
- .run = busy_run,
- .cleanup = builtin_cleanup,
-};
-
-/* null */
-static void null_run(void * Instance, unsigned long SampleCount)
-{
-}
-
-static struct spa_fga_port null_ports[] = {
- { .index = 0,
- .name = "In",
- .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
- },
- { .index = 1,
- .name = "Control",
- .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
- },
-};
-
-static const struct spa_fga_descriptor null_desc = {
- .name = "null",
- .flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
-
- .n_ports = SPA_N_ELEMENTS(null_ports),
- .ports = null_ports,
-
- .instantiate = builtin_instantiate,
- .connect_port = builtin_connect_port,
- .run = null_run,
- .cleanup = builtin_cleanup,
-};
-
static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
{
switch(Index) {
@@ -3311,10 +3180,6 @@ static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
return &zeroramp_desc;
case 30:
return &noisegate_desc;
- case 31:
- return &busy_desc;
- case 32:
- return &null_desc;
}
return NULL;
}
diff --git a/spa/plugins/filter-graph/plugin_sofa.c b/spa/plugins/filter-graph/plugin_sofa.c
index 2e3a1ea3b..7ec73ea2b 100644
--- a/spa/plugins/filter-graph/plugin_sofa.c
+++ b/spa/plugins/filter-graph/plugin_sofa.c
@@ -32,7 +32,6 @@ struct spatializer_impl {
unsigned long rate;
float *port[7];
int n_samples, blocksize, tailsize;
- float gain;
float *tmp[2];
struct MYSOFA_EASY *sofa;
@@ -72,7 +71,6 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
impl->plugin = pl;
impl->dsp = pl->dsp;
impl->log = pl->log;
- impl->gain = 1.0f;
while ((len = spa_json_object_next(&it[0], key, sizeof(key), &val)) > 0) {
if (spa_streq(key, "blocksize")) {
@@ -96,13 +94,6 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
goto error;
}
}
- else if (spa_streq(key, "gain")) {
- if (spa_json_parse_float(val, len, &impl->gain) <= 0) {
- spa_log_error(impl->log, "spatializer:gain requires a number");
- errno = EINVAL;
- goto error;
- }
- }
}
if (!filename[0]) {
spa_log_error(impl->log, "spatializer:filename was not given");
@@ -177,14 +168,11 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
reason = "Only sources with MC supported";
errno = ENOTSUP;
break;
+ default:
case MYSOFA_INTERNAL_ERROR:
errno = EIO;
reason = "Internal error";
break;
- default:
- errno = ret;
- reason = strerror(errno);
- break;
}
spa_log_error(impl->log, "Unable to load HRTF from %s: %s (%d)", filename, reason, ret);
goto error;
@@ -195,8 +183,8 @@ static void * spatializer_instantiate(const struct spa_fga_plugin *plugin, const
if (impl->tailsize <= 0)
impl->tailsize = SPA_CLAMP(4096, impl->blocksize, 32768);
- spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize gain:%f sofa:%s", impl->n_samples,
- impl->blocksize, impl->tailsize, impl->gain, filename);
+ spa_log_info(impl->log, "using n_samples:%u %d:%d blocksize sofa:%s", impl->n_samples,
+ impl->blocksize, impl->tailsize, filename);
impl->tmp[0] = calloc(impl->plugin->quantum_limit, sizeof(float));
impl->tmp[1] = calloc(impl->plugin->quantum_limit, sizeof(float));
@@ -262,13 +250,6 @@ static void spatializer_reload(void * Instance)
if (impl->r_conv[2])
convolver_free(impl->r_conv[2]);
- if (impl->gain != 1.0f) {
- for (int i = 0; i < impl->n_samples; i++) {
- left_ir[i] *= impl->gain;
- right_ir[i] *= impl->gain;
- }
- }
-
impl->l_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
left_ir, impl->n_samples);
impl->r_conv[2] = convolver_new(impl->dsp, impl->blocksize, impl->tailsize,
diff --git a/src/daemon/filter-chain/spatializer-7.1.conf b/src/daemon/filter-chain/spatializer-7.1.conf
index bb19d5443..944ed6205 100644
--- a/src/daemon/filter-chain/spatializer-7.1.conf
+++ b/src/daemon/filter-chain/spatializer-7.1.conf
@@ -19,8 +19,6 @@ context.modules = [
name = spFL
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- # The gain depends on the .sofa file in use
- gain = 0.5
}
control = {
"Azimuth" = 30.0
@@ -34,7 +32,6 @@ context.modules = [
name = spFR
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 330.0
@@ -48,7 +45,6 @@ context.modules = [
name = spFC
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 0.0
@@ -62,7 +58,6 @@ context.modules = [
name = spRL
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 150.0
@@ -76,7 +71,6 @@ context.modules = [
name = spRR
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 210.0
@@ -90,7 +84,6 @@ context.modules = [
name = spSL
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 90.0
@@ -104,7 +97,6 @@ context.modules = [
name = spSR
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 270.0
@@ -118,7 +110,6 @@ context.modules = [
name = spLFE
config = {
filename = "~/.config/hrtf-sofa/hrtf b_nh724.sofa"
- gain = 0.5
}
control = {
"Azimuth" = 0.0
@@ -127,32 +118,8 @@ context.modules = [
}
}
- { type = builtin label = mixer name = mixL
- control = {
- # Set individual left mixer gain if needed
- #"Gain 1" = 1.0
- #"Gain 2" = 1.0
- #"Gain 3" = 1.0
- #"Gain 4" = 1.0
- #"Gain 5" = 1.0
- #"Gain 6" = 1.0
- #"Gain 7" = 1.0
- #"Gain 8" = 1.0
- }
- }
- { type = builtin label = mixer name = mixR
- control = {
- # Set individual right mixer gain if needed
- #"Gain 1" = 1.0
- #"Gain 2" = 1.0
- #"Gain 3" = 1.0
- #"Gain 4" = 1.0
- #"Gain 5" = 1.0
- #"Gain 6" = 1.0
- #"Gain 7" = 1.0
- #"Gain 8" = 1.0
- }
- }
+ { type = builtin label = mixer name = mixL }
+ { type = builtin label = mixer name = mixR }
]
links = [
# output
diff --git a/src/examples/base64.h b/src/examples/base64.h
new file mode 100644
index 000000000..50e6d64b2
--- /dev/null
+++ b/src/examples/base64.h
@@ -0,0 +1,46 @@
+/* PipeWire */
+/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
+/* SPDX-License-Identifier: MIT */
+
+static inline void base64_encode(const uint8_t *data, size_t len, char *enc, char pad)
+{
+ static const char tab[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ size_t i;
+ for (i = 0; i < len; i += 3) {
+ uint32_t v;
+ v = data[i+0] << 16;
+ v |= (i+1 < len ? data[i+1] : 0) << 8;
+ v |= (i+2 < len ? data[i+2] : 0);
+ *enc++ = tab[(v >> (3*6)) & 0x3f];
+ *enc++ = tab[(v >> (2*6)) & 0x3f];
+ *enc++ = i+1 < len ? tab[(v >> (1*6)) & 0x3f] : pad;
+ *enc++ = i+2 < len ? tab[(v >> (0*6)) & 0x3f] : pad;
+ }
+ *enc = '\0';
+}
+
+static inline size_t base64_decode(const char *data, size_t len, uint8_t *dec)
+{
+ uint8_t tab[] = {
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+ -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
+ -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
+ size_t i, j;
+ for (i = 0, j = 0; i < len; i += 4) {
+ uint32_t v;
+ v = tab[data[i+0]-43] << (3*6);
+ v |= tab[data[i+1]-43] << (2*6);
+ v |= (data[i+2] == '=' ? 0 : tab[data[i+2]-43]) << (1*6);
+ v |= (data[i+3] == '=' ? 0 : tab[data[i+3]-43]);
+ dec[j++] = (v >> 16) & 0xff;
+ if (data[i+2] != '=') dec[j++] = (v >> 8) & 0xff;
+ if (data[i+3] != '=') dec[j++] = v & 0xff;
+ }
+ return j;
+}
diff --git a/src/examples/utils.h b/src/examples/utils.h
deleted file mode 100644
index 079f22d55..000000000
--- a/src/examples/utils.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* PipeWire */
-/* SPDX-FileCopyrightText: Copyright © 2026 Red Hat */
-/* SPDX-License-Identifier: MIT */
-
-static inline char *
-encode_hex(const uint8_t *data, size_t size)
-{
- FILE *ms;
- char *encoded = NULL;
- size_t encoded_size = 0;
- size_t i;
-
- ms = open_memstream(&encoded, &encoded_size);
- for (i = 0; i < size; i++) {
- fprintf(ms, "%02x", data[i]);
- }
- fclose(ms);
-
- return encoded;
-}
-
-static inline int8_t
-ascii_hex_to_hex(uint8_t ascii_hex)
-{
- if (ascii_hex >= '0' && ascii_hex <= '9')
- return ascii_hex - '0';
- else if (ascii_hex >= 'a' && ascii_hex <= 'f')
- return ascii_hex - 'a' + 10;
- else if (ascii_hex >= 'A' && ascii_hex <= 'F')
- return ascii_hex - 'A' + 10;
- else
- return -1;
-}
-
-static inline int
-decode_hex(const char *encoded, uint8_t *data, size_t size)
-{
- size_t length;
- size_t i;
-
- length = strlen(encoded);
-
- if (size < (length / 2) * sizeof(uint8_t))
- return -1;
-
- i = 0;
- while (i < length) {
- int8_t top = ascii_hex_to_hex(encoded[i]);
- int8_t bottom = ascii_hex_to_hex(encoded[i + 1]);
-
- if (top == -1 || bottom == -1)
- return -1;
-
- uint8_t el = top << 4 | bottom;
- data[i / 2] = el;
- i += 2;
- }
-
- return 1;
-}
diff --git a/src/examples/video-play-fixate.c b/src/examples/video-play-fixate.c
index 13b617a59..6fdc561e6 100644
--- a/src/examples/video-play-fixate.c
+++ b/src/examples/video-play-fixate.c
@@ -29,7 +29,7 @@
#include
#include
-#include "utils.h"
+#include "base64.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
@@ -372,56 +372,46 @@ collect_device_ids(struct data *data, const char *json)
int len;
const char *value;
struct spa_json sub;
- char key[1024];
if ((len = spa_json_begin(&it, json, strlen(json), &value)) <= 0) {
fprintf(stderr, "invalid device IDs value\n");
return;
}
- if (!spa_json_is_object(value, len)) {
- fprintf(stderr, "device IDs not object\n");
+ if (!spa_json_is_array(value, len)) {
+ fprintf(stderr, "device IDs not array\n");
return;
}
spa_json_enter(&it, &sub);
- while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) {
- struct spa_json devices_sub;
+ while ((len = spa_json_next(&sub, &value)) > 0) {
+ char *string;
+ union {
+ dev_t device_id;
+ uint8_t buffer[1024];
+ } dec;
- if (!spa_json_is_array(value, len)) {
- fprintf(stderr, "available-devices not array\n");
+ string = alloca(len + 1);
+
+ if (!spa_json_is_string(value, len)) {
+ fprintf(stderr, "device ID not string\n");
return;
}
- spa_json_enter(&sub, &devices_sub);
- while ((len = spa_json_next(&devices_sub, &value)) > 0) {
- char *string;
- union {
- dev_t device_id;
- uint8_t buffer[1024];
- } dec;
-
- string = alloca(len + 1);
-
- if (!spa_json_is_string(value, len)) {
- fprintf(stderr, "device ID not string\n");
- return;
- }
-
- if (spa_json_parse_string(value, len, string) <= 0) {
- fprintf(stderr, "invalid device ID string\n");
- return;
- }
-
- if (decode_hex(string, dec.buffer, sizeof (dec.buffer)) < 0) {
- fprintf(stderr, "invalid device ID string\n");
- return;
- }
-
- fprintf(stderr, "discovered device ID %u:%u\n",
- major(dec.device_id), minor(dec.device_id));
-
- data->device_ids[data->n_device_ids++] = dec.device_id;
+ if (spa_json_parse_string(value, len, string) <= 0) {
+ fprintf(stderr, "invalid device ID string\n");
+ return;
}
+
+ if (base64_decode(string, strlen(string),
+ (uint8_t *)&dec.device_id) < sizeof(dev_t)) {
+ fprintf(stderr, "invalid device ID\n");
+ return;
+ }
+
+ fprintf(stderr, "discovered device ID %u:%u\n",
+ major(dec.device_id), minor(dec.device_id));
+
+ data->device_ids[data->n_device_ids++] = dec.device_id;
}
}
@@ -448,9 +438,8 @@ discover_capabilities(struct data *data, const struct spa_pod *param)
return;
spa_dict_for_each(it, &dict) {
- if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION)) {
- int version = atoi(it->value);
- if (version >= 1)
+ if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
+ spa_streq(it->value, "true")) {
data->device_negotiation_supported = true;
} else if (spa_streq(it->key, PW_CAPABILITY_DEVICE_IDS)) {
collect_device_ids(data, it->value);
@@ -798,7 +787,7 @@ int main(int argc, char *argv[])
params[n_params++] =
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
&SPA_DICT_ITEMS(
- SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1")));
+ SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true")));
#endif
/* now connect the stream, we need a direction (input/output),
diff --git a/src/examples/video-src-fixate.c b/src/examples/video-src-fixate.c
index 6d08074de..0671ab1be 100644
--- a/src/examples/video-src-fixate.c
+++ b/src/examples/video-src-fixate.c
@@ -30,7 +30,7 @@
#include
#include
-#include "utils.h"
+#include "base64.h"
/* Comment out to test device ID negotation backward compatibility. */
#define SUPPORT_DEVICE_ID_NEGOTIATION 1
@@ -450,9 +450,8 @@ discover_capabilities(struct data *data, const struct spa_pod *param)
return;
spa_dict_for_each(it, &dict) {
- if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION)) {
- int version = atoi(it->value);
- if (version >= 1)
+ if (spa_streq(it->key, PW_CAPABILITY_DEVICE_ID_NEGOTIATION) &&
+ spa_streq(it->value, "true")) {
data->device_negotiation_supported = true;
}
}
@@ -784,26 +783,23 @@ int main(int argc, char *argv[])
size_t i;
ms = open_memstream(&device_ids, &device_ids_size);
- fprintf(ms, "{\"available-devices\": [");
+ fprintf(ms, "[");
for (i = 0; i < SPA_N_ELEMENTS(devices); i++) {
dev_t device_id = makedev(devices[i].major, devices[i].minor);
- char *device_id_encoded;
-
- device_id_encoded = encode_hex((const uint8_t *) &device_id, sizeof (device_id));
+ char device_id_encoded[256];
+ base64_encode((const uint8_t *) &device_id, sizeof (device_id), device_id_encoded, '\0');
if (i > 0)
fprintf(ms, ",");
fprintf(ms, "\"%s\"", device_id_encoded);
-
- free(device_id_encoded);
}
- fprintf(ms, "]}");
+ fprintf(ms, "]");
fclose(ms);
#endif /* SUPPORT_DEVICE_IDS_LIST */
params[n_params++] =
spa_param_dict_build_dict(&b, SPA_PARAM_Capability,
- &SPA_DICT_ITEMS(SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "1"),
+ &SPA_DICT_ITEMS(SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_ID_NEGOTIATION, "true"),
#ifdef SUPPORT_DEVICE_IDS_LIST
SPA_DICT_ITEM(PW_CAPABILITY_DEVICE_IDS, device_ids)
#endif /* SUPPORT_DEVICE_IDS_LIST */
diff --git a/src/gst/gstpipewirepool.c b/src/gst/gstpipewirepool.c
index 3bcf99de7..5581c64ac 100644
--- a/src/gst/gstpipewirepool.c
+++ b/src/gst/gstpipewirepool.c
@@ -209,6 +209,8 @@ void gst_pipewire_pool_wrap_buffer (GstPipeWirePool *pool, struct pw_buffer *b)
data->b = b;
data->buf = buf;
data->crop = spa_buffer_find_meta_data (b->buffer, SPA_META_VideoCrop, sizeof(*data->crop));
+ if (data->crop)
+ gst_buffer_add_video_crop_meta(buf);
data->videotransform =
spa_buffer_find_meta_data (b->buffer, SPA_META_VideoTransform, sizeof(*data->videotransform));
data->cursor = spa_buffer_find_meta_data (b->buffer, SPA_META_Cursor, sizeof(*data->cursor));
diff --git a/src/gst/gstpipewiresrc.c b/src/gst/gstpipewiresrc.c
index b0de17dfd..8bfd799a1 100644
--- a/src/gst/gstpipewiresrc.c
+++ b/src/gst/gstpipewiresrc.c
@@ -781,7 +781,7 @@ static GstBuffer *dequeue_buffer(GstPipeWireSrc *pwsrc)
crop = data->crop;
if (crop) {
- GstVideoCropMeta *meta = gst_buffer_add_video_crop_meta(buf);
+ GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buf);
if (meta) {
meta->x = crop->region.position.x;
meta->y = crop->region.position.y;
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 6b215f3a0..0605900e5 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -276,6 +276,10 @@ pipewire_module_link_factory = shared_library('pipewire-module-link-factory',
pipewire_module_protocol_deps = [mathlib, dl_lib, pipewire_dep]
+if systemd_dep.found()
+ pipewire_module_protocol_deps += systemd_dep
+endif
+
if selinux_dep.found()
pipewire_module_protocol_deps += selinux_dep
endif
@@ -569,22 +573,6 @@ if build_module_zeroconf_discover
endif
summary({'zeroconf-discover': build_module_zeroconf_discover}, bool_yn: true, section: 'Optional Modules')
-# Several modules (rtp-sink, rtp-source, raop-sink) use the same code
-# for actual RTP transport. To not have to recompile the same code
-# multiple times, and to make the build script a little more robust
-# (by avoiding build script code duplication), create a static library
-# that contains that common code.
-pipewire_module_rtp_common_lib = static_library('pipewire-module-rtp-common-lib',
- [ 'module-rtp/stream.c' ],
- include_directories : [configinc],
- install : false,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
-)
-pipewire_module_rtp_common_dep = declare_dependency(
- link_with: pipewire_module_rtp_common_lib,
- dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
-)
-
build_module_raop_discover = avahi_dep.found()
if build_module_raop_discover
pipewire_module_raop_discover = shared_library('pipewire-module-raop-discover',
@@ -617,12 +605,13 @@ build_module_raop = openssl_lib.found()
if build_module_raop
pipewire_module_raop_sink = shared_library('pipewire-module-raop-sink',
[ 'module-raop-sink.c',
- 'module-raop/rtsp-client.c' ],
+ 'module-raop/rtsp-client.c',
+ 'module-rtp/stream.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [pipewire_module_rtp_common_dep, openssl_lib],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep, openssl_lib],
)
endif
summary({'raop-sink (requires OpenSSL)': build_module_raop}, bool_yn: true, section: 'Optional Modules')
@@ -631,33 +620,36 @@ roc_dep = dependency('roc', version: '>= 0.4.0', required: get_option('roc'))
summary({'ROC': roc_dep.found()}, bool_yn: true, section: 'Streaming between daemons')
pipewire_module_rtp_source = shared_library('pipewire-module-rtp-source',
- [ 'module-rtp-source.c' ],
+ [ 'module-rtp-source.c',
+ 'module-rtp/stream.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [pipewire_module_rtp_common_dep],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
)
pipewire_module_rtp_sink = shared_library('pipewire-module-rtp-sink',
- [ 'module-rtp-sink.c' ],
+ [ 'module-rtp-sink.c',
+ 'module-rtp/stream.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [pipewire_module_rtp_common_dep],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, opus_dep],
)
build_module_rtp_session = avahi_dep.found()
if build_module_rtp_session
pipewire_module_rtp_session = shared_library('pipewire-module-rtp-session',
- [ 'module-zeroconf-discover/avahi-poll.c',
- 'module-rtp-session.c' ],
+ [ 'module-rtp/stream.c',
+ 'module-zeroconf-discover/avahi-poll.c',
+ 'module-rtp-session.c' ],
include_directories : [configinc],
install : true,
install_dir : modules_install_dir,
install_rpath: modules_install_dir,
- dependencies : [pipewire_module_rtp_common_dep, avahi_dep],
+ dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, avahi_dep, opus_dep],
)
endif
diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c
index 64a92c5ac..eb6a6c8ef 100644
--- a/src/modules/module-filter-chain.c
+++ b/src/modules/module-filter-chain.c
@@ -193,10 +193,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* graph will then be duplicated as many times to match the number of input/output
* channels of the streams.
*
- * If the graph has no inputs and the capture channels is set as 0, only the
- * playback stream will be created. Likewise, if there are no outputs and the
- * playback channels is 0, there will be no capture stream created.
- *
* ### Volumes
*
* Normally the volume of the sink/source is handled by the stream software volume.
@@ -656,40 +652,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* of "Attack (s)" seconds. The noise gate stays open for at least "Hold (s)"
* seconds before it can close again.
*
- * ### Busy
- *
- * The `busy` plugin has no input or output ports and it can be used to keep the
- * CPU or graph busy for the given percent of time.
- *
- * The node requires a `config` section with extra configuration:
- *
- *\code{.unparsed}
- * filter.graph = {
- * nodes = [
- * {
- * type = builtin
- * name = ...
- * label = busy
- * config = {
- * wait-percent = 0.0
- * cpu-percent = 50.0
- * }
- * ...
- * }
- * }
- * ...
- * }
- *\endcode
- *
- * - `wait-percent` the percentage of time to wait. This keeps the graph busy but
- * not the CPU. Default 0.0
- * - `cpu-percent` the percentage of time to keep the CPU busy. This keeps both the
- * graph and CPU busy. Default 0.0
- *
- * ### Null
- *
- * The `null` plugin has one data input "In" and one control input "Control" that
- * simply discards the data.
*
* ## SOFA filters
*
@@ -717,7 +679,6 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* blocksize = ...
* tailsize = ...
* filename = ...
- * gain = ...
* }
* control = {
* "Azimuth" = ...
@@ -734,10 +695,9 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* - `blocksize` specifies the size of the blocks to use in the FFT. It is a value
* between 64 and 256. When not specified, this value is
* computed automatically from the number of samples in the file.
- * - `tailsize` specifies the size of the tail blocks to use in the FFT.
- * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension
- * and contain the HRTF for the various spatial positions.
- * - `gain` the overall gain to apply to the IR file.
+ * - `tailsize` specifies the size of the tail blocks to use in the FFT.
+ * - `filename` The SOFA file to load. SOFA files usually end in the .sofa extension
+ * and contain the HRTF for the various spatial positions.
*
* - `Azimuth` controls the azimuth, this is the direction the sound is coming from
* in degrees between 0 and 360. 0 is straight ahead. 90 is left, 180
@@ -1255,105 +1215,92 @@ static void capture_destroy(void *d)
impl->capture = NULL;
}
-static void do_process(struct impl *impl)
-{
- struct pw_buffer *in, *out;
- uint32_t i, n_in = 0, n_out = 0, data_size = 0;
- struct spa_data *bd;
- const void *cin[128];
- void *cout[128];
-
- in = out = NULL;
- if (impl->capture) {
- while (true) {
- struct pw_buffer *t;
- if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
- break;
- if (in)
- pw_stream_queue_buffer(impl->capture, in);
- in = t;
- }
- if (in == NULL) {
- pw_log_debug("%p: out of capture buffers: %m", impl);
- } else {
- for (i = 0; i < in->buffer->n_datas; i++) {
- uint32_t offs, size;
-
- bd = &in->buffer->datas[i];
-
- offs = SPA_MIN(bd->chunk->offset, bd->maxsize);
- size = SPA_MIN(bd->chunk->size, bd->maxsize - offs);
-
- cin[n_in++] = SPA_PTROFF(bd->data, offs, void);
-
- data_size = i == 0 ? size : SPA_MIN(data_size, size);
- }
- }
- }
- if (impl->playback) {
- out = pw_stream_dequeue_buffer(impl->playback);
- if (out == NULL) {
- pw_log_debug("%p: out of playback buffers: %m", impl);
- } else {
- if (data_size == 0)
- data_size = out->requested * sizeof(float);
-
- for (i = 0; i < out->buffer->n_datas; i++) {
- bd = &out->buffer->datas[i];
-
- data_size = SPA_MIN(data_size, bd->maxsize);
-
- cout[n_out++] = bd->data;
-
- bd->chunk->offset = 0;
- bd->chunk->size = data_size;
- bd->chunk->stride = sizeof(float);
- }
- }
- pw_log_trace_fp("%p: size:%d requested:%"PRIu64, impl,
- data_size, out->requested);
- }
-
- for (; n_in < impl->n_inputs; i++)
- cin[n_in++] = NULL;
- for (; n_out < impl->n_outputs; i++)
- cout[n_out++] = NULL;
-
- if (impl->graph_active)
- spa_filter_graph_process(impl->graph, cin, cout, data_size / sizeof(float));
-
- if (in != NULL)
- pw_stream_queue_buffer(impl->capture, in);
- if (out != NULL)
- pw_stream_queue_buffer(impl->playback, out);
-}
-
static void capture_process(void *d)
{
struct impl *impl = d;
int res;
-
- if (impl->playback) {
- if ((res = pw_stream_trigger_process(impl->playback)) < 0) {
- pw_log_debug("playback trigger error: %s", spa_strerror(res));
- while (impl->capture) {
- struct pw_buffer *t;
- if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
- break;
- /* playback part is not ready, consume, discard and recycle
- * the capture buffers */
- pw_stream_queue_buffer(impl->capture, t);
- }
+ if ((res = pw_stream_trigger_process(impl->playback)) < 0) {
+ pw_log_debug("playback trigger error: %s", spa_strerror(res));
+ while (true) {
+ struct pw_buffer *t;
+ if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
+ break;
+ /* playback part is not ready, consume, discard and recycle
+ * the capture buffers */
+ pw_stream_queue_buffer(impl->capture, t);
}
- } else {
- do_process(impl);
}
}
static void playback_process(void *d)
{
struct impl *impl = d;
- do_process(impl);
+ struct pw_buffer *in, *out;
+ uint32_t i, data_size = 0;
+ int32_t stride = 0;
+ struct spa_data *bd;
+ const void *cin[128];
+ void *cout[128];
+
+ in = NULL;
+ while (true) {
+ struct pw_buffer *t;
+ if ((t = pw_stream_dequeue_buffer(impl->capture)) == NULL)
+ break;
+ if (in)
+ pw_stream_queue_buffer(impl->capture, in);
+ in = t;
+ }
+ if (in == NULL)
+ pw_log_debug("%p: out of capture buffers: %m", impl);
+
+ if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL)
+ pw_log_debug("%p: out of playback buffers: %m", impl);
+
+ if (in == NULL || out == NULL)
+ goto done;
+
+ for (i = 0; i < in->buffer->n_datas; i++) {
+ uint32_t offs, size;
+
+ bd = &in->buffer->datas[i];
+
+ offs = SPA_MIN(bd->chunk->offset, bd->maxsize);
+ size = SPA_MIN(bd->chunk->size, bd->maxsize - offs);
+
+ cin[i] = SPA_PTROFF(bd->data, offs, void);
+
+ data_size = i == 0 ? size : SPA_MIN(data_size, size);
+ stride = SPA_MAX(stride, bd->chunk->stride);
+ }
+ for (; i < impl->n_inputs; i++)
+ cin[i] = NULL;
+
+ for (i = 0; i < out->buffer->n_datas; i++) {
+ bd = &out->buffer->datas[i];
+
+ data_size = SPA_MIN(data_size, bd->maxsize);
+
+ cout[i] = bd->data;
+
+ bd->chunk->offset = 0;
+ bd->chunk->size = data_size;
+ bd->chunk->stride = stride;
+ }
+ for (; i < impl->n_outputs; i++)
+ cout[i] = NULL;
+
+ pw_log_trace_fp("%p: stride:%d size:%d requested:%"PRIu64" (%"PRIu64")", impl,
+ stride, data_size, out->requested, out->requested * stride);
+
+ if (impl->graph_active)
+ spa_filter_graph_process(impl->graph, cin, cout, data_size / sizeof(float));
+
+done:
+ if (in != NULL)
+ pw_stream_queue_buffer(impl->capture, in);
+ if (out != NULL)
+ pw_stream_queue_buffer(impl->playback, out);
}
static int activate_graph(struct impl *impl)
@@ -1422,9 +1369,6 @@ static void update_latency(struct impl *impl, enum spa_direction direction, bool
struct pw_stream *s = direction == SPA_DIRECTION_OUTPUT ?
impl->playback : impl->capture;
- if (s == NULL)
- return;
-
spa_pod_builder_init(&b, buffer, sizeof(buffer));
latency = impl->latency[direction];
spa_process_latency_info_add(&impl->process_latency, &latency);
@@ -1481,13 +1425,10 @@ static void param_tag_changed(struct impl *impl, const struct spa_pod *param,
if (param == 0 || spa_tag_parse(param, &tag, &state) < 0)
return;
- if (tag.direction == SPA_DIRECTION_INPUT) {
- if (impl->capture)
- pw_stream_update_params(impl->capture, params, 1);
- } else {
- if (impl->playback)
- pw_stream_update_params(impl->playback, params, 1);
- }
+ if (tag.direction == SPA_DIRECTION_INPUT)
+ pw_stream_update_params(impl->capture, params, 1);
+ else
+ pw_stream_update_params(impl->playback, params, 1);
}
static void capture_state_changed(void *data, enum pw_stream_state old,
@@ -1564,7 +1505,8 @@ static void param_changed(struct impl *impl, uint32_t id, const struct spa_pod *
return;
error:
- pw_stream_set_error(stream, res, "can't start graph: %s", spa_strerror(res));
+ pw_stream_set_error(direction == SPA_DIRECTION_INPUT ? impl->capture : impl->playback,
+ res, "can't start graph: %s", spa_strerror(res));
}
static void capture_param_changed(void *data, uint32_t id, const struct spa_pod *param)
@@ -1623,7 +1565,7 @@ static void playback_state_changed(void *data, enum pw_stream_state old,
}
return;
error:
- pw_stream_set_error(impl->playback, res, "can't start graph: %s",
+ pw_stream_set_error(impl->capture, res, "can't start graph: %s",
spa_strerror(res));
}
@@ -1652,39 +1594,43 @@ static const struct pw_stream_events out_stream_events = {
static int setup_streams(struct impl *impl)
{
int res;
- uint32_t i, n_params, *offs, flags;
+ uint32_t i, n_params, *offs;
struct pw_array offsets;
const struct spa_pod **params = NULL;
struct spa_pod_dynamic_builder b;
struct spa_filter_graph *graph = impl->graph;
- if (impl->capture_info.channels > 0) {
- impl->capture = pw_stream_new(impl->core,
- "filter capture", impl->capture_props);
- impl->capture_props = NULL;
- if (impl->capture == NULL)
- return -errno;
+ impl->capture = pw_stream_new(impl->core,
+ "filter capture", impl->capture_props);
+ impl->capture_props = NULL;
+ if (impl->capture == NULL)
+ return -errno;
- pw_stream_add_listener(impl->capture,
- &impl->capture_listener,
- &in_stream_events, impl);
- }
+ pw_stream_add_listener(impl->capture,
+ &impl->capture_listener,
+ &in_stream_events, impl);
- if (impl->playback_info.channels > 0) {
- impl->playback = pw_stream_new(impl->core,
- "filter playback", impl->playback_props);
- impl->playback_props = NULL;
- if (impl->playback == NULL)
- return -errno;
+ impl->playback = pw_stream_new(impl->core,
+ "filter playback", impl->playback_props);
+ impl->playback_props = NULL;
+ if (impl->playback == NULL)
+ return -errno;
- pw_stream_add_listener(impl->playback,
- &impl->playback_listener,
- &out_stream_events, impl);
- }
+ pw_stream_add_listener(impl->playback,
+ &impl->playback_listener,
+ &out_stream_events, impl);
spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
pw_array_init(&offsets, 512);
+ if ((offs = pw_array_add(&offsets, sizeof(uint32_t))) == NULL) {
+ res = -errno;
+ goto done;
+ }
+ *offs = b.b.state.offset;
+ spa_format_audio_raw_build(&b.b,
+ SPA_PARAM_EnumFormat, &impl->capture_info);
+
for (i = 0;; i++) {
uint32_t save = b.b.state.offset;
if (spa_filter_graph_enum_prop_info(graph, i, &b.b, NULL) != 1)
@@ -1707,7 +1653,7 @@ static int setup_streams(struct impl *impl)
res = -ENOMEM;
goto done;
}
- if ((params = calloc(n_params+1, sizeof(struct spa_pod*))) == NULL) {
+ if ((params = calloc(n_params, sizeof(struct spa_pod*))) == NULL) {
res = -errno;
goto done;
}
@@ -1716,44 +1662,32 @@ static int setup_streams(struct impl *impl)
for (i = 0; i < n_params; i++)
params[i] = spa_pod_builder_deref(&b.b, offs[i]);
- if (impl->capture) {
- params[n_params++] = spa_format_audio_raw_build(&b.b,
- SPA_PARAM_EnumFormat, &impl->capture_info);
- flags = PW_STREAM_FLAG_AUTOCONNECT |
+ res = pw_stream_connect(impl->capture,
+ PW_DIRECTION_INPUT,
+ PW_ID_ANY,
+ PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
- PW_STREAM_FLAG_RT_PROCESS;
- if (impl->playback)
- flags |= PW_STREAM_FLAG_ASYNC;
+ PW_STREAM_FLAG_RT_PROCESS |
+ PW_STREAM_FLAG_ASYNC,
+ params, n_params);
- res = pw_stream_connect(impl->capture,
- PW_DIRECTION_INPUT,
- PW_ID_ANY,
- flags,
- params, n_params);
+ spa_pod_dynamic_builder_clean(&b);
+ if (res < 0)
+ goto done;
- spa_pod_dynamic_builder_clean(&b);
- if (res < 0)
- goto done;
+ n_params = 0;
+ spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
+ params[n_params++] = spa_format_audio_raw_build(&b.b,
+ SPA_PARAM_EnumFormat, &impl->playback_info);
- n_params = 0;
- spa_pod_dynamic_builder_init(&b, NULL, 0, 4096);
- }
- if (impl->playback) {
- params[n_params++] = spa_format_audio_raw_build(&b.b,
- SPA_PARAM_EnumFormat, &impl->playback_info);
-
- flags = PW_STREAM_FLAG_AUTOCONNECT |
+ res = pw_stream_connect(impl->playback,
+ PW_DIRECTION_OUTPUT,
+ PW_ID_ANY,
+ PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
- PW_STREAM_FLAG_RT_PROCESS;
- if (impl->capture)
- flags |= PW_STREAM_FLAG_TRIGGER;
-
- res = pw_stream_connect(impl->playback,
- PW_DIRECTION_OUTPUT,
- PW_ID_ANY,
- flags,
- params, n_params);
- }
+ PW_STREAM_FLAG_RT_PROCESS |
+ PW_STREAM_FLAG_TRIGGER,
+ params, n_params);
spa_pod_dynamic_builder_clean(&b);
done:
@@ -1777,11 +1711,20 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
{
struct impl *impl = object;
struct spa_dict *props = info->props;
- uint32_t i, val = 0;
+ uint32_t i;
+
+ if (impl->capture_info.channels == 0)
+ impl->capture_info.channels = info->n_inputs;
+ if (impl->playback_info.channels == 0)
+ impl->playback_info.channels = info->n_outputs;
impl->n_inputs = info->n_inputs;
impl->n_outputs = info->n_outputs;
+ if (impl->capture_info.channels == impl->playback_info.channels) {
+ copy_position(&impl->capture_info, &impl->playback_info);
+ copy_position(&impl->playback_info, &impl->capture_info);
+ }
for (i = 0; props && i < props->n_items; i++) {
const char *k = props->items[i].key;
const char *s = props->items[i].value;
@@ -1795,22 +1738,6 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
}
}
}
- else if (spa_streq(k, "n_default_inputs") &&
- impl->capture_info.channels == 0 &&
- spa_atou32(s, &val, 0)) {
- pw_log_info("using default inputs %d", val);
- impl->capture_info.channels = val;
- }
- else if (spa_streq(k, "n_default_outputs") &&
- impl->playback_info.channels == 0 &&
- spa_atou32(s, &val, 0)) {
- pw_log_info("using default outputs %d", val);
- impl->playback_info.channels = val;
- }
- }
- if (impl->capture_info.channels == impl->playback_info.channels) {
- copy_position(&impl->capture_info, &impl->playback_info);
- copy_position(&impl->playback_info, &impl->capture_info);
}
}
@@ -1833,11 +1760,7 @@ static void graph_props_changed(void *object, enum spa_direction direction)
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
spa_filter_graph_get_props(graph, &b.b, (struct spa_pod **)¶ms[0]);
- if (impl->capture)
- pw_stream_update_params(impl->capture, params, 1);
- else if (impl->playback)
- pw_stream_update_params(impl->playback, params, 1);
-
+ pw_stream_update_params(impl->capture, params, 1);
spa_pod_dynamic_builder_clean(&b);
}
diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c
index 2be92a847..96e99f35e 100644
--- a/src/modules/module-protocol-native.c
+++ b/src/modules/module-protocol-native.c
@@ -34,6 +34,10 @@
#include
#include
+#ifdef HAVE_SYSTEMD
+#include
+#endif
+
#ifdef HAVE_SELINUX
#include
#endif
@@ -41,7 +45,6 @@
#include
#include
-#include "network-utils.h"
#include "pipewire/private.h"
#include "modules/module-protocol-native/connection.h"
@@ -906,12 +909,13 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc
int fd = -1, res;
bool activated = false;
+#ifdef HAVE_SYSTEMD
{
- int i, n = listen_fd();
+ int i, n = sd_listen_fds(0);
for (i = 0; i < n; ++i) {
- if (is_socket_unix(LISTEN_FDS_START + i, SOCK_STREAM,
- s->addr.sun_path) > 0) {
- fd = LISTEN_FDS_START + i;
+ if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM,
+ 1, s->addr.sun_path, 0) > 0) {
+ fd = SD_LISTEN_FDS_START + i;
activated = true;
pw_log_info("server %p: Found socket activation socket for '%s'",
s, s->addr.sun_path);
@@ -919,6 +923,7 @@ static int add_socket(struct pw_protocol *protocol, struct server *s, struct soc
}
}
}
+#endif
if (fd < 0) {
struct stat socket_stat;
diff --git a/src/modules/module-protocol-pulse/client.h b/src/modules/module-protocol-pulse/client.h
index 81d790b51..2c413e51c 100644
--- a/src/modules/module-protocol-pulse/client.h
+++ b/src/modules/module-protocol-pulse/client.h
@@ -62,12 +62,8 @@ struct client {
struct pw_manager_object *metadata_schema_sm_settings;
bool have_force_mono_audio;
- bool default_force_mono_audio;
- bool have_bluetooth_headset_autoswitch;
- bool default_bluetooth_headset_autoswitch;
struct pw_manager_object *metadata_sm_settings;
bool force_mono_audio;
- bool bluetooth_headset_autoswitch;
uint32_t connect_tag;
diff --git a/src/modules/module-protocol-pulse/defs.h b/src/modules/module-protocol-pulse/defs.h
index f3e25f137..51e2453ff 100644
--- a/src/modules/module-protocol-pulse/defs.h
+++ b/src/modules/module-protocol-pulse/defs.h
@@ -39,8 +39,6 @@
#define MODULE_INDEX_MASK 0xfffffffu
#define MODULE_FLAG (1u << 29)
-#define STREAM_CREATE_TIMEOUT (35 * SPA_NSEC_PER_SEC)
-
#define DEFAULT_SINK "@DEFAULT_SINK@"
#define DEFAULT_SOURCE "@DEFAULT_SOURCE@"
#define DEFAULT_MONITOR "@DEFAULT_MONITOR@"
@@ -326,6 +324,5 @@ static inline uint32_t port_type_value(const char *port_type)
#define METADATA_TARGET_NODE "target.node"
#define METADATA_TARGET_OBJECT "target.object"
#define METADATA_FEATURES_AUDIO_MONO "node.features.audio.mono"
-#define METADATA_BLUETOOTH_HEADSET_AUTOSWITCH "bluetooth.autoswitch-to-headset-profile"
#endif /* PULSE_SERVER_DEFS_H */
diff --git a/src/modules/module-protocol-pulse/manager.c b/src/modules/module-protocol-pulse/manager.c
index 72d51b020..5b597f4eb 100644
--- a/src/modules/module-protocol-pulse/manager.c
+++ b/src/modules/module-protocol-pulse/manager.c
@@ -718,7 +718,7 @@ static void on_core_error(void *data, uint32_t id, int seq, int res, const char
{
struct manager *m = data;
- if (id == PW_ID_CORE && (res == -EPIPE || res == -EPROTO)) {
+ if (id == PW_ID_CORE && res == -EPIPE) {
pw_log_debug("connection error: %d, %s", res, message);
manager_emit_disconnect(m);
}
diff --git a/src/modules/module-protocol-pulse/message-handler.c b/src/modules/module-protocol-pulse/message-handler.c
index b44d4f422..48c7c7be3 100644
--- a/src/modules/module-protocol-pulse/message-handler.c
+++ b/src/modules/module-protocol-pulse/message-handler.c
@@ -110,59 +110,14 @@ static int core_object_force_mono_output(struct client *client, const char *para
if (spa_streq(params, "true")) {
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "true");
- client->force_mono_audio = true;
} else if (spa_streq(params, "false")) {
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "false");
- client->force_mono_audio = false;
} else if (spa_streq(params, "null")) {
ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
METADATA_FEATURES_AUDIO_MONO, NULL, NULL);
- client->force_mono_audio = client->default_force_mono_audio;
} else {
- fprintf(response, "Value must be true, false, or null");
- return -EINVAL;
- }
-
- if (ret < 0)
- fprintf(response, "Could not set metadata: %s", spa_strerror(ret));
- else
- fprintf(response, "%s", params);
-
- return ret;
- }
-}
-
-static int core_object_bluetooth_headset_autoswitch(struct client *client, const char *params, FILE *response)
-{
- if (!client->have_bluetooth_headset_autoswitch) {
- /* Not supported, return a null value to indicate that */
- fprintf(response, "null");
- return 0;
- }
-
- if (!params || params[0] == '\0') {
- /* No parameter => query the current value */
- fprintf(response, "%s", client->bluetooth_headset_autoswitch ? "true" : "false");
- return 0;
- } else {
- /* The caller is trying to set a value or clear with a null */
- int ret;
-
- if (spa_streq(params, "true")) {
- ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
- METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "true");
- client->bluetooth_headset_autoswitch = true;
- } else if (spa_streq(params, "false")) {
- ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
- METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "false");
- client->bluetooth_headset_autoswitch = false;
- } else if (spa_streq(params, "null")) {
- ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE,
- METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, NULL, NULL);
- client->bluetooth_headset_autoswitch = client->default_bluetooth_headset_autoswitch;
- } else {
- fprintf(response, "Value must be true, false, or null");
+ fprintf(response, "Value must be true, false, or clear");
return -EINVAL;
}
@@ -183,15 +138,14 @@ static int core_object_message_handler(struct client *client, struct pw_manager_
fprintf(response,
"/core []\n"
"available commands:\n"
- " help this help\n"
- " list-handlers show available object handlers\n"
- " pipewire-pulse:malloc-info show malloc_info\n"
- " pipewire-pulse:malloc-trim run malloc_trim\n"
- " pipewire-pulse:log-level update log level with \n"
- " pipewire-pulse:list-modules list all module names\n"
- " pipewire-pulse:describe-module describe module info for \n"
- " pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs\n"
- " pipewire-pulse:bluetooth-headset-autoswitch use bluetooth headset mic if available"
+ " help this help\n"
+ " list-handlers show available object handlers\n"
+ " pipewire-pulse:malloc-info show malloc_info\n"
+ " pipewire-pulse:malloc-trim run malloc_trim\n"
+ " pipewire-pulse:log-level update log level with \n"
+ " pipewire-pulse:list-modules list all module names\n"
+ " pipewire-pulse:describe-module describe module info for \n"
+ " pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs"
);
} else if (spa_streq(message, "list-handlers")) {
bool first = true;
@@ -254,8 +208,6 @@ static int core_object_message_handler(struct client *client, struct pw_manager_
}
} else if (spa_streq(message, "pipewire-pulse:force-mono-output")) {
return core_object_force_mono_output(client, params, response);
- } else if (spa_streq(message, "pipewire-pulse:bluetooth-headset-autoswitch")) {
- return core_object_bluetooth_headset_autoswitch(client, params, response);
} else {
return -ENOSYS;
}
diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c
index 59610ef57..c22499718 100644
--- a/src/modules/module-protocol-pulse/pulse-server.c
+++ b/src/modules/module-protocol-pulse/pulse-server.c
@@ -973,33 +973,12 @@ static void manager_metadata(void *data, struct pw_manager_object *o,
if (subject == PW_ID_CORE && o == client->metadata_routes)
client_update_routes(client, key, value);
if (subject == PW_ID_CORE && o == client->metadata_schema_sm_settings) {
- char default_[16];
-
- if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) {
+ if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO))
client->have_force_mono_audio = true;
-
- if (spa_json_str_object_find(value, strlen(value),
- "default", default_, sizeof(default_)) < 0)
- client->default_force_mono_audio = false;
- else
- client->default_force_mono_audio = spa_streq(default_, "true");
- }
-
- if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) {
- client->have_bluetooth_headset_autoswitch = true;
-
- if (spa_json_str_object_find(value, strlen(value),
- "default", default_, sizeof(default_)) < 0)
- client->default_bluetooth_headset_autoswitch = false;
- else
- client->default_bluetooth_headset_autoswitch = spa_streq(default_, "true");
- }
}
if (subject == PW_ID_CORE && o == client->metadata_sm_settings) {
if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO))
client->force_mono_audio = spa_streq(value, "true");
- if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH))
- client->bluetooth_headset_autoswitch = spa_streq(value, "true");
}
}
diff --git a/src/modules/module-protocol-pulse/sample-play.c b/src/modules/module-protocol-pulse/sample-play.c
index 09b0e75cc..db3893c0f 100644
--- a/src/modules/module-protocol-pulse/sample-play.c
+++ b/src/modules/module-protocol-pulse/sample-play.c
@@ -17,12 +17,10 @@
#include
#include
-#include "defs.h"
#include "format.h"
#include "log.h"
#include "sample.h"
#include "sample-play.h"
-#include "internal.h"
static void sample_play_stream_state_changed(void *data, enum pw_stream_state old,
enum pw_stream_state state, const char *error)
@@ -32,32 +30,17 @@ static void sample_play_stream_state_changed(void *data, enum pw_stream_state ol
switch (state) {
case PW_STREAM_STATE_UNCONNECTED:
case PW_STREAM_STATE_ERROR:
- pw_timer_queue_cancel(&p->timer);
sample_play_emit_done(p, -EIO);
break;
case PW_STREAM_STATE_PAUSED:
p->id = pw_stream_get_node_id(p->stream);
sample_play_emit_ready(p, p->id);
break;
- case PW_STREAM_STATE_STREAMING:
- pw_timer_queue_cancel(&p->timer);
- break;
default:
break;
}
}
-static void sample_play_start_timeout(void *user_data)
-{
- struct sample_play *p = user_data;
-
- pw_log_info("timeout on sample %s", p->sample->name);
-
- if (p->stream)
- pw_stream_set_active(p->stream, false);
- sample_play_emit_done(p, -ETIMEDOUT);
-}
-
static void sample_play_stream_destroy(void *data)
{
struct sample_play *p = data;
@@ -180,10 +163,6 @@ struct sample_play *sample_play_new(struct pw_core *core,
if (res < 0)
goto error_cleanup;
- /* Time out if we don't get a link; same timeout as for normal streams */
- pw_timer_queue_add(sample->impl->timer_queue, &p->timer, NULL,
- STREAM_CREATE_TIMEOUT, sample_play_start_timeout, p);
-
return p;
error_cleanup:
@@ -202,8 +181,6 @@ void sample_play_destroy(struct sample_play *p)
spa_hook_list_clean(&p->hooks);
- pw_timer_queue_cancel(&p->timer);
-
free(p);
}
diff --git a/src/modules/module-protocol-pulse/sample-play.h b/src/modules/module-protocol-pulse/sample-play.h
index 98af7ee29..a34ca9213 100644
--- a/src/modules/module-protocol-pulse/sample-play.h
+++ b/src/modules/module-protocol-pulse/sample-play.h
@@ -11,8 +11,6 @@
#include
#include
-#include
-
struct sample;
struct pw_core;
struct pw_loop;
@@ -43,7 +41,6 @@ struct sample_play {
uint32_t offset;
uint32_t stride;
struct spa_hook_list hooks;
- struct pw_timer timer;
void *user_data;
};
diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c
index aeab710b0..4e744e33f 100644
--- a/src/modules/module-protocol-pulse/server.c
+++ b/src/modules/module-protocol-pulse/server.c
@@ -21,6 +21,9 @@
#include
#include
+#ifdef HAVE_SYSTEMD
+#include
+#endif
#include
#include
@@ -574,19 +577,26 @@ static bool is_stale_socket(int fd, const struct sockaddr_un *addr_un)
return false;
}
-static int check_socket_activation(const char *path)
+#ifdef HAVE_SYSTEMD
+static int check_systemd_activation(const char *path)
{
- const int n = listen_fd();
+ const int n = sd_listen_fds(0);
for (int i = 0; i < n; i++) {
- const int fd = LISTEN_FDS_START + i;
+ const int fd = SD_LISTEN_FDS_START + i;
- if (is_socket_unix(fd, SOCK_STREAM, path) > 0)
+ if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path, 0) > 0)
return fd;
}
return -1;
}
+#else
+static inline int check_systemd_activation(SPA_UNUSED const char *path)
+{
+ return -1;
+}
+#endif
static int start_unix_server(struct server *server, const struct sockaddr_storage *addr)
{
@@ -596,10 +606,10 @@ static int start_unix_server(struct server *server, const struct sockaddr_storag
spa_assert(addr_un->sun_family == AF_UNIX);
- fd = check_socket_activation(addr_un->sun_path);
+ fd = check_systemd_activation(addr_un->sun_path);
if (fd >= 0) {
server->activated = true;
- pw_log_info("server %p: found socket activation socket for '%s'",
+ pw_log_info("server %p: found systemd socket activation socket for '%s'",
server, addr_un->sun_path);
goto done;
}
diff --git a/src/modules/module-protocol-pulse/stream.c b/src/modules/module-protocol-pulse/stream.c
index a6beb9fd7..496bcb52c 100644
--- a/src/modules/module-protocol-pulse/stream.c
+++ b/src/modules/module-protocol-pulse/stream.c
@@ -107,7 +107,7 @@ struct stream *stream_new(struct client *client, enum stream_type type, uint32_t
/* Time out if we don't get a link and can't send a reply to create in 35s. Client will time out in
* 30s and clean up its stream anyway. */
pw_timer_queue_add(stream->impl->timer_queue, &stream->timer, NULL,
- STREAM_CREATE_TIMEOUT, create_stream_timeout, stream);
+ 35 * SPA_NSEC_PER_SEC, create_stream_timeout, stream);
return stream;
diff --git a/src/modules/module-rtp/audio.c b/src/modules/module-rtp/audio.c
index d20e9a37c..13873f29c 100644
--- a/src/modules/module-rtp/audio.c
+++ b/src/modules/module-rtp/audio.c
@@ -22,15 +22,6 @@ static void ringbuffer_clear(struct spa_ringbuffer *rbuf SPA_UNUSED,
memset(iov[1].iov_base, 0, iov[1].iov_len);
}
-static inline uint64_t scale_u64(uint64_t val, uint32_t num, uint32_t denom)
-{
-#if 0
- return ((__uint128_t)val * num) / denom;
-#else
- return (uint64_t)((double)val / denom * num);
-#endif
-}
-
static void rtp_audio_process_playback(void *data)
{
struct impl *impl = data;
@@ -70,9 +61,6 @@ static void rtp_audio_process_playback(void *data)
* read or write index itself.) */
if (impl->direct_timestamp) {
- uint32_t num_samples_to_read;
- uint32_t read_index;
-
/* In direct timestamp mode, the focus lies on synchronized playback, not
* on a constant latency. The ring buffer fill level is not of interest
* here. The code in rtp_audio_receive() writes to the ring buffer at
@@ -101,32 +89,22 @@ static void rtp_audio_process_playback(void *data)
* timestamp mode, since all of them shift the timestamp by the same
* `sess.latency.msec` into the future.
*
- * Since in this mode, a constant latency is not important, tracking
- * the fill level to keep it steady makes no sense. Consequently,
- * no DLL is needed. Also, matching the pace of the synchronized clock
- * is done by having the graph driver be synchronized to that clock,
- * which will in turn cause any output sinks to adjust their DLLs
- * (or similar control loop mechanisms) to match the pace of their
- * data consumption with the pace of the driver.
- *
- * The fill level is still important though to correctly handle corner
- * cases where the ring buffer is (almost) empty. If fewer samples
- * are available than what the read operation wants, the deficit
- * has to be compensated with nullbytes. To that end, the "avail"
- * quantity tracks how many samples are actually available. */
+ * "Fill level" makes no sense in this mode, since a constant latency
+ * is not important in this mode, so no DLL is needed. Also, matching
+ * the pace of the synchronized clock is done by having the graph
+ * driver be synchronized to that clock, which will in turn cause
+ * any output sinks to adjust their DLLs (or similar control loop
+ * mechanisms) to match the pace of their data consumption with the
+ * pace of the driver. */
if (impl->io_position) {
- uint32_t clock_rate = impl->io_position->clock.rate.denom;
-
- /* Translate the clock position to an RTP timestamp and
- * shift it to compensate for device delay and ASRC delay.
- * The device delay is scaled along with the clock position,
- * since both are expressed in clock sample units, while
- * pwt.buffered is expressed in stream time. */
- timestamp = scale_u64(impl->io_position->clock.position + device_delay,
- impl->rate, clock_rate) + pwt.buffered;
+ /* Use the clock position directly as the read index.
+ * Do NOT add device_delay here - the sink's DLL handles
+ * matching its hardware clock to the driver pace. Adding
+ * device_delay would create a feedback loop since rate
+ * adjustments affect both ringbuffer and device buffer. */
+ timestamp = impl->io_position->clock.position;
spa_ringbuffer_read_update(&impl->ring, timestamp);
- avail = spa_ringbuffer_get_read_index(&impl->ring, &read_index);
} else {
/* In the unlikely case that no spa_io_position pointer
* was passed yet by PipeWire to this node, resort to a
@@ -134,72 +112,26 @@ static void rtp_audio_process_playback(void *data)
* This most likely is not in sync with other nodes,
* but _something_ is needed as read index until the
* spa_io_position is available. */
- avail = spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
- read_index = timestamp;
+ spa_ringbuffer_get_read_index(&impl->ring, ×tamp);
}
- /* If avail is 0, it means that the ring buffer is empty. <0 means
- * that there is an underrun, typically because the PTP time now
- * is ahead of the RTP data (this can happen when the PTP master
- * changes for example). And in cases where only a little bit of
- * data is left, it is important to not try to use more than what
- * is actually available.
- * Overruns would happen if the write pointer is further ahead than
- * what the ringbuffer size actually allows. This too can happen
- * if the PTP time jumps. No actual buffer overflow would happen
- * then, since the write operations always apply modulo to the
- * timestamps to wrap around the ringbuffer borders.
- */
- bool has_underrun = (avail < 0);
- bool has_overrun = !has_underrun && ((uint32_t)avail) > impl->actual_max_buffer_size;
- num_samples_to_read = has_underrun ? 0 : SPA_MIN((uint32_t)avail, wanted);
+ spa_ringbuffer_read_data(&impl->ring,
+ impl->buffer,
+ impl->actual_max_buffer_size,
+ ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
+ d[0].data, wanted * stride);
- /* Do some additional logging in the under/overrun cases. */
- if (SPA_UNLIKELY(pw_log_topic_enabled(SPA_LOG_LEVEL_TRACE, PW_LOG_TOPIC_DEFAULT)))
- {
- uint32_t write_index;
- int32_t filled = spa_ringbuffer_get_write_index(&impl->ring, &write_index);
-
- if (has_underrun) {
- pw_log_trace("Direct timestamp mode: Read index underrun: write_index: %"
- PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32,
- write_index, read_index, wanted, filled);
- } else if (has_overrun) {
- pw_log_trace("Direct timestamp mode: Read index overrun: write_index: %"
- PRIu32 ", read_index: %" PRIu32 ", wanted: %u - filled: %" PRIi32
- ", buffer size: %u", write_index, read_index, wanted, filled,
- impl->actual_max_buffer_size);
- }
- }
-
- if (num_samples_to_read > 0) {
- spa_ringbuffer_read_data(&impl->ring,
- impl->buffer,
- impl->actual_max_buffer_size,
- ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
- d[0].data, num_samples_to_read * stride);
-
- /* Clear the bytes that were just retrieved. Since the fill level
- * is not tracked in this buffer mode, it is possible that as soon
- * as actual playback ends, the RTP source node re-reads old data.
- * Make sure it reads silence when no actual new data is present
- * and the RTP source node still runs. Do this by filling the
- * region of the retrieved data with null bytes. */
- ringbuffer_clear(&impl->ring,
- impl->buffer,
- impl->actual_max_buffer_size,
- ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
- num_samples_to_read * stride);
- }
-
- if (num_samples_to_read < wanted) {
- /* If fewer samples were available than what was wanted,
- * fill the remaining space in the destination memory
- * with nullsamples. */
- void *bytes_to_clear = SPA_PTROFF(d[0].data, num_samples_to_read * stride, void);
- size_t num_bytes_to_clear = (wanted - num_samples_to_read) * stride;
- spa_memzero(bytes_to_clear, num_bytes_to_clear);
- }
+ /* Clear the bytes that were just retrieved. Since the fill level
+ * is not tracked in this buffer mode, it is possible that as soon
+ * as actual playback ends, the RTP source node re-reads old data.
+ * Make sure it reads silence when no actual new data is present
+ * and the RTP source node still runs. Do this by filling the
+ * region of the retrieved data with null bytes. */
+ ringbuffer_clear(&impl->ring,
+ impl->buffer,
+ impl->actual_max_buffer_size,
+ ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
+ wanted * stride);
if (!impl->io_position) {
/* In the unlikely case that no spa_io_position pointer
@@ -290,25 +222,6 @@ static void rtp_audio_process_playback(void *data)
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
d[0].data, wanted * stride);
- /* Clear the bytes that were just retrieved. Unlike in the
- * direct timestamp mode, here, bytes are always read out
- * of the ring buffer in sequence - the read pointer does
- * not "jump around" (which can happen in direct timestamp
- * mode if the last iteration has been a while ago and the
- * driver clock time advanced significantly, or if the driver
- * time experienced a discontinuity). However, should there
- * be packet loss, it could lead to segments in the ring
- * buffer that should have been written to but weren't written
- * to. These segments would then contain old stale data. By
- * clearing data out of the ring buffer after reading it, it
- * is ensured that no stale data can exist - in the packet loss
- * case, the outcome would be a gap made of nullsamples instead. */
- ringbuffer_clear(&impl->ring,
- impl->buffer,
- impl->actual_max_buffer_size,
- ((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
- wanted * stride);
-
timestamp += wanted;
spa_ringbuffer_read_update(&impl->ring, timestamp);
}
@@ -421,43 +334,17 @@ static int rtp_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len,
* and not _appended_. In this example, `expected_write` would
* be 100 (since `expected_write` is the current write index),
* `write` would be 90, `samples` would be 10. In this case,
- * the (expected_write < (write + samples)) inequality does
- * not hold, so data is being _inserted_. By contrast, during
- * normal operation, `write` and `expected_write` are equal,
- * so the aforementioned inequality _does_ hold, meaning that
- * data is being appended.
- *
- * The code below handles this, and also handles a 32-bit
- * integer overflow corner case where the comparison has
- * to be done differently to account for the wrap-around.
+ * the inequality below does not hold, so data is being
+ * _inserted_. By contrast, during normal operation, `write`
+ * and `expected_write` are equal, so the inequality below
+ * _does_ hold, meaning that data is being appended.
*
* (Note that this write index update is only important if
* the constant delay mode is active, or if no spa_io_position
* was not provided yet. See the rtp_audio_process_playback()
* code for more about this.) */
-
- /* Compute new_write, handling potential 32-bit overflow.
- * In unsigned arithmetic, if write + samples exceeds UINT32_MAX,
- * it wraps around to a smaller value. We detect this by checking
- * if new_write < write (which can only happen on overflow). */
- const uint32_t new_write = write + samples;
- const bool wrapped_around = new_write < write;
-
- /* Determine if new_write is ahead of expected_write.
- * We're appending (ahead) if:
- *
- * 1. Normal case: new_write > expected_write (forward progress)
- * 2. Wrap-around case: new_write wrapped around (wrapped_around == true),
- * meaning we've cycled through the 32-bit index space and are
- * continuing from the beginning. In this case, we're always ahead.
- *
- * We're NOT appending (inserting/behind) if:
- * - new_write <= expected_write AND no wrap-around occurred
- * (we're filling a gap or writing behind the current position) */
- const bool is_appending = wrapped_around || (new_write > expected_write);
-
- if (is_appending) {
- write = new_write;
+ if (expected_write < (write + samples)) {
+ write += samples;
spa_ringbuffer_write_update(&impl->ring, write);
}
}
@@ -539,27 +426,20 @@ static void rtp_audio_flush_packets(struct impl *impl, uint32_t num_packets, uin
iov[0].iov_len = sizeof(header);
while (num_packets > 0) {
- uint32_t rtp_timestamp;
-
if (impl->marker_on_first && impl->first)
header.m = 1;
else
header.m = 0;
-
- rtp_timestamp = impl->ts_offset + (set_timestamp ? set_timestamp : timestamp);
-
header.sequence_number = htons(impl->seq);
- header.timestamp = htonl(rtp_timestamp);
+ header.timestamp = htonl(impl->ts_offset + (set_timestamp ? set_timestamp : timestamp));
set_iovec(&impl->ring,
impl->buffer, impl->actual_max_buffer_size,
((uint64_t)timestamp * stride) % impl->actual_max_buffer_size,
&iov[1], tosend * stride);
- pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%u (%f s)",
- tosend, num_packets, impl->ts_offset, timestamp,
- (double)timestamp * impl->io_position->clock.rate.num /
- impl->io_position->clock.rate.denom);
+ pw_log_trace("sending %d packet:%d ts_offset:%d timestamp:%d",
+ tosend, num_packets, impl->ts_offset, timestamp);
rtp_stream_emit_send_packet(impl, iov, 3);
@@ -620,7 +500,6 @@ static void rtp_audio_process_capture(void *data)
uint32_t pending, num_queued;
struct spa_io_position *pos;
uint64_t next_nsec, quantum;
- struct pw_time pwt;
if (impl->separate_sender) {
/* apply the DLL rate */
@@ -638,8 +517,6 @@ static void rtp_audio_process_capture(void *data)
stride = impl->stride;
wanted = size / stride;
- pw_stream_get_time_n(impl->stream, &pwt, sizeof(pwt));
-
filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_timestamp);
pos = impl->io_position;
@@ -656,21 +533,6 @@ static void rtp_audio_process_capture(void *data)
impl->sink_resamp_delay = impl->io_rate_match->delay;
impl->sink_quantum = (uint64_t)(pos->clock.duration * SPA_NSEC_PER_SEC / rate);
}
-
- /* Compensate for the stream resampler's delay. */
- actual_timestamp -= pwt.buffered;
-
- /* If we got a request for less than quantum worth of samples, it indicates that there
- * is a gap created by the resampler. We have to skip it to avoid timestamp discontinuity. */
- if (pwt.buffered > 0) {
- int32_t ideal_quantum = (int32_t)scale_u64(pos->clock.duration, impl->rate, rate);
- if (wanted < ideal_quantum) {
- int32_t num_samples_to_skip = ideal_quantum - wanted;
- pw_log_info("wanted: %" PRId32 " < ideal quantum: %" PRId32 " - skipping %"
- PRId32" samples", wanted, ideal_quantum, num_samples_to_skip);
- actual_timestamp += num_samples_to_skip;
- }
- }
} else {
actual_timestamp = expected_timestamp;
next_nsec = 0;
@@ -707,8 +569,7 @@ static void rtp_audio_process_capture(void *data)
if (!impl->have_sync) {
pw_log_info("(re)sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u",
actual_timestamp, impl->seq, impl->ts_offset, impl->ssrc);
- spa_ringbuffer_read_update(&impl->ring, actual_timestamp);
- spa_ringbuffer_write_update(&impl->ring, actual_timestamp);
+ impl->ring.readindex = impl->ring.writeindex = actual_timestamp;
memset(impl->buffer, 0, BUFFER_SIZE);
impl->have_sync = true;
expected_timestamp = actual_timestamp;
diff --git a/src/modules/module-rtp/stream.c b/src/modules/module-rtp/stream.c
index d69b16524..e19d88a1f 100644
--- a/src/modules/module-rtp/stream.c
+++ b/src/modules/module-rtp/stream.c
@@ -454,10 +454,6 @@ static int stream_stop(struct impl *impl)
* meaning that the timer was no longer running, and the connection
* could be closed. */
if (!timer_running) {
- /* Clear the ringbuffer to prevent old invalid packets from being
- * sent when processing resumes via rtp_audio_flush_packets() */
- if (impl->reset_ringbuffer)
- impl->reset_ringbuffer(impl);
set_internal_stream_state(impl, RTP_STREAM_INTERNAL_STATE_STOPPED);
pw_log_info("stream stopped");
}
diff --git a/src/modules/module-snapcast-discover.c b/src/modules/module-snapcast-discover.c
index 3568d82d4..596d5677b 100644
--- a/src/modules/module-snapcast-discover.c
+++ b/src/modules/module-snapcast-discover.c
@@ -162,8 +162,7 @@ static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
-#define SERVICE_TYPE_JSONRPC "_snapcast-jsonrpc._tcp"
-#define SERVICE_TYPE_CONTROL "_snapcast-ctrl._tcp"
+#define SERVICE_TYPE_CONTROL "_snapcast-jsonrpc._tcp"
struct impl {
struct pw_context *context;
@@ -177,8 +176,7 @@ struct impl {
AvahiPoll *avahi_poll;
AvahiClient *client;
- AvahiServiceBrowser *jsonrpc_browser;
- AvahiServiceBrowser *ctrl_browser;
+ AvahiServiceBrowser *sink_browser;
struct spa_list tunnel_list;
uint32_t id;
@@ -254,10 +252,8 @@ static void impl_free(struct impl *impl)
spa_list_consume(t, &impl->tunnel_list, link)
free_tunnel(t);
- if (impl->jsonrpc_browser)
- avahi_service_browser_free(impl->jsonrpc_browser);
- if (impl->ctrl_browser)
- avahi_service_browser_free(impl->ctrl_browser);
+ if (impl->sink_browser)
+ avahi_service_browser_free(impl->sink_browser);
if (impl->client)
avahi_client_free(impl->client);
if (impl->avahi_poll)
@@ -822,13 +818,9 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
case AVAHI_CLIENT_S_REGISTERING:
case AVAHI_CLIENT_S_RUNNING:
case AVAHI_CLIENT_S_COLLISION:
- if (impl->ctrl_browser == NULL)
- impl->ctrl_browser = make_browser(impl, SERVICE_TYPE_CONTROL);
- if (impl->ctrl_browser == NULL)
- goto error;
- if (impl->jsonrpc_browser == NULL)
- impl->jsonrpc_browser = make_browser(impl, SERVICE_TYPE_JSONRPC);
- if (impl->jsonrpc_browser == NULL)
+ if (impl->sink_browser == NULL)
+ impl->sink_browser = make_browser(impl, SERVICE_TYPE_CONTROL);
+ if (impl->sink_browser == NULL)
goto error;
break;
case AVAHI_CLIENT_FAILURE:
@@ -837,13 +829,9 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
SPA_FALLTHROUGH;
case AVAHI_CLIENT_CONNECTING:
- if (impl->ctrl_browser) {
- avahi_service_browser_free(impl->ctrl_browser);
- impl->ctrl_browser = NULL;
- }
- if (impl->jsonrpc_browser) {
- avahi_service_browser_free(impl->jsonrpc_browser);
- impl->jsonrpc_browser = NULL;
+ if (impl->sink_browser) {
+ avahi_service_browser_free(impl->sink_browser);
+ impl->sink_browser = NULL;
}
break;
default:
diff --git a/src/modules/network-utils.h b/src/modules/network-utils.h
index a89b7d3bd..16f9d9273 100644
--- a/src/modules/network-utils.h
+++ b/src/modules/network-utils.h
@@ -7,12 +7,6 @@
#include
#include
#include
-#include
-#include
-#include
-#include
-
-#include
#ifdef __FreeBSD__
#define ifr_ifindex ifr_index
@@ -137,70 +131,5 @@ static inline bool pw_net_addr_is_any(struct sockaddr_storage *addr)
return false;
}
-#ifndef LISTEN_FDS_START
-#define LISTEN_FDS_START 3
-#endif
-
-/* Returns the number of file descriptors passed for socket activation.
- * Returns 0 if none, -1 on error. */
-static inline int listen_fd(void)
-{
- uint32_t n;
- int i, flags;
-
- if (!spa_atou32(getenv("LISTEN_FDS"), &n, 10) || n > INT_MAX - LISTEN_FDS_START) {
- errno = EINVAL;
- return -1;
- }
-
- for (i = 0; i < (int)n; i++) {
- flags = fcntl(LISTEN_FDS_START + i, F_GETFD);
- if (flags == -1)
- return -1;
- if (fcntl(LISTEN_FDS_START + i, F_SETFD, flags | FD_CLOEXEC) == -1)
- return -1;
- }
-
- unsetenv("LISTEN_FDS");
-
- return (int)n;
-}
-
-/* Check if the fd is a listening unix socket of the given type,
- * optionally bound to the given path. */
-static inline int is_socket_unix(int fd, int type, const char *path)
-{
- struct sockaddr_un addr;
- int val;
- socklen_t len = sizeof(val);
-
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &len) < 0)
- return -errno;
- if (val != type)
- return 0;
-
- if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) < 0)
- return -errno;
- if (!val)
- return 0;
-
- if (path) {
- len = sizeof(addr);
- memset(&addr, 0, sizeof(addr));
- if (getsockname(fd, (struct sockaddr *)&addr, &len) < 0)
- return -errno;
- if (addr.sun_family != AF_UNIX)
- return 0;
- size_t length = strlen(path);
- if (length > 0) {
- if (len < offsetof(struct sockaddr_un, sun_path) + length)
- return 0;
- if (memcmp(addr.sun_path, path, length) != 0)
- return 0;
- }
- }
-
- return 1;
-}
#endif /* NETWORK_UTILS_H */
diff --git a/src/pipewire/impl-port.c b/src/pipewire/impl-port.c
index ed6fe9f1e..9d49a8097 100644
--- a/src/pipewire/impl-port.c
+++ b/src/pipewire/impl-port.c
@@ -1595,8 +1595,6 @@ void pw_impl_port_destroy(struct pw_impl_port *port)
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
free(port->tag[SPA_DIRECTION_INPUT]);
free(port->tag[SPA_DIRECTION_OUTPUT]);
- free(port->cap[SPA_DIRECTION_INPUT]);
- free(port->cap[SPA_DIRECTION_OUTPUT]);
pw_map_clear(&port->mix_port_map);
diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c
index 8bd3e343c..bdd44a255 100644
--- a/src/tools/pw-cat.c
+++ b/src/tools/pw-cat.c
@@ -211,12 +211,6 @@ static const struct format_info {
{ "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 },
{ "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 },
{ "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 },
-
- { "mp1", SF_FORMAT_MPEG_LAYER_I, SPA_AUDIO_FORMAT_F32, 1 },
- { "mp2", SF_FORMAT_MPEG_LAYER_II, SPA_AUDIO_FORMAT_F32, 1 },
- { "mp3", SF_FORMAT_MPEG_LAYER_III, SPA_AUDIO_FORMAT_F32, 1 },
- { "vorbis", SF_FORMAT_VORBIS, SPA_AUDIO_FORMAT_F32, 1 },
- { "opus", SF_FORMAT_OPUS, SPA_AUDIO_FORMAT_F32, 1 },
};
static const struct format_info *format_info_by_name(const char *str)
@@ -1684,6 +1678,12 @@ static void format_from_filename(SF_INFO *info, const char *filename)
int i, count = 0;
int format = -1;
+#if __BYTE_ORDER == __BIG_ENDIAN
+ info->format |= SF_ENDIAN_BIG;
+#else
+ info->format |= SF_ENDIAN_LITTLE;
+#endif
+
if (sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(int)) != 0)
count = 0;
@@ -1700,42 +1700,17 @@ static void format_from_filename(SF_INFO *info, const char *filename)
break;
}
}
- if (format == -1) {
- if (sf_command(NULL, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof(int)) != 0)
- count = 0;
-
- for (i = 0; i < count; i++) {
- SF_FORMAT_INFO fi;
-
- spa_zero(fi);
- fi.format = i;
- if (sf_command(NULL, SFC_GET_SIMPLE_FORMAT, &fi, sizeof(fi)) != 0)
- continue;
-
- if (spa_strendswith(filename, fi.extension)) {
- format = fi.format;
- info->format = 0;
- break;
- }
- }
- }
if (format == -1)
format = spa_streq(filename, "-") ? SF_FORMAT_AU : SF_FORMAT_WAV;
if (format == SF_FORMAT_WAV && info->channels > 2)
format = SF_FORMAT_WAVEX;
- switch (format & SF_FORMAT_TYPEMASK) {
- case SF_FORMAT_OGG:
- case SF_FORMAT_FLAC:
- case SF_FORMAT_MPEG:
- case SF_FORMAT_AIFF:
- info->format |= SF_ENDIAN_FILE;
- break;
- default:
- info->format |= SF_ENDIAN_CPU;
- break;
- }
info->format |= format;
+
+ if (format == SF_FORMAT_OGG || format == SF_FORMAT_FLAC)
+ info->format = (info->format & ~SF_FORMAT_ENDMASK) | SF_ENDIAN_FILE;
+ if (format == SF_FORMAT_OGG)
+ info->format = (info->format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS;
}
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION