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 Dec 8, 2021
1 parent 724891b commit 71d0735
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 7 deletions.
7 changes: 7 additions & 0 deletions doc/bluealsa-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ Properties object Device [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.

boolean SoftVolume [readwrite]

This property determines whether BlueALSA will make
Expand Down
27 changes: 27 additions & 0 deletions src/ba-transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,33 @@ int ba_transport_pcm_get_delay(const struct ba_transport_pcm *pcm) {
return pcm->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;
}

unsigned int ba_transport_pcm_volume_level_to_bt(
const struct ba_transport_pcm *pcm,
int value) {
Expand Down
5 changes: 5 additions & 0 deletions src/ba-transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,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;

/* internal software volume control */
bool soft_volume;
Expand Down Expand Up @@ -369,6 +371,9 @@ bool ba_transport_pcm_is_active(

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);

unsigned int ba_transport_pcm_volume_level_to_bt(
const struct ba_transport_pcm *pcm,
Expand Down
15 changes: 15 additions & 0 deletions src/bluealsa-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ static GVariant *ba_variant_new_pcm_delay(const struct ba_transport_pcm *pcm) {
return g_variant_new_uint16(ba_transport_pcm_get_delay(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 @@ -160,6 +164,7 @@ static void ba_variant_populate_pcm(GVariantBuilder *props, const struct ba_tran
g_variant_builder_add(props, "{sv}", "Sampling", ba_variant_new_pcm_sampling(pcm));
g_variant_builder_add(props, "{sv}", "Codec", ba_variant_new_pcm_codec(pcm));
g_variant_builder_add(props, "{sv}", "Delay", ba_variant_new_pcm_delay(pcm));
g_variant_builder_add(props, "{sv}", "ClientDelay", ba_variant_new_pcm_client_delay(pcm));
g_variant_builder_add(props, "{sv}", "SoftVolume", ba_variant_new_pcm_soft_volume(pcm));
g_variant_builder_add(props, "{sv}", "Volume", ba_variant_new_pcm_volume(pcm));
}
Expand Down Expand Up @@ -706,6 +711,8 @@ static GVariant *bluealsa_pcm_get_property(const char *property,
return ba_variant_new_pcm_codec(pcm);
if (strcmp(property, "Delay") == 0)
return ba_variant_new_pcm_delay(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 @@ -721,6 +728,12 @@ static bool bluealsa_pcm_set_property(const char *property, GVariant *value,

struct ba_transport_pcm *pcm = (struct ba_transport_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 @@ -822,6 +835,8 @@ void bluealsa_dbus_pcm_update(struct ba_transport_pcm *pcm, unsigned int mask) {
g_variant_builder_add(&props, "{sv}", "Codec", ba_variant_new_pcm_codec(pcm));
if (mask & BA_DBUS_PCM_UPDATE_DELAY)
g_variant_builder_add(&props, "{sv}", "Delay", ba_variant_new_pcm_delay(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
15 changes: 8 additions & 7 deletions src/bluealsa-dbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@
#include "ba-device.h"
#include "ba-transport.h"

#define BA_DBUS_PCM_UPDATE_FORMAT (1 << 0)
#define BA_DBUS_PCM_UPDATE_CHANNELS (1 << 1)
#define BA_DBUS_PCM_UPDATE_SAMPLING (1 << 2)
#define BA_DBUS_PCM_UPDATE_CODEC (1 << 3)
#define BA_DBUS_PCM_UPDATE_DELAY (1 << 4)
#define BA_DBUS_PCM_UPDATE_SOFT_VOLUME (1 << 5)
#define BA_DBUS_PCM_UPDATE_VOLUME (1 << 6)
#define BA_DBUS_PCM_UPDATE_FORMAT (1 << 0)
#define BA_DBUS_PCM_UPDATE_CHANNELS (1 << 1)
#define BA_DBUS_PCM_UPDATE_SAMPLING (1 << 2)
#define BA_DBUS_PCM_UPDATE_CODEC (1 << 3)
#define BA_DBUS_PCM_UPDATE_DELAY (1 << 4)
#define BA_DBUS_PCM_UPDATE_CLIENT_DELAY (1 << 5)
#define BA_DBUS_PCM_UPDATE_SOFT_VOLUME (1 << 6)
#define BA_DBUS_PCM_UPDATE_VOLUME (1 << 7)

#define BA_DBUS_RFCOMM_UPDATE_FEATURES (1 << 0)
#define BA_DBUS_RFCOMM_UPDATE_BATTERY (1 << 1)
Expand Down
8 changes: 8 additions & 0 deletions src/bluealsa-iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ static const GDBusPropertyInfo bluealsa_iface_pcm_Delay = {
-1, "Delay", "q", G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL
};

static const GDBusPropertyInfo bluealsa_iface_pcm_ClientDelay = {
-1, "ClientDelay", "q",
G_DBUS_PROPERTY_INFO_FLAGS_READABLE |
G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
NULL
};

static const GDBusPropertyInfo bluealsa_iface_pcm_SoftVolume = {
-1, "SoftVolume", "b",
G_DBUS_PROPERTY_INFO_FLAGS_READABLE |
Expand All @@ -220,6 +227,7 @@ static const GDBusPropertyInfo *bluealsa_iface_pcm_properties[] = {
&bluealsa_iface_pcm_Sampling,
&bluealsa_iface_pcm_Codec,
&bluealsa_iface_pcm_Delay,
&bluealsa_iface_pcm_ClientDelay,
&bluealsa_iface_pcm_SoftVolume,
&bluealsa_iface_pcm_Volume,
NULL,
Expand Down
11 changes: 11 additions & 0 deletions src/shared/dbus-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,12 @@ dbus_bool_t bluealsa_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 @@ -937,6 +943,11 @@ static dbus_bool_t bluealsa_dbus_message_iter_get_pcm_props_cb(const char *key,
goto fail;
dbus_message_iter_get_basic(variant, &pcm->delay);
}
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.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct ba_service_props {
/**
* BlueALSA PCM object property. */
enum ba_pcm_property {
BLUEALSA_PCM_CLIENT_DELAY,
BLUEALSA_PCM_SOFT_VOLUME,
BLUEALSA_PCM_VOLUME,
};
Expand Down Expand Up @@ -116,6 +117,8 @@ struct ba_pcm {
char codec[16];
/* approximate PCM delay */
dbus_uint16_t delay;
/* 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 @@ -607,6 +607,27 @@ static void *pcm_worker_routine(struct pcm_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;
}

}

}

}

fail:
Expand Down
1 change: 1 addition & 0 deletions utils/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ static void print_properties(const struct ba_pcm *pcm, DBusError *err) {
print_codecs(pcm->pcm_path, err);
printf("Selected codec: %s\n", pcm->codec);
printf("Delay: %#.1f ms\n", (double)pcm->delay / 10);
printf("ClientDelay: %#.1f ms\n", (double)pcm->client_delay / 10);
printf("SoftVolume: %s\n", pcm->soft_volume ? "Y" : "N");
print_volume(pcm);
print_mute(pcm);
Expand Down

0 comments on commit 71d0735

Please sign in to comment.