bluez-alsa icon indicating copy to clipboard operation
bluez-alsa copied to clipboard

Multi client support

Open borine opened this issue 7 months ago • 6 comments

Permit more than one client to open each PCM. For playback clients, the streams from each client are mixed together before encoding. For capture clients each client receives a copy of the decoded stream.

Stream mixing is done in C code, there is no explicit SIMD code. However, compiling with -O3 does appear to allow gcc to optimize the mix loop very well on x86_64. I have not tested optimization with aarch64 (my old RPi machines are all 32-bit with no SIMD); but even without optimization I can run 3 playback clients simultaneously on a RPi zero W.

There was some interest in this feature three years ago, but there have been only a few enquiries more recently. I think this is pushing the scope boundary a little, as for most systems requiring multiple simultaneous audio applications there are also other requirements which mean that pipewire is probably a better fit. However I do have one genuine use case which is a "kiosk" system running only chromium which requires each chromium tab to independently open the PCM; and for this system BlueALSA with multi-client support is ideal.

borine avatar May 08 '25 10:05 borine

Codecov Report

:x: Patch coverage is 63.55599% with 371 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 68.76%. Comparing base (2c09e0b) to head (5670ba3).

Files with missing lines Patch % Lines
src/ba-pcm-client.c 53.46% 141 Missing :warning:
src/ba-pcm-multi.c 71.75% 98 Missing :warning:
src/ba-pcm-mix-buffer.c 59.57% 76 Missing :warning:
src/main.c 0.00% 19 Missing :warning:
src/asound/bluealsa-pcm.c 40.74% 16 Missing :warning:
test/test-io.c 84.44% 8 Missing and 6 partials :warning:
test/test-a2dp.c 0.00% 3 Missing :warning:
src/ba-transport-pcm.c 85.71% 2 Missing :warning:
src/bluealsa-dbus.c 88.23% 2 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #762      +/-   ##
==========================================
- Coverage   69.28%   68.76%   -0.52%     
==========================================
  Files         105      108       +3     
  Lines       17167    18154     +987     
  Branches     2739     2907     +168     
==========================================
+ Hits        11894    12484     +590     
- Misses       5156     5547     +391     
- Partials      117      123       +6     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar May 08 '25 10:05 codecov[bot]

I've tried to cherry-pick the Ensure encoder buffers are flushed when draining commit to master (the code with some cleanups is here: https://github.com/arkq/bluez-alsa/tree/drain), but when testing I've spotted that in case of mpg123 and cmus (and maybe some others) the final effect is not satisfactory. The problem is that these players, when stoping, trigger drop -> resume -> drain -> drop actions. And because of that my headset plays a click when silence is received. That happens with SBC and AAC codecs (I have not tested other codes yet, but I guess that the effect will be the same). Have you ever spotted some "clicks" when stopping playback (when using this drain flushing code)? Later today I will check with some other sinks. The problem does not occur with aplay, because it does not drop PCM before drain. I've also tested with mpv, but it does not use drain at all (at least when playing music), so mpv is not affected.

arkq avatar May 29 '25 07:05 arkq

Have you ever spotted some "clicks" when stopping playback

Possibly - I think mpg123 did this - I will investigate today.

drop -> resume -> drain -> drop

hmm, that is very odd. I guess at ALSA API level that is

snd_pcm_drop();
snd_pcm_prepare();
snd_pcm_drain(); 

I will write a small test program to try that sequence, which should make it easier to debug the bluealsad code.

borine avatar May 29 '25 08:05 borine

I've just tried with mpg123 v1.32.5 from the ubuntu repository and did not get any clicks at end of playback. I used your "drain" branch, and also this PR branch both with --multi-client and without. For me the sequence of client commands at end of playback was Drain -> Drain -> Drop -> Drop so perhaps the ubuntu version or build of mpg123 is different from yours? I will try to write a test program later.

borine avatar May 29 '25 09:05 borine

I'm testing on Debian 12:

$ mpg123 --version
mpg123 1.31.2

I've checked with upstream master version of mpg123:

$ src/mpg123 --version
mpg123 1.33.0-dev

and the outcome is the same - click at the end.

Here is the calltrace from the termination:

| safe_exit
| | dump_close
| | | play_prebuffer
| | out123_drop
| | audio_cleanup
| | out123_del
| | | out123_close
| | | | out123_drain
| | | | | out123_pause
| | | | out123_stop
| | | out123_set_buffer
| | | | out123_close
| | | | | out123_drain
| | | | | out123_stop
| | mpg123_delete
| | | mpg123_close
| | mpg123_exit
| | stream_close
| | split_dir_file
| | term_exit
| | | term_have_fun
| | | term_restore

So, at least based on function names, it seems that mpg123 calls drop then it closes the output (which calls drain, then stop).

I think that a workaround would to to track whether we have some samples after the drop, and if there are no new samples, drain is not required. I'll try to add that to the IO code and will think about it whether it will not cause some undesired effects.

arkq avatar May 29 '25 17:05 arkq

I've added the logic which checks whether inserting silence is required or not. It seems that it works OK, but some synthetic test for that would be nice.

arkq avatar May 29 '25 19:05 arkq