Skip to content

Draft: bluetooth: Switch codecs without tearing down sink/source/thread

Implement codec switching without tearing down state. This speeds things up and prevents temporarily switching to another sink output (ie. blasting audio over speakers). Additionally it paves the way to implementing reconfiguration support, where the BT connection follows (if codec/capabilities allow) the native sampling rate of input audio/sink without resampling.

Fixes #1113

The main source of routing audio to another sink on codec switch is the PA sink disappearing, together with its IO thread and other primitives. This is annoying for users but also wasteful; all we need to do for a codec switch is make the sink temporarily unavailable, release the transport, perform the usual SetConfiguration on another SEP and Acquire the resulting stream that is made available, to finally reconfigure the sink with the new sampling rate and format and make it available again.

Remaining issues:

When switching codecs that change MTU write-buffer size:

E: [bluetooth] source-output.c: Assertion 'pa_frame_aligned(chunk->length, &o->source->sample_spec)' failed at ../pulseaudio/src/pulsecore/source-output.c:754, function pa_source_output_push(). Aborting.

It seems the source did not get reconfigured properly while the sink-input did:

D: [bluetooth] sink-input.c: Requesting rewind due to corking
D: [pulseaudio] sink.c: Suspending sink bluez_sink.XXX.a2dp_sink due to changing format, desired format = float32le rate = 44100
I: [pulseaudio] sink.c: bluez_sink.XXX.a2dp_sink: format: s24le -> float32le
I: [pulseaudio] sink.c: Reconfigured successfully
D: [pulseaudio] resampler.c: Resampler:
D: [pulseaudio] resampler.c:   rate 88200 -> 44100 (method speex-float-1)
D: [pulseaudio] resampler.c:   format s16le -> float32le (intermediate float32le)
D: [pulseaudio] resampler.c:   channels 2 -> 2 (resampling 2)
I: [pulseaudio] speex.c: Choosing speex quality setting 1.
D: [pulseaudio] memblockq.c: memblockq requested: maxlength=33554432, tlength=0, base=8, prebuf=0, minreq=1 maxrewind=0
D: [pulseaudio] memblockq.c: memblockq sanitized: maxlength=33554432, tlength=33554432, base=8, prebuf=0, minreq=8 maxrewind=0
D: [pulseaudio] sink-input.c: Updated resampler for sink input 7

And overall handling of implicit and explicit state transitions is beyond confusing :/. Feedback on improving this is welcome, considering that later changes will initiate a codec switch (until we implement AVDTP_RECONFIGURE_CMD in BlueZ) from sink_reconfigure_cb.


This things will be or are already being looked into following this MR:

  • Implement caps renegotation for GStreamer, without destroying and recreating gst_info;
  • Unify codec variants (like LDAC HQ/SQ/MQ) so that only one, reconfigurable codec object exists for it
    (fwiw this likely can't be done for aptX and aptX HD, which are different codecs entirely with different codec_id - even though they share the same reconfigurable GST pipeline);
  • Implement better negotation between pa_sample_spec, what the enc/dec supports, and what the device reports in capabilities;
  • Set avoid_resampling and use aforementioned negotation to find the best intersection between codec capabilities and requested pa_sample_spec.
    This has already been implemented, and besides sampling rate (part of caps) includes more lenient formats when supported by the encoder/decoder, rather than forcing PA to perform conversion.

CC @SanchayanMaity @arun

Can we get bluetooth labels on MRs as well?

Edited by Marijn Suijten

Merge request reports