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

Tanu Kaskinen tanuk at iki.fi
Mon Sep 17 12:27:25 UTC 2018


On Thu, 2018-09-13 at 11:12 +0200, Pali Rohár wrote:
> On Wednesday 05 September 2018 13:57:08 Tanu Kaskinen wrote:
> > > +#### 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? Now we have to hardcode the
> > openaptx specific CFLAGS and LIBADD for libbluez5-util. If you ever
> > need to add new flags, 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.
> 
> Intension is that library is small and does not need any special cflags
> or ldflags. So .pc file is not needed at all. And if library or include
> file is in non-standard location then user really need to specify where
> it is. But same argument can be used when .pc file is in non-standard
> location. User again need to do some magic.
> 
> > > +        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?
> 
> I do not know.
> 
> > > +            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.
> 
> I thought that it would be better to not wrap above lines in another
> #ifdef, but rather function register_endpoint would do nothing when
> particular codec (e.g. aptX) was not enabled at compile time.

I thought that we'd end up telling bluetoothd that we support aptX when
we don't, but since register_endpoint() returns early, that's not an
issue. I still think #ifdefs are preferable here, since it makes it
obvious that the register_endpoint() calls don't do anything, but
that's pretty minor thing.

> > 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, order is important. Bluez based on registration order choose codec.
> 
> > > @@ -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).
> 
> ok.
> 
> > > +    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.
> 
> What is the preferred way?

I suggest intializing p to NULL at the beginning of the function and
having pa_assert(p) before the *p = profile line.

> 
> > >      case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
> > >          cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
> > >          cp->priority = 30;
> > > @@ -1840,8 +1871,10 @@ off:
> > >  }
> > >  
> > >  static int uuid_to_profile(const char *uuid, pa_bluetooth_profile_t *_r) {
> > > +    /* FIXME: PA_BLUETOOTH_UUID_A2DP_SINK maps to both SBC and APTX */
> > >      if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
> > >          *_r = PA_BLUETOOTH_PROFILE_A2DP_SBC_SINK;
> > > +    /* FIXME: PA_BLUETOOTH_UUID_A2DP_SOURCE maps to both SBC and APTX */
> > >      else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
> > >          *_r = PA_BLUETOOTH_PROFILE_A2DP_SBC_SOURCE;
> > >      else if (pa_bluetooth_uuid_is_hsp_hs(uuid) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF))
> > > @@ -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.
> 
> Ok.
> 
> > > +    if (sample_spec->channels != 2 || !(capabilities->channel_mode & APTX_CHANNEL_MODE_STEREO)) {
> > > +        pa_log_error("No supported channel modes");
> > > +        return 0;
> > > +    }
> > 
> > sample_spec->channels is the global default channel count, and it's
> > used only as a hint for choosing the preferable channel mode. Since we
> > support only one channel mode, we can ignore sample_spec->channels
> > altogether.
> 
> Ok.
> 
> > > +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.
> 
> Ok.
> 
> Input is sequence of 4 raw 24bit signed stereo samples. And at one time
> we need to process multiple of 8 due to synchronization as aptX uses 3
> bits in every eight sequence for checksum. aptX compression ratio is
> fixed 6:1. Therefore 8*4*6 bytes are on input and 8*4 bytes on output.

Thanks! Makes sense.

-- 
Tanu

https://www.patreon.com/tanuk
https://liberapay.com/tanuk



More information about the pulseaudio-discuss mailing list