diff --git a/configure.ac b/configure.ac index c9c414f81..b736fc3d7 100644 --- a/configure.ac +++ b/configure.ac @@ -1069,7 +1069,7 @@ PA_MACHINE_ID_FALLBACK="${localstatedir}/lib/dbus/machine-id" AX_DEFINE_DIR(PA_MACHINE_ID_FALLBACK, PA_MACHINE_ID_FALLBACK, [Fallback machine-id file]) -#### BlueZ support (optional, dependent on D-Bus and SBC) #### +#### BlueZ support (optional, dependent on FFmpeg, FDK-AAC, D-Bus and SBC) #### AC_ARG_ENABLE([bluez4], AS_HELP_STRING([--disable-bluez4],[Disable optional BlueZ 4 support])) @@ -1089,11 +1089,25 @@ AS_IF([test "x$enable_bluez4" = "xyes" && test "x$HAVE_BLUEZ_4" != "x1"], AC_SUBST(HAVE_BLUEZ_4) AM_CONDITIONAL([HAVE_BLUEZ_4], [test "x$HAVE_BLUEZ_4" = x1]) +## FDK-AAC ## +AS_IF([test "x$enable_bluez5" != "xno"], + [PKG_CHECK_MODULES(FDK_AAC, [ fdk-aac >= 0.1.5 ], HAVE_FDK_AAC=1, HAVE_FDK_AAC=0)], + HAVE_FDK_AAC=0) + + +## FFmpeg libavcodec ## +AS_IF([test "x$enable_bluez5" != "xno"], + [PKG_CHECK_MODULES(FF_AVCODEC, [ libavcodec >= 58.18.100 ], HAVE_FF_AVCODEC=1, HAVE_FF_AVCODEC=0)], + HAVE_FF_AVCODEC=0) +AS_IF([test "x$enable_bluez5" != "xno"], + [PKG_CHECK_MODULES(FF_AVUTIL, [ libavcodec >= 56.14.100 ], HAVE_FF_AVUTIL=1, HAVE_FF_AVUTIL=0)], + HAVE_FF_AVUTIL=0) + ## BlueZ 5 ## -AS_IF([test "x$enable_bluez5" != "xno" && test "x$HAVE_DBUS" = "x1" && test "x$HAVE_SBC" = "x1"], HAVE_BLUEZ_5=1, +AS_IF([test "x$enable_bluez5" != "xno" && test "x$HAVE_DBUS" = "x1" && test "x$HAVE_SBC" = "x1" && test "x$HAVE_FDK_AAC" = "x1" && test "x$HAVE_FF_AVCODEC" = "x1" && test "x$HAVE_FF_AVUTIL" = "x1"], HAVE_BLUEZ_5=1, HAVE_BLUEZ_5=0) AS_IF([test "x$enable_bluez5" = "xyes" && test "x$HAVE_BLUEZ_5" != "x1"], - [AC_MSG_ERROR([*** BLUEZ 5 support not found (requires sbc and D-Bus)])]) + [AC_MSG_ERROR([*** BLUEZ 5 support not found (requires FFmpeg, FDK-AAC, sbc and D-Bus)])]) AC_SUBST(HAVE_BLUEZ_5) AM_CONDITIONAL([HAVE_BLUEZ_5], [test "x$HAVE_BLUEZ_5" = x1]) diff --git a/src/Makefile.am b/src/Makefile.am index f4464d253..80cd56357 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2137,8 +2137,16 @@ module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) -DPA_ # Bluetooth BlueZ 5 sink / source libbluez5_util_la_SOURCES = \ modules/bluetooth/bluez5-util.c \ - modules/bluetooth/bluez5-util.h \ - modules/bluetooth/a2dp-codecs.h + modules/bluetooth/a2dp/a2dp_util.c \ + modules/bluetooth/a2dp/a2dp_sbc.c \ + modules/bluetooth/a2dp/a2dp_aac.c \ + modules/bluetooth/a2dp/a2dp_aptx.c \ + modules/bluetooth/a2dp/a2dp_ldac.c \ + modules/bluetooth/a2dp/a2dp-api.h \ + modules/bluetooth/a2dp/a2dp-codecs.h \ + modules/bluetooth/a2dp/ldacBT.h \ + modules/bluetooth/a2dp/ldacBT_abr.h \ + modules/bluetooth/bluez5-util.h if HAVE_BLUEZ_5_OFONO_HEADSET libbluez5_util_la_SOURCES += \ modules/bluetooth/backend-ofono.c @@ -2149,8 +2157,8 @@ libbluez5_util_la_SOURCES += \ endif libbluez5_util_la_LDFLAGS = -avoid-version -libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) -libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) $(DBUS_LIBS) $(FDK_AAC_LIBS) +libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) $(DBUS_CFLAGS) $(FF_AVCODEC_CFLAGS) $(FF_AVUTIL_CFLAGS) $(FDK_AAC_CFLAGS) module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS) @@ -2159,8 +2167,8 @@ module_bluez5_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_MODULE_NAME= module_bluez5_device_la_SOURCES = modules/bluetooth/module-bluez5-device.c module_bluez5_device_la_LDFLAGS = $(MODULE_LDFLAGS) -module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la -module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) -DPA_MODULE_NAME=module_bluez5_device +module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) libbluez5-util.la +module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) -DPA_MODULE_NAME=module_bluez5_device # Apple Airtunes/RAOP module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c diff --git a/src/modules/bluetooth/a2dp/a2dp-api.h b/src/modules/bluetooth/a2dp/a2dp-api.h new file mode 100644 index 000000000..a5f4eeb22 --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp-api.h @@ -0,0 +1,181 @@ +#ifndef fooa2dpcodecapifoo +#define fooa2dpcodecapifoo + +#ifdef HAVE_CONFIG_H + +#include + +#endif + +#include +#include +#include + +#include "a2dp-codecs.h" +#include "rtp.h" + +typedef struct pa_a2dp_codec pa_a2dp_codec_t; +typedef struct pa_a2dp_config pa_a2dp_config_t; + +extern const pa_a2dp_codec_t pa_a2dp_sbc; +extern const pa_a2dp_codec_t pa_a2dp_aac; +extern const pa_a2dp_codec_t pa_a2dp_aptx; +extern const pa_a2dp_codec_t pa_a2dp_aptx_hd; +extern const pa_a2dp_codec_t pa_a2dp_ldac; + + +/* Implement in module-bluez5-device.c, run from .encode */ + +typedef void (*pa_a2dp_source_read_cb_t)(const void **read_buf, size_t read_buf_size, void *data); + +typedef void (*pa_a2dp_source_read_buf_free_cb_t)(const void **read_buf, void *data); + + +typedef enum pa_a2dp_codec_index { + PA_A2DP_SINK_MIN, + PA_A2DP_SINK_SBC, + PA_A2DP_SINK_AAC, + PA_A2DP_SINK_APTX, + PA_A2DP_SINK_APTX_HD, + PA_A2DP_SINK_MAX, + PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX, + PA_A2DP_SOURCE_SBC, + PA_A2DP_SOURCE_AAC, + PA_A2DP_SOURCE_APTX, + PA_A2DP_SOURCE_APTX_HD, + PA_A2DP_SOURCE_LDAC, + PA_A2DP_SOURCE_MAX, + PA_A2DP_CODEC_INDEX_UNAVAILABLE +} pa_a2dp_codec_index_t; + +typedef struct pa_a2dp_sink { + int priority; + + /* Load decoder syms if it's not loaded; Return true if it's loaded */ + bool (*decoder_load)(); + + /* Memory management is pa_a2dp_sink's work */ + bool (*init)(void **codec_data); + + /* Optional. Update user configurations + * Note: not transport 'configuration' or 'capabilities' */ + int (*update_user_config)(pa_proplist *user_config, void **codec_data); + + void (*config_transport)(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data); + + void (*get_block_size)(size_t read_link_mtu, size_t *read_block_size, void **codec_data); + + void (*setup_stream)(void **codec_data); + + size_t + (*decode)(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *decoded, + uint32_t *timestamp, void **codec_data); + + void (*free)(void **codec_data); +} pa_a2dp_sink_t; + + +typedef struct pa_a2dp_source { + int priority; + + /* Load encoder syms if it's not loaded; Return true if it's loaded */ + bool (*encoder_load)(); + + /* Memory management is pa_a2dp_source's work */ + bool (*init)(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data); + + /* Optional. Update user configurations + * Note: not transport 'configuration' or 'capabilities' */ + int (*update_user_config)(pa_proplist *user_config, void **codec_data); + + void (*config_transport)(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data); + + void (*get_block_size)(size_t write_link_mtu, size_t *write_block_size, void **codec_data); + + void (*setup_stream)(void **codec_data); + + /* Pass read_cb_data to pa_a2dp_source_read_cb, pa_a2dp_source_read_buf_free_cb */ + size_t (*encode)(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *encoded, + void *read_cb_data, void **codec_data); + + /* Optional */ + void (*set_tx_length)(size_t len, void **codec_data); + + /* Optional */ + void (*decrease_quality)(void **codec_data); + + void (*free)(void **codec_data); +} pa_a2dp_source_t; + + +struct pa_a2dp_codec { + const char *name; + uint8_t codec; + const a2dp_vendor_codec_t *vendor_codec; + pa_a2dp_sink_t *a2dp_sink; + pa_a2dp_source_t *a2dp_source; + + /* Memory management is pa_a2dp_codec's work */ + size_t (*get_capabilities)(void **capabilities); + + void (*free_capabilities)(void **capabilities); + + size_t (*select_configuration)(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration); + + void (*free_configuration)(void **configuration); + + /* Return if configuration valid */ + bool (*validate_configuration)(const uint8_t *selected_configuration, const size_t configuration_size); + +}; + + +typedef struct pa_a2dp_freq_cap { + uint32_t rate; + uint32_t cap; +} pa_a2dp_freq_cap_t; + + + +/* Utils */ + +bool pa_a2dp_select_cap_frequency(uint32_t freq_cap, pa_sample_spec default_sample_spec, + const pa_a2dp_freq_cap_t *freq_cap_table, + size_t n, pa_a2dp_freq_cap_t *result); + +void pa_a2dp_init(pa_a2dp_config_t **a2dp_config); + +void pa_a2dp_set_max_priority(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config); + +void pa_a2dp_set_disable(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config); + +void pa_a2dp_free(pa_a2dp_config_t **a2dp_config); + + +void pa_a2dp_get_sink_indices(pa_hashmap **sink_indices, pa_a2dp_config_t **a2dp_config); + +void pa_a2dp_get_source_indices(pa_hashmap **source_indices, pa_a2dp_config_t **a2dp_config); + +void pa_a2dp_get_ordered_indices(pa_hashmap **ordered_indices, pa_a2dp_config_t **a2dp_config); + + +void pa_a2dp_codec_index_to_endpoint(pa_a2dp_codec_index_t codec_index, const char **endpoint); + +void pa_a2dp_endpoint_to_codec_index(const char *endpoint, pa_a2dp_codec_index_t *codec_index); + +void pa_a2dp_codec_index_to_a2dp_codec(pa_a2dp_codec_index_t codec_index, const pa_a2dp_codec_t **a2dp_codec); + +void +pa_a2dp_a2dp_codec_to_codec_index(const pa_a2dp_codec_t *a2dp_codec, bool is_sink, pa_a2dp_codec_index_t *codec_index); + +void pa_a2dp_get_a2dp_codec(uint8_t codec, const a2dp_vendor_codec_t *vendor_codec, const pa_a2dp_codec_t **a2dp_codec); + +bool pa_a2dp_codec_index_is_sink(pa_a2dp_codec_index_t codec_index); + +bool pa_a2dp_codec_index_is_source(pa_a2dp_codec_index_t codec_index); + + +#endif diff --git a/src/modules/bluetooth/a2dp/a2dp-codecs.h b/src/modules/bluetooth/a2dp/a2dp-codecs.h new file mode 100644 index 000000000..0bdd29110 --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp-codecs.h @@ -0,0 +1,453 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2018 Pali Rohár + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#define A2DP_CODEC_SBC 0x00 +#define A2DP_CODEC_MPEG12 0x01 +#define A2DP_CODEC_MPEG24 0x02 +#define A2DP_CODEC_ATRAC 0x04 +#define A2DP_CODEC_VENDOR 0xFF + +#define SBC_SAMPLING_FREQ_16000 (1 << 3) +#define SBC_SAMPLING_FREQ_32000 (1 << 2) +#define SBC_SAMPLING_FREQ_44100 (1 << 1) +#define SBC_SAMPLING_FREQ_48000 1 + +#define SBC_CHANNEL_MODE_MONO (1 << 3) +#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define SBC_CHANNEL_MODE_STEREO (1 << 1) +#define SBC_CHANNEL_MODE_JOINT_STEREO 1 + +#define SBC_BLOCK_LENGTH_4 (1 << 3) +#define SBC_BLOCK_LENGTH_8 (1 << 2) +#define SBC_BLOCK_LENGTH_12 (1 << 1) +#define SBC_BLOCK_LENGTH_16 1 + +#define SBC_SUBBANDS_4 (1 << 1) +#define SBC_SUBBANDS_8 1 + +#define SBC_ALLOCATION_SNR (1 << 1) +#define SBC_ALLOCATION_LOUDNESS 1 + +#define SBC_MAX_BITPOOL 64 +#define SBC_MIN_BITPOOL 2 + +#define MPEG_CHANNEL_MODE_MONO (1 << 3) +#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define MPEG_CHANNEL_MODE_STEREO (1 << 1) +#define MPEG_CHANNEL_MODE_JOINT_STEREO 1 + +#define MPEG_LAYER_MP1 (1 << 2) +#define MPEG_LAYER_MP2 (1 << 1) +#define MPEG_LAYER_MP3 1 + +#define MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define MPEG_SAMPLING_FREQ_48000 1 + +#define MPEG_BIT_RATE_INDEX_0 (1 << 0) +#define MPEG_BIT_RATE_INDEX_1 (1 << 1) +#define MPEG_BIT_RATE_INDEX_2 (1 << 2) +#define MPEG_BIT_RATE_INDEX_3 (1 << 3) +#define MPEG_BIT_RATE_INDEX_4 (1 << 4) +#define MPEG_BIT_RATE_INDEX_5 (1 << 5) +#define MPEG_BIT_RATE_INDEX_6 (1 << 6) +#define MPEG_BIT_RATE_INDEX_7 (1 << 7) +#define MPEG_BIT_RATE_INDEX_8 (1 << 8) +#define MPEG_BIT_RATE_INDEX_9 (1 << 9) +#define MPEG_BIT_RATE_INDEX_10 (1 << 10) +#define MPEG_BIT_RATE_INDEX_11 (1 << 11) +#define MPEG_BIT_RATE_INDEX_12 (1 << 12) +#define MPEG_BIT_RATE_INDEX_13 (1 << 13) +#define MPEG_BIT_RATE_INDEX_14 (1 << 14) + +#define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 +#define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2 +#define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3 +#define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4 +#define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5 +#define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6 +#define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7 +#define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8 +#define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9 +#define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10 +#define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11 +#define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12 +#define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13 +#define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14 + +#define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 +#define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2 +#define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3 +#define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4 +#define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5 +#define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6 +#define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7 +#define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8 +#define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9 +#define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10 +#define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11 +#define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12 +#define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13 +#define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14 + +#define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 +#define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2 +#define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3 +#define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4 +#define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5 +#define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6 +#define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7 +#define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8 +#define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9 +#define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10 +#define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11 +#define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12 +#define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13 +#define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14 + +#define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0 + +#define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2) +#define MPEG_SET_BITRATE(a, b) \ + do { \ + (a).bitrate1 = ((b) >> 8) & 0x7f; \ + (a).bitrate2 = (b) & 0xff; \ + } while (0) + +#define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80 +#define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40 +#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20 +#define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10 + +#define AAC_SAMPLING_FREQ_8000 0x0800 +#define AAC_SAMPLING_FREQ_11025 0x0400 +#define AAC_SAMPLING_FREQ_12000 0x0200 +#define AAC_SAMPLING_FREQ_16000 0x0100 +#define AAC_SAMPLING_FREQ_22050 0x0080 +#define AAC_SAMPLING_FREQ_24000 0x0040 +#define AAC_SAMPLING_FREQ_32000 0x0020 +#define AAC_SAMPLING_FREQ_44100 0x0010 +#define AAC_SAMPLING_FREQ_48000 0x0008 +#define AAC_SAMPLING_FREQ_64000 0x0004 +#define AAC_SAMPLING_FREQ_88200 0x0002 +#define AAC_SAMPLING_FREQ_96000 0x0001 + +#define AAC_CHANNELS_1 0x02 +#define AAC_CHANNELS_2 0x01 + +#define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \ + (a).bitrate2 << 8 | (a).bitrate3) +#define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2) + +#define AAC_SET_BITRATE(a, b) \ + do { \ + (a).bitrate1 = (b >> 16) & 0x7f; \ + (a).bitrate2 = (b >> 8) & 0xff; \ + (a).bitrate3 = b & 0xff; \ + } while (0) +#define AAC_SET_FREQUENCY(a, f) \ + do { \ + (a).frequency1 = (f >> 4) & 0xff; \ + (a).frequency2 = f & 0x0f; \ + } while (0) + +#define AAC_INIT_BITRATE(b) \ + .bitrate1 = (b >> 16) & 0x7f, \ + .bitrate2 = (b >> 8) & 0xff, \ + .bitrate3 = b & 0xff, +#define AAC_INIT_FREQUENCY(f) \ + .frequency1 = (f >> 4) & 0xff, \ + .frequency2 = f & 0x0f, + +#define APTX_VENDOR_ID 0x0000004f +#define APTX_CODEC_ID 0x0001 + +#define APTX_CHANNEL_MODE_MONO 0x01 +#define APTX_CHANNEL_MODE_STEREO 0x02 + +#define APTX_SAMPLING_FREQ_16000 0x08 +#define APTX_SAMPLING_FREQ_32000 0x04 +#define APTX_SAMPLING_FREQ_44100 0x02 +#define APTX_SAMPLING_FREQ_48000 0x01 + +#define FASTSTREAM_VENDOR_ID 0x0000000a +#define FASTSTREAM_CODEC_ID 0x0001 + +#define FASTSTREAM_DIRECTION_SINK 0x1 +#define FASTSTREAM_DIRECTION_SOURCE 0x2 + +#define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2 +#define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1 + +#define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2 + +#define APTX_LL_VENDOR_ID 0x0000000a +#define APTX_LL_CODEC_ID 0x0002 + +#define APTX_LL_CHANNEL_MODE_MONO 0x01 +#define APTX_LL_CHANNEL_MODE_STEREO 0x02 + +#define APTX_LL_SAMPLING_FREQ_16000 0x08 +#define APTX_LL_SAMPLING_FREQ_32000 0x04 +#define APTX_LL_SAMPLING_FREQ_44100 0x02 +#define APTX_LL_SAMPLING_FREQ_48000 0x01 + +/* Default parameters for aptX Low Latency encoder */ + +/* Target codec buffer level = 180 */ +#define APTX_LL_TARGET_LEVEL2 0xb4 +#define APTX_LL_TARGET_LEVEL1 0x00 + +/* Initial codec buffer level = 360 */ +#define APTX_LL_INITIAL_LEVEL2 0x68 +#define APTX_LL_INITIAL_LEVEL1 0x01 + +/* SRA max rate 0.005 * 10000 = 50 */ +#define APTX_LL_SRA_MAX_RATE 0x32 + +/* SRA averaging time = 1s */ +#define APTX_LL_SRA_AVG_TIME 0x01 + +/* Good working codec buffer level = 180 */ +#define APTX_LL_GOOD_WORKING_LEVEL2 0xB4 +#define APTX_LL_GOOD_WORKING_LEVEL1 0x00 + +#define APTX_HD_VENDOR_ID 0x000000D7 +#define APTX_HD_CODEC_ID 0x0024 + +#define APTX_HD_CHANNEL_MODE_MONO 0x1 +#define APTX_HD_CHANNEL_MODE_STEREO 0x2 + +#define APTX_HD_SAMPLING_FREQ_16000 0x8 +#define APTX_HD_SAMPLING_FREQ_32000 0x4 +#define APTX_HD_SAMPLING_FREQ_44100 0x2 +#define APTX_HD_SAMPLING_FREQ_48000 0x1 + +#define LDAC_VENDOR_ID 0x0000012d +#define LDAC_CODEC_ID 0x00aa + +#define LDAC_SAMPLING_FREQ_44100 0x20 +#define LDAC_SAMPLING_FREQ_48000 0x10 +#define LDAC_SAMPLING_FREQ_88200 0x08 +#define LDAC_SAMPLING_FREQ_96000 0x04 +#define LDAC_SAMPLING_FREQ_176400 0x02 +#define LDAC_SAMPLING_FREQ_192000 0x01 + +#define LDAC_CHANNEL_MODE_MONO 0x04 +#define LDAC_CHANNEL_MODE_DUAL 0x02 +#define LDAC_CHANNEL_MODE_STEREO 0x01 + +typedef struct { + uint8_t vendor_id4; + uint8_t vendor_id3; + uint8_t vendor_id2; + uint8_t vendor_id1; + uint8_t codec_id2; + uint8_t codec_id1; +} __attribute__ ((packed)) a2dp_vendor_codec_t; + +#define A2DP_GET_VENDOR_ID(a) ( \ + (((uint32_t)(a).vendor_id4) << 0) | \ + (((uint32_t)(a).vendor_id3) << 8) | \ + (((uint32_t)(a).vendor_id2) << 16) | \ + (((uint32_t)(a).vendor_id1) << 24) \ + ) +#define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8)) +#define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \ + .vendor_id4 = (((v) >> 0) & 0xff), \ + .vendor_id3 = (((v) >> 8) & 0xff), \ + .vendor_id2 = (((v) >> 16) & 0xff), \ + .vendor_id1 = (((v) >> 24) & 0xff), \ + .codec_id2 = (((c) >> 0) & 0xff), \ + .codec_id1 = (((c) >> 8) & 0xff), \ + }) + +typedef struct { + uint8_t reserved; + uint8_t target_level2; + uint8_t target_level1; + uint8_t initial_level2; + uint8_t initial_level1; + uint8_t sra_max_rate; + uint8_t sra_avg_time; + uint8_t good_working_level2; + uint8_t good_working_level1; +} __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t frequency; + uint8_t channel_mode; +} __attribute__ ((packed)) a2dp_ldac_t; + +#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN + +typedef struct { + uint8_t channel_mode:4; + uint8_t frequency:4; + uint8_t allocation_method:2; + uint8_t subbands:2; + uint8_t block_length:4; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)) a2dp_sbc_t; + +typedef struct { + uint8_t channel_mode:4; + uint8_t crc:1; + uint8_t layer:3; + uint8_t frequency:6; + uint8_t mpf:1; + uint8_t rfa:1; + uint8_t bitrate1:7; + uint8_t vbr:1; + uint8_t bitrate2; +} __attribute__ ((packed)) a2dp_mpeg_t; + +typedef struct { + uint8_t object_type; + uint8_t frequency1; + uint8_t rfa:2; + uint8_t channels:2; + uint8_t frequency2:4; + uint8_t bitrate1:7; + uint8_t vbr:1; + uint8_t bitrate2; + uint8_t bitrate3; +} __attribute__ ((packed)) a2dp_aac_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t channel_mode:4; + uint8_t frequency:4; +} __attribute__ ((packed)) a2dp_aptx_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t direction; + uint8_t sink_frequency:4; + uint8_t source_frequency:4; +} __attribute__ ((packed)) a2dp_faststream_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t channel_mode:4; + uint8_t frequency:4; + uint8_t bidirect_link:1; + uint8_t has_new_caps:1; + uint8_t reserved:6; + a2dp_aptx_ll_new_caps_t new_caps[0]; +} __attribute__ ((packed)) a2dp_aptx_ll_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t channel_mode:4; + uint8_t frequency:4; + uint8_t reserved0; + uint8_t reserved1; + uint8_t reserved2; + uint8_t reserved3; +} __attribute__ ((packed)) a2dp_aptx_hd_t; + +#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN + +typedef struct { + uint8_t frequency:4; + uint8_t channel_mode:4; + uint8_t block_length:4; + uint8_t subbands:2; + uint8_t allocation_method:2; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)) a2dp_sbc_t; + +typedef struct { + uint8_t layer:3; + uint8_t crc:1; + uint8_t channel_mode:4; + uint8_t rfa:1; + uint8_t mpf:1; + uint8_t frequency:6; + uint8_t vbr:1; + uint8_t bitrate1:7; + uint8_t bitrate2; +} __attribute__ ((packed)) a2dp_mpeg_t; + +typedef struct { + uint8_t object_type; + uint8_t frequency1; + uint8_t frequency2:4; + uint8_t channels:2; + uint8_t rfa:2; + uint8_t vbr:1; + uint8_t bitrate1:7; + uint8_t bitrate2; + uint8_t bitrate3; +} __attribute__ ((packed)) a2dp_aac_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t frequency:4; + uint8_t channel_mode:4; +} __attribute__ ((packed)) a2dp_aptx_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t direction; + uint8_t source_frequency:4; + uint8_t sink_frequency:4; +} __attribute__ ((packed)) a2dp_faststream_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t frequency:4; + uint8_t channel_mode:4; + uint8_t reserved:6; + uint8_t has_new_caps:1; + uint8_t bidirect_link:1; + a2dp_aptx_ll_new_caps_t new_caps[0]; +} __attribute__ ((packed)) a2dp_aptx_ll_t; + +typedef struct { + a2dp_vendor_codec_t info; + uint8_t frequency:4; + uint8_t channel_mode:4; + uint8_t reserved0; + uint8_t reserved1; + uint8_t reserved2; + uint8_t reserved3; +} __attribute__ ((packed)) a2dp_aptx_hd_t; + +#else +#error "Unknown byte order" +#endif diff --git a/src/modules/bluetooth/a2dp/a2dp_aac.c b/src/modules/bluetooth/a2dp/a2dp_aac.c new file mode 100644 index 000000000..64556e8ab --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp_aac.c @@ -0,0 +1,743 @@ +#include +#include + +#include +#include + +#include + +#include "a2dp-api.h" + +#define streq(a, b) (!strcmp((a),(b))) + +#define AAC_DEFAULT_BITRATE 320000u + +typedef struct aac_info { + pa_a2dp_source_read_cb_t read_pcm; + pa_a2dp_source_read_buf_free_cb_t read_buf_free; + + bool is_a2dp_sink; + + uint16_t seq_num; + + HANDLE_AACDECODER aacdecoder_handle; + bool aacdecoder_handle_opened; + + HANDLE_AACENCODER aacenc_handle; + bool aacenc_handle_opened; + AACENC_InfoStruct aacenc_info; + + uint32_t bitrate; + size_t mtu; + + /* Constant Bitrate: 0 + * Variable Bitrate: 1-5 (Only effects when both bluetooth devices have vbr support ) */ + int aac_enc_bitrate_mode; + uint32_t aac_afterburner; + pa_sample_format_t force_pa_fmt; + + pa_sample_spec sample_spec; + + size_t read_block_size; + size_t write_block_size; + +} aac_info_t; + +static bool pa_aac_decoder_load() { + /* AAC libs dynamically linked */ + return true; +} + +static bool pa_aac_encoder_load() { + /* AAC libs dynamically linked */ + return true; +} + +static bool +pa_aac_decoder_init(void **codec_data) { + aac_info_t *info = pa_xmalloc0(sizeof(aac_info_t)); + *codec_data = info; + info->is_a2dp_sink = true; + return true; +} + +static bool +pa_aac_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { + aac_info_t *info = pa_xmalloc0(sizeof(aac_info_t)); + *codec_data = info; + info->is_a2dp_sink = false; + info->read_pcm = read_cb; + info->read_buf_free = free_cb; + info->aacenc_handle_opened = false; + info->aac_enc_bitrate_mode = 5; + info->aac_afterburner = false; + info->force_pa_fmt = PA_SAMPLE_INVALID; + return true; +} + +/* KEY VALUE DESC DEFAULT + * aac_bitrate_mode [1, 5] Variable Bitrate (VBR) (encoder) 5 + * 0 Constant Bitrate (CBR) (encoder) + * + * aac_fmt s16 16-bit signed LE (encoder) auto + * s32 32-bit signed LE (encoder) + * auto + * + * aac_afterburner FDK-AAC afterburner feature (encoder) off + */ +static int pa_aac_update_user_config(pa_proplist *user_config, void **codec_data) { + aac_info_t *i = *codec_data; + const char *aac_bitrate_mode_str, *aac_fmt_str, *aac_afterburner_str; + int aac_bitrate_mode = 0, ret = 0; + pa_assert(i); + + aac_bitrate_mode_str = pa_proplist_gets(user_config, "aac_bitrate_mode"); + aac_fmt_str = pa_proplist_gets(user_config, "aac_fmt"); + aac_afterburner_str = pa_proplist_gets(user_config, "aac_afterburner"); + + if (aac_bitrate_mode_str) { + aac_bitrate_mode = atoi(aac_bitrate_mode_str); + + if (aac_bitrate_mode >= 0 && aac_bitrate_mode <= 5) { + i->aac_enc_bitrate_mode = aac_bitrate_mode; + ret++; + } else + pa_log ("aac_bitrate_mode parameter must in range [0, 5] (found %s)", aac_bitrate_mode_str); + } + + if (aac_fmt_str) { + if (streq(aac_fmt_str, "s16")) { + i->force_pa_fmt = PA_SAMPLE_S16LE; + ret++; + } else if (streq(aac_fmt_str, "s32")) { + i->force_pa_fmt = PA_SAMPLE_S32LE; + ret++; + } else if (streq(aac_fmt_str, "auto")) { + i->force_pa_fmt = PA_SAMPLE_INVALID; + ret++; + } else + pa_log ("aac_fmt parameter must be either s16, s32 or auto (found %s)", aac_fmt_str); + } + + if (aac_afterburner_str) { + if (streq("on", aac_afterburner_str)) { + i->aac_afterburner = 1; + ret++; + } else if (streq("off", aac_afterburner_str)) { + i->aac_afterburner = 0; + ret++; + } else + pa_log ("aac_afterburner parameter must be either on or off (found %s)", aac_afterburner_str); + } + + return ret; +} + +static size_t +pa_aac_decode(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *_decoded, + uint32_t *timestamp, void **codec_data) { + const struct rtp_header *header; + const UCHAR *p; + INT_PCM *d; + UINT to_decode, pkt_size; + UINT total_written = 0; + aac_info_t *aac_info = *codec_data; + pa_assert(aac_info); + + header = read_buf; + *timestamp = ntohl(header->timestamp); + + p = (UCHAR *) read_buf + sizeof(*header); + pkt_size = to_decode = (UINT) (read_buf_size - sizeof(*header)); + + d = write_buf; + + *_decoded = 0; + while (PA_LIKELY(to_decode > 0)) { + CStreamInfo* info; + + AAC_DECODER_ERROR aac_err = aacDecoder_Fill(aac_info->aacdecoder_handle, + (UCHAR **) &p, &pkt_size, &to_decode); + + if (PA_UNLIKELY(aac_err != AAC_DEC_OK)) { + pa_log_error("aacDecoder_Fill() error 0x%x", aac_err); + *_decoded = 0; + return 0; + } + + while (true) { + INT written; + aac_err = aacDecoder_DecodeFrame(aac_info->aacdecoder_handle, d, (INT) write_buf_size, 0); + if (PA_UNLIKELY(aac_err == AAC_DEC_NOT_ENOUGH_BITS)) + break; + if (PA_UNLIKELY(aac_err != AAC_DEC_OK)){ + pa_log_error("aacDecoder_DecodeFrame() error 0x%x", aac_err); + break; + } + + info = aacDecoder_GetStreamInfo(aac_info->aacdecoder_handle); + if(PA_UNLIKELY(!info || info->sampleRate <= 0)) { + pa_log_error("Invalid stream info"); + break; + } + + written = info->frameSize * info->numChannels * 2; + d += written; + total_written += (UINT) written; + } + } + + *_decoded = pkt_size; + + return total_written; +} + +static size_t +pa_aac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, + void **codec_data) { + struct rtp_header *header; + size_t nbytes; + uint8_t *d; + const uint8_t *p; + int to_write; + unsigned frame_count; + aac_info_t *aac_info = *codec_data; + const size_t sample_size = pa_sample_size(&aac_info->sample_spec), + frame_size = pa_frame_size(&aac_info->sample_spec); + void *in_bufs[1] = {NULL}; + void *out_bufs[1] = {NULL}; + int in_bufferIdentifiers[1] = {IN_AUDIO_DATA}; + int out_bufferIdentifiers[1] = {OUT_BITSTREAM_DATA}; + int in_bufSizes[1] = {(int) (aac_info->aacenc_info.frameLength * frame_size)}; + int out_bufSizes[1]; + int bufElSizes[1] = {(int) sample_size}; + AACENC_BufDesc in_bufDesc = { + .numBufs = 1, + .bufs = in_bufs, + .bufferIdentifiers = in_bufferIdentifiers, + .bufSizes = in_bufSizes, + .bufElSizes = bufElSizes + }; + AACENC_BufDesc out_bufDesc = { + .numBufs = 1, + .bufs = out_bufs, + .bufferIdentifiers = out_bufferIdentifiers, + .bufSizes = out_bufSizes, + .bufElSizes = bufElSizes + }; + AACENC_InArgs in_args = { + .numAncBytes = 0, + .numInSamples = aac_info->aacenc_info.frameLength * aac_info->aacenc_info.inputChannels + }; + AACENC_OutArgs out_args; + + pa_assert(aac_info); + + header = write_buf; + + frame_count = 0; + + aac_info->read_pcm((const void **) &p, (size_t) in_bufSizes[0], read_cb_data); + + in_bufDesc.bufs[0] = (void *) p; + + d = (uint8_t *) write_buf + sizeof(*header); + to_write = (int) (write_buf_size - sizeof(*header)); + out_bufDesc.bufs[0] = d; + out_bufSizes[0] = to_write; + + + *_encoded = 0; + + while (PA_UNLIKELY(in_args.numInSamples && to_write > 0)) { + size_t encoded; + + AACENC_ERROR aac_err = aacEncEncode(aac_info->aacenc_handle, &in_bufDesc, &out_bufDesc, &in_args, &out_args); + + if (PA_UNLIKELY(aac_err != AACENC_OK)) { + pa_log_error("AAC encoding error, 0x%x", aac_err); + aac_info->read_buf_free((const void **) &p, read_cb_data); + *_encoded = 0; + return 0; + } + + encoded = out_args.numInSamples * sample_size; + + in_args.numInSamples -= out_args.numInSamples; + p += encoded; + *_encoded += encoded; + + to_write -= out_args.numOutBytes; + d += out_args.numOutBytes; + + frame_count++; + } + + aac_info->read_buf_free((const void **) &p, read_cb_data); + + memset(write_buf, 0, sizeof(*header)); + header->v = 2; + header->pt = 1; + header->sequence_number = htons(aac_info->seq_num++); + header->timestamp = htonl(timestamp); + header->ssrc = htonl(1); + + nbytes = d - (uint8_t *) write_buf; + + return nbytes; +} + +static void +pa_aac_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data) { + AACENC_ERROR aac_err; + aac_info_t *aac_info = *codec_data; + a2dp_aac_t *config = (a2dp_aac_t *) configuration; + UINT aot, sample_rate, channels; + pa_sample_format_t fmt; + + pa_assert(aac_info); + pa_assert_se(configuration_size == sizeof(*config)); + + aac_info->bitrate = PA_MIN(AAC_DEFAULT_BITRATE, ((uint32_t) AAC_GET_BITRATE(*config))); + + + if(aac_info->is_a2dp_sink) + sample_spec->format = PA_SAMPLE_S16LE; + else{ + if (aac_info->force_pa_fmt == PA_SAMPLE_INVALID) + fmt = default_sample_spec.format; + else + fmt = aac_info->force_pa_fmt; + + switch (fmt) { + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S32BE: + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + sample_spec->format = PA_SAMPLE_S32LE; + break; + default: + sample_spec->format = PA_SAMPLE_S16LE; + } + } + + switch (config->object_type) { + case AAC_OBJECT_TYPE_MPEG2_AAC_LC: + aot = AOT_AAC_LC; + break; + case AAC_OBJECT_TYPE_MPEG4_AAC_LC: + aot = AOT_AAC_LC; + break; + case AAC_OBJECT_TYPE_MPEG4_AAC_LTP: + aot = AOT_AAC_LTP; + break; + case AAC_OBJECT_TYPE_MPEG4_AAC_SCA: + aot = AOT_AAC_SCAL; + break; + default: + pa_log_error("Invalid AAC object type"); + pa_assert_not_reached(); + } + + switch (AAC_GET_FREQUENCY(*config)) { + case AAC_SAMPLING_FREQ_8000: + sample_rate = 8000; + sample_spec->rate = 8000; + break; + case AAC_SAMPLING_FREQ_11025: + sample_rate = 11025; + sample_spec->rate = 11025; + break; + case AAC_SAMPLING_FREQ_12000: + sample_rate = 12000; + sample_spec->rate = 12000; + break; + case AAC_SAMPLING_FREQ_16000: + sample_rate = 16000; + sample_spec->rate = 16000; + break; + case AAC_SAMPLING_FREQ_22050: + sample_rate = 22050; + sample_spec->rate = 22050; + break; + case AAC_SAMPLING_FREQ_24000: + sample_rate = 24000; + sample_spec->rate = 24000; + break; + case AAC_SAMPLING_FREQ_32000: + sample_rate = 32000; + sample_spec->rate = 32000; + break; + case AAC_SAMPLING_FREQ_44100: + sample_rate = 44100; + sample_spec->rate = 44100; + break; + case AAC_SAMPLING_FREQ_48000: + sample_rate = 48000; + sample_spec->rate = 48000; + break; + case AAC_SAMPLING_FREQ_64000: + sample_rate = 64000; + sample_spec->rate = 64000; + break; + case AAC_SAMPLING_FREQ_88200: + sample_rate = 88200; + sample_spec->rate = 88200; + break; + case AAC_SAMPLING_FREQ_96000: + sample_rate = 96000; + sample_spec->rate = 96000; + break; + default: + pa_log_error("Invalid AAC frequency"); + pa_assert_not_reached(); + } + + switch (config->channels) { + case AAC_CHANNELS_1: + channels = MODE_1; + sample_spec->channels = 1; + break; + case AAC_CHANNELS_2: + channels = MODE_2; + sample_spec->channels = 2; + break; + default: + pa_log_error("Invalid AAC channel mode"); + pa_assert_not_reached(); + } + + aac_info->sample_spec = *sample_spec; + + /* AAC SINK */ + if (aac_info->is_a2dp_sink) { + if (!aac_info->aacdecoder_handle_opened) { + aac_info->aacdecoder_handle = aacDecoder_Open(TT_MP4_LATM_MCP1, 1); + aac_info->aacdecoder_handle_opened = true; + } + + pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(aac_info->aacdecoder_handle, AAC_PCM_MIN_OUTPUT_CHANNELS, + sample_spec->channels)); + pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(aac_info->aacdecoder_handle, AAC_PCM_MAX_OUTPUT_CHANNELS, + sample_spec->channels)); + + return; + } + + + /* AAC SOURCE */ + + if (!aac_info->aacenc_handle_opened) { + aac_err = aacEncOpen(&aac_info->aacenc_handle, 0, 2); + + if (aac_err != AACENC_OK) { + pa_log_error("Cannot open AAC encoder handle: AAC error 0x%x", aac_err); + pa_assert_not_reached(); + } + aac_info->aacenc_handle_opened = true; + } + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AOT, aot); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_SAMPLERATE, sample_rate); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_CHANNELMODE, channels); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + if (config->vbr) { + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATEMODE, + (UINT) aac_info->aac_enc_bitrate_mode); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + } + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AUDIOMUXVER, 2); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_SIGNALING_MODE, 1); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATE, aac_info->bitrate); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_HEADER_PERIOD, 1); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_AFTERBURNER, aac_info->aac_afterburner); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncEncode(aac_info->aacenc_handle, NULL, NULL, NULL, NULL); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + pa_assert_se(AACENC_OK == aacEncInfo(aac_info->aacenc_handle, &aac_info->aacenc_info)); + + pa_assert(aac_info->aacenc_info.inputChannels == aac_info->sample_spec.channels); + +}; + +static void pa_aac_get_read_block_size(size_t read_link_mtu, size_t *read_block_size, void **codec_data) { + aac_info_t *aac_info = *codec_data; + pa_assert(aac_info); + + aac_info->mtu = read_link_mtu; + + /* aacEncoder.pdf Section 3.2.1 + * AAC-LC audio frame contains 1024 PCM samples per channel */ + *read_block_size = 1024 * pa_frame_size(&aac_info->sample_spec); + aac_info->read_block_size = *read_block_size; +}; + +static void pa_aac_get_write_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { + aac_info_t *aac_info = *codec_data; + pa_assert(aac_info); + + aac_info->mtu = write_link_mtu; + + /* aacEncoder.pdf section 3.2.1 + * AAC-LC audio frame contains 1024 PCM samples per channel */ + *write_block_size = 1024 * pa_frame_size(&aac_info->sample_spec); + aac_info->write_block_size = *write_block_size; +}; + +static void pa_aac_setup_stream(void **codec_data) { + AACENC_ERROR aac_err; + aac_info_t *aac_info = *codec_data; + uint32_t max_bitrate; + pa_assert(aac_info); + + max_bitrate = (uint32_t) ((8 * (aac_info->mtu - sizeof(struct rtp_header)) * aac_info->sample_spec.rate) / 1024); + + aac_info->bitrate = PA_MIN(max_bitrate, aac_info->bitrate); + + pa_log_debug("Maximum AAC transmission bitrate: %d bps; Bitrate in use: %d bps", max_bitrate, aac_info->bitrate); + + /* AAC SINK */ + if (aac_info->is_a2dp_sink) { + return; + } + + + /* AAC SOURCE */ + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_BITRATE, aac_info->bitrate); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + + aac_err = aacEncoder_SetParam(aac_info->aacenc_handle, AACENC_PEAK_BITRATE, (UINT) max_bitrate); + if (aac_err != AACENC_OK) + pa_assert_not_reached(); + +}; + +static void pa_aac_free(void **codec_data) { + aac_info_t *aac_info = *codec_data; + if (!aac_info) + return; + + if (aac_info->aacenc_handle_opened) + aacEncClose(&aac_info->aacenc_handle); + + if (aac_info->aacdecoder_handle_opened) + aacDecoder_Close(aac_info->aacdecoder_handle); + + pa_xfree(aac_info); + *codec_data = NULL; + +}; + +static size_t pa_aac_get_capabilities(void **_capabilities) { + a2dp_aac_t *capabilities = pa_xmalloc0(sizeof(a2dp_aac_t)); + + capabilities->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC; + capabilities->channels = AAC_CHANNELS_1 | AAC_CHANNELS_2; + AAC_SET_BITRATE(*capabilities, AAC_DEFAULT_BITRATE); + AAC_SET_FREQUENCY(*capabilities, (AAC_SAMPLING_FREQ_8000 | AAC_SAMPLING_FREQ_11025 | AAC_SAMPLING_FREQ_12000 | + AAC_SAMPLING_FREQ_16000 | AAC_SAMPLING_FREQ_22050 | AAC_SAMPLING_FREQ_24000 | + AAC_SAMPLING_FREQ_32000 | AAC_SAMPLING_FREQ_44100 | AAC_SAMPLING_FREQ_48000 | + AAC_SAMPLING_FREQ_64000 | AAC_SAMPLING_FREQ_88200 | AAC_SAMPLING_FREQ_96000)); + capabilities->vbr = 1; + *_capabilities = capabilities; + + return sizeof(*capabilities); +}; + +static size_t +pa_aac_select_configuration(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + a2dp_aac_t *cap = (a2dp_aac_t *) supported_capabilities; + a2dp_aac_t *config = pa_xmalloc0(sizeof(a2dp_aac_t)); + pa_a2dp_freq_cap_t aac_freq_cap, aac_freq_table[] = { + {8000U, AAC_SAMPLING_FREQ_8000}, + {11025U, AAC_SAMPLING_FREQ_11025}, + {12000U, AAC_SAMPLING_FREQ_12000}, + {16000U, AAC_SAMPLING_FREQ_16000}, + {22050U, AAC_SAMPLING_FREQ_22050}, + {24000U, AAC_SAMPLING_FREQ_24000}, + {32000U, AAC_SAMPLING_FREQ_32000}, + {44100U, AAC_SAMPLING_FREQ_44100}, + {48000U, AAC_SAMPLING_FREQ_48000}, + {64000U, AAC_SAMPLING_FREQ_64000}, + {88200U, AAC_SAMPLING_FREQ_88200}, + {96000U, AAC_SAMPLING_FREQ_96000} + }; + + if (capabilities_size != sizeof(a2dp_aac_t)) + return 0; + + if (!pa_a2dp_select_cap_frequency(AAC_GET_FREQUENCY(*cap), default_sample_spec, aac_freq_table, + PA_ELEMENTSOF(aac_freq_table), &aac_freq_cap)) + return 0; + + AAC_SET_FREQUENCY(*config, aac_freq_cap.cap); + + AAC_SET_BITRATE(*config, AAC_GET_BITRATE(*cap)); + + if (default_sample_spec.channels <= 1) { + if (cap->channels & AAC_CHANNELS_1) + config->channels = AAC_CHANNELS_1; + else if (cap->channels & AAC_CHANNELS_2) + config->channels = AAC_CHANNELS_2; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + + if (default_sample_spec.channels >= 2) { + if (cap->channels & AAC_CHANNELS_2) + config->channels = AAC_CHANNELS_2; + else if (cap->channels & AAC_CHANNELS_1) + config->channels = AAC_CHANNELS_1; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + + if (cap->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) + config->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC; + else if (cap->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) + config->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC; + else { + pa_log_error("No supported aac object type"); + return 0; + } + + config->vbr = cap->vbr; + + *configuration = config; + return sizeof(*config); +}; + +static void pa_aac_free_capabilities(void **capabilities) { + if (!capabilities || !*capabilities) + return; + pa_xfree(*capabilities); + *capabilities = NULL; +} + +static bool pa_aac_validate_configuration(const uint8_t *selected_configuration, const size_t configuration_size) { + a2dp_aac_t *c = (a2dp_aac_t *) selected_configuration; + + if (configuration_size != sizeof(a2dp_aac_t)) { + pa_log_error("AAC configuration array of invalid size"); + return false; + } + + switch (c->object_type) { + case AAC_OBJECT_TYPE_MPEG2_AAC_LC: + case AAC_OBJECT_TYPE_MPEG4_AAC_LC: + break; + default: + pa_log_error("Invalid object type in AAC configuration"); + return false; + } + + switch (AAC_GET_FREQUENCY(*c)) { + case AAC_SAMPLING_FREQ_8000: + case AAC_SAMPLING_FREQ_11025: + case AAC_SAMPLING_FREQ_12000: + case AAC_SAMPLING_FREQ_16000: + case AAC_SAMPLING_FREQ_22050: + case AAC_SAMPLING_FREQ_24000: + case AAC_SAMPLING_FREQ_32000: + case AAC_SAMPLING_FREQ_44100: + case AAC_SAMPLING_FREQ_48000: + case AAC_SAMPLING_FREQ_64000: + case AAC_SAMPLING_FREQ_88200: + case AAC_SAMPLING_FREQ_96000: + break; + default: + pa_log_error("Invalid sampling frequency in AAC configuration"); + return false; + } + + switch (c->channels) { + case AAC_CHANNELS_1: + case AAC_CHANNELS_2: + break; + default: + pa_log_error("Invalid channel mode in AAC Configuration"); + return false; + } + + return true; +}; + + +static pa_a2dp_source_t pa_aac_source = { + .encoder_load = pa_aac_encoder_load, + .init = pa_aac_encoder_init, + .update_user_config = pa_aac_update_user_config, + .encode = pa_aac_encode, + .config_transport=pa_aac_config_transport, + .get_block_size=pa_aac_get_write_block_size, + .setup_stream = pa_aac_setup_stream, + .set_tx_length = NULL, + .decrease_quality = NULL, + .free = pa_aac_free +}; + +static pa_a2dp_sink_t pa_aac_sink = { + .decoder_load = pa_aac_decoder_load, + .init = pa_aac_decoder_init, + .update_user_config = NULL, + .config_transport=pa_aac_config_transport, + .get_block_size=pa_aac_get_read_block_size, + .setup_stream = pa_aac_setup_stream, + .decode = pa_aac_decode, + .free = pa_aac_free +}; + +const pa_a2dp_codec_t pa_a2dp_aac = { + .name = "AAC", + .codec = A2DP_CODEC_MPEG24, + .vendor_codec = NULL, + .a2dp_sink = &pa_aac_sink, + .a2dp_source = &pa_aac_source, + .get_capabilities = pa_aac_get_capabilities, + .select_configuration = pa_aac_select_configuration, + .free_capabilities = pa_aac_free_capabilities, + .free_configuration = pa_aac_free_capabilities, + .validate_configuration = pa_aac_validate_configuration +}; diff --git a/src/modules/bluetooth/a2dp/a2dp_aptx.c b/src/modules/bluetooth/a2dp/a2dp_aptx.c new file mode 100644 index 000000000..6a08a5aad --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp_aptx.c @@ -0,0 +1,608 @@ + +#include +#include + +#include +#include + +#include + +#include "a2dp-api.h" + +#include "aptx_libs.c" + +#define streq(a, b) (!strcmp((a),(b))) + + +typedef struct aptx_info { + pa_a2dp_source_read_cb_t read_pcm; + pa_a2dp_source_read_buf_free_cb_t read_buf_free; + + bool is_a2dp_sink; + bool is_hd; + + size_t aptx_frame_size; + int nb_samples; + const AVCodec *av_codec; + AVCodecContext *av_codec_ctx; + int channel_mode; + uint16_t seq_num; + + + size_t block_size; + + +} aptx_info_t; + +static const AVCodec *av_codec_aptx_decoder = NULL; +static const AVCodec *av_codec_aptx_encoder = NULL; + +static const AVCodec *av_codec_aptx_hd_decoder = NULL; +static const AVCodec *av_codec_aptx_hd_encoder = NULL; + + +static bool pa_aptx_decoder_load() { + if(!aptx_libs_load()) + return false; + if (av_codec_aptx_decoder) + return true; + av_codec_aptx_decoder = avcodec_find_decoder_func(AV_CODEC_ID_APTX); + if (!av_codec_aptx_decoder) { + pa_log_debug("Cannot find APTX decoder in FFmpeg avcodec library"); + return false; + } + + return true; +} + +static bool pa_aptx_encoder_load() { + if(!aptx_libs_load()) + return false; + if (av_codec_aptx_encoder) + return true; + av_codec_aptx_encoder = avcodec_find_encoder_func(AV_CODEC_ID_APTX); + if (!av_codec_aptx_encoder) { + pa_log_debug("Cannot find APTX encoder in FFmpeg avcodec library"); + return false; + } + + return true; +} + +static bool pa_aptx_hd_decoder_load() { + if(!aptx_libs_load()) + return false; + if (av_codec_aptx_hd_decoder) + return true; + av_codec_aptx_hd_decoder = avcodec_find_decoder_func(AV_CODEC_ID_APTX_HD); + if (!av_codec_aptx_hd_decoder) { + pa_log_debug("Cannot find APTX HD decoder in FFmpeg avcodec library"); + return false; + } + + return true; +} + +static bool pa_aptx_hd_encoder_load() { + if(!aptx_libs_load()) + return false; + if (av_codec_aptx_hd_encoder) + return true; + av_codec_aptx_hd_encoder = avcodec_find_encoder_func(AV_CODEC_ID_APTX_HD); + if (!av_codec_aptx_hd_encoder) { + pa_log_debug("Cannot find APTX HD encoder in FFmpeg avcodec library"); + return false; + } + + return true; +} + +static bool _internal_pa_dual_decoder_init(bool is_hd, void **codec_data) { + aptx_info_t *info = pa_xmalloc0(sizeof(aptx_info_t)); + *codec_data = info; + info->is_hd = is_hd; + info->is_a2dp_sink = true; + info->aptx_frame_size = is_hd ? 6 : 4; + info->av_codec = is_hd ? av_codec_aptx_hd_decoder : av_codec_aptx_decoder; + return true; +} + +static bool +_internal_pa_dual_encoder_init(bool is_hd, pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, + void **codec_data) { + aptx_info_t *info = pa_xmalloc0(sizeof(aptx_info_t)); + *codec_data = info; + info->is_hd = is_hd; + info->is_a2dp_sink = false; + info->read_pcm = read_cb; + info->read_buf_free = free_cb; + info->aptx_frame_size = is_hd ? 6 : 4; + info->av_codec = is_hd ? av_codec_aptx_hd_encoder : av_codec_aptx_encoder; + return true; +} + +static int pa_dual_update_user_config(pa_proplist *user_config, void **codec_data) { + return 0; +} + +static size_t +pa_dual_decode(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *_decoded, + uint32_t *timestamp, void **codec_data) { + const struct rtp_header *header; + void *p; + int ret; + size_t i; + AVPacket *pkt; + size_t to_decode; + size_t total_written = 0; + AVFrame *av_frame = NULL; + aptx_info_t *aptx_info = *codec_data; + + + pa_assert(aptx_info); + pa_assert(aptx_info->av_codec); + pa_assert(aptx_info->av_codec_ctx); + + if (aptx_info->is_hd) { + header = read_buf; + + *timestamp = ntohl(header->timestamp); + + p = (uint8_t *) read_buf + sizeof(*header); + to_decode = read_buf_size - sizeof(*header); + } else { + *timestamp = (uint32_t) -1; + + p = (uint8_t *) read_buf; + to_decode = read_buf_size; + } + + av_frame = av_frame_alloc_func(); + pkt = av_packet_alloc_func(); + pkt->data = p; + pkt->size = (int) to_decode; + + + *_decoded = 0; + + ret = avcodec_send_packet_func(aptx_info->av_codec_ctx, pkt); + if (PA_UNLIKELY(ret < 0)) { + pa_log_debug("Error submitting the packet to the decoder"); + goto done; + } + ret = avcodec_receive_frame_func(aptx_info->av_codec_ctx, av_frame); + if (PA_UNLIKELY(ret < 0)) { + pa_log_debug("Error during decoding"); + goto done; + } + + *_decoded = aptx_info->aptx_frame_size * av_frame->nb_samples / 4; + + total_written = (size_t) av_frame->nb_samples * (4 * 2); + + pa_assert_fp(_decoded <= read_buf_size); + pa_assert_fp(total_written <= write_buf_size); + + for (i = 0; i < av_frame->nb_samples * sizeof(uint32_t); i += sizeof(uint32_t)) { + memcpy((uint8_t *) write_buf + i * 2, av_frame->data[0] + i, sizeof(uint32_t)); + memcpy((uint8_t *) write_buf + i * 2 + sizeof(uint32_t), av_frame->data[1] + i, sizeof(uint32_t)); + } + +done: + av_frame_free_func(&av_frame); + av_packet_free_func(&pkt); + return total_written; +} + +static size_t +pa_dual_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, + void **codec_data) { + struct rtp_header *header; + size_t nbytes; + void *d; + const void *p; + AVFrame *av_frame; + AVPacket *pkt; + aptx_info_t *aptx_info = *codec_data; + int ret; + size_t i; + + pa_assert(aptx_info); + pa_assert(aptx_info->av_codec); + pa_assert(aptx_info->av_codec_ctx); + + pa_assert_fp(aptx_info->av_codec_ctx->frame_size <= write_buf_size); + + aptx_info->read_pcm(&p, (size_t) aptx_info->block_size, read_cb_data); + + if (aptx_info->is_hd) { + header = write_buf; + memset(write_buf, 0, sizeof(*header)); + header->v = 2; + header->pt = 1; + header->sequence_number = htons(aptx_info->seq_num++); + header->timestamp = htonl(timestamp); + header->ssrc = htonl(1); + d = (uint8_t *) write_buf + sizeof(*header); + } else { + d = (uint8_t *) write_buf; + } + + av_frame = av_frame_alloc_func(); + av_frame->nb_samples = aptx_info->nb_samples; + av_frame->format = aptx_info->av_codec_ctx->sample_fmt; + av_frame->channel_layout = aptx_info->av_codec_ctx->channel_layout; + + pkt = av_packet_alloc_func(); + + pa_assert_se(av_frame_get_buffer_func(av_frame, 0) >= 0); + pa_assert_se(av_frame_make_writable_func(av_frame) >= 0); + + + for (i = 0; i < av_frame->nb_samples * sizeof(uint32_t); i += sizeof(uint32_t)) { + memcpy(av_frame->data[0] + i, (uint8_t *) p + i * 2, sizeof(uint32_t)); + memcpy(av_frame->data[1] + i, (uint8_t *) p + i * 2 + sizeof(uint32_t), sizeof(uint32_t)); + } + *_encoded = 0; + + ret = avcodec_send_frame_func(aptx_info->av_codec_ctx, av_frame); + + if (PA_UNLIKELY(ret < 0)) { + fprintf(stderr, "Error sending the frame to the encoder\n"); + nbytes = 0; + goto done; + } + + ret = avcodec_receive_packet_func(aptx_info->av_codec_ctx, pkt); + + if (PA_UNLIKELY(ret != 0)) { + fprintf(stderr, "Error receiving the packet from the encoder\n"); + nbytes = 0; + goto done; + } + + memcpy(d, pkt->data, (size_t) pkt->size); + d = (uint8_t *) d + pkt->size; + + nbytes = (uint8_t *) d - (uint8_t *) write_buf; + *_encoded += aptx_info->block_size; + +done: + av_frame_free_func(&av_frame); + av_packet_free_func(&pkt); + aptx_info->read_buf_free(&p, read_cb_data); + return nbytes; +} + +static void +pa_dual_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data) { + aptx_info_t *aptx_info = *codec_data; + a2dp_aptx_t *config = (a2dp_aptx_t *) configuration; + AVCodecContext *aptx_ctx; + pa_assert(aptx_info); + pa_assert(aptx_info->av_codec); + pa_assert_se(configuration_size == (aptx_info->is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t))); + + if (aptx_info->av_codec_ctx) + avcodec_free_context_func(&aptx_info->av_codec_ctx); + + aptx_info->av_codec_ctx = avcodec_alloc_context3_func(aptx_info->av_codec); + aptx_ctx = aptx_info->av_codec_ctx; + + aptx_ctx->sample_fmt = AV_SAMPLE_FMT_S32P; + sample_spec->format = PA_SAMPLE_S32LE; + + switch (config->frequency) { + case APTX_SAMPLING_FREQ_16000: + aptx_ctx->sample_rate = 16000; + aptx_ctx->bit_rate = 16000; + sample_spec->rate = 16000U; + break; + case APTX_SAMPLING_FREQ_32000: + aptx_ctx->sample_rate = 32000; + aptx_ctx->bit_rate = 32000; + sample_spec->rate = 32000U; + break; + case APTX_SAMPLING_FREQ_44100: + aptx_ctx->sample_rate = 44100; + aptx_ctx->bit_rate = 44100; + sample_spec->rate = 44100U; + break; + case APTX_SAMPLING_FREQ_48000: + aptx_ctx->sample_rate = 48000; + aptx_ctx->bit_rate = 48000; + sample_spec->rate = 48000U; + break; + default: + pa_assert_not_reached(); + } + + switch (config->channel_mode) { + case APTX_CHANNEL_MODE_STEREO: + aptx_ctx->channel_layout = AV_CH_LAYOUT_STEREO; + aptx_ctx->channels = 2; + sample_spec->channels = 2; + break; + default: + pa_assert_not_reached(); + } + + pa_assert_se(avcodec_open2_func(aptx_info->av_codec_ctx, aptx_info->av_codec, NULL) == 0); + +}; + +static void pa_dual_get_read_block_size(size_t read_link_mtu, size_t *read_block_size, void **codec_data) { + aptx_info_t *aptx_info = *codec_data; + size_t aptx_frame_size = aptx_info->aptx_frame_size; + size_t rtp_use_size = aptx_info->is_hd ? sizeof(struct rtp_header) : 0; + pa_assert(aptx_info); + + /* +  * PCM 32-bit, 2 channel (4 bytes * 2) +  * PCM frames/APTX frames == 4 +  * */ + *read_block_size = (read_link_mtu - rtp_use_size) / aptx_frame_size * (4 * 2) * 4; + aptx_info->block_size = *read_block_size; +}; + +static void pa_dual_get_write_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { + aptx_info_t *aptx_info = *codec_data; + size_t aptx_frame_size = aptx_info->aptx_frame_size; + size_t rtp_use_size = aptx_info->is_hd ? sizeof(struct rtp_header) : 0; + pa_assert(aptx_info); + + /* + * PCM 32-bit, 2 channel (4 bytes * 2) + * PCM frames/APTX frames == 4 + * */ + *write_block_size = (write_link_mtu - rtp_use_size) / aptx_frame_size * (4 * 2) * 4; + aptx_info->block_size = *write_block_size; +}; + + +static void pa_dual_setup_stream(void **codec_data) { + aptx_info_t *aptx_info = *codec_data; + pa_assert(aptx_info); + aptx_info->nb_samples = (int) (aptx_info->block_size / (4 * 2)); + aptx_info->av_codec_ctx->frame_size = (int) (aptx_info->aptx_frame_size * aptx_info->nb_samples / 4); +}; + +static void pa_dual_free(void **codec_data) { + aptx_info_t *aptx_info = *codec_data; + if (!aptx_info) + return; + if (aptx_info->av_codec_ctx) + avcodec_free_context_func(&aptx_info->av_codec_ctx); + pa_xfree(aptx_info); + *codec_data = NULL; + +}; + +static size_t _internal_pa_dual_get_capabilities(bool is_hd, void **_capabilities) { + + const size_t cap_size = is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t); + a2dp_aptx_t *capabilities = (a2dp_aptx_t *) pa_xmalloc0(cap_size); + + if (is_hd) { + capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID); + } else { + capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID); + } + + capabilities->channel_mode = APTX_CHANNEL_MODE_STEREO; + capabilities->frequency = APTX_SAMPLING_FREQ_16000 | APTX_SAMPLING_FREQ_32000 | APTX_SAMPLING_FREQ_44100 | + APTX_SAMPLING_FREQ_48000; + *_capabilities = capabilities; + + return cap_size; +}; + + +static size_t +_internal_pa_dual_select_configuration(bool is_hd, const pa_sample_spec default_sample_spec, + const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + a2dp_aptx_t *cap; + a2dp_aptx_t *config; + const size_t cap_size = is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t); + pa_a2dp_freq_cap_t aptx_freq_cap, aptx_freq_table[] = { + {16000U, APTX_SAMPLING_FREQ_16000}, + {32000U, APTX_SAMPLING_FREQ_32000}, + {44100U, APTX_SAMPLING_FREQ_44100}, + {48000U, APTX_SAMPLING_FREQ_48000} + }; + + cap = (a2dp_aptx_t *) supported_capabilities; + + if (capabilities_size != cap_size) + return 0; + + config = (a2dp_aptx_t *) pa_xmalloc0(cap_size); + + if (is_hd) { + config->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID); + } else { + config->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID); + } + + if (!pa_a2dp_select_cap_frequency(cap->frequency, default_sample_spec, aptx_freq_table, + PA_ELEMENTSOF(aptx_freq_table), &aptx_freq_cap)) + return 0; + + config->frequency = (uint8_t) aptx_freq_cap.cap; + + if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO) + config->channel_mode = APTX_CHANNEL_MODE_STEREO; + else { + pa_log_error("No supported channel modes"); + return 0; + } + + *configuration = config; + return cap_size; +}; + +static void pa_dual_free_capabilities(void **capabilities) { + if (!capabilities || !*capabilities) + return; + pa_xfree(*capabilities); + *capabilities = NULL; +} + +static bool _internal_pa_dual_validate_configuration(bool is_hd, const uint8_t *selected_configuration, + const size_t configuration_size) { + a2dp_aptx_t *c = (a2dp_aptx_t *) selected_configuration; + + if (configuration_size != (is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t))) { + pa_log_error("APTX configuration array of invalid size"); + return false; + } + + switch (c->frequency) { + case APTX_SAMPLING_FREQ_16000: + case APTX_SAMPLING_FREQ_32000: + case APTX_SAMPLING_FREQ_44100: + case APTX_SAMPLING_FREQ_48000: + break; + default: + pa_log_error("Invalid sampling frequency in APTX configuration"); + return false; + } + + switch (c->channel_mode) { + case APTX_CHANNEL_MODE_STEREO: + break; + default: + pa_log_error("Invalid channel mode in APTX Configuration"); + return false; + } + return true; +}; + + +static bool pa_aptx_decoder_init(void **codec_data) { + return _internal_pa_dual_decoder_init(false, codec_data); +} + +static bool pa_aptx_hd_decoder_init(void **codec_data) { + return _internal_pa_dual_decoder_init(true, codec_data); +} + +static bool +pa_aptx_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { + return _internal_pa_dual_encoder_init(false, read_cb, free_cb, codec_data); +} + +static bool +pa_aptx_hd_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, + void **codec_data) { + return _internal_pa_dual_encoder_init(true, read_cb, free_cb, codec_data); +} + + +static size_t pa_aptx_get_capabilities(void **_capabilities) { + return _internal_pa_dual_get_capabilities(false, _capabilities); +} + +static size_t pa_aptx_select_configuration(const pa_sample_spec default_sample_spec, + const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + return _internal_pa_dual_select_configuration(false, default_sample_spec, supported_capabilities, capabilities_size, + configuration); +} + +static bool pa_aptx_validate_configuration(const uint8_t *selected_configuration, + const size_t configuration_size) { + return _internal_pa_dual_validate_configuration(false, selected_configuration, configuration_size); +} + +static size_t pa_aptx_hd_get_capabilities(void **_capabilities) { + return _internal_pa_dual_get_capabilities(true, _capabilities); +} + +static size_t pa_aptx_hd_select_configuration(const pa_sample_spec default_sample_spec, + const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + return _internal_pa_dual_select_configuration(true, default_sample_spec, supported_capabilities, capabilities_size, + configuration); +} + +static bool pa_aptx_hd_validate_configuration(const uint8_t *selected_configuration, + const size_t configuration_size) { + return _internal_pa_dual_validate_configuration(true, selected_configuration, configuration_size); +} + +static pa_a2dp_source_t pa_aptx_source = { + .encoder_load = pa_aptx_encoder_load, + .init = pa_aptx_encoder_init, + .update_user_config = pa_dual_update_user_config, + .encode = pa_dual_encode, + .config_transport = pa_dual_config_transport, + .get_block_size = pa_dual_get_write_block_size, + .setup_stream = pa_dual_setup_stream, + .set_tx_length = NULL, + .decrease_quality = NULL, + .free = pa_dual_free +}; + +static pa_a2dp_sink_t pa_aptx_sink = { + .decoder_load = pa_aptx_decoder_load, + .init = pa_aptx_decoder_init, + .update_user_config = pa_dual_update_user_config, + .config_transport = pa_dual_config_transport, + .get_block_size = pa_dual_get_read_block_size, + .setup_stream = pa_dual_setup_stream, + .decode = pa_dual_decode, + .free = pa_dual_free +}; + +const pa_a2dp_codec_t pa_a2dp_aptx = { + .name = "aptX", + .codec = A2DP_CODEC_VENDOR, + .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID), + .a2dp_sink = &pa_aptx_sink, + .a2dp_source = &pa_aptx_source, + .get_capabilities = pa_aptx_get_capabilities, + .select_configuration = pa_aptx_select_configuration, + .free_capabilities = pa_dual_free_capabilities, + .free_configuration = pa_dual_free_capabilities, + .validate_configuration = pa_aptx_validate_configuration +}; + +static pa_a2dp_source_t pa_aptx_hd_source = { + .encoder_load = pa_aptx_hd_encoder_load, + .init = pa_aptx_hd_encoder_init, + .update_user_config = pa_dual_update_user_config, + .encode = pa_dual_encode, + .config_transport = pa_dual_config_transport, + .get_block_size = pa_dual_get_write_block_size, + .setup_stream = pa_dual_setup_stream, + .set_tx_length = NULL, + .decrease_quality = NULL, + .free = pa_dual_free +}; + +static pa_a2dp_sink_t pa_aptx_hd_sink = { + .decoder_load = pa_aptx_hd_decoder_load, + .init = pa_aptx_hd_decoder_init, + .update_user_config = pa_dual_update_user_config, + .config_transport = pa_dual_config_transport, + .get_block_size = pa_dual_get_read_block_size, + .setup_stream = pa_dual_setup_stream, + .decode = pa_dual_decode, + .free = pa_dual_free +}; + +const pa_a2dp_codec_t pa_a2dp_aptx_hd = { + .name = "aptX_HD", + .codec = A2DP_CODEC_VENDOR, + .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID), + .a2dp_sink = &pa_aptx_hd_sink, + .a2dp_source = &pa_aptx_hd_source, + .get_capabilities = pa_aptx_hd_get_capabilities, + .select_configuration = pa_aptx_hd_select_configuration, + .free_capabilities = pa_dual_free_capabilities, + .free_configuration = pa_dual_free_capabilities, + .validate_configuration = pa_aptx_hd_validate_configuration +}; diff --git a/src/modules/bluetooth/a2dp/a2dp_ldac.c b/src/modules/bluetooth/a2dp/a2dp_ldac.c new file mode 100644 index 000000000..1e728a023 --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp_ldac.c @@ -0,0 +1,574 @@ + +#include +#include + +#ifdef HAVE_CONFIG_H + +#include + +#endif + +#include +#include + +#include "ldacBT.h" +#include "ldacBT_abr.h" + +#include "a2dp-api.h" + +#include "ldac_libs.c" + +#define streq(a, b) (!strcmp((a),(b))) + +#define AVDT_MEDIA_HDR_SIZE 12 + +#define LDAC_ABR_THRESHOLD_CRITICAL 6 +#define LDAC_ABR_THRESHOLD_DANGEROUSTREND 4 +#define LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ 2 + + +typedef struct ldac_info { + HANDLE_LDAC_BT hLdacBt; + HANDLE_LDAC_ABR hLdacAbr; + + pa_a2dp_source_read_cb_t read_pcm; + pa_a2dp_source_read_buf_free_cb_t read_buf_free; + + int eqmid; + bool enable_abr; + int channel_mode; + pa_sample_format_t force_pa_fmt; + LDACBT_SMPL_FMT_T pcm_fmt; + uint32_t pcm_frequency; + + uint16_t pcm_lsu; + size_t ldac_frame_size; + size_t pcm_read_size; + size_t q_write_block_size; + pa_sample_spec sample_spec; + + uint16_t seq_num; + uint32_t layer_specific; + uint32_t written; + size_t tx_length; + + size_t mtu; + +} ldac_info_t; + +static bool pa_ldac_encoder_load() { + return ldac_encoder_load(); +} + +static bool +pa_ldac_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { + ldac_info_t *info = pa_xmalloc0(sizeof(ldac_info_t)); + *codec_data = info; + info->read_pcm = read_cb; + info->read_buf_free = free_cb; + info->eqmid = LDACBT_EQMID_HQ; + if(ldac_abr_loaded) + info->enable_abr = true; + info->force_pa_fmt = PA_SAMPLE_INVALID; + return true; +} + +static int pa_ldac_update_user_config(pa_proplist *user_config, void **codec_data) { + ldac_info_t *i = *codec_data; + const char *ldac_eqmid_str, *ldac_fmt_str; + int ret = 0; + ldac_eqmid_str = pa_proplist_gets(user_config, "ldac_eqmid"); + ldac_fmt_str = pa_proplist_gets(user_config, "ldac_fmt"); + + pa_log_debug("LDAC ABR library loaded: %s",ldac_abr_loaded?"true":"false"); + + if (ldac_eqmid_str) { + if (streq(ldac_eqmid_str, "hq")) { + i->eqmid = LDACBT_EQMID_HQ; + i->enable_abr = false; + ret++; + } else if (streq(ldac_eqmid_str, "sq")) { + i->eqmid = LDACBT_EQMID_SQ; + i->enable_abr = false; + ret++; + } else if (streq(ldac_eqmid_str, "mq")) { + i->eqmid = LDACBT_EQMID_MQ; + i->enable_abr = false; + ret++; + } else if (streq(ldac_eqmid_str, "auto") || + streq(ldac_eqmid_str, "abr")) { + i->eqmid = LDACBT_EQMID_HQ; + if(ldac_abr_loaded) + i->enable_abr = true; + ret++; + } else { + pa_log("ldac_eqmid parameter must be either hq, sq, mq, or auto/abr (found %s)", ldac_eqmid_str); + } + } + + if (ldac_fmt_str) { + if (streq(ldac_fmt_str, "s16")) { + i->force_pa_fmt = PA_SAMPLE_S16LE; + ret++; + } else if (streq(ldac_fmt_str, "s24")) { + i->force_pa_fmt = PA_SAMPLE_S24LE; + ret++; + } else if (streq(ldac_fmt_str, "s32")) { + i->force_pa_fmt = PA_SAMPLE_S32LE; + ret++; + } else if (streq(ldac_fmt_str, "f32")) { + i->force_pa_fmt = PA_SAMPLE_FLOAT32LE; + ret++; + } else if (streq(ldac_fmt_str, "auto")) { + i->force_pa_fmt = PA_SAMPLE_INVALID; + ret++; + } else { + pa_log("ldac_fmt parameter must be either s16, s24, s32, f32 or auto (found %s)", ldac_fmt_str); + } + } + + return ret; +} + +static size_t +pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, + void **codec_data) { + struct rtp_header *header; + struct rtp_payload *payload; + size_t nbytes; + void *d; + const void *p; + size_t to_write, to_encode, ldac_enc_read; + unsigned frame_count; + ldac_info_t *ldac_info = *codec_data; + pa_assert(ldac_info); + pa_assert(ldac_info->hLdacBt); + + + if (ldac_info->hLdacAbr && ldac_info->enable_abr) { + ldac_ABR_Proc_func(ldac_info->hLdacBt, ldac_info->hLdacAbr, + (unsigned int) (ldac_info->tx_length / ldac_info->q_write_block_size), + (unsigned int) ldac_info->enable_abr); + } + + + ldac_enc_read = (pa_frame_size(&ldac_info->sample_spec) * LDACBT_ENC_LSU); + + header = write_buf; + payload = (struct rtp_payload *) ((uint8_t *) write_buf + sizeof(*header)); + + frame_count = 0; + + /* MAX pcm size for 1 LDAC packet (LDAC MQ) */ + to_encode = (ldac_info->mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / 110 * ldac_info->pcm_read_size; + + d = (uint8_t *) write_buf + sizeof(*header) + sizeof(*payload); + to_write = write_buf_size - sizeof(*header) - sizeof(*payload); + + *_encoded = 0; + while (PA_LIKELY(to_encode > 0 && to_write > 0 && frame_count == 0)) { + int written; + int encoded; + int ldac_frame_num; + int ret_code; + ldac_info->read_pcm(&p, ldac_enc_read, read_cb_data); + + ret_code = ldacBT_encode_func(ldac_info->hLdacBt, (void *) p, &encoded, (uint8_t *) d, &written, &ldac_frame_num); + + ldac_info->read_buf_free(&p, read_cb_data); + + if (PA_UNLIKELY(ret_code < 0)) { + int err; + pa_log_error("LDAC encoding error, written:%d encoded:%d ldac_frame_num:%d", written, encoded, + ldac_frame_num); + err = ldacBT_get_error_code_func(ldac_info->hLdacBt); + pa_log_error("LDACBT_API_ERR:%d LDACBT_HANDLE_ERR:%d LDACBT_BLOCK_ERR:%d", LDACBT_API_ERR(err), + LDACBT_HANDLE_ERR(err), LDACBT_BLOCK_ERR(err)); + *_encoded = 0; + return 0; + } + + pa_assert_fp(encoded == (int) ldac_enc_read); + pa_assert_fp(written <= (int) to_write); + + *_encoded += encoded; + to_encode -= encoded; + + d = (uint8_t *) d + written; + to_write -= written; + + frame_count += ldac_frame_num; + + } + + + PA_ONCE_BEGIN + { + const int v = ldacBT_get_version_func(); + pa_log_notice("Using LDAC library: version: %x.%02x.%02x", + v >> 16, + (v >> 8) & 0x0ff, + v & 0x0ff + ); + } + PA_ONCE_END; + + /* write it to the fifo */ + memset(write_buf, 0, sizeof(*header) + sizeof(*payload)); + header->v = 2; + header->pt = 1; + header->sequence_number = htons(ldac_info->seq_num++); + header->timestamp = htonl(timestamp); + header->ssrc = htonl(1); + payload->frame_count = frame_count; + ldac_info->layer_specific += frame_count; + + nbytes = (uint8_t *) d - (uint8_t *) write_buf; + + ldac_info->written += nbytes - sizeof(*header) - sizeof(*payload); + + return nbytes; +} + +static void +pa_ldac_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data) { + ldac_info_t *ldac_info = *codec_data; + a2dp_ldac_t *config = (a2dp_ldac_t *) configuration; + pa_sample_format_t fmt; + pa_assert(ldac_info); + pa_assert_se(configuration_size == sizeof(*config)); + + ldac_info->hLdacBt = NULL; + ldac_info->hLdacAbr = NULL; + + if (ldac_info->force_pa_fmt == PA_SAMPLE_INVALID) + fmt = default_sample_spec.format; + else + fmt = ldac_info->force_pa_fmt; + + switch (fmt) { + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + ldac_info->pcm_fmt = LDACBT_SMPL_FMT_F32; + sample_spec->format = PA_SAMPLE_FLOAT32LE; + break; + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S32BE: + ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S32; + sample_spec->format = PA_SAMPLE_S32LE; + break; + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S24; + sample_spec->format = PA_SAMPLE_S24LE; + break; + default: + ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S16; + sample_spec->format = PA_SAMPLE_S16LE; + } + + + switch (config->frequency) { + case LDACBT_SAMPLING_FREQ_044100: + ldac_info->pcm_frequency = 44100U; + sample_spec->rate = 44100U; + break; + case LDACBT_SAMPLING_FREQ_048000: + ldac_info->pcm_frequency = 48000U; + sample_spec->rate = 48000U; + break; + case LDACBT_SAMPLING_FREQ_088200: + ldac_info->pcm_frequency = 88200U; + sample_spec->rate = 88200U; + break; + case LDACBT_SAMPLING_FREQ_096000: + ldac_info->pcm_frequency = 96000U; + sample_spec->rate = 96000U; + break; + case LDACBT_SAMPLING_FREQ_176400: + ldac_info->pcm_frequency = 176400U; + sample_spec->rate = 176400U; + break; + case LDACBT_SAMPLING_FREQ_192000: + ldac_info->pcm_frequency = 192000U; + sample_spec->rate = 192000U; + break; + default: + pa_assert_not_reached(); + } + + switch (config->channel_mode) { + case LDACBT_CHANNEL_MODE_MONO: + ldac_info->channel_mode = LDACBT_CHANNEL_MODE_MONO; + sample_spec->channels = 1; + break; + case LDACBT_CHANNEL_MODE_DUAL_CHANNEL: + ldac_info->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; + sample_spec->channels = 2; + break; + case LDACBT_CHANNEL_MODE_STEREO: + ldac_info->channel_mode = LDACBT_CHANNEL_MODE_STEREO; + sample_spec->channels = 2; + break; + default: + pa_assert_not_reached(); + } + + switch (ldac_info->pcm_frequency) { + case 44100: + case 48000: + ldac_info->pcm_lsu = 128; + break; + case 88200: + case 96000: + ldac_info->pcm_lsu = 256; + break; + case 176400: + case 192000: + ldac_info->pcm_lsu = 512; + break; + default: + pa_assert_not_reached(); + } + + switch (ldac_info->eqmid) { + case LDACBT_EQMID_HQ: + ldac_info->ldac_frame_size = 330; + break; + case LDACBT_EQMID_SQ: + ldac_info->ldac_frame_size = 220; + break; + case LDACBT_EQMID_MQ: + ldac_info->ldac_frame_size = 110; + break; + default: + pa_assert_not_reached(); + } + + ldac_info->sample_spec = *sample_spec; + ldac_info->pcm_read_size = (ldac_info->pcm_lsu * pa_frame_size(&ldac_info->sample_spec)); + +}; + +static void pa_ldac_get_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { + ldac_info_t *ldac_info = *codec_data; + pa_assert(ldac_info); + + ldac_info->mtu = write_link_mtu; + + ldac_info->q_write_block_size = ((write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / ldac_info->ldac_frame_size * ldac_info->pcm_read_size); + *write_block_size = LDACBT_MAX_LSU * pa_frame_size(&ldac_info->sample_spec); +}; + + +static void pa_ldac_setup_stream(void **codec_data) { + int ret; + ldac_info_t *ldac_info = *codec_data; + pa_assert(ldac_info); + + ldac_info->layer_specific = 0; + ldac_info->written = 0; + if (ldac_info->hLdacBt) + ldacBT_free_handle_func(ldac_info->hLdacBt); + ldac_info->hLdacBt = ldacBT_get_handle_func(); + + + ret = ldacBT_init_handle_encode_func(ldac_info->hLdacBt, + (int) ldac_info->mtu + AVDT_MEDIA_HDR_SIZE, + ldac_info->eqmid, + ldac_info->channel_mode, + ldac_info->pcm_fmt, + ldac_info->pcm_frequency); + if (ret != 0) { + pa_log_warn("Failed to init ldacBT handle"); + goto fail; + } + + if (!ldac_abr_loaded) + return; + + if (ldac_info->hLdacAbr) + ldac_ABR_free_handle_func(ldac_info->hLdacAbr); + ldac_info->hLdacAbr = ldac_ABR_get_handle_func(); + + ret = ldac_ABR_Init_func(ldac_info->hLdacAbr, + (unsigned int) pa_bytes_to_usec(ldac_info->q_write_block_size, &ldac_info->sample_spec) / 1000); + if (ret != 0) { + pa_log_warn("Failed to init ldacBT_ABR handle"); + goto fail1; + } + + ldac_ABR_set_thresholds_func(ldac_info->hLdacAbr, LDAC_ABR_THRESHOLD_CRITICAL, + LDAC_ABR_THRESHOLD_DANGEROUSTREND, LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ); + return; + +fail: + ldacBT_free_handle_func(ldac_info->hLdacBt); + ldac_info->hLdacBt = NULL; + if (!ldac_abr_loaded) + return; +fail1: + ldac_ABR_free_handle_func(ldac_info->hLdacAbr); + ldac_info->hLdacAbr = NULL; + ldac_info->enable_abr = false; +}; + +static void pa_ldac_set_tx_length(size_t len, void **codec_data) { + ldac_info_t *ldac_info = *codec_data; + pa_assert(ldac_info); + ldac_info->tx_length = len; +}; + +static void pa_ldac_free(void **codec_data) { + ldac_info_t *ldac_info = *codec_data; + if (!ldac_info) + return; + + if (ldac_info->hLdacBt) + ldacBT_free_handle_func(ldac_info->hLdacBt); + + if (ldac_info->hLdacAbr && ldac_abr_loaded) + ldac_ABR_free_handle_func(ldac_info->hLdacAbr); + + pa_xfree(ldac_info); + *codec_data = NULL; + +}; + +static size_t pa_ldac_get_capabilities(void **_capabilities) { + a2dp_ldac_t *capabilities = pa_xmalloc0(sizeof(a2dp_ldac_t)); + + capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID); + capabilities->frequency = LDACBT_SAMPLING_FREQ_044100 | LDACBT_SAMPLING_FREQ_048000 | + LDACBT_SAMPLING_FREQ_088200 | LDACBT_SAMPLING_FREQ_096000; + capabilities->channel_mode = LDACBT_CHANNEL_MODE_MONO | LDACBT_CHANNEL_MODE_DUAL_CHANNEL | + LDACBT_CHANNEL_MODE_STEREO; + *_capabilities = capabilities; + + return sizeof(*capabilities); +}; + +static size_t +pa_ldac_select_configuration(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + a2dp_ldac_t *cap = (a2dp_ldac_t *) supported_capabilities; + a2dp_ldac_t *config = pa_xmalloc0(sizeof(a2dp_ldac_t)); + pa_a2dp_freq_cap_t ldac_freq_cap, ldac_freq_table[] = { + {44100U, LDACBT_SAMPLING_FREQ_044100}, + {48000U, LDACBT_SAMPLING_FREQ_048000}, + {88200U, LDACBT_SAMPLING_FREQ_088200}, + {96000U, LDACBT_SAMPLING_FREQ_096000} + }; + + if (capabilities_size != sizeof(a2dp_ldac_t)) + return 0; + + config->info = A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID); + + if (!pa_a2dp_select_cap_frequency(cap->frequency, default_sample_spec, ldac_freq_table, + PA_ELEMENTSOF(ldac_freq_table), &ldac_freq_cap)) + return 0; + + config->frequency = (uint8_t) ldac_freq_cap.cap; + + if (default_sample_spec.channels <= 1) { + if (cap->channel_mode & LDACBT_CHANNEL_MODE_MONO) + config->channel_mode = LDACBT_CHANNEL_MODE_MONO; + else if (cap->channel_mode & LDACBT_CHANNEL_MODE_STEREO) + config->channel_mode = LDACBT_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL) + config->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + + if (default_sample_spec.channels >= 2) { + if (cap->channel_mode & LDACBT_CHANNEL_MODE_STEREO) + config->channel_mode = LDACBT_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL) + config->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; + else if (cap->channel_mode & LDACBT_CHANNEL_MODE_MONO) + config->channel_mode = LDACBT_CHANNEL_MODE_MONO; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + *configuration = config; + return sizeof(*config); +}; + +static void pa_ldac_free_capabilities(void **capabilities) { + if (!capabilities || !*capabilities) + return; + pa_xfree(*capabilities); + *capabilities = NULL; +} + +static bool pa_ldac_validate_configuration(const uint8_t *selected_configuration, const size_t configuration_size) { + a2dp_ldac_t *c = (a2dp_ldac_t *) selected_configuration; + + if (configuration_size != sizeof(a2dp_ldac_t)) { + pa_log_error("LDAC configuration array of invalid size"); + return false; + } + + switch (c->frequency) { + case LDACBT_SAMPLING_FREQ_044100: + case LDACBT_SAMPLING_FREQ_048000: + case LDACBT_SAMPLING_FREQ_088200: + case LDACBT_SAMPLING_FREQ_096000: + case LDACBT_SAMPLING_FREQ_176400: + case LDACBT_SAMPLING_FREQ_192000: + break; + default: + pa_log_error("Invalid sampling frequency in LDAC configuration"); + return false; + } + + switch (c->channel_mode) { + case LDACBT_CHANNEL_MODE_STEREO: + case LDACBT_CHANNEL_MODE_DUAL_CHANNEL: + case LDACBT_CHANNEL_MODE_MONO: + break; + default: + pa_log_error("Invalid channel mode in LDAC Configuration"); + return false; + } + + return true; +}; + + +static pa_a2dp_source_t pa_ldac_source = { + .encoder_load = pa_ldac_encoder_load, + .init = pa_ldac_encoder_init, + .update_user_config = pa_ldac_update_user_config, + .encode = pa_ldac_encode, + .config_transport = pa_ldac_config_transport, + .get_block_size = pa_ldac_get_block_size, + .setup_stream = pa_ldac_setup_stream, + .set_tx_length = pa_ldac_set_tx_length, + .decrease_quality = NULL, + .free = pa_ldac_free +}; + +const pa_a2dp_codec_t pa_a2dp_ldac = { + .name = "LDAC", + .codec = A2DP_CODEC_VENDOR, + .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID), + .a2dp_sink = NULL, + .a2dp_source = &pa_ldac_source, + .get_capabilities = pa_ldac_get_capabilities, + .select_configuration = pa_ldac_select_configuration, + .free_capabilities = pa_ldac_free_capabilities, + .free_configuration = pa_ldac_free_capabilities, + .validate_configuration = pa_ldac_validate_configuration +}; diff --git a/src/modules/bluetooth/a2dp/a2dp_sbc.c b/src/modules/bluetooth/a2dp/a2dp_sbc.c new file mode 100644 index 000000000..d546d31ea --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp_sbc.c @@ -0,0 +1,658 @@ +#include +#include +#include + +#ifdef HAVE_CONFIG_H + +#include + +#endif + +#include +#include + +#include "a2dp-api.h" + +#define streq(a, b) (!strcmp((a),(b))) + +#define BITPOOL_DEC_LIMIT 32 +#define BITPOOL_DEC_STEP 5 + +typedef struct sbc_info { + pa_a2dp_source_read_cb_t read_pcm; + pa_a2dp_source_read_buf_free_cb_t read_buf_free; + + bool is_a2dp_sink; + + int channel_mode; + sbc_t sbc; /* Codec data */ + bool sbc_initialized; /* Keep track if the encoder is initialized */ + size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ + uint16_t seq_num; /* Cumulative packet sequence */ + uint8_t min_bitpool; + uint8_t max_bitpool; + + size_t read_block_size; + size_t write_block_size; + +} sbc_info_t; + +static bool pa_sbc_decoder_load() { + /* SBC libs dynamically linked */ + return true; +} + +static bool pa_sbc_encoder_load() { + /* SBC libs dynamically linked */ + return true; +} + +static bool +pa_sbc_decoder_init(void **codec_data) { + sbc_info_t *info = pa_xmalloc0(sizeof(sbc_info_t)); + *codec_data = info; + info->is_a2dp_sink = true; + return true; +} + +static bool +pa_sbc_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { + sbc_info_t *info = pa_xmalloc0(sizeof(sbc_info_t)); + *codec_data = info; + info->is_a2dp_sink = false; + info->read_pcm = read_cb; + info->read_buf_free = free_cb; + return true; +} + +static int pa_sbc_update_user_config(pa_proplist *user_config, void **codec_data) { + return 0; +} + +static size_t +pa_sbc_decode(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *_decoded, + uint32_t *timestamp, void **codec_data) { + const struct rtp_header *header; + const struct rtp_payload *payload; + const void *p; + void *d; + size_t to_write, to_decode; + size_t total_written = 0; + sbc_info_t *sbc_info = *codec_data; + pa_assert(sbc_info); + + header = read_buf; + payload = (struct rtp_payload *) ((uint8_t *) read_buf + sizeof(*header)); + + *timestamp = ntohl(header->timestamp); + + p = (uint8_t *) read_buf + sizeof(*header) + sizeof(*payload); + to_decode = read_buf_size - sizeof(*header) - sizeof(*payload); + + d = write_buf; + to_write = write_buf_size; + + *_decoded = 0; + while (PA_LIKELY(to_decode > 0)) { + size_t written; + ssize_t decoded; + + decoded = sbc_decode(&sbc_info->sbc, + p, to_decode, + d, to_write, + &written); + + if (PA_UNLIKELY(decoded <= 0)) { + pa_log_error("SBC decoding error (%li)", (long) decoded); + *_decoded = 0; + return 0; + } + + total_written += written; + + /* Reset frame length, it can be changed due to bitpool change */ + sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); + + pa_assert_fp((size_t) decoded <= to_decode); + pa_assert_fp((size_t) decoded == sbc_info->frame_length); + + pa_assert_fp((size_t) written == sbc_info->codesize); + + *_decoded += decoded; + p = (const uint8_t *) p + decoded; + to_decode -= decoded; + + d = (uint8_t *) d + written; + to_write -= written; + } + + return total_written; +} + +static size_t +pa_sbc_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, + void **codec_data) { + struct rtp_header *header; + struct rtp_payload *payload; + size_t nbytes; + void *d; + const void *p; + size_t to_write, to_encode; + unsigned frame_count; + sbc_info_t *sbc_info = *codec_data; + pa_assert(sbc_info); + + header = write_buf; + payload = (struct rtp_payload *) ((uint8_t *) write_buf + sizeof(*header)); + + frame_count = 0; + + /* Try to create a packet of the full MTU */ + + sbc_info->read_pcm(&p, (size_t) sbc_info->write_block_size, read_cb_data); + + to_encode = sbc_info->write_block_size; + + d = (uint8_t *) write_buf + sizeof(*header) + sizeof(*payload); + to_write = write_buf_size - sizeof(*header) - sizeof(*payload); + + *_encoded = 0; + while (PA_LIKELY(to_encode > 0 && to_write > 0)) { + ssize_t written; + ssize_t encoded; + + encoded = sbc_encode(&sbc_info->sbc, + p, to_encode, + d, to_write, + &written); + + if (PA_UNLIKELY(encoded <= 0)) { + pa_log_error("SBC encoding error (%li)", (long) encoded); + sbc_info->read_buf_free(&p, read_cb_data); + *_encoded = 0; + return 0; + } + + pa_assert_fp((size_t) encoded <= to_encode); + pa_assert_fp((size_t) encoded == sbc_info->codesize); + + pa_assert_fp((size_t) written <= to_write); + pa_assert_fp((size_t) written == sbc_info->frame_length); + + p = (const uint8_t *) p + encoded; + to_encode -= encoded; + *_encoded += encoded; + + d = (uint8_t *) d + written; + to_write -= written; + + frame_count++; + } + + sbc_info->read_buf_free(&p, read_cb_data); + + pa_assert(to_encode == 0); + + PA_ONCE_BEGIN + { + const char *impl = sbc_get_implementation_info(&sbc_info->sbc); + pa_log_debug("Using SBC encoder implementation: %s", impl ? impl : "NULL"); + } + PA_ONCE_END; + + /* write it to the fifo */ + memset(write_buf, 0, sizeof(*header) + sizeof(*payload)); + header->v = 2; + header->pt = 1; + header->sequence_number = htons(sbc_info->seq_num++); + header->timestamp = htonl(timestamp); + header->ssrc = htonl(1); + payload->frame_count = frame_count; + + nbytes = (uint8_t *) d - (uint8_t *) write_buf; + + return nbytes; +} + +static void +pa_sbc_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, + pa_sample_spec *sample_spec, void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + a2dp_sbc_t *config = (a2dp_sbc_t *) configuration; + + pa_assert(sbc_info); + pa_assert_se(configuration_size == sizeof(*config)); + + if (sbc_info->sbc_initialized) + sbc_reinit(&sbc_info->sbc, 0); + else + sbc_init(&sbc_info->sbc, 0); + sbc_info->sbc_initialized = true; + + sample_spec->format = PA_SAMPLE_S16LE; + + switch (config->frequency) { + case SBC_SAMPLING_FREQ_16000: + sbc_info->sbc.frequency = SBC_FREQ_16000; + sample_spec->rate = 16000U; + break; + case SBC_SAMPLING_FREQ_32000: + sbc_info->sbc.frequency = SBC_FREQ_32000; + sample_spec->rate = 32000U; + break; + case SBC_SAMPLING_FREQ_44100: + sbc_info->sbc.frequency = SBC_FREQ_44100; + sample_spec->rate = 44100U; + break; + case SBC_SAMPLING_FREQ_48000: + sbc_info->sbc.frequency = SBC_FREQ_48000; + sample_spec->rate = 48000U; + break; + default: + pa_assert_not_reached(); + } + + switch (config->channel_mode) { + case SBC_CHANNEL_MODE_MONO: + sbc_info->sbc.mode = SBC_MODE_MONO; + sample_spec->channels = 1; + break; + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; + sample_spec->channels = 2; + break; + case SBC_CHANNEL_MODE_STEREO: + sbc_info->sbc.mode = SBC_MODE_STEREO; + sample_spec->channels = 2; + break; + case SBC_CHANNEL_MODE_JOINT_STEREO: + sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; + sample_spec->channels = 2; + break; + default: + pa_assert_not_reached(); + } + + switch (config->allocation_method) { + case SBC_ALLOCATION_SNR: + sbc_info->sbc.allocation = SBC_AM_SNR; + break; + case SBC_ALLOCATION_LOUDNESS: + sbc_info->sbc.allocation = SBC_AM_LOUDNESS; + break; + default: + pa_assert_not_reached(); + } + + switch (config->subbands) { + case SBC_SUBBANDS_4: + sbc_info->sbc.subbands = SBC_SB_4; + break; + case SBC_SUBBANDS_8: + sbc_info->sbc.subbands = SBC_SB_8; + break; + default: + pa_assert_not_reached(); + } + + switch (config->block_length) { + case SBC_BLOCK_LENGTH_4: + sbc_info->sbc.blocks = SBC_BLK_4; + break; + case SBC_BLOCK_LENGTH_8: + sbc_info->sbc.blocks = SBC_BLK_8; + break; + case SBC_BLOCK_LENGTH_12: + sbc_info->sbc.blocks = SBC_BLK_12; + break; + case SBC_BLOCK_LENGTH_16: + sbc_info->sbc.blocks = SBC_BLK_16; + break; + default: + pa_assert_not_reached(); + } + + + sbc_info->min_bitpool = config->min_bitpool; + sbc_info->max_bitpool = config->max_bitpool; + + /* Set minimum bitpool for source to get the maximum possible block_size */ + sbc_info->sbc.bitpool = sbc_info->is_a2dp_sink ? sbc_info->min_bitpool : sbc_info->max_bitpool; + sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); + sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); + + pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", + sbc_info->sbc.allocation, sbc_info->sbc.subbands ? 8 : 4, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); +}; + +static void pa_sbc_get_read_block_size(size_t read_link_mtu, size_t *read_block_size, void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + pa_assert(sbc_info); + *read_block_size = + (read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / sbc_info->frame_length * sbc_info->codesize; + sbc_info->read_block_size = *read_block_size; +}; + +static void pa_sbc_get_write_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + pa_assert(sbc_info); + *write_block_size = + (write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) + / sbc_info->frame_length * sbc_info->codesize; + sbc_info->write_block_size = *write_block_size; +}; + +static void a2dp_set_bitpool(uint8_t bitpool, void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + + if (sbc_info->sbc.bitpool == bitpool) + return; + + if (bitpool > sbc_info->max_bitpool) + bitpool = sbc_info->max_bitpool; + else if (bitpool < sbc_info->min_bitpool) + bitpool = sbc_info->min_bitpool; + + sbc_info->sbc.bitpool = bitpool; + + sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); + sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); + + pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool); +} + +static void a2dp_reduce_bitpool(void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + uint8_t bitpool; + + /* Check if bitpool is already at its limit */ + if (sbc_info->sbc.bitpool <= BITPOOL_DEC_LIMIT) + return; + + bitpool = (uint8_t)(sbc_info->sbc.bitpool - BITPOOL_DEC_STEP); + + if (bitpool < BITPOOL_DEC_LIMIT) + bitpool = BITPOOL_DEC_LIMIT; + + a2dp_set_bitpool(bitpool, codec_data); +} + +static void pa_sbc_setup_stream(void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + pa_assert(sbc_info); + if (!sbc_info->is_a2dp_sink) + a2dp_set_bitpool(sbc_info->max_bitpool, codec_data); +}; + +static void pa_sbc_free(void **codec_data) { + sbc_info_t *sbc_info = *codec_data; + if (!sbc_info) + return; + + + pa_xfree(sbc_info); + *codec_data = NULL; + +}; + +static size_t pa_sbc_get_capabilities(void **_capabilities) { + a2dp_sbc_t *capabilities = pa_xmalloc0(sizeof(a2dp_sbc_t)); + + capabilities->channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_JOINT_STEREO; + capabilities->frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | + SBC_SAMPLING_FREQ_48000; + capabilities->allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS; + capabilities->subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; + capabilities->block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; + capabilities->min_bitpool = SBC_MIN_BITPOOL; + capabilities->max_bitpool = SBC_MAX_BITPOOL; + + *_capabilities = capabilities; + + return sizeof(*capabilities); +}; + +static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { + /* These bitpool values were chosen based on the A2DP spec recommendation */ + switch (freq) { + case SBC_SAMPLING_FREQ_16000: + case SBC_SAMPLING_FREQ_32000: + return 53; + + case SBC_SAMPLING_FREQ_44100: + + switch (mode) { + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + return 31; + + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: + return 53; + default: + break; + } + + pa_log_warn("Invalid channel mode %u", mode); + return 53; + + case SBC_SAMPLING_FREQ_48000: + + switch (mode) { + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + return 29; + + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: + return 51; + default: + break; + } + + pa_log_warn("Invalid channel mode %u", mode); + return 51; + default: + break; + } + + pa_log_warn("Invalid sampling freq %u", freq); + return 53; +} + +static size_t +pa_sbc_select_configuration(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, + const size_t capabilities_size, void **configuration) { + a2dp_sbc_t *cap = (a2dp_sbc_t *) supported_capabilities; + a2dp_sbc_t *config = pa_xmalloc0(sizeof(a2dp_sbc_t)); + pa_a2dp_freq_cap_t sbc_freq_cap, sbc_freq_table[] = { + {16000U, SBC_SAMPLING_FREQ_16000}, + {32000U, SBC_SAMPLING_FREQ_32000}, + {44100U, SBC_SAMPLING_FREQ_44100}, + {48000U, SBC_SAMPLING_FREQ_48000} + }; + + if (capabilities_size != sizeof(a2dp_sbc_t)) + return 0; + + if (!pa_a2dp_select_cap_frequency(cap->frequency, default_sample_spec, sbc_freq_table, + PA_ELEMENTSOF(sbc_freq_table), &sbc_freq_cap)) + return 0; + + config->frequency = (uint8_t) sbc_freq_cap.cap; + + if (default_sample_spec.channels <= 1) { + if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) + config->channel_mode = SBC_CHANNEL_MODE_MONO; + else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) + config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; + else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO) + config->channel_mode = SBC_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) + config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + + if (default_sample_spec.channels >= 2) { + if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) + config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; + else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO) + config->channel_mode = SBC_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) + config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; + else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) + config->channel_mode = SBC_CHANNEL_MODE_MONO; + else { + pa_log_error("No supported channel modes"); + return 0; + } + } + if (cap->block_length & SBC_BLOCK_LENGTH_16) + config->block_length = SBC_BLOCK_LENGTH_16; + else if (cap->block_length & SBC_BLOCK_LENGTH_12) + config->block_length = SBC_BLOCK_LENGTH_12; + else if (cap->block_length & SBC_BLOCK_LENGTH_8) + config->block_length = SBC_BLOCK_LENGTH_8; + else if (cap->block_length & SBC_BLOCK_LENGTH_4) + config->block_length = SBC_BLOCK_LENGTH_4; + else { + pa_log_error("No supported block lengths"); + return 0; + } + + if (cap->subbands & SBC_SUBBANDS_8) + config->subbands = SBC_SUBBANDS_8; + else if (cap->subbands & SBC_SUBBANDS_4) + config->subbands = SBC_SUBBANDS_4; + else { + pa_log_error("No supported subbands"); + return 0; + } + + if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS) + config->allocation_method = SBC_ALLOCATION_LOUDNESS; + else if (cap->allocation_method & SBC_ALLOCATION_SNR) + config->allocation_method = SBC_ALLOCATION_SNR; + + config->min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, cap->min_bitpool); + config->max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config->frequency, config->channel_mode), + cap->max_bitpool); + + if (config->min_bitpool > config->max_bitpool) + return 0; + + *configuration = config; + return sizeof(*config); +}; + +static void pa_sbc_free_capabilities(void **capabilities) { + if (!capabilities || !*capabilities) + return; + pa_xfree(*capabilities); + *capabilities = NULL; +} + +static bool pa_sbc_validate_configuration(const uint8_t *selected_configuration, const size_t configuration_size) { + a2dp_sbc_t *c = (a2dp_sbc_t *) selected_configuration; + + if (configuration_size != sizeof(a2dp_sbc_t)) { + pa_log_error("SBC configuration array of invalid size"); + return false; + } + + switch (c->frequency) { + case SBC_SAMPLING_FREQ_16000: + case SBC_SAMPLING_FREQ_32000: + case SBC_SAMPLING_FREQ_44100: + case SBC_SAMPLING_FREQ_48000: + break; + default: + pa_log_error("Invalid sampling frequency in SBC configuration"); + return false; + } + + switch (c->channel_mode) { + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: + break; + default: + pa_log_error("Invalid channel mode in SBC Configuration"); + return false; + } + + switch (c->allocation_method) { + case SBC_ALLOCATION_SNR: + case SBC_ALLOCATION_LOUDNESS: + break; + default: + pa_log_error("Invalid allocation method in SBC configuration"); + return false; + } + + switch (c->subbands) { + case SBC_SUBBANDS_4: + case SBC_SUBBANDS_8: + break; + default: + pa_log_error("Invalid SBC subbands in SBC configuration"); + return false; + } + + switch (c->block_length) { + case SBC_BLOCK_LENGTH_4: + case SBC_BLOCK_LENGTH_8: + case SBC_BLOCK_LENGTH_12: + case SBC_BLOCK_LENGTH_16: + break; + default: + pa_log_error("Invalid block length in configuration"); + return false; + } + + return true; +}; + + +static pa_a2dp_source_t pa_sbc_source = { + .encoder_load = pa_sbc_encoder_load, + .init = pa_sbc_encoder_init, + .update_user_config = pa_sbc_update_user_config, + .encode = pa_sbc_encode, + .config_transport=pa_sbc_config_transport, + .get_block_size=pa_sbc_get_write_block_size, + .setup_stream = pa_sbc_setup_stream, + .set_tx_length = NULL, + .decrease_quality = a2dp_reduce_bitpool, + .free = pa_sbc_free +}; + +static pa_a2dp_sink_t pa_sbc_sink = { + .decoder_load = pa_sbc_decoder_load, + .init = pa_sbc_decoder_init, + .update_user_config = pa_sbc_update_user_config, + .config_transport = pa_sbc_config_transport, + .get_block_size = pa_sbc_get_read_block_size, + .setup_stream = pa_sbc_setup_stream, + .decode = pa_sbc_decode, + .free = pa_sbc_free +}; + +const pa_a2dp_codec_t pa_a2dp_sbc = { + .name = "SBC", + .codec = A2DP_CODEC_SBC, + .vendor_codec = NULL, + .a2dp_sink = &pa_sbc_sink, + .a2dp_source = &pa_sbc_source, + .get_capabilities = pa_sbc_get_capabilities, + .select_configuration = pa_sbc_select_configuration, + .free_capabilities = pa_sbc_free_capabilities, + .free_configuration = pa_sbc_free_capabilities, + .validate_configuration = pa_sbc_validate_configuration +}; diff --git a/src/modules/bluetooth/a2dp/a2dp_util.c b/src/modules/bluetooth/a2dp/a2dp_util.c new file mode 100644 index 000000000..b07b47cb0 --- /dev/null +++ b/src/modules/bluetooth/a2dp/a2dp_util.c @@ -0,0 +1,386 @@ + +#include +#include + +#include "a2dp-api.h" + +#define streq(a, b) (!strcmp((a),(b))) + +#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" +#define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" + +#define A2DP_SBC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/SBC" +#define A2DP_SBC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/SBC" + +#define A2DP_AAC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/AAC" +#define A2DP_AAC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/AAC" + +#define A2DP_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR" +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR" + +#define A2DP_APTX_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/APTX" +#define A2DP_APTX_SNK_ENDPOINT A2DP_VENDOR_SNK_ENDPOINT "/APTX" + +#define A2DP_APTX_HD_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/APTXHD" +#define A2DP_APTX_HD_SNK_ENDPOINT A2DP_VENDOR_SNK_ENDPOINT "/APTXHD" + +#define A2DP_LDAC_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/LDAC" + +#define PA_A2DP_PRIORITY_DISABLE 0 +#define PA_A2DP_PRIORITY_MIN 1 + + +struct pa_a2dp_config { + int max_priority; + pa_hashmap *a2dp_sinks; + pa_hashmap *a2dp_sources; + pa_hashmap *active_index_priorities; + pa_hashmap *ordered_indices; +}; + +static unsigned int_hash_func(const void *p) { + return (unsigned) *((const int *) p); +} + +static int int_compare_func(const void *a, const void *b) { + const int x = *((const int *) a); + const int y = *((const int *) b); + return x < y ? -1 : (x > y ? 1 : 0); +}; + + +void pa_a2dp_init(pa_a2dp_config_t **a2dp_config) { + pa_a2dp_config_t *config; + pa_a2dp_codec_index_t codec_index = PA_A2DP_SINK_MIN; + const pa_a2dp_codec_t *a2dp_codec; + + config = pa_xmalloc(sizeof(pa_a2dp_config_t)); + *a2dp_config = config; + + config->a2dp_sinks = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); + config->a2dp_sources = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); + config->active_index_priorities = pa_hashmap_new_full(int_hash_func, int_compare_func, + pa_xfree, pa_xfree); + config->ordered_indices = NULL; + + config->max_priority = PA_A2DP_PRIORITY_MIN - 1; + while (++codec_index < PA_A2DP_SINK_MAX) { + pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); + if (!a2dp_codec || !a2dp_codec->a2dp_sink || !a2dp_codec->a2dp_sink->decoder_load()) + continue; + ++config->max_priority; + pa_hashmap_put(config->a2dp_sinks, pa_xmemdup(&config->max_priority, sizeof(int)), + pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t))); + pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), + pa_xmemdup(&config->max_priority, sizeof(int))); + a2dp_codec->a2dp_sink->priority = config->max_priority; + } + while (++codec_index < PA_A2DP_SOURCE_MAX) { + pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); + if (!a2dp_codec || !a2dp_codec->a2dp_source || !a2dp_codec->a2dp_source->encoder_load()) + continue; + ++config->max_priority; + pa_hashmap_put(config->a2dp_sources, pa_xmemdup(&config->max_priority, sizeof(int)), + pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t))); + pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), + pa_xmemdup(&config->max_priority, sizeof(int))); + a2dp_codec->a2dp_source->priority = config->max_priority; + } +}; + +void pa_a2dp_set_max_priority(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config) { + const pa_a2dp_codec_t *a2dp_codec; + pa_a2dp_config_t *config = *a2dp_config; + + pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); + + if (!a2dp_codec || !pa_hashmap_remove(config->active_index_priorities, &codec_index)) { + printf("no entry;"); + pa_log_debug("No such codec: %d", codec_index); + return; + } + + ++config->max_priority; + pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), + pa_xmemdup(&config->max_priority, sizeof(int))); + + if (pa_a2dp_codec_index_is_sink(codec_index)) + a2dp_codec->a2dp_sink->priority = config->max_priority; + else + a2dp_codec->a2dp_source->priority = config->max_priority; +}; + +void pa_a2dp_set_disable(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config) { + const pa_a2dp_codec_t *a2dp_codec; + pa_a2dp_config_t *config = *a2dp_config; + pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); + + if (!a2dp_codec || !pa_hashmap_remove(config->active_index_priorities, &codec_index)) { + pa_log_debug("No such codec: %d", codec_index); + return; + } + + if (pa_a2dp_codec_index_is_sink(codec_index)) + a2dp_codec->a2dp_sink->priority = PA_A2DP_PRIORITY_DISABLE; + else + a2dp_codec->a2dp_source->priority = PA_A2DP_PRIORITY_DISABLE; +}; + +void pa_a2dp_free(pa_a2dp_config_t **a2dp_config) { + pa_a2dp_config_t *config = *a2dp_config; + + if (!config) + return; + if (config->ordered_indices) + pa_hashmap_free(config->ordered_indices); + + if (config->active_index_priorities) + pa_hashmap_free(config->active_index_priorities); + + if (config->a2dp_sinks) + pa_hashmap_free(config->a2dp_sinks); + + if (config->a2dp_sources) + pa_hashmap_free(config->a2dp_sources); + + pa_xfree(config); + *a2dp_config = NULL; +} + + +void pa_a2dp_get_sink_indices(pa_hashmap **sink_indices, pa_a2dp_config_t **a2dp_config) { + pa_a2dp_config_t *config = *a2dp_config; + *sink_indices = config->a2dp_sinks; +}; + +void pa_a2dp_get_source_indices(pa_hashmap **source_indices, pa_a2dp_config_t **a2dp_config) { + pa_a2dp_config_t *config = *a2dp_config; + *source_indices = config->a2dp_sources; +}; + +void pa_a2dp_get_ordered_indices(pa_hashmap **ordered_indices, pa_a2dp_config_t **a2dp_config) { + void *state; + pa_a2dp_codec_index_t *index, *indices; + int *priority, i; + pa_a2dp_config_t *config = *a2dp_config; + + indices = pa_xmalloc(sizeof(pa_a2dp_codec_index_t) * (config->max_priority + 1)); + + for (i = 0; i <= config->max_priority; i++) + indices[i] = PA_A2DP_CODEC_INDEX_UNAVAILABLE; + + PA_HASHMAP_FOREACH_KV(index, priority, config->active_index_priorities, state) { + if (*priority <= 0) + continue; + indices[*priority] = *index; + } + + if (config->ordered_indices) + pa_hashmap_free(config->ordered_indices); + config->ordered_indices = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); + + for (i = config->max_priority; i >= PA_A2DP_PRIORITY_MIN; i--) { + if (indices[i] == PA_A2DP_CODEC_INDEX_UNAVAILABLE) + continue; + priority = pa_xmemdup(&i, sizeof(int)); + index = pa_xmemdup(indices + i, sizeof(pa_a2dp_codec_index_t)); + pa_hashmap_put(config->ordered_indices, priority, index); + } + + *ordered_indices = config->ordered_indices; +}; + + +void pa_a2dp_codec_index_to_endpoint(pa_a2dp_codec_index_t codec_index, const char **endpoint) { + switch (codec_index) { + case PA_A2DP_SINK_SBC: + *endpoint = A2DP_SBC_SNK_ENDPOINT; + break; + case PA_A2DP_SOURCE_SBC: + *endpoint = A2DP_SBC_SRC_ENDPOINT; + break; + case PA_A2DP_SINK_AAC: + *endpoint = A2DP_AAC_SNK_ENDPOINT; + break; + case PA_A2DP_SOURCE_AAC: + *endpoint = A2DP_AAC_SRC_ENDPOINT; + break; + case PA_A2DP_SINK_APTX: + *endpoint = A2DP_APTX_SNK_ENDPOINT; + break; + case PA_A2DP_SOURCE_APTX: + *endpoint = A2DP_APTX_SRC_ENDPOINT; + break; + case PA_A2DP_SINK_APTX_HD: + *endpoint = A2DP_APTX_HD_SNK_ENDPOINT; + break; + case PA_A2DP_SOURCE_APTX_HD: + *endpoint = A2DP_APTX_HD_SRC_ENDPOINT; + break; + case PA_A2DP_SOURCE_LDAC: + *endpoint = A2DP_LDAC_SRC_ENDPOINT; + break; + default: + *endpoint = NULL; + } +}; + +void pa_a2dp_endpoint_to_codec_index(const char *endpoint, pa_a2dp_codec_index_t *codec_index) { + if (streq(endpoint, A2DP_SBC_SNK_ENDPOINT)) + *codec_index = PA_A2DP_SINK_SBC; + else if (streq(endpoint, A2DP_SBC_SRC_ENDPOINT)) + *codec_index = PA_A2DP_SOURCE_SBC; + else if (streq(endpoint, A2DP_AAC_SNK_ENDPOINT)) + *codec_index = PA_A2DP_SINK_AAC; + else if (streq(endpoint, A2DP_AAC_SRC_ENDPOINT)) + *codec_index = PA_A2DP_SOURCE_AAC; + else if (streq(endpoint, A2DP_APTX_SNK_ENDPOINT)) + *codec_index = PA_A2DP_SINK_APTX; + else if (streq(endpoint, A2DP_APTX_SRC_ENDPOINT)) + *codec_index = PA_A2DP_SOURCE_APTX; + else if (streq(endpoint, A2DP_APTX_HD_SNK_ENDPOINT)) + *codec_index = PA_A2DP_SINK_APTX_HD; + else if (streq(endpoint, A2DP_APTX_HD_SRC_ENDPOINT)) + *codec_index = PA_A2DP_SOURCE_APTX_HD; + else if (streq(endpoint, A2DP_LDAC_SRC_ENDPOINT)) + *codec_index = PA_A2DP_SOURCE_LDAC; + else + *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; +}; + +void pa_a2dp_codec_index_to_a2dp_codec(pa_a2dp_codec_index_t codec_index, const pa_a2dp_codec_t **a2dp_codec) { + switch (codec_index) { + case PA_A2DP_SINK_SBC: + case PA_A2DP_SOURCE_SBC: + *a2dp_codec = &pa_a2dp_sbc; + break; + case PA_A2DP_SINK_AAC: + case PA_A2DP_SOURCE_AAC: + *a2dp_codec = &pa_a2dp_aac; + break; + case PA_A2DP_SINK_APTX: + case PA_A2DP_SOURCE_APTX: + *a2dp_codec = &pa_a2dp_aptx; + break; + case PA_A2DP_SINK_APTX_HD: + case PA_A2DP_SOURCE_APTX_HD: + *a2dp_codec = &pa_a2dp_aptx_hd; + break; + case PA_A2DP_SOURCE_LDAC: + *a2dp_codec = &pa_a2dp_ldac; + break; + default: + *a2dp_codec = NULL; + } +}; + +void pa_a2dp_a2dp_codec_to_codec_index(const pa_a2dp_codec_t *a2dp_codec, bool is_a2dp_sink, + pa_a2dp_codec_index_t *codec_index) { + if (!a2dp_codec) { + *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; + return; + } + switch (a2dp_codec->codec) { + case A2DP_CODEC_SBC: + *codec_index = is_a2dp_sink ? PA_A2DP_SINK_SBC : PA_A2DP_SOURCE_SBC; + return; + case A2DP_CODEC_MPEG24: + *codec_index = is_a2dp_sink ? PA_A2DP_SINK_AAC : PA_A2DP_SOURCE_AAC; + return; + case A2DP_CODEC_VENDOR: + if (!a2dp_codec->vendor_codec) { + *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; + return; + } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == APTX_VENDOR_ID && + A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == APTX_CODEC_ID) { + *codec_index = is_a2dp_sink ? PA_A2DP_SINK_APTX : PA_A2DP_SOURCE_APTX; + return; + } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == APTX_HD_VENDOR_ID && + A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == APTX_HD_CODEC_ID) { + *codec_index = is_a2dp_sink ? PA_A2DP_SINK_APTX_HD : PA_A2DP_SOURCE_APTX_HD; + return; + } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == LDAC_VENDOR_ID && + A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == LDAC_CODEC_ID) { + *codec_index = is_a2dp_sink ? PA_A2DP_CODEC_INDEX_UNAVAILABLE : PA_A2DP_SOURCE_LDAC; + return; + } + *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; + break; + default: + *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; + } +}; + +void +pa_a2dp_get_a2dp_codec(uint8_t codec, const a2dp_vendor_codec_t *vendor_codec, const pa_a2dp_codec_t **a2dp_codec) { + switch (codec) { + case A2DP_CODEC_SBC: + *a2dp_codec = &pa_a2dp_sbc; + return; + case A2DP_CODEC_MPEG24: + *a2dp_codec = &pa_a2dp_aac; + return; + case A2DP_CODEC_VENDOR: + if (!vendor_codec) { + *a2dp_codec = NULL; + pa_assert_not_reached(); + } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == APTX_VENDOR_ID && + A2DP_GET_CODEC_ID(*vendor_codec) == APTX_CODEC_ID) { + *a2dp_codec = &pa_a2dp_aptx; + return; + } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == APTX_HD_VENDOR_ID && + A2DP_GET_CODEC_ID(*vendor_codec) == APTX_HD_CODEC_ID) { + *a2dp_codec = &pa_a2dp_aptx_hd; + return; + } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == LDAC_VENDOR_ID && + A2DP_GET_CODEC_ID(*vendor_codec) == LDAC_CODEC_ID) { + *a2dp_codec = &pa_a2dp_ldac; + return; + } + *a2dp_codec = NULL; + break; + default: + *a2dp_codec = NULL; + } +}; + +bool pa_a2dp_codec_index_is_sink(pa_a2dp_codec_index_t codec_index) { + if (codec_index > PA_A2DP_SINK_MIN && codec_index < PA_A2DP_SINK_MAX) + return true; + return false; +}; + +bool pa_a2dp_codec_index_is_source(pa_a2dp_codec_index_t codec_index) { + if (codec_index > PA_A2DP_SOURCE_MIN && codec_index < PA_A2DP_SOURCE_MAX) + return true; + return false; +}; + +bool +pa_a2dp_select_cap_frequency(uint32_t freq_cap, pa_sample_spec default_sample_spec, + const pa_a2dp_freq_cap_t *freq_cap_table, + size_t n, pa_a2dp_freq_cap_t *result) { + int i; + /* Find the lowest freq that is at least as high as the requested sampling rate */ + for (i = 0; (unsigned) i < n; i++) + if (freq_cap_table[i].rate >= default_sample_spec.rate && (freq_cap & freq_cap_table[i].cap)) { + *result = freq_cap_table[i]; + break; + } + + if ((unsigned) i == n) { + for (--i; i >= 0; i--) { + if (freq_cap & freq_cap_table[i].cap) { + *result = freq_cap_table[i]; + break; + } + } + + if (i < 0) { + pa_log_error("Not suitable sample rate"); + return false; + } + } + pa_assert((unsigned) i < n); + return true; +}; diff --git a/src/modules/bluetooth/a2dp/aptx_libs.c b/src/modules/bluetooth/a2dp/aptx_libs.c new file mode 100644 index 000000000..69e905687 --- /dev/null +++ b/src/modules/bluetooth/a2dp/aptx_libs.c @@ -0,0 +1,206 @@ +#include +#include +#include + +#include +#include + +#ifdef HAVE_CONFIG_H + +#include + +#endif + +#include + +static const char *AVCODEC_LIB_NAME = "libavcodec.so"; + +static const char *avcodec_find_decoder_func_name = "avcodec_find_decoder"; +static const char *avcodec_find_encoder_func_name = "avcodec_find_encoder"; +static const char *av_packet_alloc_func_name = "av_packet_alloc"; +static const char *av_packet_free_func_name = "av_packet_free"; +static const char *avcodec_send_packet_func_name = "avcodec_send_packet"; +static const char *avcodec_receive_frame_func_name = "avcodec_receive_frame"; +static const char *avcodec_send_frame_func_name = "avcodec_send_frame"; +static const char *avcodec_receive_packet_func_name = "avcodec_receive_packet"; +static const char *avcodec_alloc_context3_func_name = "avcodec_alloc_context3"; +static const char *avcodec_free_context_func_name = "avcodec_free_context"; +static const char *avcodec_open2_func_name = "avcodec_open2"; + +typedef AVCodec *(*avcodec_find_decoder_func_t)(enum AVCodecID id); + +typedef AVCodec *(*avcodec_find_encoder_func_t)(enum AVCodecID id); + +typedef AVPacket *(*av_packet_alloc_func_t)(void); + +typedef void (*av_packet_free_func_t)(AVPacket **pkt); + +typedef int (*avcodec_send_packet_func_t)(AVCodecContext *avctx, const AVPacket *avpkt); + +typedef int (*avcodec_receive_frame_func_t)(AVCodecContext *avctx, AVFrame *frame); + +typedef int (*avcodec_send_frame_func_t)(AVCodecContext *avctx, const AVFrame *frame); + +typedef int (*avcodec_receive_packet_func_t)(AVCodecContext *avctx, AVPacket *avpkt); + +typedef AVCodecContext *(*avcodec_alloc_context3_func_t)(const AVCodec *codec); + +typedef void (*avcodec_free_context_func_t)(AVCodecContext **avctx); + +typedef int (*avcodec_open2_func_t)(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); + + +static avcodec_find_decoder_func_t avcodec_find_decoder_func; +static avcodec_find_encoder_func_t avcodec_find_encoder_func; +static av_packet_alloc_func_t av_packet_alloc_func; +static av_packet_free_func_t av_packet_free_func; +static avcodec_send_packet_func_t avcodec_send_packet_func; +static avcodec_receive_frame_func_t avcodec_receive_frame_func; +static avcodec_send_frame_func_t avcodec_send_frame_func; +static avcodec_receive_packet_func_t avcodec_receive_packet_func; +static avcodec_alloc_context3_func_t avcodec_alloc_context3_func; +static avcodec_free_context_func_t avcodec_free_context_func; +static avcodec_open2_func_t avcodec_open2_func; + +static const char *AVUTIL_LIB_NAME = "libavutil.so"; + +static const char *av_frame_alloc_func_name = "av_frame_alloc"; +static const char *av_frame_get_buffer_func_name = "av_frame_get_buffer"; +static const char *av_frame_make_writable_func_name = "av_frame_make_writable"; +static const char *av_frame_free_func_name = "av_frame_free"; + + +typedef AVFrame *(*av_frame_alloc_func_t)(void); + + +typedef int (*av_frame_get_buffer_func_t)(AVFrame *frame, int align); + +typedef int (*av_frame_make_writable_func_t)(AVFrame *frame); + +typedef void (*av_frame_free_func_t)(AVFrame **frame); + + +static av_frame_alloc_func_t av_frame_alloc_func; +static av_frame_get_buffer_func_t av_frame_get_buffer_func; +static av_frame_make_writable_func_t av_frame_make_writable_func; +static av_frame_free_func_t av_frame_free_func; + + +static void *libavcodec_h = NULL; + +static void *libavutil_h = NULL; + + +static void *load_func(void *lib_handle, const char *func_name) { + void *func = dlsym(lib_handle, func_name); + if (func == NULL) { + pa_log_error("No function %s in provide library. %s", func_name, dlerror()); + return NULL; + } + return func; +} + +static void libavcodec_unload() { + avcodec_find_decoder_func = NULL; + avcodec_find_encoder_func = NULL; + av_packet_alloc_func = NULL; + av_packet_free_func = NULL; + avcodec_send_packet_func = NULL; + avcodec_receive_frame_func = NULL; + avcodec_send_frame_func = NULL; + avcodec_receive_packet_func = NULL; + avcodec_alloc_context3_func = NULL; + avcodec_free_context_func = NULL; + avcodec_open2_func = NULL; + if (libavcodec_h) { + dlclose(libavcodec_h); + libavcodec_h = NULL; + } +} + +static bool libavcodec_load() { + if (libavcodec_h) + return true; + libavcodec_h = dlopen(AVCODEC_LIB_NAME, RTLD_NOW); + if (libavcodec_h == NULL) { + pa_log_error("Cannot open libavcodec library: %s. %s", AVCODEC_LIB_NAME, dlerror()); + return false; + } + avcodec_find_decoder_func = load_func(libavcodec_h, avcodec_find_decoder_func_name); + if (avcodec_find_decoder_func == NULL) + return false; + avcodec_find_encoder_func = load_func(libavcodec_h, avcodec_find_encoder_func_name); + if (avcodec_find_encoder_func == NULL) + return false; + av_packet_alloc_func = load_func(libavcodec_h, av_packet_alloc_func_name); + if (av_packet_alloc_func == NULL) + return false; + av_packet_free_func = load_func(libavcodec_h, av_packet_free_func_name); + if (av_packet_free_func == NULL) + return false; + avcodec_send_packet_func = load_func(libavcodec_h, avcodec_send_packet_func_name); + if (avcodec_send_packet_func == NULL) + return false; + avcodec_receive_frame_func = load_func(libavcodec_h, avcodec_receive_frame_func_name); + if (avcodec_receive_frame_func == NULL) + return false; + avcodec_send_frame_func = load_func(libavcodec_h, avcodec_send_frame_func_name); + if (avcodec_send_frame_func == NULL) + return false; + avcodec_receive_packet_func = load_func(libavcodec_h, avcodec_receive_packet_func_name); + if (avcodec_receive_packet_func == NULL) + return false; + avcodec_alloc_context3_func = load_func(libavcodec_h, avcodec_alloc_context3_func_name); + if (avcodec_alloc_context3_func == NULL) + return false; + avcodec_free_context_func = load_func(libavcodec_h, avcodec_free_context_func_name); + if (avcodec_free_context_func == NULL) + return false; + avcodec_open2_func = load_func(libavcodec_h, avcodec_open2_func_name); + if (avcodec_open2_func == NULL) + return false; + return true; +} + +static void libavutil_unload() { + av_frame_alloc_func = NULL; + av_frame_get_buffer_func = NULL; + av_frame_make_writable_func = NULL; + av_frame_free_func = NULL; + if (libavutil_h) { + dlclose(libavutil_h); + libavutil_h = NULL; + } +} + +static bool libavutil_load() { + if (libavutil_h) + return true; + libavutil_h = dlopen(AVUTIL_LIB_NAME, RTLD_NOW); + if (libavutil_h == NULL) { + pa_log_error("Cannot open libavutil library: %s. %s", AVUTIL_LIB_NAME, dlerror()); + return false; + } + av_frame_alloc_func = load_func(libavutil_h, av_frame_alloc_func_name); + if (av_frame_alloc_func == NULL) + return false; + av_frame_get_buffer_func = load_func(libavutil_h, av_frame_get_buffer_func_name); + if (av_frame_get_buffer_func == NULL) + return false; + av_frame_make_writable_func = load_func(libavutil_h, av_frame_make_writable_func_name); + if (av_frame_make_writable_func == NULL) + return false; + av_frame_free_func = load_func(libavutil_h, av_frame_free_func_name); + if (av_frame_free_func == NULL) + return false; + + return true; +} + +static bool aptx_libs_load() { + if (libavcodec_load() && libavutil_load()) + return true; + libavcodec_unload(); + libavutil_unload(); + return false; +} \ No newline at end of file diff --git a/src/modules/bluetooth/a2dp/ldacBT.h b/src/modules/bluetooth/a2dp/ldacBT.h new file mode 100644 index 000000000..f43820cc3 --- /dev/null +++ b/src/modules/bluetooth/a2dp/ldacBT.h @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2013 - 2016 Sony Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LDACBT_H_ +#define _LDACBT_H_ +#ifdef __cplusplus +extern "C" { +#endif +#ifndef LDACBT_API +#define LDACBT_API +#endif /* LDACBT_API */ + +/* This file contains the definitions, declarations and macros for an implimentation of + * LDAC encode processing. + * + * The basic flow of the encode processing is as follows: + * - The program creates an handle of an LDAC api using ldacBT_get_handle(). + * - The program initialize the handle for encode using ldacBT_init_handle_encode(). + * - The program calls ldacBT_encode() to encode data. + * - If the program demands to control the Encode Quality Mode Index, then one of the following + * should be called: + * - ldacBT_set_eqmid() + * - ldacBT_alter_eqmid() + * - The program finishes the encoding with passing NULL to input pcm buffer for ldacBT_encode(), + * which enables the encoder to encode remaining data in its input buffers. + * - The handle may be closed using ldacBT_close_handle() then used again, or released with + * ldacBT_free_handle(). + * - The rest of the set functions should be called only if it is needed by the client. + * + * + * Note for an implimentation + * - Error processing + * When continuous processing for next frame is performed after error detection, following + * processing must be carried out using C function provided in the library. + * - Release of internal variables in encode processing using ldacBT_close_handle(). + * - Allocation and initialization of internal variables in encode processing using + * ldacBT_init_handle_encode(). + * Note that the encoded output for a few frames will not be present just after error recovery. + * + * - Resuming of the encode processing from an interruption + * In case of resuming of the encode processing from interruption (such as changing + * configuration, seeking and playback), initialization of internal variables in encode + * processing must be carried out as error processing described above. + * Note that the encoded output for a few frames will not be present just after initialization + * as above. + * + * + * Glossary + * channel_config_index (cci) + * The channel setting information for ldaclib. + * See ldacBT_cm_to_cci() to get value from channel_mode. + * + * channel_mode (cm) + * The channel setting information for LDAC specification of Bluetooth A2DP. + * See ldacBT_cci_to_cm() to get value from channel_config_index. + * + * ldac_transport_frame + * See LDAC specification of bluetooth A2DP. + * + * Maximum Transmission Unit (MTU) + * The minimum MTU that a L2CAP implementation for LDAC shall support is 679 bytes, because LDAC + * is optimized with 2-DH5 packet as its target. + * + * frame + * An audio signal sequence representing a certain number of PCM audio signals. + * Encoding and decoding are processed frame by frame in LDAC. Number of samples in a frame is + * determined by sampling frequency as described below. + * + * Sampling frequency and frame sample. + * Supported sampling frequencies are 44.1, 48, 88.2 and 96 kHz. + * The relationship between sampling frequency and frame sample in LDAC are shown below. + * -------------------------------------------------------- + * | sampling frequency [kHz] | 44.1 | 48 | 88.2 | 96 | + * | frame sample [samples/channel] | 128 | 256 | + * -------------------------------------------------------- + * Though the frame size varies in LDAC core as described in the table, the number of samples in + * input PCM signal for encoding is fixed to 128 sample/channel, and it is not affected by + * sampling frequency. + */ +#define LDACBT_ENC_LSU 128 +#define LDACBT_MAX_LSU 512 + +/* channel_config_index. + * Supported value are below. + */ +#define LDAC_CCI_MONO 0 /* MONO */ +#define LDAC_CCI_DUAL_CHANNEL 1 /* DUAL CHANNEL */ +#define LDAC_CCI_STEREO 2 /* STEREO */ + +/* PCM format. + * Supported PCM format are shown below. + * - LDACBT_SMPL_FMT_S16 : signed 16bits little endian. + * - LDACBT_SMPL_FMT_S24 : signed 24bits little endian. + * - LDACBT_SMPL_FMT_S32 : signed 32bits little endian. + * - LDACBT_SMPL_FMT_F32 : single-precision floating point. + * The data sequency must be interleaved format by 1 sample. + * Ex) 2 channel audio, the data sequences are aligned as below. + * seq : |L[0]|R[0]|L[1]|R[1]|... + */ +typedef enum { + LDACBT_SMPL_FMT_S16 = 0x2, + LDACBT_SMPL_FMT_S24 = 0x3, + LDACBT_SMPL_FMT_S32 = 0x4, + LDACBT_SMPL_FMT_F32 = 0x5, +} LDACBT_SMPL_FMT_T; + +/* Encode Quality Mode Index. (EQMID) + * The configuration of encoding in LDAC will be coordinated by "Encode Quality Mode Index" + * parameter. Configurable values are shown below. + * - LDACBT_EQMID_HQ : Encode setting for High Quality. + * - LDACBT_EQMID_SQ : Encode setting for Standard Quality. + * - LDACBT_EQMID_MQ : Encode setting for Mobile use Quality. + * - LDACBT_EQMID_ABR: Reserved EQMID for ABR. The value shall be 0x7F. + */ +enum { + LDACBT_EQMID_HQ = 0, + LDACBT_EQMID_SQ, + LDACBT_EQMID_MQ, + LDACBT_EQMID_NUM, /* terminator */ + LDACBT_EQMID_ABR = 0x7F, +}; + +/* Bit rates + * Bit rates in each EQMID are depend on sampling frequency. + * In this API specification, these relations are shown below. + * ___________________________________________ + * | | Sampling Frequency[kHz] | + * | EQMID | 44.1, 88.2 | 48, 96 | + * +-----------------+------------+------------+ + * | LDACBT_EQMID_HQ | 909kbps | 990kbps | + * | LDACBT_EQMID_SQ | 606kbps | 660kbps | + * | LDACBT_EQMID_MQ | 303kbps | 330kbps | + * ------------------------------------------- + */ + +/* Maximum size of the "ldac_transport_frame" sequence at transportation. */ +#define LDACBT_MAX_NBYTES 1024 /* byte */ + +/* Maximum number of channel for LDAC */ +#define LDAC_PRCNCH 2 + +/* LDAC handle type */ +typedef struct _st_ldacbt_handle * HANDLE_LDAC_BT; + +/* Allocation of LDAC handle. + * Format + * HANDLE_LDAC_BT ldacBT_get_handle( void ); + * Arguments + * None. + * Return value + * HANDLE_LDAC_BT for success, NULL for failure. + */ +LDACBT_API HANDLE_LDAC_BT ldacBT_get_handle( void ); + +/* Release of LDAC handle. + * Format + * void ldacBT_free_handle( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * None. + */ +LDACBT_API void ldacBT_free_handle( HANDLE_LDAC_BT hLdacBt ); + +/* Closing of initialized LDAC handle. + * Closed handle can be initialized and used again. + * Format + * void ldacBT_close_handle( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * None. + */ +LDACBT_API void ldacBT_close_handle( HANDLE_LDAC_BT hLdacBt ); + +/* Acquisition of the library version. + * Format + * int ldacBT_get_version( void ); + * Arguments + * None. + * Return value + * int : version number. + * 23-16 bit : major version + * 15- 8 bit : minor version + * 7- 0 bit : branch version + * Ex) 0x00010203 -> version 1.02.03 + */ +LDACBT_API int ldacBT_get_version( void ); + +/* Acquisition of the sampling frequency in current configuration. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to + * calling this function. + * Format + * int ldacBT_get_sampling_freq( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * int : sampling frequency in current configuration. -1 for failure. + */ +LDACBT_API int ldacBT_get_sampling_freq( HANDLE_LDAC_BT hLdacBt ); + +/* Acquisition of the Bit-rate. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to + * calling this function. + * Format + * int ldacBT_get_bitrate( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * int : Bit-rate for previously processed ldac_transport_frame for success. -1 for failure. + */ +LDACBT_API int ldacBT_get_bitrate( HANDLE_LDAC_BT hLdacBt ); + +/* Initialization of a LDAC handle for encode processing. + * The LDAC handle must be allocated by API function ldacBT_get_handle() prior to calling this API. + * "mtu" value should be configured to MTU size of AVDTP Transport Channel, which is determined by + * SRC and SNK devices in Bluetooth transmission. + * "eqmid" is configured to desired value of "Encode Quality Mode Index". + * "cm" is configured to channel_mode in LDAC, which is determined by SRC and SNK devices in + * Bluetooth transmission. + * "fmt" is configured to input pcm audio format. + * When the configuration of "mtu", "cm", or "sf" changed, the re-initialization is required. + * + * Format + * int ldacBT_init_handle_encode( HANDLE_LDAC_BT hLdacBt, int mtu, int eqmid, int cm, + * LDACBT_SMPL_FMT_T fmt, int sf ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * mtu int MTU value. Unit:Byte. + * eqmid int Encode Quality Mode Index. + * cm int Information of the channel_mode. + * fmt LDACBT_SMPL_FMT_T Audio format type of input pcm. + * sf int Sampling frequency of input pcm. + * Return value + * int : 0 for success, -1 for failure. + */ +LDACBT_API int ldacBT_init_handle_encode( HANDLE_LDAC_BT hLdacBt, int mtu, int eqmid, int cm, + LDACBT_SMPL_FMT_T fmt, int sf ); + +/* Configuration of Encode Quality Mode Index. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to + * calling this function. + * The API function can be called at any time, after the completion of initializing. + * Format + * int ldacBT_set_eqmid( HANDLE_LDAC_BT hLdacBt, int eqmid ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * eqmid int Encode Quality Mode Index. + * Return value + * int : 0 for success, -1 for failure. + */ +LDACBT_API int ldacBT_set_eqmid( HANDLE_LDAC_BT hLdacBt, int eqmid ); + +/* Acquisition of prescribed Encode Quality Mode Index in current configuration. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to + * calling this function. + * Format + * int ldacBT_get_eqmid( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * int : Encode Quality Mode Index for success, -1 for failure. + */ +LDACBT_API int ldacBT_get_eqmid( HANDLE_LDAC_BT hLdacBt ); + +/* Changing of configuration for Encode Quality Mode Index by one step. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to + * calling this function. + * Configuralbe values for "priority" are shown below. + * - LDACBT_EQMID_INC_QUALITY : Adjustment for EQMID by one step for the direction of + * getting close to LDACBT_EQMID_HQ. + * - LDACBT_EQMID_INC_CONNECTION : Adjustment for EQMID by one step for the direction of + * getting away from LDACBT_EQMID_HQ. + * For restoring prescribed value for "Encode Quality Mode Index", it must be configured again by + * API function ldacBT_init_handle_encode() or ldacBT_set_qmode(). + * A transition to the state other than "Encode Quality Mode Index" mention before may be occurred + * caused by an adjustment using this API function. + * The API function can be called at any time, after the completion of initializing. + * Format + * int ldacBT_alter_eqmid_priority( HANDLE_LDAC_BT hLdacBt, int priority ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * priority int The direction of changing EQMID. + * Return value + * int : 0 for success, -1 for failure. + */ +#define LDACBT_EQMID_INC_QUALITY 1 +#define LDACBT_EQMID_INC_CONNECTION -1 +LDACBT_API int ldacBT_alter_eqmid_priority( HANDLE_LDAC_BT hLdacBt, int priority ); + + +/* LDAC encode processing. + * The LDAC handle must be initialized by API function ldacBT_init_handle_encode() prior to calling + * this API function. + * + * Number of samples in input PCM signal for encoding is fixed to 128 samples per channel, and it + * is not affected by sampling frequency. + * + * The region in input signal buffer without any PCM signal must be filled with zero, if the + * number of samples is less than 128 samples. + * + * The format of PCM signal is determined by "fmt" configured by API function + * ldacBT_init_handle_encode(). + * + * Total size of referenced PCM signal (in byte) will be set in "pcm_used" on return. The value of + * "Number of input samples * Number of channels * sizeof(PCM word length)" will be set in normal. + * + * Finalize processing of encode will be carried out with setting "p_pcm" as zero. + * + * + * An output data in "ldac_transport_frame" sequence will be set to "p_stream" after several frame + * processing. So the output is not necessarily present at each calling of this API function. + * + * The presence of the output can be verified by checking whether the value of "stream_wrote", + * representing the number of written bytes for "p_stream", is positive or not. + * + * In addition, encoded data size for output will be determined by the value of "mtu" configured + * by API function ldacBT_init_handle_encode(). + * + * The number of "ldac_transport_frame" corresponding to "ldac_transport_frame" sequence as output + * will be set to "frame_num". + * + * Format + * int ldacBT_encode( HANDLE_LDAC_BT hLdacBt, void *p_pcm, int *pcm_used, + * unsigned char *p_stream, int *stream_sz, int *frame_num ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * p_pcm void * Input PCM signal sequence + * pcm_used int * Data size of referenced PCM singnal. Unit:Byte. + * p_stream unsigned char * Output "ldac_transport_frame" sequence. + * stream_sz int * Size of output data. Unit:Byte. + * frame_num int * Number of output "ldac_transport_frame" + * Return value + * int : 0 for success, -1 for failure. + */ +LDACBT_API int ldacBT_encode( HANDLE_LDAC_BT hLdacBt, void *p_pcm, int *pcm_used, + unsigned char *p_stream, int *stream_sz, int *frame_num ); + +/* Acquisition of previously established error code. + * The LDAC handle must be allocated by API function ldacBT_get_handle() prior to calling this function. + * The details of error code are described below at the end of this header file. + * Tips for error code handling. + * The macro function LDACBT_FATAL() is useful to determine whether the error code is Fatal or not. + * Ex.) if( LDACBT_FATAL(err) ) // Fatal Error occurred. + * + * The macro function LDACBT_ERROR() is useful to determine whether the error occurred or not. + * Ex.) if( LDACBT_ERROR(err) ) // Error occurred. + * + * The macro function LDACBT_HANDLE_ERR() is useful to get the handle level error code. + * Ex.) err_handle_lv = LDACBT_HANDLE_ERR(err); + * + * The macro function LDACBT_BLOCK_ERR() is useful to get the block level error code. + * Ex.) err_block_lv = LDACBT_BLOCK_ERR(err); + * + * Format + * int ldacBT_get_error_code( HANDLE_LDAC_BT hLdacBt ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * Return value + * int : Error code. + */ +LDACBT_API int ldacBT_get_error_code( HANDLE_LDAC_BT hLdacBt ); + +/******************************************************************************* + Error Code +*******************************************************************************/ +#define LDACBT_ERR_NONE 0 + +/* Non Fatal Error ***********************************************************/ +#define LDACBT_ERR_NON_FATAL 1 + +/* Non Fatal Error (Block Level) *********************************************/ +#define LDACBT_ERR_BIT_ALLOCATION 5 + +/* Non Fatal Error (Handle Level) ********************************************/ +#define LDACBT_ERR_NOT_IMPLEMENTED 128 +#define LDACBT_ERR_NON_FATAL_ENCODE 132 + +/* Fatal Error ***************************************************************/ +#define LDACBT_ERR_FATAL 256 + +/* Fatal Error (Block Level) *************************************************/ +#define LDACBT_ERR_SYNTAX_BAND 260 +#define LDACBT_ERR_SYNTAX_GRAD_A 261 +#define LDACBT_ERR_SYNTAX_GRAD_B 262 +#define LDACBT_ERR_SYNTAX_GRAD_C 263 +#define LDACBT_ERR_SYNTAX_GRAD_D 264 +#define LDACBT_ERR_SYNTAX_GRAD_E 265 +#define LDACBT_ERR_SYNTAX_IDSF 266 +#define LDACBT_ERR_SYNTAX_SPEC 267 + +#define LDACBT_ERR_BIT_PACKING 280 + +#define LDACBT_ERR_ALLOC_MEMORY 300 + +/* Fatal Error (Handle Level) ************************************************/ +#define LDACBT_ERR_FATAL_HANDLE 512 + +#define LDACBT_ERR_ILL_SYNCWORD 516 +#define LDACBT_ERR_ILL_SMPL_FORMAT 517 +#define LDACBT_ERR_ILL_PARAM 518 + +#define LDACBT_ERR_ASSERT_SAMPLING_FREQ 530 +#define LDACBT_ERR_ASSERT_SUP_SAMPLING_FREQ 531 +#define LDACBT_ERR_CHECK_SAMPLING_FREQ 532 +#define LDACBT_ERR_ASSERT_CHANNEL_CONFIG 533 +#define LDACBT_ERR_CHECK_CHANNEL_CONFIG 534 +#define LDACBT_ERR_ASSERT_FRAME_LENGTH 535 +#define LDACBT_ERR_ASSERT_SUP_FRAME_LENGTH 536 +#define LDACBT_ERR_ASSERT_FRAME_STATUS 537 +#define LDACBT_ERR_ASSERT_NSHIFT 538 +#define LDACBT_ERR_ASSERT_CHANNEL_MODE 539 + +#define LDACBT_ERR_ENC_INIT_ALLOC 550 +#define LDACBT_ERR_ENC_ILL_GRADMODE 551 +#define LDACBT_ERR_ENC_ILL_GRADPAR_A 552 +#define LDACBT_ERR_ENC_ILL_GRADPAR_B 553 +#define LDACBT_ERR_ENC_ILL_GRADPAR_C 554 +#define LDACBT_ERR_ENC_ILL_GRADPAR_D 555 +#define LDACBT_ERR_ENC_ILL_NBANDS 556 +#define LDACBT_ERR_PACK_BLOCK_FAILED 557 + +#define LDACBT_ERR_DEC_INIT_ALLOC 570 +#define LDACBT_ERR_INPUT_BUFFER_SIZE 571 +#define LDACBT_ERR_UNPACK_BLOCK_FAILED 572 +#define LDACBT_ERR_UNPACK_BLOCK_ALIGN 573 +#define LDACBT_ERR_UNPACK_FRAME_ALIGN 574 +#define LDACBT_ERR_FRAME_LENGTH_OVER 575 +#define LDACBT_ERR_FRAME_ALIGN_OVER 576 + + +/* LDAC API for Encode */ +#define LDACBT_ERR_ALTER_EQMID_LIMITED 21 +#define LDACBT_ERR_HANDLE_NOT_INIT 1000 +#define LDACBT_ERR_ILL_EQMID 1024 +#define LDACBT_ERR_ILL_SAMPLING_FREQ 1025 +#define LDACBT_ERR_ILL_NUM_CHANNEL 1026 +#define LDACBT_ERR_ILL_MTU_SIZE 1027 +/* LDAC API for Decode */ +#define LDACBT_ERR_DEC_CONFIG_UPDATED 40 + + +/* Macro Functions for Error Code ********************************************/ +#define LDACBT_API_ERR(err) ((err >> 20) & 0x0FFF) +#define LDACBT_HANDLE_ERR(err) ((err >> 10) & 0x03FF) +#define LDACBT_BLOCK_ERR(err) ( err & 0x03FF) +#define LDACBT_ERROR(err) ((LDACBT_ERR_NON_FATAL) <= LDACBT_API_ERR(err) ? 1 : 0) +#define LDACBT_FATAL(err) ((LDACBT_ERR_FATAL) <= LDACBT_API_ERR(err) ? 1 : 0) + + + +/* Codec Specific Information Elements for LDAC + * (based on "LDAC Specification of Bluetooth A2DP Rev.2.0.1") + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * service_caps[4] | SONY ID | Octet0 + * service_caps[5] | SONY ID | Octet1 + * service_caps[6] | SONY ID | Octet2 + * service_caps[7] | SONY ID | Octet3 + * service_caps[8] | SONY Specific Codec ID | Octet4 + * service_caps[9] | SONY Specific Codec ID | Octet5 + * service_caps[A] | RFA | Sampling Frequency | Octet6 + * service_caps[B] | RFA | Channel Mode ID | Octet7 + */ +#define LDACBT_MEDIA_CODEC_SC_SZ (10+2) + +/* [Octet 0-3] Vendor ID for SONY */ +#define LDACBT_VENDOR_ID0 0x2D +#define LDACBT_VENDOR_ID1 0x01 +#define LDACBT_VENDOR_ID2 0x0 +#define LDACBT_VENDOR_ID3 0x0 + +/* [Octet 4-5] Vendor Specific A2DP Codec ID for LDAC */ +#define LDACBT_CODEC_ID0 0xAA +#define LDACBT_CODEC_ID1 0x00 + +/* [Octet 6] + * [b7,b6] : RFA + * Reserved for future additions. + * Bits with this designation shall be set to zero. + * Receivers shall ignore these bits. + * ----------------------------------------------------- + * [b5-b0] : Sampling frequency and its associated bit field in LDAC are shown below. + * | 5 | 4 | 3 | 2 | 1 | 0 | + * | o | | | | | | 44100 + * | | o | | | | | 48000 + * | | | o | | | | 88200 + * | | | | o | | | 96000 + * | | | | | o | | 176400 + * | | | | | | o | 192000 + * + */ +/* Support for 44.1kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_044100 0x20 +/* Support for 48kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_048000 0x10 +/* Support for 88.2kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_088200 0x08 +/* Support for 96kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_096000 0x04 +/* Support for 176.4kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_176400 0x02 +/* Support for 192kHz sampling frequency */ +#define LDACBT_SAMPLING_FREQ_192000 0x01 + +/* [Octet 7] + * [b7-b3] : RFA + * Reserved for future additions. + * Bits with this designation shall be set to zero. + * Receivers shall ignore these bits. + * ------------------------------------------------------ + * [b2-b0] : Channel mode and its associated bit field in LDAC are shown below. + * | 2 | 1 | 0 | + * | o | | | MONO + * | | o | | DUAL CHANNEL + * | | | o | STEREO + */ +/* Support for MONO */ +#define LDACBT_CHANNEL_MODE_MONO 0x04 +/* Support for DUAL CHANNEL */ +#define LDACBT_CHANNEL_MODE_DUAL_CHANNEL 0x02 +/* Support for STEREO */ +#define LDACBT_CHANNEL_MODE_STEREO 0x01 + +#ifdef __cplusplus +} +#endif +#endif /* _LDACBT_H_ */ diff --git a/src/modules/bluetooth/a2dp/ldacBT_abr.h b/src/modules/bluetooth/a2dp/ldacBT_abr.h new file mode 100644 index 000000000..76d7a7062 --- /dev/null +++ b/src/modules/bluetooth/a2dp/ldacBT_abr.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2014 - 2017 Sony Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LDACBT_ABR_H_ +#define _LDACBT_ABR_H_ + +/* This file contains the definitions, declarations and macros for an implementation of + * LDAC Adaptive Bit Rate (hereinafter ABR) processing. + * + * The basic flow of the ABR processing is as follows: + * - The program creates a handle of LDAC ABR API using ldac_ABR_get_handle(). + * - The program initializes the handle by setting the ldac_ABR_Proc() call interval to + * ldac_ABR_Init(). + * The interval shall be as short as possible at the timing without accumulation + * of packet in the buffer if propagation environment is fine. + * - The program reinitializes the handle by calling ldac_ABR_Init() again when the + * state of the TX queue changes greatly, such as clearing the queue. + * - If the program demands to control the thresholds, then ldac_ABR_set_thresholds() + * should be called. + * - The program sets flagEnable to "1" when allowing LDAC encode bitrate to be + * adjusted by ABR, and sets it to "0" if it is not allowed. + * - The program calls ldac_ABR_Proc() at the interval set to ldac_ABR_Init() even if + * flagEnable is "0". + * The program passes TxQueueDepth and flagEnable to ldac_ABR_Proc() at this call, + * LDAC encode bitrate is adjusted only when flagEnable is "1". + * Otherwise, the internal parameters are updated and analyzed then returned. + * The ABR handle adjusts eqmid based on TxQueueDepth which is passed from the program. + * The ABR handle calls LDAC encode API ldacBT_alter_eqmid_priority() to adjust eqmid. + * The ABR handle calls LDAC encode API ldacBT_get_eqmid() to get current eqmid. + * - The handle may be released with ldac_ABR_free_handle(). + * + * Notes on debugging LDAC ABR: + * The meaning of "works fine" is that the bit rate will be low in case of bad radio situation + * and high in case of good radio situation. + * + * The bit rate transition can be debug by checking logcat messages from LDAC ABR library which + * built with the following changes in Android.bp: + * - Adding "liblog" to shared_libs. + * - Adding "-DLOCAL_DEBUG" to cflags. + * The messages are formated as follows: + * [LDAC ABR] - abrQualityModeID : 0 -- eqmid : 0 -- TxQue : 0 + * where abrQualityModeID and eqmid related to the current bit rate and TxQue shows the depth + * of current Tx queue. + * The relationship between abrQualityModeID, eqmid and the bit rate is described in + * "ldacBT_abr.c". + * + * The bit rate transition can be estimated/debug by listening to the audio played on the SNK + * device. This method cannot use to confirm the details of the bit rate transition, but useful + * to know how LDAC ABR algorithm works in a field test without checking the log. + * To try this method, rebuilding of the "libldacBT_enc" library with the following change in + * Android.bp is required: + * - Adding "-DUSE_LDAC_ENC_SETTING_FOR_ABR_DEBUG" to cflags. + * By defining the above macro, the lower the bit rate, the greatly lower the bandwidth of the audio + * played on the SNK device. Therefore, the audio played on the SNK device will sounds like a + * low-pass filtered sound when the bit rate is low and will sounds as usual when the bit rate is + * enough high. It is recommend using sound such as white noise to hear those changes for the first + * time. + * + * IMPORTANT: + * These libraries modified as described above shall be used only to confirm the bit rate transition + * and SHALL NOT BE USED FOR FINAL PRODUCTS. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LDAC_ABR_API +#define LDAC_ABR_API +#endif /* LDAC_ABR_API */ + +#include "ldacBT.h" /* HANDLE_LDAC_BT */ + +/* LDAC ABR handle type*/ +typedef struct _ldacbt_abr_param * HANDLE_LDAC_ABR; + +/* Allocation of LDAC ABR handle. + * Format + * HANDLE_LDAC_ABR ldacBT_get_handle( void ); + * Arguments + * None. + * Return value + * HANDLE_LDAC_ABR for success, NULL for failure. + */ +LDAC_ABR_API HANDLE_LDAC_ABR ldac_ABR_get_handle(void); + +/* Release of LDAC ABR handle. + * Format + * void ldac_ABR_free_handle( HANDLE_LDAC_ABR ); + * Arguments + * hLdacAbr HANDLE_LDAC_ABR LDAC ABR handle. + * Return value + * None. + */ +LDAC_ABR_API void ldac_ABR_free_handle(HANDLE_LDAC_ABR hLdacAbr); + +/* Initialize LDAC ABR. + * Format + * int ldac_ABR_Init( HANDLE_LDAC_ABR, unsigned int ); + * Arguments + * hLdacAbr HANDLE_LDAC_ABR LDAC ABR handle. + * interval_ms unsigned int interval in ms for calling ldac_ABR_Proc(). + * interval of 1ms to 500ms is valid. + * Return value + * int: 0 for success, -1 for failure. + */ +LDAC_ABR_API int ldac_ABR_Init(HANDLE_LDAC_ABR hLdacAbr, unsigned int interval_ms); + +/* Setup thresholds for LDAC ABR. + * Format + * int ldac_ABR_set_thresholds( HANDLE_LDAC_ABR, unsigned int, unsigned int, unsigned int ); + * Arguments + * hLdacAbr HANDLE_LDAC_ABR LDAC ABR handle. + * thCritical unsigned int threshold for critical TxQueueDepth status. + * thDangerousTrend unsigned int threshold for dangerous trend of TxQueueDepth. + * thSafety4HQSQ unsigned int safety threshold for LDACBT_EQMID_HQ and + * LDACBT_EQMID_SQ. + * Return value + * int: 0 for success, -1 for failure. + * Remarks + * Those thresholds should be the number of packets stored in the TX queue and should be + * greater than 0. + * The thCritical and thDangerousTrend are used for all eqmid and thSafety4HQSQ is used + * only for LDACBT_EQMID_HQ and LDACBT_EQMID_SQ. Therefore, those thresholds must satisfy + * the following releationship: + * thCritical >= thDangerousTrend >= thSafety4HQSQ + */ +LDAC_ABR_API int ldac_ABR_set_thresholds(HANDLE_LDAC_ABR hLdacAbr, unsigned int thCritical, + unsigned int thDangerousTrend, unsigned int thSafety4HQSQ); + +/* LDAC ABR main process. + * Format + * int ldac_ABR_Proc( HANDLE_LDAC_BT, HANDLE_LDAC_ABR, unsigned int, unsigned int ); + * Arguments + * hLdacBt HANDLE_LDAC_BT LDAC handle. + * hLdacAbr HANDLE_LDAC_ABR LDAC ABR handle. + * TxQueueDepth unsigned int depth of TX queue. + * flagEnable unsigned int flag indicating whether ABR is allowed to adjust LDAC + * encode bitrate + * Return value + * int: updated Encode Quality Mode Index for success, -1 for failure. + */ +LDAC_ABR_API int ldac_ABR_Proc(HANDLE_LDAC_BT hLdacBt, HANDLE_LDAC_ABR hLdacAbr, + unsigned int TxQueueDepth, unsigned int flagEnable); +#ifdef __cplusplus +} +#endif + +#endif /* _LDACBT_ABR_H_ */ + diff --git a/src/modules/bluetooth/a2dp/ldac_libs.c b/src/modules/bluetooth/a2dp/ldac_libs.c new file mode 100644 index 000000000..4dbb913f1 --- /dev/null +++ b/src/modules/bluetooth/a2dp/ldac_libs.c @@ -0,0 +1,248 @@ + +#include +#include +#include +#include "ldacBT.h" +#include "ldacBT_abr.h" + +#ifdef HAVE_CONFIG_H + +#include + +#endif + +#include + +static const char *LDAC_ENCODER_LIB_NAME = "libldacBT_enc.so"; + +static const char *LDAC_GET_HANDLE_FUNC_NAME = "ldacBT_get_handle"; +static const char *LDAC_FREE_HANDLE_FUNC_NAME = "ldacBT_free_handle"; +static const char *LDAC_CLOSE_HANDLE_FUNC_NAME = "ldacBT_close_handle"; +static const char *LDAC_GET_VERSION_FUNC_NAME = "ldacBT_get_version"; +static const char *LDAC_GET_SAMPLING_FREQ_FUNC_NAME = "ldacBT_get_sampling_freq"; +static const char *LDAC_GET_BITRATE_FUNC_NAME = "ldacBT_get_bitrate"; +static const char *LDAC_INIT_HANDLE_ENCODE_FUNC_NAME = "ldacBT_init_handle_encode"; +static const char *LDAC_SET_EQMID_FUNC_NAME = "ldacBT_set_eqmid"; +static const char *LDAC_GET_EQMID_FUNC_NAME = "ldacBT_get_eqmid"; +static const char *LDAC_ALTER_EQMID_PRIORITY_FUNC_NAME = "ldacBT_alter_eqmid_priority"; +static const char *LDAC_ENCODE_FUNC_NAME = "ldacBT_encode"; +static const char *LDAC_GET_ERROR_CODE_FUNC_NAME = "ldacBT_get_error_code"; + + +static const char *LDAC_ABR_LIB_NAME = "libldacBT_abr.so"; + +static const char *LDAC_ABR_GET_HANDLE_FUNC_NAME = "ldac_ABR_get_handle"; +static const char *LDAC_ABR_FREE_HANDLE_FUNC_NAME = "ldac_ABR_free_handle"; +static const char *LDAC_ABR_INIT_FUNC_NAME = "ldac_ABR_Init"; +static const char *LDAC_ABR_SET_THRESHOLDS_FUNC_NAME = "ldac_ABR_set_thresholds"; +static const char *LDAC_ABR_PROC_FUNC_NAME = "ldac_ABR_Proc"; + + +typedef HANDLE_LDAC_BT (*ldacBT_get_handle_func_t)(void); + +typedef void (*ldacBT_free_handle_func_t)(HANDLE_LDAC_BT hLdacBt); + +typedef void (*ldacBT_close_handle_func_t)(HANDLE_LDAC_BT hLdacBt); + +typedef int (*ldacBT_get_version_func_t)(void); + +typedef int (*ldacBT_get_sampling_freq_func_t)(HANDLE_LDAC_BT hLdacBt); + +typedef int (*ldacBT_get_bitrate_func_t)(HANDLE_LDAC_BT hLdacBt); + +typedef int (*ldacBT_init_handle_encode_func_t)(HANDLE_LDAC_BT hLdacBt, int mtu, int eqmid, int cm, + LDACBT_SMPL_FMT_T fmt, int sf); + +typedef int (*ldacBT_set_eqmid_func_t)(HANDLE_LDAC_BT hLdacBt, int eqmid); + +typedef int (*ldacBT_get_eqmid_func_t)(HANDLE_LDAC_BT hLdacBt); + +typedef int (*ldacBT_alter_eqmid_priority_func_t)(HANDLE_LDAC_BT hLdacBt, int priority); + +typedef int (*ldacBT_encode_func_t)(HANDLE_LDAC_BT hLdacBt, void *p_pcm, int *pcm_used, + unsigned char *p_stream, int *stream_sz, int *frame_num); + +typedef int (*ldacBT_get_error_code_func_t)(HANDLE_LDAC_BT hLdacBt); + + +typedef HANDLE_LDAC_ABR (*ldac_ABR_get_handle_func_t)(void); + +typedef void (*ldac_ABR_free_handle_func_t)(HANDLE_LDAC_ABR hLdacAbr); + +typedef int (*ldac_ABR_Init_func_t)(HANDLE_LDAC_ABR hLdacAbr, unsigned int interval_ms); + +typedef int (*ldac_ABR_set_thresholds_func_t)(HANDLE_LDAC_ABR hLdacAbr, unsigned int thCritical, + unsigned int thDangerousTrend, unsigned int thSafety4HQSQ); + +typedef int (*ldac_ABR_Proc_func_t)(HANDLE_LDAC_BT hLdacBt, HANDLE_LDAC_ABR hLdacAbr, + unsigned int TxQueueDepth, unsigned int flagEnable); + +static ldacBT_get_handle_func_t ldacBT_get_handle_func; +static ldacBT_free_handle_func_t ldacBT_free_handle_func; +static ldacBT_close_handle_func_t ldacBT_close_handle_func; +static ldacBT_get_version_func_t ldacBT_get_version_func; +static ldacBT_get_sampling_freq_func_t ldacBT_get_sampling_freq_func; +static ldacBT_get_bitrate_func_t ldacBT_get_bitrate_func; +static ldacBT_init_handle_encode_func_t ldacBT_init_handle_encode_func; +static ldacBT_set_eqmid_func_t ldacBT_set_eqmid_func; +static ldacBT_get_eqmid_func_t ldacBT_get_eqmid_func; +static ldacBT_alter_eqmid_priority_func_t ldacBT_alter_eqmid_priority_func; +static ldacBT_encode_func_t ldacBT_encode_func; +static ldacBT_get_error_code_func_t ldacBT_get_error_code_func; + + +static ldac_ABR_get_handle_func_t ldac_ABR_get_handle_func; +static ldac_ABR_free_handle_func_t ldac_ABR_free_handle_func; +static ldac_ABR_Init_func_t ldac_ABR_Init_func; +static ldac_ABR_set_thresholds_func_t ldac_ABR_set_thresholds_func; +static ldac_ABR_Proc_func_t ldac_ABR_Proc_func; + +static void *ldac_encoder_lib_h = NULL; +static void *ldac_abr_lib_h = NULL; + +static bool ldac_abr_loaded = false; + + +static void *load_func(void *lib_handle, const char *func_name) { + void *func = dlsym(lib_handle, func_name); + if (func == NULL) { + pa_log_error("No function %s in provide library. %s", func_name, dlerror()); + return NULL; + } + return func; +} + +static bool ldac_abr_load() { + if (ldac_abr_lib_h) + return true; + ldac_abr_lib_h = dlopen(LDAC_ABR_LIB_NAME, RTLD_NOW); + if (ldac_abr_lib_h == NULL) { + pa_log_error("Cannot open LDAC abr library: %s. %s", LDAC_ABR_LIB_NAME, dlerror()); + return false; + } + + ldac_ABR_get_handle_func = (ldac_ABR_get_handle_func_t) load_func(ldac_abr_lib_h, + LDAC_ABR_GET_HANDLE_FUNC_NAME); + if (ldac_ABR_get_handle_func == NULL) + return false; + ldac_ABR_free_handle_func = (ldac_ABR_free_handle_func_t) load_func(ldac_abr_lib_h, + LDAC_ABR_FREE_HANDLE_FUNC_NAME); + if (ldac_ABR_free_handle_func == NULL) + return false; + ldac_ABR_Init_func = (ldac_ABR_Init_func_t) load_func(ldac_abr_lib_h, LDAC_ABR_INIT_FUNC_NAME); + if (ldac_ABR_Init_func == NULL) + return false; + ldac_ABR_set_thresholds_func = (ldac_ABR_set_thresholds_func_t) load_func(ldac_abr_lib_h, + LDAC_ABR_SET_THRESHOLDS_FUNC_NAME); + if (ldac_ABR_set_thresholds_func == NULL) + return false; + ldac_ABR_Proc_func = (ldac_ABR_Proc_func_t) load_func(ldac_abr_lib_h, LDAC_ABR_PROC_FUNC_NAME); + if (ldac_ABR_Proc_func == NULL) + return false; + return true; +} + +static void ldac_abr_unload() { + if (ldac_abr_lib_h != NULL) { + dlclose(ldac_abr_lib_h); + ldac_abr_lib_h = NULL; + } + ldac_ABR_get_handle_func = NULL; + ldac_ABR_free_handle_func = NULL; + ldac_ABR_Init_func = NULL; + ldac_ABR_set_thresholds_func = NULL; + ldac_ABR_Proc_func = NULL; +} + +static bool _ldac_encoder_load() { + if (ldac_encoder_lib_h) + return true; + ldac_encoder_lib_h = dlopen(LDAC_ENCODER_LIB_NAME, RTLD_NOW); + if (ldac_encoder_lib_h == NULL) { + pa_log_error("Cannot open LDAC encoder library: %s. %s", LDAC_ENCODER_LIB_NAME, dlerror()); + return false; + } + + ldacBT_get_handle_func = (ldacBT_get_handle_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_HANDLE_FUNC_NAME); + if (ldacBT_get_handle_func == NULL) + return false; + ldacBT_free_handle_func = (ldacBT_free_handle_func_t) load_func(ldac_encoder_lib_h, LDAC_FREE_HANDLE_FUNC_NAME); + if (ldacBT_free_handle_func == NULL) + return false; + ldacBT_close_handle_func = (ldacBT_close_handle_func_t) load_func(ldac_encoder_lib_h, LDAC_CLOSE_HANDLE_FUNC_NAME); + if (ldacBT_close_handle_func == NULL) + return false; + ldacBT_get_version_func = (ldacBT_get_version_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_VERSION_FUNC_NAME); + if (ldacBT_get_version_func == NULL) + return false; + ldacBT_get_sampling_freq_func = (ldacBT_get_sampling_freq_func_t) load_func(ldac_encoder_lib_h, + LDAC_GET_SAMPLING_FREQ_FUNC_NAME); + if (ldacBT_get_sampling_freq_func == NULL) + return false; + ldacBT_get_bitrate_func = (ldacBT_get_bitrate_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_BITRATE_FUNC_NAME); + if (ldacBT_get_bitrate_func == NULL) + return false; + ldacBT_init_handle_encode_func = (ldacBT_init_handle_encode_func_t) load_func(ldac_encoder_lib_h, + LDAC_INIT_HANDLE_ENCODE_FUNC_NAME); + if (ldacBT_init_handle_encode_func == NULL) + return false; + ldacBT_set_eqmid_func = (ldacBT_set_eqmid_func_t) load_func(ldac_encoder_lib_h, LDAC_SET_EQMID_FUNC_NAME); + if (ldacBT_set_eqmid_func == NULL) + return false; + ldacBT_get_eqmid_func = (ldacBT_get_eqmid_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_EQMID_FUNC_NAME); + if (ldacBT_get_eqmid_func == NULL) + return false; + ldacBT_alter_eqmid_priority_func = (ldacBT_alter_eqmid_priority_func_t) load_func(ldac_encoder_lib_h, + LDAC_ALTER_EQMID_PRIORITY_FUNC_NAME); + if (ldacBT_alter_eqmid_priority_func == NULL) + return false; + ldacBT_encode_func = (ldacBT_encode_func_t) load_func(ldac_encoder_lib_h, LDAC_ENCODE_FUNC_NAME); + if (ldacBT_encode_func == NULL) + return false; + ldacBT_get_error_code_func = (ldacBT_get_error_code_func_t) load_func(ldac_encoder_lib_h, + LDAC_GET_ERROR_CODE_FUNC_NAME); + if (ldacBT_get_error_code_func == NULL) + return false; + + if (!ldac_abr_load()) { + pa_log_debug("Cannot load the LDAC ABR library"); + ldac_abr_unload(); + ldac_abr_loaded = false; + } else + ldac_abr_loaded = true; + return true; +} + + +static void ldac_encoder_unload() { + if (ldac_encoder_lib_h != NULL) { + dlclose(ldac_encoder_lib_h); + ldac_encoder_lib_h = NULL; + } + ldacBT_get_handle_func = NULL; + ldacBT_free_handle_func = NULL; + ldacBT_close_handle_func = NULL; + ldacBT_get_version_func = NULL; + ldacBT_get_sampling_freq_func = NULL; + ldacBT_get_bitrate_func = NULL; + ldacBT_init_handle_encode_func = NULL; + ldacBT_set_eqmid_func = NULL; + ldacBT_get_eqmid_func = NULL; + ldacBT_alter_eqmid_priority_func = NULL; + ldacBT_encode_func = NULL; + ldacBT_get_error_code_func = NULL; + ldac_ABR_get_handle_func = NULL; + ldac_ABR_free_handle_func = NULL; + ldac_ABR_Init_func = NULL; + ldac_ABR_set_thresholds_func = NULL; + ldac_ABR_Proc_func = NULL; +} + +static bool ldac_encoder_load() { + if (!_ldac_encoder_load()) { + pa_log_debug("Cannot load the LDAC encoder library"); + ldac_encoder_unload(); + return false; + } + return true; +} diff --git a/src/modules/bluetooth/a2dp/rtp.h b/src/modules/bluetooth/a2dp/rtp.h new file mode 100644 index 000000000..20694c1e1 --- /dev/null +++ b/src/modules/bluetooth/a2dp/rtp.h @@ -0,0 +1,74 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + unsigned cc:4; + unsigned x:1; + unsigned p:1; + unsigned v:2; + + unsigned pt:7; + unsigned m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + unsigned frame_count:4; + unsigned rfa0:1; + unsigned is_last_fragment:1; + unsigned is_first_fragment:1; + unsigned is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + unsigned v:2; + unsigned p:1; + unsigned x:1; + unsigned cc:4; + + unsigned m:1; + unsigned pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + unsigned is_fragmented:1; + unsigned is_first_fragment:1; + unsigned is_last_fragment:1; + unsigned rfa0:1; + unsigned frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index 2d8337317..a60c25d1e 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -33,7 +33,7 @@ #include #include -#include "a2dp-codecs.h" +#include "a2dp/a2dp-api.h" #include "bluez5-util.h" @@ -48,8 +48,6 @@ #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported" -#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" -#define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" #define ENDPOINT_INTROSPECT_XML \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ @@ -89,6 +87,8 @@ struct pa_bluetooth_discovery { pa_hashmap *devices; pa_hashmap *transports; + pa_a2dp_config_t *a2dp_config; + int headset_backend; pa_bluetooth_backend *ofono_backend, *native_backend; PA_LLIST_HEAD(pa_dbus_pending, pending); @@ -888,7 +888,9 @@ finish: static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) { DBusMessage *m; DBusMessageIter i, d; - uint8_t codec = 0; + uint8_t codec; + pa_a2dp_codec_index_t index; + const pa_a2dp_codec_t *a2dp_codec; pa_log_debug("Registering %s on adapter %s", endpoint, path); @@ -899,22 +901,23 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d); pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid); + + pa_a2dp_endpoint_to_codec_index(endpoint, &index); + pa_a2dp_codec_index_to_a2dp_codec(index, &a2dp_codec); + + if(!a2dp_codec) + return; + + codec = a2dp_codec->codec; + pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec); if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) { - a2dp_sbc_t capabilities; - - capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | - SBC_CHANNEL_MODE_JOINT_STEREO; - capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | - SBC_SAMPLING_FREQ_48000; - capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS; - capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; - capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; - capabilities.min_bitpool = MIN_BITPOOL; - capabilities.max_bitpool = MAX_BITPOOL; - - pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); + void * capabilities; + size_t capabilities_size = a2dp_codec->get_capabilities(&capabilities); + pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, capabilities, + (unsigned int) capabilities_size); + a2dp_codec->free_capabilities(&capabilities); } dbus_message_iter_close_container(&i, &d); @@ -927,6 +930,9 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa const char *path; void *state; pa_bluetooth_device *d; + pa_a2dp_codec_index_t *index; + pa_hashmap *indices; + const char *endpoint; pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH); dbus_message_iter_get_basic(dict_i, &path); @@ -964,8 +970,13 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa if (!a->valid) return; - register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE); - register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK); + pa_a2dp_get_ordered_indices(&indices, &y->a2dp_config); + PA_HASHMAP_FOREACH(index, indices, state) { + pa_a2dp_codec_index_to_endpoint(*index, &endpoint); + register_endpoint(y, path, endpoint, + pa_a2dp_codec_index_is_sink(*index) ? PA_BLUETOOTH_UUID_A2DP_SINK + : PA_BLUETOOTH_UUID_A2DP_SOURCE); + } } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) { @@ -1257,47 +1268,6 @@ fail: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { - /* These bitpool values were chosen based on the A2DP spec recommendation */ - switch (freq) { - case SBC_SAMPLING_FREQ_16000: - case SBC_SAMPLING_FREQ_32000: - return 53; - - case SBC_SAMPLING_FREQ_44100: - - switch (mode) { - case SBC_CHANNEL_MODE_MONO: - case SBC_CHANNEL_MODE_DUAL_CHANNEL: - return 31; - - case SBC_CHANNEL_MODE_STEREO: - case SBC_CHANNEL_MODE_JOINT_STEREO: - return 53; - } - - pa_log_warn("Invalid channel mode %u", mode); - return 53; - - case SBC_SAMPLING_FREQ_48000: - - switch (mode) { - case SBC_CHANNEL_MODE_MONO: - case SBC_CHANNEL_MODE_DUAL_CHANNEL: - return 29; - - case SBC_CHANNEL_MODE_STEREO: - case SBC_CHANNEL_MODE_JOINT_STEREO: - return 51; - } - - pa_log_warn("Invalid channel mode %u", mode); - return 51; - } - - pa_log_warn("Invalid sampling freq %u", freq); - return 53; -} const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { switch(profile) { @@ -1326,6 +1296,18 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF; DBusMessageIter args, props; DBusMessage *r; + pa_a2dp_codec_index_t index; + const pa_a2dp_codec_t * a2dp_codec; + pa_a2dp_sink_t *a2dp_sink = NULL; + pa_a2dp_source_t *a2dp_source = NULL; + + endpoint_path = dbus_message_get_path(m); + + pa_a2dp_endpoint_to_codec_index(endpoint_path, &index); + pa_a2dp_codec_index_to_a2dp_codec(index, &a2dp_codec); + + if(!a2dp_codec) + goto fail2; if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) { pa_log_error("Invalid signature for method SetConfiguration()"); @@ -1367,13 +1349,14 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage dbus_message_iter_get_basic(&value, &uuid); - endpoint_path = dbus_message_get_path(m); - if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) { + if (pa_a2dp_codec_index_is_source(index)) { if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) p = PA_BLUETOOTH_PROFILE_A2DP_SINK; - } else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) { + a2dp_source = a2dp_codec->a2dp_source; + } else if (pa_a2dp_codec_index_is_sink(index)) { if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; + a2dp_sink = a2dp_codec->a2dp_sink; } if (p == PA_BLUETOOTH_PROFILE_OFF) { @@ -1389,7 +1372,6 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage dbus_message_iter_get_basic(&value, &dev_path); } else if (pa_streq(key, "Configuration")) { DBusMessageIter array; - a2dp_sbc_t *c; if (var != DBUS_TYPE_ARRAY) { pa_log_error("Property %s of wrong type %c", key, (char)var); @@ -1404,40 +1386,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage } dbus_message_iter_get_fixed_array(&array, &config, &size); - if (size != sizeof(a2dp_sbc_t)) { - pa_log_error("Configuration array of invalid size"); - goto fail; - } - - c = (a2dp_sbc_t *) config; - if (c->frequency != SBC_SAMPLING_FREQ_16000 && c->frequency != SBC_SAMPLING_FREQ_32000 && - c->frequency != SBC_SAMPLING_FREQ_44100 && c->frequency != SBC_SAMPLING_FREQ_48000) { - pa_log_error("Invalid sampling frequency in configuration"); + if(!a2dp_codec->validate_configuration(config, (const size_t) size)) goto fail; - } - - if (c->channel_mode != SBC_CHANNEL_MODE_MONO && c->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL && - c->channel_mode != SBC_CHANNEL_MODE_STEREO && c->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) { - pa_log_error("Invalid channel mode in configuration"); - goto fail; - } - - if (c->allocation_method != SBC_ALLOCATION_SNR && c->allocation_method != SBC_ALLOCATION_LOUDNESS) { - pa_log_error("Invalid allocation method in configuration"); - goto fail; - } - - if (c->subbands != SBC_SUBBANDS_4 && c->subbands != SBC_SUBBANDS_8) { - pa_log_error("Invalid SBC subbands in configuration"); - goto fail; - } - - if (c->block_length != SBC_BLOCK_LENGTH_4 && c->block_length != SBC_BLOCK_LENGTH_8 && - c->block_length != SBC_BLOCK_LENGTH_12 && c->block_length != SBC_BLOCK_LENGTH_16) { - pa_log_error("Invalid block length in configuration"); - goto fail; - } } dbus_message_iter_next(&props); @@ -1459,6 +1410,11 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage goto fail2; } + if(!a2dp_sink && !a2dp_source){ + pa_log_error("No a2dp_sink or a2dp_source available for endpoint %s", endpoint_path); + goto fail2; + } + sender = dbus_message_get_sender(m); pa_assert_se(r = dbus_message_new_method_return(m)); @@ -1468,6 +1424,10 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage t = pa_bluetooth_transport_new(d, sender, path, p, config, size); t->acquire = bluez5_transport_acquire_cb; t->release = bluez5_transport_release_cb; + t->codec = a2dp_codec->codec; + t->a2dp_codec = a2dp_codec; + t->a2dp_sink = a2dp_sink; + t->a2dp_source = a2dp_source; pa_bluetooth_transport_put(t); pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); @@ -1484,22 +1444,15 @@ fail2: static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) { pa_bluetooth_discovery *y = userdata; - a2dp_sbc_t *cap, config; - uint8_t *pconf = (uint8_t *) &config; - int i, size; + void *cap, *pconf; + int size,config_size; DBusMessage *r; DBusError err; + pa_a2dp_codec_index_t index; + const pa_a2dp_codec_t *a2dp_codec; + const char * endpoint = dbus_message_get_path(m); - static const struct { - uint32_t rate; - uint8_t cap; - } freq_table[] = { - { 16000U, SBC_SAMPLING_FREQ_16000 }, - { 32000U, SBC_SAMPLING_FREQ_32000 }, - { 44100U, SBC_SAMPLING_FREQ_44100 }, - { 48000U, SBC_SAMPLING_FREQ_48000 } - }; - + pa_log_debug("selecing configuration"); dbus_error_init(&err); if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { @@ -1508,102 +1461,22 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMess goto fail; } - if (size != sizeof(config)) { - pa_log_error("Capabilities array has invalid size"); - goto fail; - } - - pa_zero(config); + pa_a2dp_endpoint_to_codec_index(endpoint, &index); + pa_a2dp_codec_index_to_a2dp_codec(index, &a2dp_codec); - /* Find the lowest freq that is at least as high as the requested sampling rate */ - for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) - if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) { - config.frequency = freq_table[i].cap; - break; - } - - if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { - for (--i; i >= 0; i--) { - if (cap->frequency & freq_table[i].cap) { - config.frequency = freq_table[i].cap; - break; - } - } - - if (i < 0) { - pa_log_error("Not suitable sample rate"); - goto fail; - } - } - - pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); - - if (y->core->default_sample_spec.channels <= 1) { - if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) - config.channel_mode = SBC_CHANNEL_MODE_MONO; - else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) - config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; - else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO) - config.channel_mode = SBC_CHANNEL_MODE_STEREO; - else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) - config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; - else { - pa_log_error("No supported channel modes"); - goto fail; - } - } - - if (y->core->default_sample_spec.channels >= 2) { - if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) - config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; - else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO) - config.channel_mode = SBC_CHANNEL_MODE_STEREO; - else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) - config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; - else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) - config.channel_mode = SBC_CHANNEL_MODE_MONO; - else { - pa_log_error("No supported channel modes"); - goto fail; - } - } - - if (cap->block_length & SBC_BLOCK_LENGTH_16) - config.block_length = SBC_BLOCK_LENGTH_16; - else if (cap->block_length & SBC_BLOCK_LENGTH_12) - config.block_length = SBC_BLOCK_LENGTH_12; - else if (cap->block_length & SBC_BLOCK_LENGTH_8) - config.block_length = SBC_BLOCK_LENGTH_8; - else if (cap->block_length & SBC_BLOCK_LENGTH_4) - config.block_length = SBC_BLOCK_LENGTH_4; - else { - pa_log_error("No supported block lengths"); + if(!a2dp_codec) goto fail; - } - if (cap->subbands & SBC_SUBBANDS_8) - config.subbands = SBC_SUBBANDS_8; - else if (cap->subbands & SBC_SUBBANDS_4) - config.subbands = SBC_SUBBANDS_4; - else { - pa_log_error("No supported subbands"); + config_size = (int) a2dp_codec->select_configuration(y->core->default_sample_spec, cap, (const size_t) size, &pconf); + if (size != config_size) { + pa_log_error("Capabilities array has invalid size %d, %d",size, config_size); goto fail; } - if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS) - config.allocation_method = SBC_ALLOCATION_LOUDNESS; - else if (cap->allocation_method & SBC_ALLOCATION_SNR) - config.allocation_method = SBC_ALLOCATION_SNR; - - config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool); - config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool); - - if (config.min_bitpool > config.max_bitpool) - goto fail; - pa_assert_se(r = dbus_message_new_method_return(m)); pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, DBUS_TYPE_INVALID)); + a2dp_codec->free_configuration(&pconf); return r; fail: @@ -1668,6 +1541,7 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi struct pa_bluetooth_discovery *y = userdata; DBusMessage *r = NULL; const char *path, *interface, *member; + pa_a2dp_codec_index_t index; pa_assert(y); @@ -1677,7 +1551,8 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); - if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT)) + pa_a2dp_endpoint_to_codec_index(path, &index); + if (index == PA_A2DP_CODEC_INDEX_UNAVAILABLE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { @@ -1706,6 +1581,10 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi } static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) { + void *state; + pa_a2dp_codec_index_t *index; + pa_hashmap *indices; + const char *endpoint; static const DBusObjectPathVTable vtable_endpoint = { .message_function = endpoint_handler, }; @@ -1714,33 +1593,49 @@ static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t prof switch(profile) { case PA_BLUETOOTH_PROFILE_A2DP_SINK: - pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT, - &vtable_endpoint, y)); + pa_a2dp_get_source_indices(&indices, &y->a2dp_config); break; case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: - pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT, - &vtable_endpoint, y)); + pa_a2dp_get_sink_indices(&indices, &y->a2dp_config); break; default: pa_assert_not_reached(); break; } + + PA_HASHMAP_FOREACH(index, indices, state){ + pa_a2dp_codec_index_to_endpoint(*index, &endpoint); + if(!endpoint) + continue; + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint, + &vtable_endpoint, y)); + } } static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) { + void *state; + pa_a2dp_codec_index_t *index; + pa_hashmap *indices; + const char *endpoint; + pa_assert(y); switch(profile) { case PA_BLUETOOTH_PROFILE_A2DP_SINK: - dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT); + pa_a2dp_get_source_indices(&indices, &y->a2dp_config); break; case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: - dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT); + pa_a2dp_get_sink_indices(&indices, &y->a2dp_config); break; default: pa_assert_not_reached(); break; } + + PA_HASHMAP_FOREACH(index,indices,state){ + pa_a2dp_codec_index_to_endpoint(*index, &endpoint); + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint); + } } pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) { @@ -1760,6 +1655,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending); + pa_a2dp_init(&y->a2dp_config); + for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++) pa_hook_init(&y->hooks[i], y); @@ -1871,6 +1768,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK); endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE); + if(y->a2dp_config) + pa_a2dp_free(&y->a2dp_config); + pa_dbus_connection_unref(y->connection); } diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index ad30708f0..c3be423e8 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -22,6 +22,8 @@ #include +#include "a2dp/a2dp-api.h" + #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" @@ -78,6 +80,10 @@ struct pa_bluetooth_transport { char *path; pa_bluetooth_profile_t profile; + const pa_a2dp_codec_t *a2dp_codec; + pa_a2dp_sink_t *a2dp_sink; + pa_a2dp_source_t *a2dp_source; + uint8_t codec; uint8_t *config; size_t config_size; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 2a36adfdb..77a12e6ed 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -45,9 +45,8 @@ #include #include -#include "a2dp-codecs.h" +#include "a2dp/a2dp-api.h" #include "bluez5-util.h" -#include "rtp.h" PA_MODULE_AUTHOR("João Paulo Rechi Vita"); PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source"); @@ -61,13 +60,12 @@ PA_MODULE_USAGE("path=" #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) #define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) -#define BITPOOL_DEC_LIMIT 32 -#define BITPOOL_DEC_STEP 5 #define HSP_MAX_GAIN 15 static const char* const valid_modargs[] = { "path", "autodetect_mtu", + "a2dp_config", NULL }; @@ -93,17 +91,13 @@ typedef struct bluetooth_msg { PA_DEFINE_PRIVATE_CLASS(bluetooth_msg, pa_msgobject); #define BLUETOOTH_MSG(o) (bluetooth_msg_cast(o)) -typedef struct sbc_info { - sbc_t sbc; /* Codec data */ - bool sbc_initialized; /* Keep track if the encoder is initialized */ - size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ - uint16_t seq_num; /* Cumulative packet sequence */ - uint8_t min_bitpool; - uint8_t max_bitpool; - - void* buffer; /* Codec transfer buffer */ - size_t buffer_size; /* Size of the buffer */ -} sbc_info_t; +typedef struct pa_a2dp_info { + pa_proplist *a2dp_config; + void *a2dp_sink_data; + void *a2dp_source_data; + void *buffer; + size_t buffer_size; +} pa_a2dp_info_t; struct userdata { pa_module *module; @@ -145,7 +139,7 @@ struct userdata { pa_smoother *read_smoother; pa_memchunk write_memchunk; pa_sample_spec sample_spec; - struct sbc_info sbc_info; + pa_a2dp_info_t a2dp_info; }; typedef enum pa_bluetooth_form_factor { @@ -417,105 +411,55 @@ static void a2dp_prepare_buffer(struct userdata *u) { pa_assert(u); - if (u->sbc_info.buffer_size >= min_buffer_size) + if (u->a2dp_info.buffer_size >= min_buffer_size) return; - u->sbc_info.buffer_size = 2 * min_buffer_size; - pa_xfree(u->sbc_info.buffer); - u->sbc_info.buffer = pa_xmalloc(u->sbc_info.buffer_size); + u->a2dp_info.buffer_size = 2 * min_buffer_size; + pa_xfree(u->a2dp_info.buffer); + u->a2dp_info.buffer = pa_xmalloc(u->a2dp_info.buffer_size); +} + +static void a2dp_encoder_buffer_read_cb(const void **read_buf, size_t read_buf_size, void *userdata) { + struct userdata *u = (struct userdata *) userdata; + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, read_buf_size, &u->write_memchunk); + pa_assert(u->write_memchunk.length == read_buf_size); + *read_buf = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); +} + +static void a2dp_encoder_buffer_free_cb(const void **read_buf, void *userdata) { + struct userdata *u = (struct userdata *) userdata; + if (!read_buf) + return;; + pa_memblock_release(u->write_memchunk.memblock); + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + *read_buf = NULL; } /* Run from IO thread */ static int a2dp_process_render(struct userdata *u) { - struct sbc_info *sbc_info; - struct rtp_header *header; - struct rtp_payload *payload; - size_t nbytes; - void *d; - const void *p; - size_t to_write, to_encode; - unsigned frame_count; + size_t nbytes, encoded; int ret = 0; pa_assert(u); pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK); pa_assert(u->sink); - - /* First, render some data */ - if (!u->write_memchunk.memblock) - pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); - - pa_assert(u->write_memchunk.length == u->write_block_size); + pa_assert(u->transport); + pa_assert(u->transport->a2dp_source); a2dp_prepare_buffer(u); - sbc_info = &u->sbc_info; - header = sbc_info->buffer; - payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header)); - - frame_count = 0; - - /* Try to create a packet of the full MTU */ - - p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); - to_encode = u->write_memchunk.length; - - d = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload); - to_write = sbc_info->buffer_size - sizeof(*header) - sizeof(*payload); - - while (PA_LIKELY(to_encode > 0 && to_write > 0)) { - ssize_t written; - ssize_t encoded; - - encoded = sbc_encode(&sbc_info->sbc, - p, to_encode, - d, to_write, - &written); - - if (PA_UNLIKELY(encoded <= 0)) { - pa_log_error("SBC encoding error (%li)", (long) encoded); - pa_memblock_release(u->write_memchunk.memblock); - return -1; - } - - pa_assert_fp((size_t) encoded <= to_encode); - pa_assert_fp((size_t) encoded == sbc_info->codesize); - - pa_assert_fp((size_t) written <= to_write); - pa_assert_fp((size_t) written == sbc_info->frame_length); - - p = (const uint8_t*) p + encoded; - to_encode -= encoded; - - d = (uint8_t*) d + written; - to_write -= written; - - frame_count++; - } - - pa_memblock_release(u->write_memchunk.memblock); - - pa_assert(to_encode == 0); - - PA_ONCE_BEGIN { - pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc))); - } PA_ONCE_END; - - /* write it to the fifo */ - memset(sbc_info->buffer, 0, sizeof(*header) + sizeof(*payload)); - header->v = 2; - header->pt = 1; - header->sequence_number = htons(sbc_info->seq_num++); - header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec)); - header->ssrc = htonl(1); - payload->frame_count = frame_count; - - nbytes = (uint8_t*) d - (uint8_t*) sbc_info->buffer; + nbytes = u->transport->a2dp_source->encode((uint32_t) (u->write_index / pa_frame_size(&u->sample_spec)), + u->a2dp_info.buffer, u->a2dp_info.buffer_size, + &encoded, u, &u->a2dp_info.a2dp_source_data); + if(nbytes == 0) + return -1; for (;;) { ssize_t l; - l = pa_write(u->stream_fd, sbc_info->buffer, nbytes, &u->stream_write_type); + l = pa_write(u->stream_fd, u->a2dp_info.buffer, nbytes, &u->stream_write_type); pa_assert(l != 0); @@ -546,9 +490,7 @@ static int a2dp_process_render(struct userdata *u) { break; } - u->write_index += (uint64_t) u->write_memchunk.length; - pa_memblock_unref(u->write_memchunk.memblock); - pa_memchunk_reset(&u->write_memchunk); + u->write_index += encoded; ret = 1; @@ -567,6 +509,8 @@ static int a2dp_process_push(struct userdata *u) { pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE); pa_assert(u->source); pa_assert(u->read_smoother); + pa_assert(u->transport); + pa_assert(u->transport->a2dp_sink); memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); memchunk.index = memchunk.length = 0; @@ -574,22 +518,18 @@ static int a2dp_process_push(struct userdata *u) { for (;;) { bool found_tstamp = false; pa_usec_t tstamp; - struct sbc_info *sbc_info; - struct rtp_header *header; - struct rtp_payload *payload; - const void *p; + pa_a2dp_info_t *a2dp_info; void *d; ssize_t l; - size_t to_write, to_decode; + size_t decoded; size_t total_written = 0; + uint32_t timestamp; a2dp_prepare_buffer(u); - sbc_info = &u->sbc_info; - header = sbc_info->buffer; - payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header)); + a2dp_info = &u->a2dp_info; - l = pa_read(u->stream_fd, sbc_info->buffer, sbc_info->buffer_size, &u->stream_write_type); + l = pa_read(u->stream_fd, a2dp_info->buffer, a2dp_info->buffer_size, &u->stream_write_type); if (l <= 0) { @@ -606,7 +546,7 @@ static int a2dp_process_push(struct userdata *u) { break; } - pa_assert((size_t) l <= sbc_info->buffer_size); + pa_assert((size_t) l <= a2dp_info->buffer_size); /* TODO: get timestamp from rtp */ if (!found_tstamp) { @@ -614,50 +554,22 @@ static int a2dp_process_push(struct userdata *u) { tstamp = pa_rtclock_now(); } - p = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload); - to_decode = l - sizeof(*header) - sizeof(*payload); - d = pa_memblock_acquire(memchunk.memblock); - to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock); - - while (PA_LIKELY(to_decode > 0)) { - size_t written; - ssize_t decoded; - - decoded = sbc_decode(&sbc_info->sbc, - p, to_decode, - d, to_write, - &written); - - if (PA_UNLIKELY(decoded <= 0)) { - pa_log_error("SBC decoding error (%li)", (long) decoded); - pa_memblock_release(memchunk.memblock); - pa_memblock_unref(memchunk.memblock); - return 0; - } + memchunk.length = pa_memblock_get_length(memchunk.memblock); - total_written += written; - - /* Reset frame length, it can be changed due to bitpool change */ - sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); - - pa_assert_fp((size_t) decoded <= to_decode); - pa_assert_fp((size_t) decoded == sbc_info->frame_length); - - pa_assert_fp((size_t) written == sbc_info->codesize); - - p = (const uint8_t*) p + decoded; - to_decode -= decoded; - - d = (uint8_t*) d + written; - to_write -= written; + total_written = u->transport->a2dp_sink->decode(a2dp_info->buffer, (size_t) l, d, memchunk.length, &decoded, + ×tamp, &a2dp_info->a2dp_sink_data); + if(total_written == 0){ + pa_memblock_release(memchunk.memblock); + pa_memblock_unref(memchunk.memblock); + return 0; } u->read_index += (uint64_t) total_written; pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); pa_smoother_resume(u->read_smoother, tstamp, true); - memchunk.length -= to_write; + memchunk.length = total_written; pa_memblock_release(memchunk.memblock); @@ -704,40 +616,10 @@ static void update_buffer_size(struct userdata *u) { } } -/* Run from I/O thread */ -static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) { - struct sbc_info *sbc_info; - - pa_assert(u); - - sbc_info = &u->sbc_info; - - if (sbc_info->sbc.bitpool == bitpool) - return; - - if (bitpool > sbc_info->max_bitpool) - bitpool = sbc_info->max_bitpool; - else if (bitpool < sbc_info->min_bitpool) - bitpool = sbc_info->min_bitpool; - - sbc_info->sbc.bitpool = bitpool; - - sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); - sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); - - pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool); - - u->read_block_size = - (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) - / sbc_info->frame_length * sbc_info->codesize; - - u->write_block_size = - (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) - / sbc_info->frame_length * sbc_info->codesize; - +static void a2dp_set_sink(struct userdata *u){ pa_sink_set_max_request_within_thread(u->sink, u->write_block_size); pa_sink_set_fixed_latency_within_thread(u->sink, - FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->write_block_size, &u->sample_spec)); + FIXED_LATENCY_PLAYBACK_A2DP + pa_bytes_to_usec(u->write_block_size, &u->sample_spec)); /* If there is still data in the memchunk, we have to discard it * because the write_block_size may have changed. */ @@ -749,27 +631,6 @@ static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) { update_buffer_size(u); } -/* Run from I/O thread */ -static void a2dp_reduce_bitpool(struct userdata *u) { - struct sbc_info *sbc_info; - uint8_t bitpool; - - pa_assert(u); - - sbc_info = &u->sbc_info; - - /* Check if bitpool is already at its limit */ - if (sbc_info->sbc.bitpool <= BITPOOL_DEC_LIMIT) - return; - - bitpool = sbc_info->sbc.bitpool - BITPOOL_DEC_STEP; - - if (bitpool < BITPOOL_DEC_LIMIT) - bitpool = BITPOOL_DEC_LIMIT; - - a2dp_set_bitpool(u, bitpool); -} - static void teardown_stream(struct userdata *u) { if (u->rtpoll_item) { pa_rtpoll_item_free(u->rtpoll_item); @@ -859,13 +720,15 @@ static void transport_config_mtu(struct userdata *u) { u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec); } } else { - u->read_block_size = - (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) - / u->sbc_info.frame_length * u->sbc_info.codesize; - - u->write_block_size = - (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) - / u->sbc_info.frame_length * u->sbc_info.codesize; + u->read_block_size = u->read_link_mtu; + u->write_block_size = u->write_link_mtu; + if (u->transport->a2dp_sink) + u->transport->a2dp_sink->get_block_size(u->read_link_mtu, &u->read_block_size, &u->a2dp_info.a2dp_sink_data); + else if (u->transport->a2dp_source) + u->transport->a2dp_source->get_block_size(u->write_link_mtu, &u->write_block_size, + &u->a2dp_info.a2dp_source_data); + else + pa_assert_not_reached(); } if (u->sink) { @@ -905,9 +768,19 @@ static void setup_stream(struct userdata *u) { pa_log_debug("Stream properly set up, we're ready to roll!"); - if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { - a2dp_set_bitpool(u, u->sbc_info.max_bitpool); - update_buffer_size(u); + if (u->transport->a2dp_sink) { + u->transport->a2dp_sink->setup_stream(&u->a2dp_info.a2dp_sink_data); + u->transport->a2dp_sink->get_block_size(u->read_link_mtu, &u->read_block_size, + &u->a2dp_info.a2dp_sink_data); + pa_proplist_sets(u->source->proplist, "bluetooth.a2dp_codec", u->transport->a2dp_codec->name); + + } else if (u->transport->a2dp_source) { + u->transport->a2dp_source->setup_stream(&u->a2dp_info.a2dp_source_data); + u->transport->a2dp_source->get_block_size(u->write_link_mtu, &u->write_block_size, + &u->a2dp_info.a2dp_source_data); + a2dp_set_sink(u); + pa_proplist_sets(u->sink->proplist, "bluetooth.a2dp_codec", u->transport->a2dp_codec->name); + } u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); @@ -1094,6 +967,8 @@ static int add_source(struct userdata *u) { pa_assert_not_reached(); break; } + else if(u->transport->a2dp_codec && u->transport->a2dp_sink) + pa_proplist_sets(data.proplist, "bluetooth.a2dp_codec", u->transport->a2dp_codec->name); u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); pa_source_new_data_done(&data); @@ -1269,6 +1144,9 @@ static int add_sink(struct userdata *u) { pa_assert_not_reached(); break; } + else if(u->transport->a2dp_codec && u->transport->a2dp_source) + pa_proplist_sets(data.proplist, "bluetooth.a2dp_codec", u->transport->a2dp_codec->name); + u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); pa_sink_new_data_done(&data); @@ -1295,111 +1173,39 @@ static void transport_config(struct userdata *u) { u->sample_spec.channels = 1; u->sample_spec.rate = 8000; } else { - sbc_info_t *sbc_info = &u->sbc_info; - a2dp_sbc_t *config; - + pa_proplist *user_config = NULL; pa_assert(u->transport); - u->sample_spec.format = PA_SAMPLE_S16LE; - config = (a2dp_sbc_t *) u->transport->config; - - if (sbc_info->sbc_initialized) - sbc_reinit(&sbc_info->sbc, 0); - else - sbc_init(&sbc_info->sbc, 0); - sbc_info->sbc_initialized = true; - - switch (config->frequency) { - case SBC_SAMPLING_FREQ_16000: - sbc_info->sbc.frequency = SBC_FREQ_16000; - u->sample_spec.rate = 16000U; - break; - case SBC_SAMPLING_FREQ_32000: - sbc_info->sbc.frequency = SBC_FREQ_32000; - u->sample_spec.rate = 32000U; - break; - case SBC_SAMPLING_FREQ_44100: - sbc_info->sbc.frequency = SBC_FREQ_44100; - u->sample_spec.rate = 44100U; - break; - case SBC_SAMPLING_FREQ_48000: - sbc_info->sbc.frequency = SBC_FREQ_48000; - u->sample_spec.rate = 48000U; - break; - default: - pa_assert_not_reached(); - } - - switch (config->channel_mode) { - case SBC_CHANNEL_MODE_MONO: - sbc_info->sbc.mode = SBC_MODE_MONO; - u->sample_spec.channels = 1; - break; - case SBC_CHANNEL_MODE_DUAL_CHANNEL: - sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; - u->sample_spec.channels = 2; - break; - case SBC_CHANNEL_MODE_STEREO: - sbc_info->sbc.mode = SBC_MODE_STEREO; - u->sample_spec.channels = 2; - break; - case SBC_CHANNEL_MODE_JOINT_STEREO: - sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; - u->sample_spec.channels = 2; - break; - default: - pa_assert_not_reached(); - } + if (u->a2dp_info.a2dp_config) + user_config = pa_proplist_copy(u->a2dp_info.a2dp_config); - switch (config->allocation_method) { - case SBC_ALLOCATION_SNR: - sbc_info->sbc.allocation = SBC_AM_SNR; - break; - case SBC_ALLOCATION_LOUDNESS: - sbc_info->sbc.allocation = SBC_AM_LOUDNESS; - break; - default: - pa_assert_not_reached(); - } + if (u->transport->a2dp_sink) { + pa_assert_se(u->transport->a2dp_sink->init(&u->a2dp_info.a2dp_sink_data)); - switch (config->subbands) { - case SBC_SUBBANDS_4: - sbc_info->sbc.subbands = SBC_SB_4; - break; - case SBC_SUBBANDS_8: - sbc_info->sbc.subbands = SBC_SB_8; - break; - default: - pa_assert_not_reached(); - } + if (u->transport->a2dp_sink->update_user_config && user_config) + u->transport->a2dp_sink->update_user_config(user_config, + &u->a2dp_info.a2dp_sink_data); - switch (config->block_length) { - case SBC_BLOCK_LENGTH_4: - sbc_info->sbc.blocks = SBC_BLK_4; - break; - case SBC_BLOCK_LENGTH_8: - sbc_info->sbc.blocks = SBC_BLK_8; - break; - case SBC_BLOCK_LENGTH_12: - sbc_info->sbc.blocks = SBC_BLK_12; - break; - case SBC_BLOCK_LENGTH_16: - sbc_info->sbc.blocks = SBC_BLK_16; - break; - default: - pa_assert_not_reached(); - } + u->transport->a2dp_sink->config_transport(u->core->default_sample_spec, u->transport->config, + u->transport->config_size, + &u->sample_spec, &u->a2dp_info.a2dp_sink_data); + } else if (u->transport->a2dp_source) { + pa_assert_se( + u->transport->a2dp_source->init(a2dp_encoder_buffer_read_cb, a2dp_encoder_buffer_free_cb, + &u->a2dp_info.a2dp_source_data)); - sbc_info->min_bitpool = config->min_bitpool; - sbc_info->max_bitpool = config->max_bitpool; + if (u->transport->a2dp_source->update_user_config && user_config) + u->transport->a2dp_source->update_user_config(user_config, + &u->a2dp_info.a2dp_source_data); - /* Set minimum bitpool for source to get the maximum possible block_size */ - sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool; - sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); - sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); + u->transport->a2dp_source->config_transport(u->core->default_sample_spec, u->transport->config, + u->transport->config_size, + &u->sample_spec, &u->a2dp_info.a2dp_source_data); + } else + pa_assert_not_reached(); - pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", - sbc_info->sbc.allocation, sbc_info->sbc.subbands ? 8 : 4, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); + if(user_config) + pa_proplist_free(user_config); } } @@ -1619,6 +1425,11 @@ static void thread_func(void *userdata) { if (audio_sent <= time_passed) { size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->sample_spec); + if (u->transport->a2dp_source && u->transport->a2dp_source->set_tx_length){ + u->transport->a2dp_source->set_tx_length(bytes_to_send, &u->a2dp_info.a2dp_source_data); + u->transport->a2dp_source->get_block_size(u->write_link_mtu, &u->write_block_size, &u->a2dp_info.a2dp_source_data); + } + /* There are more than two blocks that need to be written. It seems that * the socket has not been accepting data fast enough (could be due to * hiccups in the wireless transmission). We need to discard everything @@ -1650,8 +1461,13 @@ static void thread_func(void *userdata) { skip_bytes -= bytes_to_render; } - if (u->write_index > 0 && u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) - a2dp_reduce_bitpool(u); + if (u->write_index > 0 && u->transport->a2dp_source && + u->transport->a2dp_source->decrease_quality) { + + u->transport->a2dp_source->decrease_quality(&u->a2dp_info.a2dp_source_data); + u->transport->a2dp_source->get_block_size(u->write_link_mtu, &u->write_block_size, &u->a2dp_info.a2dp_source_data); + } + } blocks_to_write = 1; @@ -1806,6 +1622,14 @@ static void stop_thread(struct userdata *u) { } if (u->transport) { + if(u->transport->a2dp_sink && u->a2dp_info.a2dp_sink_data){ + u->transport->a2dp_sink->free(&u->a2dp_info.a2dp_sink_data); + u->a2dp_info.a2dp_sink_data = NULL; + } + if(u->transport->a2dp_source && u->a2dp_info.a2dp_sink_data){ + u->transport->a2dp_source->free(&u->a2dp_info.a2dp_source_data); + u->a2dp_info.a2dp_source_data = NULL; + } transport_release(u); u->transport = NULL; } @@ -2414,6 +2238,12 @@ int pa__init(pa_module* m) { u->device->autodetect_mtu = autodetect_mtu; + u->a2dp_info.a2dp_config = pa_proplist_new(); + if (pa_modargs_get_proplist(ma, "a2dp_config", u->a2dp_info.a2dp_config, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid proplist key=value pairs for a2dp_config parameter"); + goto fail_free_modargs; + } + pa_modargs_free(ma); u->device_connection_changed_slot = @@ -2491,11 +2321,17 @@ void pa__done(pa_module *m) { if (u->transport_microphone_gain_changed_slot) pa_hook_slot_free(u->transport_microphone_gain_changed_slot); - if (u->sbc_info.buffer) - pa_xfree(u->sbc_info.buffer); + if (u->a2dp_info.buffer) + pa_xfree(u->a2dp_info.buffer); + + if (u->a2dp_info.a2dp_sink_data) + pa_xfree(u->a2dp_info.a2dp_sink_data); + + if (u->a2dp_info.a2dp_source_data) + pa_xfree(u->a2dp_info.a2dp_source_data); - if (u->sbc_info.sbc_initialized) - sbc_finish(&u->sbc_info.sbc); + if (u->a2dp_info.a2dp_config) + pa_proplist_free(u->a2dp_info.a2dp_config); if (u->msg) pa_xfree(u->msg); diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c index 44578214c..68a05e754 100644 --- a/src/modules/bluetooth/module-bluez5-discover.c +++ b/src/modules/bluetooth/module-bluez5-discover.c @@ -41,6 +41,7 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "headset", "autodetect_mtu", + "a2dp_config", NULL }; @@ -51,6 +52,7 @@ struct userdata { pa_hook_slot *device_connection_changed_slot; pa_bluetooth_discovery *discovery; bool autodetect_mtu; + const char *a2dp_config; }; static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { @@ -71,7 +73,8 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) { /* a new device has been connected */ pa_module *m; - char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu); + char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i a2dp_config=\"%s\"", + d->path, (int) u->autodetect_mtu, u->a2dp_config); pa_log_debug("Loading module-bluez5-device %s", args); pa_module_load(&m, u->module->core, "module-bluez5-device", args); @@ -99,7 +102,7 @@ const char *default_headset_backend = "ofono"; int pa__init(pa_module *m) { struct userdata *u; pa_modargs *ma; - const char *headset_str; + const char *headset_str, *a2dp_config; int headset_backend; bool autodetect_mtu; @@ -128,10 +131,14 @@ int pa__init(pa_module *m) { goto fail; } + pa_assert_se(a2dp_config = pa_modargs_get_value(ma, "a2dp_config", "")); + + m->userdata = u = pa_xnew0(struct userdata, 1); u->module = m; u->core = m->core; u->autodetect_mtu = autodetect_mtu; + u->a2dp_config = pa_xmemdup(a2dp_config, strlen(a2dp_config) + 1); u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend))) @@ -168,5 +175,8 @@ void pa__done(pa_module *m) { if (u->loaded_device_paths) pa_hashmap_free(u->loaded_device_paths); + if (u->a2dp_config) + pa_xfree((void *) u->a2dp_config); + pa_xfree(u); }