[pulseaudio-discuss] [PATCH v2 2/2] Bluetooth A2DP aptX codec support

Pali Rohár pali.rohar at gmail.com
Sat Jan 12 12:01:08 UTC 2019


On Wednesday 05 September 2018 13:57:08 Tanu Kaskinen wrote:
> On Sat, 2018-07-28 at 17:34 +0200, Pali Rohár wrote:
> > This patch provides support for aptX codec in bluetooth A2DP profile. In
> > pulseaudio it is implemented as a new profile a2dp_aptx_sink. For aptX
> > encoding it uses open source LGPLv2.1+ licensed libopenaptx library which
> > can be found at https://github.com/pali/libopenaptx.
> > 
> > Limitations:
> > 
> > Codec selection (either SBC or aptX) is done by bluez itself and it does
> > not provide API for switching codec. Therefore pulseaudio is not able to
> > change codec and it is up to bluez if it decide to use aptX or not.
> > 
> > Only standard aptX codec is supported for now. Support for other variants
> > like aptX HD, aptX Low Latency, FastStream may come up later.
> > ---
> >  configure.ac                                 |  19 ++
> >  src/Makefile.am                              |   5 +
> >  src/modules/bluetooth/a2dp-codecs.h          | 118 ++++++++++-
> >  src/modules/bluetooth/bluez5-util.c          |  48 ++++-
> >  src/modules/bluetooth/bluez5-util.h          |   2 +
> >  src/modules/bluetooth/module-bluez5-device.c |  65 +++++-
> >  src/modules/bluetooth/pa-a2dp-codec-aptx.c   | 297 +++++++++++++++++++++++++++
> >  src/modules/bluetooth/pa-a2dp-codec.h        |   1 +
> >  8 files changed, 548 insertions(+), 7 deletions(-)
> >  create mode 100644 src/modules/bluetooth/pa-a2dp-codec-aptx.c
> > 
> > diff --git a/configure.ac b/configure.ac
> > index d2bfab23b..c2d13fa53 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -1094,6 +1094,23 @@ AC_SUBST(HAVE_BLUEZ_5_NATIVE_HEADSET)
> >  AM_CONDITIONAL([HAVE_BLUEZ_5_NATIVE_HEADSET], [test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = x1])
> >  AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], AC_DEFINE([HAVE_BLUEZ_5_NATIVE_HEADSET], 1, [Bluez 5 native headset backend enabled]))
> >  
> > +#### Bluetooth A2DP aptX codec (optional) ###
> > +
> > +AC_ARG_ENABLE([aptx],
> > +    AS_HELP_STRING([--disable-aptx],[Disable optional bluetooth A2DP aptX codec support (via libopenaptx)]))
> > +
> > +AS_IF([test "x$HAVE_BLUEZ_5" = "x1" && test "x$enable_aptx" != "xno"],
> > +    [AC_CHECK_HEADER([openaptx.h],
> > +        [AC_CHECK_LIB([openaptx], [aptx_init], [HAVE_OPENAPTX=1], [HAVE_OPENAPTX=0])],
> > +        [HAVE_OPENAPTX=0])])
> 
> Have you considered providing a .pc file?

I will think about it (again).

> Now we have to hardcode the
> openaptx specific CFLAGS and LIBADD for libbluez5-util.

As a first step, I will remove hardcoded CFLAGS and LIBADD from
libbluez5-util. In autoconf, everything is possible to discover, so
really no need to hardcode and let autoconf find them.

> If you ever need to add new flags,

This is something which is not going to happen.

> all openaptx users need to update their build
> systems. Also, if the library is installed to a non-standard location,
> the .pc file can set the -L and -I flags to point to the right place.

> > diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
> > index 9c4e3367b..c139f7fc3 100644
> > --- a/src/modules/bluetooth/bluez5-util.c
> > +++ b/src/modules/bluetooth/bluez5-util.c
> > @@ -50,7 +50,9 @@
> >  #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
> >  
> >  #define A2DP_SOURCE_SBC_ENDPOINT "/MediaEndpoint/A2DPSourceSBC"
> > +#define A2DP_SOURCE_APTX_ENDPOINT "/MediaEndpoint/A2DPSourceAPTX"
> >  #define A2DP_SINK_SBC_ENDPOINT "/MediaEndpoint/A2DPSinkSBC"
> > +#define A2DP_SINK_APTX_ENDPOINT "/MediaEndpoint/A2DPSinkAPTX"
> >  
> >  #define ENDPOINT_INTROSPECT_XML                                         \
> >      DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
> > @@ -173,8 +175,22 @@ static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_pr
> >      switch (profile) {
> >          case PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK:
> >              return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
> > +        case PA_BLUETOOTH_PROFILE_A2DP_APTX_SINK:
> > +#ifdef HAVE_OPENAPTX
> > +            /* TODO: Implement once bluez provides API to check if codec is supported */
> > +            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
> 
> Is someone working on that API?

Yes, Luiz posted patches to bluez mailing list, and I will use this API
in new patch version. Also I will add fallback code, so users of "old"
bluez would be still able to use pulseaudio, just with same limitation
as now (no codec switching).

> > +#else
> > +            return false;
> > +#endif
> >          case PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE:
> >              return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
> > +        case PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE:
> > +#ifdef HAVE_OPENAPTX
> > +            /* TODO: Implement once bluez provides API to check if codec is supported */
> > +            return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
> > +#else
> > +            return false;
> > +#endif

And all these #ifdefs are removed in new patch version. There would be
no codec specific code.

> > @@ -961,7 +977,9 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
> >              if (!a->valid)
> >                  return;
> >  
> > +            register_endpoint(y, path, A2DP_SOURCE_APTX_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
> >              register_endpoint(y, path, A2DP_SOURCE_SBC_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
> > +            register_endpoint(y, path, A2DP_SINK_APTX_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
> >              register_endpoint(y, path, A2DP_SINK_SBC_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
> 
> We shouldn't register aptX endpoints if aptX support is not enabled.

Yes, fixing this via new/update pulseaudio codec API.

> Does the registration order matter? I have some vague recollection from
> the earlier discussion that BlueZ chooses the codec based on the
> endpoint registration order - is that true? If the order is important,
> we should document that here.

Yes, you are right.

New/updated pulseaudio codec API contains priority list, so this code is
updated to respect codec API priority.

> > @@ -1652,7 +1694,9 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe
> >      }
> >      y->matches_added = true;
> >  
> > +    endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_APTX_SINK);
> >      endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK);
> > +    endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE);
> >      endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE);
> 
> We shouldn't set up endpoints for aptX if the aptX support is not
> enabled.

Fixing it.

> >  
> >      get_managed_objects(y);
> > @@ -1721,7 +1765,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
> >          if (y->filter_added)
> >              dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
> >  
> > +        endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_APTX_SINK);
> >          endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK);
> > +        endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE);
> >          endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE);
> 
> If endpoint_init() is made conditional, these need to be made
> conditional too (according to the dbus docs, unregistering an object
> path that hasn't been registered first is considered an application
> bug).

Yes, fixing it.

> > diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
> > index 365b9ef6f..29d862fe1 100644
> > --- a/src/modules/bluetooth/bluez5-util.h
> > +++ b/src/modules/bluetooth/bluez5-util.h
> > @@ -55,7 +55,9 @@ typedef enum pa_bluetooth_hook {
> >  
> >  typedef enum profile {
> >      PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK,
> > +    PA_BLUETOOTH_PROFILE_A2DP_APTX_SINK,
> >      PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE,
> > +    PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE,
> >      PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT,
> >      PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY,
> >      PA_BLUETOOTH_PROFILE_OFF

This profile enum would be dynamically constructed, so bluez5-util would
not contain codec specific data.

> > diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
> > index e626e80e9..e8a07d067 100644
> > --- a/src/modules/bluetooth/module-bluez5-device.c
> > +++ b/src/modules/bluetooth/module-bluez5-device.c
> > @@ -1757,6 +1776,18 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro
> >          p = PA_CARD_PROFILE_DATA(cp);
> >          break;
> >  
> > +    case PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE:
> > +        cp = pa_card_profile_new(name, _("High Fidelity aptX Capture (A2DP Source)"), sizeof(pa_bluetooth_profile_t));
> > +        cp->priority = 25;
> > +        cp->n_sinks = 0;
> > +        cp->n_sources = 1;
> > +        cp->max_sink_channels = 0;
> > +        cp->max_source_channels = 2;
> > +        pa_hashmap_put(input_port->profiles, cp->name, cp);
> > +
> > +        p = PA_CARD_PROFILE_DATA(cp);
> > +        break;
> > +
> 
> My compiler started to be unsure whether p is always initialized:
> 
>   CC       modules/bluetooth/module_bluez5_device_la-module-bluez5-device.lo
> modules/bluetooth/module-bluez5-device.c: In function ‘create_card_profile’:
> modules/bluetooth/module-bluez5-device.c:1821:8: warning: ‘p’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>      *p = profile;
>      ~~~^~~~~~~~~
> 
> This is a false positive, but we should do something to make the
> compiler shut up.

Please look at compile warnings after I send new patch series. I'm
testing compilation on Debian 9 (with default gcc compiler) and
currently I do not have warnings.

So if if you find something in patch version, let me know.

> > @@ -1907,6 +1940,28 @@ static int add_card(struct userdata *u) {
> >          pa_hashmap_put(data.profiles, cp->name, cp);
> >      }
> >  
> > +    PA_HASHMAP_FOREACH(uuid, d->uuids, state) {
> > +        pa_bluetooth_profile_t profile;
> > +
> > +        /* FIXME: PA_BLUETOOTH_UUID_A2DP_SINK maps to both SBC and APTX */
> > +        /* FIXME: PA_BLUETOOTH_UUID_A2DP_SOURCE maps to both SBC and APTX */
> > +        if (uuid_to_profile(uuid, &profile) < 0)
> > +            continue;
> > +
> > +        /* Handle APTX */
> > +        if (profile == PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK)
> > +            profile = PA_BLUETOOTH_PROFILE_A2DP_APTX_SINK;
> > +        else if (profile == PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE)
> > +            profile = PA_BLUETOOTH_PROFILE_A2DP_APTX_SOURCE;
> > +        else
> > +            continue;
> > +
> > +        if (!pa_hashmap_get(data.profiles, pa_bluetooth_profile_to_string(profile))) {
> > +            cp = create_card_profile(u, profile, data.ports);
> > +            pa_hashmap_put(data.profiles, cp->name, cp);
> > +        }
> > +    }
> > +
> 
> We shouldn't create the card profile if aptX support is disabled.

Yes, this will be handled in new codec API in new patch version.

> >      pa_assert(!pa_hashmap_isempty(data.profiles));
> >  
> >      cp = pa_card_profile_new("off", _("Off"), sizeof(pa_bluetooth_profile_t));
> > diff --git a/src/modules/bluetooth/pa-a2dp-codec-aptx.c b/src/modules/bluetooth/pa-a2dp-codec-aptx.c
> > new file mode 100644
> > index 000000000..8ce1fc67c
> > --- /dev/null
> > +++ b/src/modules/bluetooth/pa-a2dp-codec-aptx.c
> > +static void pa_aptx_fill_blocksize(void **info_ptr, size_t read_link_mtu, size_t write_link_mtu, size_t *read_block_size, size_t *write_block_size) {
> > +    *write_block_size = (write_link_mtu/(8*4)) * 8*4*6;
> > +    *read_block_size = (read_link_mtu/(8*4)) * 8*4*6;
> 
> Please add comments that explain the math here.

Adding comment into code.

-- 
Pali Rohár
pali.rohar at gmail.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20190112/c9357ac6/attachment.sig>


More information about the pulseaudio-discuss mailing list