Skip to content

Commit

Permalink
multi-client support
Browse files Browse the repository at this point in the history
  • Loading branch information
borine committed Aug 13, 2024
1 parent 959573c commit 8f47ec0
Show file tree
Hide file tree
Showing 32 changed files with 2,108 additions and 54 deletions.
2 changes: 2 additions & 0 deletions .github/iwyu.imp
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@

{ include: [ '<spandsp/plc.h>', private, '<spandsp.h>', public ] },

{ include: [ '<bits/types/struct_itimerspec.h>', private, '<sys/timerfd.h>', public ] },

]
3 changes: 3 additions & 0 deletions .github/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ BTRH
BTT
bttransport
btusb
# aspell considers PIPE_BUF to be 2 words :(
BUF
CCE
CIEV
CIND
Expand All @@ -183,6 +185,7 @@ ENODEV
EP
EPIPE
EPMR
epoll
EQMID
errno
FB
Expand Down
6 changes: 6 additions & 0 deletions doc/bluealsa.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ OPTIONS
Without this option, **bluealsa** registers itself as an "org.bluealsa"
D-Bus service. For more information see the EXAMPLES_ below.

-M, --multi-client
Permit multiple clients to connect to the same PCM stream.
Without this option, only one client can connect to a PCM.
With this option, for playback clients, the streams are mixed together;
for capture each client receives a copy of the stream.

-i hciX, --device=hciX
HCI device to use. Can be specified multiple times to select more than one
HCI. Because HCI numbering can change after a system reboot, this option
Expand Down
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ bluealsa_SOURCES = \
ba-transport-pcm.c \
bluealsa-dbus.c \
bluealsa-iface.xml \
bluealsa-mix-buffer.c \
bluealsa-pcm-client.c \
bluealsa-pcm-multi.c \
bluez.c \
bluez-iface.xml \
codec-sbc.c \
Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-aac.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "ba-config.h"
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "bluealsa-pcm-multi.h"
#include "io.h"
#include "rtp.h"
#include "utils.h"
Expand Down Expand Up @@ -288,6 +289,11 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

rtp_header_t *rtp_header;
/* initialize RTP header and get anchor for payload */
uint8_t *rtp_payload = rtp_a2dp_init(bt.data, &rtp_header, NULL, 0);
Expand Down Expand Up @@ -468,6 +474,11 @@ void *a2dp_aac_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

struct rtp_state rtp = { .synced = false };
/* RTP clock frequency equal to 90kHz */
rtp_state_init(&rtp, samplerate, 90000);
Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-aptx-hd.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "ba-config.h"
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "bluealsa-pcm-multi.h"
#include "codec-aptx.h"
#include "io.h"
#include "rtp.h"
Expand Down Expand Up @@ -138,6 +139,11 @@ void *a2dp_aptx_hd_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

rtp_header_t *rtp_header;
/* initialize RTP header and get anchor for payload */
uint8_t *rtp_payload = rtp_a2dp_init(bt.data, &rtp_header, NULL, 0);
Expand Down Expand Up @@ -271,6 +277,11 @@ void *a2dp_aptx_hd_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

struct rtp_state rtp = { .synced = false };
/* RTP clock frequency equal to audio samplerate */
rtp_state_init(&rtp, samplerate, samplerate);
Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-aptx.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "ba-config.h"
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "bluealsa-pcm-multi.h"
#include "codec-aptx.h"
#include "io.h"
#include "shared/a2dp-codecs.h"
Expand Down Expand Up @@ -135,6 +136,11 @@ void *a2dp_aptx_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

debug_transport_pcm_thread_loop(t_pcm, "START");
for (ba_transport_pcm_state_set_running(t_pcm);;) {

Expand Down Expand Up @@ -249,6 +255,11 @@ void *a2dp_aptx_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

debug_transport_pcm_thread_loop(t_pcm, "START");
for (ba_transport_pcm_state_set_running(t_pcm);;) {

Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-faststream.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "ba-config.h"
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "bluealsa-pcm-multi.h"
#include "codec-sbc.h"
#include "io.h"
#include "shared/a2dp-codecs.h"
Expand Down Expand Up @@ -150,6 +151,11 @@ void *a2dp_faststream_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

debug_transport_pcm_thread_loop(t_pcm, "START");
for (ba_transport_pcm_state_set_running(t_pcm);;) {

Expand Down Expand Up @@ -270,6 +276,11 @@ void *a2dp_faststream_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

debug_transport_pcm_thread_loop(t_pcm, "START");
for (ba_transport_pcm_state_set_running(t_pcm);;) {

Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-ldac.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "ba-config.h"
#include "bluealsa-pcm-multi.h"
#include "io.h"
#include "rtp.h"
#include "utils.h"
Expand Down Expand Up @@ -166,6 +167,11 @@ void *a2dp_ldac_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

rtp_header_t *rtp_header;
rtp_media_header_t *rtp_media_header;
/* initialize RTP headers and get anchor for payload */
Expand Down Expand Up @@ -323,6 +329,11 @@ void *a2dp_ldac_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

struct rtp_state rtp = { .synced = false };
/* RTP clock frequency equal to audio samplerate */
rtp_state_init(&rtp, samplerate, samplerate);
Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-mpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "ba-config.h"
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "bluealsa-pcm-multi.h"
#include "io.h"
#include "rtp.h"
#include "utils.h"
Expand Down Expand Up @@ -226,6 +227,11 @@ void *a2dp_mp3_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

rtp_header_t *rtp_header;
rtp_mpeg_audio_header_t *rtp_mpeg_audio_header;
/* initialize RTP headers and get anchor for payload */
Expand Down Expand Up @@ -411,6 +417,11 @@ void *a2dp_mpeg_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

struct rtp_state rtp = { .synced = false };
/* RTP clock frequency equal to 90kHz */
rtp_state_init(&rtp, samplerate, 90000);
Expand Down
11 changes: 11 additions & 0 deletions src/a2dp-sbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "ba-transport.h"
#include "ba-transport-pcm.h"
#include "ba-config.h"
#include "bluealsa-pcm-multi.h"
#include "codec-sbc.h"
#include "io.h"
#include "rtp.h"
Expand Down Expand Up @@ -177,6 +178,11 @@ void *a2dp_sbc_enc_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

rtp_header_t *rtp_header;
rtp_media_header_t *rtp_media_header;

Expand Down Expand Up @@ -320,6 +326,11 @@ void *a2dp_sbc_dec_thread(struct ba_transport_pcm *t_pcm) {
goto fail_ffb;
}

/* start multi client thread if required. */
if (t_pcm->multi &&
!bluealsa_pcm_multi_init(t_pcm->multi, pcm.nmemb))
goto fail_ffb;

struct rtp_state rtp = { .synced = false };
/* RTP clock frequency equal to audio samplerate */
rtp_state_init(&rtp, samplerate, samplerate);
Expand Down
62 changes: 47 additions & 15 deletions src/asound/bluealsa-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,10 @@ static int bluealsa_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
return ret;

if (pcm->ba_pcm.channels != channels || pcm->ba_pcm.sampling != sampling) {
if (pcm->ba_pcm.running) {
SNDERR("Couldn't change BlueALSA PCM configuration");
return -EINVAL;
}
debug2("Changing BlueALSA PCM configuration: %u ch, %u Hz -> %u ch, %u Hz",
pcm->ba_pcm.channels, pcm->ba_pcm.sampling, channels, sampling);

Expand Down Expand Up @@ -1330,6 +1334,21 @@ static int bluealsa_set_hw_constraint(struct bluealsa_pcm *pcm) {
unsigned int list[ARRAYSIZE(codec->sampling)];
unsigned int n;

/* If the PCM is already running, we must not change the codec config as
* that would terminate the stream for the running client */
if (pcm->ba_pcm.running) {
if ((err = snd_pcm_ioplug_set_param_minmax(io,
SND_PCM_IOPLUG_HW_CHANNELS, pcm->ba_pcm.channels,
pcm->ba_pcm.channels)) < 0)
return err;

if ((err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
pcm->ba_pcm.sampling, pcm->ba_pcm.sampling)) < 0)
return err;

return 0;
}

/* Populate the list of supported channels and sampling rates. For codecs
* with fixed configuration, the list will contain only one element. For
* other codecs, the list might contain all supported configurations. */
Expand Down Expand Up @@ -1557,26 +1576,39 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluealsa) {
* sampling rate and channels for HW constraints. */
const char *canonical = ba_dbus_pcm_codec_get_canonical_name(codec_name);
const bool name_changed = strcmp(canonical, pcm->ba_pcm.codec.name) != 0;
if (name_changed && !ba_dbus_pcm_select_codec(&pcm->dbus_ctx, pcm->ba_pcm.pcm_path,
canonical, NULL, 0, 0, 0, BA_PCM_SELECT_CODEC_FLAG_NONE, &err)) {
SNDERR("Couldn't select BlueALSA PCM codec: %s", err.message);
dbus_error_free(&err);

if (pcm->ba_pcm.running) {
if (name_changed)
SNDERR("Couldn't change BlueALSA PCM codec");
else if (codec_config_len > 0 && (
pcm->ba_pcm.codec.data_len != codec_config_len ||
memcmp(pcm->ba_pcm.codec.data, codec_config, codec_config_len) != 0))
SNDERR("Couldn't change BlueALSA PCM codec configuration");
}
else {
if (name_changed && !ba_dbus_pcm_select_codec(&pcm->dbus_ctx, pcm->ba_pcm.pcm_path,
canonical, NULL, 0, 0, 0, BA_PCM_SELECT_CODEC_FLAG_NONE, &err)) {
SNDERR("Couldn't select BlueALSA PCM codec: %s", err.message);
dbus_error_free(&err);
}
else {

memcpy(pcm->ba_pcm_codec_config, codec_config, codec_config_len);
pcm->ba_pcm_codec_config_len = codec_config_len;
memcpy(pcm->ba_pcm_codec_config, codec_config, codec_config_len);
pcm->ba_pcm_codec_config_len = codec_config_len;

/* Changing the codec may change the audio format, sampling rate
* and/or channels. We need to refresh our cache of PCM
* properties. */
if (name_changed &&
!ba_dbus_pcm_get(&pcm->dbus_ctx, &ba_addr, ba_profile,
stream == SND_PCM_STREAM_PLAYBACK ? BA_PCM_MODE_SINK : BA_PCM_MODE_SOURCE,
&pcm->ba_pcm, &err)) {
SNDERR("Couldn't get BlueALSA PCM: %s", err.message);
ret = -dbus_error_to_errno(&err);
goto fail;
}

/* Changing the codec may change the audio format, sampling rate and/or
* channels. We need to refresh our cache of PCM properties. */
if (name_changed && !ba_dbus_pcm_get(&pcm->dbus_ctx, &ba_addr, ba_profile,
stream == SND_PCM_STREAM_PLAYBACK ? BA_PCM_MODE_SINK : BA_PCM_MODE_SOURCE,
&pcm->ba_pcm, &err)) {
SNDERR("Couldn't get BlueALSA PCM: %s", err.message);
ret = -dbus_error_to_errno(&err);
goto fail;
}

}
}

Expand Down
2 changes: 2 additions & 0 deletions src/ba-config.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ struct ba_config config = {

.disable_realtek_usb_fix = false,

.multi_enabled = false,

/* CVSD is a mandatory codec */
.hfp.codecs.cvsd = true,
#if ENABLE_MSBC
Expand Down
3 changes: 3 additions & 0 deletions src/ba-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ struct ba_config {
/* disable alt-3 MTU for mSBC with Realtek USB adapters */
bool disable_realtek_usb_fix;

/* Is multi client support enabled? */
bool multi_enabled;

struct {

/* available HFP codecs */
Expand Down
Loading

0 comments on commit 8f47ec0

Please sign in to comment.