Skip to content

Commit

Permalink
PCM client delay reporting for A2DP sink profile
Browse files Browse the repository at this point in the history
This feature will not work unless there will be a proper delay reporting
implementation in BlueZ (for A2DP sink profile).
  • Loading branch information
arkq committed Jan 14, 2024
1 parent e64ae50 commit d03eb9e
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 3 deletions.
5 changes: 5 additions & 0 deletions doc/org.bluealsa.PCM1.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ array{byte} CodecConfiguration [readonly]
uint16 Delay [readonly]
Approximate PCM delay in 1/10 of millisecond.

uint16 ClientDelay [readwrite]
Approximate client side delay in 1/10 of millisecond. This property shall
be set by the client in order to account for the client side delay when
capturing or playing audio respectively for PCM source or sink.

int16 DelayAdjustment [readonly]
An adjustment (+/-) included within the reported Delay in 1/10 of
millisecond to compensate for devices that do not report accurate delay
Expand Down
27 changes: 27 additions & 0 deletions src/ba-transport-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,33 @@ int ba_transport_pcm_get_delay(const struct ba_transport_pcm *pcm) {
return delay;
}

int ba_transport_pcm_set_delay(
struct ba_transport_pcm *pcm,
int delay) {

const struct ba_transport *t = pcm->t;

pcm->client_delay = delay;

/* Forward client delay to BlueZ, but only in case of A2DP sink profile,
* so it will be sent to connected Bluetooth client, e.g. phone. */
if (t->type.profile == BA_TRANSPORT_PROFILE_A2DP_SINK) {

GError *err = NULL;
g_dbus_set_property(config.dbus, t->bluez_dbus_owner, t->bluez_dbus_path,
BLUEZ_IFACE_MEDIA_TRANSPORT, "Delay", g_variant_new_uint16(delay), &err);

if (err != NULL) {
if (err->code != G_DBUS_ERROR_PROPERTY_READ_ONLY)
warn("Couldn't set A2DP transport delay: %s", err->message);
g_error_free(err);
}

}

return 0;
}

int16_t ba_transport_pcm_delay_adjustment_get(
const struct ba_transport_pcm *pcm) {

Expand Down
5 changes: 5 additions & 0 deletions src/ba-transport-pcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ struct ba_transport_pcm {
/* Overall PCM delay in 1/10 of millisecond, caused by
* audio encoding or decoding and data transfer. */
unsigned int delay;
/* Client delay in 1/10 of millisecond. */
unsigned int client_delay;

/* guard delay adjustments access */
pthread_mutex_t delay_adjustments_mtx;
Expand Down Expand Up @@ -238,6 +240,9 @@ int ba_transport_pcm_volume_update(

int ba_transport_pcm_get_delay(
const struct ba_transport_pcm *pcm);
int ba_transport_pcm_set_delay(
struct ba_transport_pcm *pcm,
int delay);

int16_t ba_transport_pcm_delay_adjustment_get(
const struct ba_transport_pcm *pcm);
Expand Down
14 changes: 14 additions & 0 deletions src/bluealsa-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ static GVariant *ba_variant_new_pcm_delay_adjustment(const struct ba_transport_p
return g_variant_new_int16(ba_transport_pcm_delay_adjustment_get(pcm));
}

static GVariant *ba_variant_new_pcm_client_delay(const struct ba_transport_pcm *pcm) {
return g_variant_new_uint16(pcm->client_delay);
}

static GVariant *ba_variant_new_pcm_soft_volume(const struct ba_transport_pcm *pcm) {
return g_variant_new_boolean(pcm->soft_volume);
}
Expand Down Expand Up @@ -877,6 +881,8 @@ static GVariant *bluealsa_pcm_get_property(const char *property,
return ba_variant_new_pcm_delay(pcm);
if (strcmp(property, "DelayAdjustment") == 0)
return ba_variant_new_pcm_delay_adjustment(pcm);
if (strcmp(property, "ClientDelay") == 0)
return ba_variant_new_pcm_client_delay(pcm);
if (strcmp(property, "SoftVolume") == 0)
return ba_variant_new_pcm_soft_volume(pcm);
if (strcmp(property, "Volume") == 0)
Expand All @@ -898,6 +904,12 @@ static bool bluealsa_pcm_set_property(const char *property, GVariant *value,

struct ba_transport_pcm *pcm = userdata;

if (strcmp(property, "ClientDelay") == 0) {
ba_transport_pcm_set_delay(pcm, g_variant_get_uint16(value));
bluealsa_dbus_pcm_update(pcm, BA_DBUS_PCM_UPDATE_CLIENT_DELAY);
return TRUE;
}

if (strcmp(property, "SoftVolume") == 0) {
pcm->soft_volume = g_variant_get_boolean(value);
bluealsa_dbus_pcm_update(pcm, BA_DBUS_PCM_UPDATE_SOFT_VOLUME);
Expand Down Expand Up @@ -1009,6 +1021,8 @@ void bluealsa_dbus_pcm_update(struct ba_transport_pcm *pcm, unsigned int mask) {
g_variant_builder_add(&props, "{sv}", "Delay", ba_variant_new_pcm_delay(pcm));
if (mask & BA_DBUS_PCM_UPDATE_DELAY_ADJUSTMENT)
g_variant_builder_add(&props, "{sv}", "DelayAdjustment", ba_variant_new_pcm_delay_adjustment(pcm));
if (mask & BA_DBUS_PCM_UPDATE_CLIENT_DELAY)
g_variant_builder_add(&props, "{sv}", "ClientDelay", ba_variant_new_pcm_client_delay(pcm));
if (mask & BA_DBUS_PCM_UPDATE_SOFT_VOLUME)
g_variant_builder_add(&props, "{sv}", "SoftVolume", ba_variant_new_pcm_soft_volume(pcm));
if (mask & BA_DBUS_PCM_UPDATE_VOLUME)
Expand Down
7 changes: 4 additions & 3 deletions src/bluealsa-dbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
#define BA_DBUS_PCM_UPDATE_CODEC_CONFIG (1 << 4)
#define BA_DBUS_PCM_UPDATE_DELAY (1 << 5)
#define BA_DBUS_PCM_UPDATE_DELAY_ADJUSTMENT (1 << 6)
#define BA_DBUS_PCM_UPDATE_SOFT_VOLUME (1 << 7)
#define BA_DBUS_PCM_UPDATE_VOLUME (1 << 8)
#define BA_DBUS_PCM_UPDATE_RUNNING (1 << 9)
#define BA_DBUS_PCM_UPDATE_CLIENT_DELAY (1 << 7)
#define BA_DBUS_PCM_UPDATE_SOFT_VOLUME (1 << 8)
#define BA_DBUS_PCM_UPDATE_VOLUME (1 << 9)
#define BA_DBUS_PCM_UPDATE_RUNNING (1 << 10)

#define BA_DBUS_RFCOMM_UPDATE_FEATURES (1 << 0)
#define BA_DBUS_RFCOMM_UPDATE_BATTERY (1 << 1)
Expand Down
1 change: 1 addition & 0 deletions src/bluealsa-iface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<property name="CodecConfiguration" type="ay" access="read"/>
<property name="Delay" type="q" access="read"/>
<property name="DelayAdjustment" type="n" access="read"/>
<property name="ClientDelay" type="q" access="readwrite"/>
<property name="SoftVolume" type="b" access="readwrite"/>
<property name="Volume" type="q" access="readwrite"/>
</interface>
Expand Down
11 changes: 11 additions & 0 deletions src/shared/dbus-client-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ dbus_bool_t ba_dbus_pcm_update(
int type = -1;

switch (property) {
case BLUEALSA_PCM_CLIENT_DELAY:
_property = "ClientDelay";
variant = DBUS_TYPE_UINT16_AS_STRING;
value = &pcm->client_delay;
type = DBUS_TYPE_UINT16;
break;
case BLUEALSA_PCM_SOFT_VOLUME:
_property = "SoftVolume";
variant = DBUS_TYPE_BOOLEAN_AS_STRING;
Expand Down Expand Up @@ -739,6 +745,11 @@ static dbus_bool_t dbus_message_iter_get_ba_pcm_props_cb(const char *key,
goto fail;
dbus_message_iter_get_basic(&variant, &pcm->delay_adjustment);
}
else if (strcmp(key, "ClientDelay") == 0) {
if (type != (type_expected = DBUS_TYPE_UINT16))
goto fail;
dbus_message_iter_get_basic(variant, &pcm->client_delay);
}
else if (strcmp(key, "SoftVolume") == 0) {
if (type != (type_expected = DBUS_TYPE_BOOLEAN))
goto fail;
Expand Down
3 changes: 3 additions & 0 deletions src/shared/dbus-client-pcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
/**
* BlueALSA PCM object property. */
enum ba_pcm_property {
BLUEALSA_PCM_CLIENT_DELAY,
BLUEALSA_PCM_SOFT_VOLUME,
BLUEALSA_PCM_VOLUME,
};
Expand Down Expand Up @@ -128,6 +129,8 @@ struct ba_pcm {
dbus_uint16_t delay;
/* manual delay adjustment */
dbus_int16_t delay_adjustment;
/* approximate client delay */
dbus_uint16_t client_delay;
/* software volume */
dbus_bool_t soft_volume;

Expand Down
21 changes: 21 additions & 0 deletions utils/aplay/aplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,27 @@ static void *io_worker_routine(struct io_worker *w) {
/* move leftovers to the beginning and reposition tail */
ffb_shift(&buffer, frames * w->ba_pcm.channels);

snd_pcm_sframes_t delay_frames;
if ((ret = snd_pcm_delay(w->pcm, &delay_frames)) != 0)
warn("Couldn't get PCM delay: %s", snd_strerror(ret));
else {

const int delay = delay_frames * 10000 / w->ba_pcm.sampling;
if (abs(delay - w->ba_pcm.client_delay) >= 500 /* update if >= 50ms */) {

w->ba_pcm.client_delay = delay;

DBusError err = DBUS_ERROR_INIT;
if (!bluealsa_dbus_pcm_update(&dbus_ctx, &w->ba_pcm, BLUEALSA_PCM_CLIENT_DELAY, &err)) {
error("Couldn't update PCM: %s", err.message);
dbus_error_free(&err);
goto fail;
}

}

}

continue;

close_alsa:
Expand Down
1 change: 1 addition & 0 deletions utils/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ void cli_print_pcm_properties(const struct ba_pcm *pcm, DBusError *err) {
cli_print_pcm_selected_codec(pcm);
printf("Delay: %#.1f ms\n", (double)pcm->delay / 10);
printf("DelayAdjustment: %#.1f ms\n", (double)pcm->delay_adjustment / 10);
printf("ClientDelay: %#.1f ms\n", (double)pcm->client_delay / 10);
cli_print_pcm_soft_volume(pcm);
cli_print_pcm_volume(pcm);
cli_print_pcm_mute(pcm);
Expand Down

0 comments on commit d03eb9e

Please sign in to comment.