From 0003d7a2d03e75c5a7400a170ba5cb3c27bcf6c5 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Tue, 6 Jan 2026 16:11:37 +0200 Subject: [PATCH] bluez5: iso-io: add debug option for forcing same data in all streams When debugging desync issues, it's useful to force same data be sent on all ISO streams. Add option for that. --- doc/dox/config/pipewire-props.7.md | 4 ++++ spa/plugins/bluez5/iso-io.c | 27 +++++++++++++++++++++++++++ spa/plugins/bluez5/iso-io.h | 2 ++ spa/plugins/bluez5/media-sink.c | 6 ++++++ 4 files changed, 39 insertions(+) diff --git a/doc/dox/config/pipewire-props.7.md b/doc/dox/config/pipewire-props.7.md index dd5df08ea..c16839b59 100644 --- a/doc/dox/config/pipewire-props.7.md +++ b/doc/dox/config/pipewire-props.7.md @@ -1382,6 +1382,10 @@ Latency adjustment to apply on the node. Larger values add a constant latency, but reduces timing jitter caused by Bluetooth transport. +@PAR@ node-prop bluez5.debug.iso-mono = false # boolean +Debugging option for forcing ISO sinks send identical packets out for +all streams. + # PORT PROPERTIES @IDX@ props Port properties are usually not directly configurable via PipeWire diff --git a/spa/plugins/bluez5/iso-io.c b/spa/plugins/bluez5/iso-io.c index 85c211492..f53da2f4d 100644 --- a/spa/plugins/bluez5/iso-io.c +++ b/spa/plugins/bluez5/iso-io.c @@ -257,6 +257,7 @@ static void group_on_timeout(struct spa_source *source) struct stream *stream; bool resync = false; bool fail = false; + bool debug_mono = false; uint64_t exp; uint64_t now_realtime; int res; @@ -295,6 +296,8 @@ static void group_on_timeout(struct spa_source *source) if (!group->started && !stream->idle && stream->this.size > 0) group->started = true; + + debug_mono = debug_mono || stream->this.debug_mono; } if (group_latency_check(group)) { @@ -303,6 +306,30 @@ static void group_on_timeout(struct spa_source *source) goto done; } + /* Force same data in all streams */ + if (debug_mono) { + struct stream *s0 = NULL; + + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; + if (stream->this.size) { + s0 = stream; + break; + } + } + if (s0) { + spa_list_for_each(stream, &group->streams, link) { + if (!stream->sink) + continue; + if (stream != s0) { + stream->this.size = s0->this.size; + memcpy(stream->this.buf, s0->this.buf, s0->this.size); + } + } + } + } + /* Produce output */ spa_list_for_each(stream, &group->streams, link) { int res = 0; diff --git a/spa/plugins/bluez5/iso-io.h b/spa/plugins/bluez5/iso-io.h index a0082b420..eb3a94415 100644 --- a/spa/plugins/bluez5/iso-io.h +++ b/spa/plugins/bluez5/iso-io.h @@ -35,6 +35,8 @@ struct spa_bt_iso_io struct spa_audio_info format; /**< Audio format */ void *codec_data; /**< Codec data */ + bool debug_mono; /**< Duplicate packets from first sink to other sinks */ + void *user_data; }; diff --git a/spa/plugins/bluez5/media-sink.c b/spa/plugins/bluez5/media-sink.c index 136ac6d82..290852f16 100644 --- a/spa/plugins/bluez5/media-sink.c +++ b/spa/plugins/bluez5/media-sink.c @@ -159,6 +159,7 @@ struct impl { unsigned int is_duplex:1; unsigned int is_internal:1; + unsigned int iso_debug_mono:1; struct spa_source source; int timerfd; @@ -1566,6 +1567,8 @@ static int transport_start(struct impl *this) this->codec->description); return -EIO; } + + this->transport->iso_io->debug_mono = this->iso_debug_mono; } else { this->own_codec_data = false; this->codec_data = this->transport->iso_io->codec_data; @@ -2625,6 +2628,9 @@ impl_init(const struct spa_handle_factory *factory, if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL) this->is_internal = spa_atob(str); + if (info && (str = spa_dict_lookup(info, "bluez5.debug.iso-mono")) != NULL) + this->iso_debug_mono = spa_atob(str); + if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) sscanf(str, "pointer:%p", &this->transport);