diff --git a/doc/a2dpconf.1.rst b/doc/a2dpconf.1.rst index 4c7cd6049..53720e564 100644 --- a/doc/a2dpconf.1.rst +++ b/doc/a2dpconf.1.rst @@ -36,7 +36,7 @@ OPTIONS Print version and exit. -v, --verbose - Sow verbose bit-stream details. + Show verbose bit-stream details. Display each field as a binary mask with each bit represented by a single character. diff --git a/src/a2dp-aac.c b/src/a2dp-aac.c index 2e8a680d0..94585fef5 100644 --- a/src/a2dp-aac.c +++ b/src/a2dp-aac.c @@ -30,6 +30,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "utils.h" @@ -397,6 +398,7 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed, we have to append new data to * the existing one. Since we do not use ring buffer, we will simply diff --git a/src/a2dp-aptx-hd.c b/src/a2dp-aptx-hd.c index 16664f21d..f6bd9144d 100644 --- a/src/a2dp-aptx-hd.c +++ b/src/a2dp-aptx-hd.c @@ -26,6 +26,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "codec-aptx.h" #include "io.h" #include "rtp.h" @@ -213,6 +214,7 @@ void *a2dp_aptx_hd_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* reinitialize output buffer */ ffb_rewind(&bt); diff --git a/src/a2dp-aptx.c b/src/a2dp-aptx.c index 171bb3445..67dfd43b5 100644 --- a/src/a2dp-aptx.c +++ b/src/a2dp-aptx.c @@ -25,6 +25,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "codec-aptx.h" #include "io.h" #include "shared/a2dp-codecs.h" @@ -194,6 +195,7 @@ void *a2dp_aptx_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* reinitialize output buffer */ ffb_rewind(&bt); diff --git a/src/a2dp-faststream.c b/src/a2dp-faststream.c index 08b704928..a2fd830a9 100644 --- a/src/a2dp-faststream.c +++ b/src/a2dp-faststream.c @@ -24,6 +24,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "codec-sbc.h" #include "io.h" #include "shared/a2dp-codecs.h" @@ -221,6 +222,7 @@ void *a2dp_fs_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-lc3plus.c b/src/a2dp-lc3plus.c index 66f782cfd..ebcea364f 100644 --- a/src/a2dp-lc3plus.c +++ b/src/a2dp-lc3plus.c @@ -30,6 +30,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "utils.h" @@ -377,6 +378,7 @@ void *a2dp_lc3plus_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-ldac.c b/src/a2dp-ldac.c index 53004d615..75a166041 100644 --- a/src/a2dp-ldac.c +++ b/src/a2dp-ldac.c @@ -27,6 +27,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" #include "ba-config.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "utils.h" @@ -263,6 +264,7 @@ void *a2dp_ldac_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); } diff --git a/src/a2dp-lhdc.c b/src/a2dp-lhdc.c index 3df791637..cf2ea5ec5 100644 --- a/src/a2dp-lhdc.c +++ b/src/a2dp-lhdc.c @@ -29,6 +29,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" #include "ba-config.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "utils.h" @@ -294,6 +295,7 @@ void *a2dp_lhdc_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); } diff --git a/src/a2dp-mpeg.c b/src/a2dp-mpeg.c index cf9cefc87..ed3b8f99a 100644 --- a/src/a2dp-mpeg.c +++ b/src/a2dp-mpeg.c @@ -35,6 +35,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "utils.h" @@ -316,6 +317,7 @@ void *a2dp_mp3_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed (due to frame alignment), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-opus.c b/src/a2dp-opus.c index 2e82f86fb..da74ea87f 100644 --- a/src/a2dp-opus.c +++ b/src/a2dp-opus.c @@ -28,6 +28,7 @@ #include "ba-config.h" #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "io.h" #include "rtp.h" #include "shared/a2dp-codecs.h" @@ -237,6 +238,7 @@ void *a2dp_opus_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed (due to encoder frame * constraint), we have to append new data to the existing one. diff --git a/src/a2dp-sbc.c b/src/a2dp-sbc.c index b62a83d4f..1abd2109d 100644 --- a/src/a2dp-sbc.c +++ b/src/a2dp-sbc.c @@ -29,6 +29,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" #include "ba-config.h" +#include "bluealsa-dbus.h" #include "codec-sbc.h" #include "io.h" #include "rtp.h" @@ -266,6 +267,7 @@ void *a2dp_sbc_enc_thread(struct ba_transport_pcm *t_pcm) { /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/ba-transport-pcm.c b/src/ba-transport-pcm.c index d9306aa14..7bf4917e9 100644 --- a/src/ba-transport-pcm.c +++ b/src/ba-transport-pcm.c @@ -750,39 +750,58 @@ int ba_transport_pcm_delay_get(const struct ba_transport_pcm *pcm) { int ba_transport_pcm_delay_sync(struct ba_transport_pcm *pcm, unsigned int update_mask) { struct ba_transport *t = pcm->t; - int delay = 0; - - delay += pcm->codec_delay_dms; - delay += pcm->processing_delay_dms; - delay += pcm->client_delay_dms; /* In case of A2DP Sink, update the delay property of the BlueZ media * transport interface. BlueZ should forward this value to the remote * device, so it can adjust audio/video synchronization. */ - if (t->profile == BA_TRANSPORT_PROFILE_A2DP_SINK && - t->a2dp.delay_reporting && - abs(delay - t->a2dp.delay) >= 100 /* 10ms */) { - - GError *err = NULL; - t->a2dp.delay = delay; - 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) - /* Even though BlueZ documentation says that the Delay property is - * read-write, it might not be true. In case when the delay write - * operation fails with "not writable" error, we should not try to - * update the delay report value any more. */ - t->a2dp.delay_reporting = false; - warn("Couldn't set A2DP transport delay: %s", err->message); - g_error_free(err); + if (t->profile == BA_TRANSPORT_PROFILE_A2DP_SINK) { + + int delay = 0; + delay += pcm->codec_delay_dms; + delay += pcm->processing_delay_dms; + delay += pcm->client_delay_dms; + + if (t->a2dp.delay_reporting && + abs(delay - t->a2dp.delay) >= 100 /* 10ms */) { + + GError *err = NULL; + t->a2dp.delay = delay; + 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) + /* Even though BlueZ documentation says that the Delay + * property is read-write, it might not be true. In case + * when the delay write operation fails with "not writable" + * error, we should not try to update the delay report + * value any more. */ + t->a2dp.delay_reporting = false; + warn("Couldn't set A2DP transport delay: %s", err->message); + g_error_free(err); + } + } + } + /* If the remote device does not provide delay update reports we can still + * inform local D-Bus clients of our internal processing delay. */ + if ((t->profile & BA_TRANSPORT_PROFILE_MASK_A2DP) == 0 || + !t->a2dp.delay_reporting) { + if (update_mask == BA_DBUS_PCM_UPDATE_DELAY) { + /* To avoid creating a flood of D-Bus signals, we only notify clients + * when the codec + processing value changes by more than 10ms. */ + int delay = pcm->codec_delay_dms + pcm->processing_delay_dms; + if (abs(delay - (int)pcm->reported_codec_delay_dms) < 100 /* 10ms */) + goto final; + pcm->reported_codec_delay_dms = delay; + } } /* Notify all connected D-Bus clients. */ bluealsa_dbus_pcm_update(pcm, update_mask); + +final: return 0; } diff --git a/src/ba-transport-pcm.h b/src/ba-transport-pcm.h index acd1e9d85..7ff4f436c 100644 --- a/src/ba-transport-pcm.h +++ b/src/ba-transport-pcm.h @@ -129,6 +129,9 @@ struct ba_transport_pcm { * the host computational power. It is used to compensate for the time * required to encode or decode audio. */ unsigned int processing_delay_dms; + /* The last reported total codec + processing delay. It is used to limit + * the rate at which changes are reported via D-Bus. */ + unsigned int reported_codec_delay_dms; /* Positive (or negative) delay reported by the client. */ int client_delay_dms; diff --git a/src/bluealsa-dbus.c b/src/bluealsa-dbus.c index 431b1199d..f6283c244 100644 --- a/src/bluealsa-dbus.c +++ b/src/bluealsa-dbus.c @@ -538,6 +538,11 @@ static void bluealsa_pcm_open(GDBusMethodInvocation *inv, void *userdata) { goto fail; } + /* If the transport thread has updated its codec delay value during its + * start procedure then we inform clients that the delay has changed. */ + if (pcm->codec_delay_dms > 0) + bluealsa_dbus_pcm_update(pcm, BA_DBUS_PCM_UPDATE_DELAY); + } pthread_mutex_lock(&pcm->mutex); diff --git a/src/sco-cvsd.c b/src/sco-cvsd.c index dea164c16..a769a9231 100644 --- a/src/sco-cvsd.c +++ b/src/sco-cvsd.c @@ -18,6 +18,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "io.h" #include "shared/defs.h" #include "shared/ffb.h" @@ -80,6 +81,7 @@ void *sco_cvsd_enc_thread(struct ba_transport_pcm *t_pcm) { asrsync_sync(&io.asrs, mtu_samples); /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); } diff --git a/src/sco-lc3-swb.c b/src/sco-lc3-swb.c index b9efb590f..4b549188f 100644 --- a/src/sco-lc3-swb.c +++ b/src/sco-lc3-swb.c @@ -22,6 +22,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "codec-lc3-swb.h" #include "io.h" #include "shared/defs.h" @@ -86,6 +87,7 @@ void *sco_lc3_swb_enc_thread(struct ba_transport_pcm *t_pcm) { asrsync_sync(&io.asrs, codec.frames * LC3_SWB_CODESAMPLES); /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* Move unprocessed data to the front of our linear * buffer and clear the LC3-SWB frame counter. */ diff --git a/src/sco-msbc.c b/src/sco-msbc.c index 3c9ceaae7..d2ec189bb 100644 --- a/src/sco-msbc.c +++ b/src/sco-msbc.c @@ -19,6 +19,7 @@ #include "ba-transport.h" #include "ba-transport-pcm.h" +#include "bluealsa-dbus.h" #include "codec-msbc.h" #include "io.h" #include "shared/defs.h" @@ -93,6 +94,7 @@ void *sco_msbc_enc_thread(struct ba_transport_pcm *t_pcm) { asrsync_sync(&io.asrs, msbc.frames * MSBC_CODESAMPLES); /* update busy delay (encoding overhead) */ t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; + ba_transport_pcm_delay_sync(t_pcm, BA_DBUS_PCM_UPDATE_DELAY); /* Move unprocessed data to the front of our linear * buffer and clear the mSBC frame counter. */ diff --git a/test/test-a2dp.c b/test/test-a2dp.c index c69f9828e..0c1c95d70 100644 --- a/test/test-a2dp.c +++ b/test/test-a2dp.c @@ -51,6 +51,8 @@ int ba_transport_pcm_state_set(struct ba_transport_pcm *pcm, enum ba_transport_pcm_signal ba_transport_pcm_signal_recv(struct ba_transport_pcm *pcm) { (void)pcm; return -1; } void ba_transport_pcm_thread_cleanup(struct ba_transport_pcm *pcm) { (void)pcm; } +int ba_transport_pcm_delay_sync(struct ba_transport_pcm *pcm, unsigned int update_mask) { + (void)pcm; (void)update_mask; return -1; } CK_START_TEST(test_a2dp_codecs_codec_id_from_string) { ck_assert_uint_eq(a2dp_codecs_codec_id_from_string("SBC"), A2DP_CODEC_SBC);