[1/4] A2DP api with multi-codec support

Submitted by Huang-Huang Bao on Dec. 13, 2018, 11:43 a.m.

Details

Message ID 20181213114339.9651-2-eh5@sokka.cn
State Superseded
Headers show
Series "Adds A2DP AAC, aptX(HD), LDAC codecs" ( rev: 2 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Huang-Huang Bao Dec. 13, 2018, 11:43 a.m.
Separate A2DP processes from bluez5-util, module-bluez5-device.

Add prop "bluetooth.a2dp_codec" to pulseaudio sink/source properties.
---
 src/Makefile.am                               |  15 +-
 src/modules/bluetooth/a2dp-codecs.h           | 115 ---
 src/modules/bluetooth/a2dp/a2dp-api.h         | 170 +++++
 src/modules/bluetooth/a2dp/a2dp-codecs.h      | 266 +++++++
 src/modules/bluetooth/a2dp/a2dp_sbc.c         | 659 ++++++++++++++++++
 src/modules/bluetooth/a2dp/a2dp_util.c        | 298 ++++++++
 src/modules/bluetooth/{ => a2dp}/rtp.h        |   0
 src/modules/bluetooth/bluez5-util.c           | 307 +++-----
 src/modules/bluetooth/bluez5-util.h           |   6 +
 src/modules/bluetooth/module-bluez5-device.c  | 460 ++++--------
 .../bluetooth/module-bluez5-discover.c        |  14 +-
 11 files changed, 1672 insertions(+), 638 deletions(-)
 delete mode 100644 src/modules/bluetooth/a2dp-codecs.h
 create mode 100644 src/modules/bluetooth/a2dp/a2dp-api.h
 create mode 100644 src/modules/bluetooth/a2dp/a2dp-codecs.h
 create mode 100644 src/modules/bluetooth/a2dp/a2dp_sbc.c
 create mode 100644 src/modules/bluetooth/a2dp/a2dp_util.c
 rename src/modules/bluetooth/{ => a2dp}/rtp.h (100%)

Patch hide | download patch | download mbox

diff --git a/src/Makefile.am b/src/Makefile.am
index 2dbb4563b..521b9b684 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2123,8 +2123,11 @@  module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) -DPA_MODULE_NAME=module_bluet
 libbluez5_util_la_SOURCES = \
 		modules/bluetooth/bluez5-util.c \
 		modules/bluetooth/bluez5-util.h \
-		modules/bluetooth/a2dp-codecs.h \
-		modules/bluetooth/rtp.h
+		modules/bluetooth/a2dp/a2dp_util.c \
+		modules/bluetooth/a2dp/a2dp_sbc.c \
+		modules/bluetooth/a2dp/a2dp-api.h \
+		modules/bluetooth/a2dp/a2dp-codecs.h \
+		modules/bluetooth/a2dp/rtp.h
 if HAVE_BLUEZ_5_OFONO_HEADSET
 libbluez5_util_la_SOURCES += \
 		modules/bluetooth/backend-ofono.c
@@ -2135,8 +2138,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) $(DBUS_LIBS) $(SBC_LIBS)
+libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
 
 module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
 module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
@@ -2145,8 +2148,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-codecs.h b/src/modules/bluetooth/a2dp-codecs.h
deleted file mode 100644
index 8afcfcb24..000000000
--- a/src/modules/bluetooth/a2dp-codecs.h
+++ /dev/null
@@ -1,115 +0,0 @@ 
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2006-2010  Nokia Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#define A2DP_CODEC_SBC			0x00
-#define A2DP_CODEC_MPEG12		0x01
-#define A2DP_CODEC_MPEG24		0x02
-#define A2DP_CODEC_ATRAC		0x03
-
-#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 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 MAX_BITPOOL 64
-#define MIN_BITPOOL 2
-
-#if __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;
-	uint16_t bitrate;
-} __attribute__ ((packed)) a2dp_mpeg_t;
-
-#elif __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;
-	uint16_t bitrate;
-} __attribute__ ((packed)) a2dp_mpeg_t;
-
-#else
-#error "Unknown byte order"
-#endif
diff --git a/src/modules/bluetooth/a2dp/a2dp-api.h b/src/modules/bluetooth/a2dp/a2dp-api.h
new file mode 100644
index 000000000..b357555d0
--- /dev/null
+++ b/src/modules/bluetooth/a2dp/a2dp-api.h
@@ -0,0 +1,170 @@ 
+#ifndef fooa2dpcodecapifoo
+#define fooa2dpcodecapifoo
+
+#ifdef HAVE_CONFIG_H
+
+#include <config.h>
+
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/proplist.h>
+#include <pulsecore/hashmap.h>
+
+#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;
+
+
+/* Run from <pa_a2dp_sink_t>.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);
+
+
+// Larger index stands for higher priority by default
+typedef enum pa_a2dp_codec_index {
+    PA_A2DP_SINK_MIN,
+    PA_A2DP_SINK_SBC,
+    PA_A2DP_SINK_MAX,
+    PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX,
+    PA_A2DP_SOURCE_SBC,
+    PA_A2DP_SOURCE_MAX,
+    PA_A2DP_CODEC_INDEX_UNAVAILABLE
+} pa_a2dp_codec_index_t;
+
+typedef struct pa_a2dp_sink {
+    int priority;
+
+    /* Load decoder 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 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);
+
+    /* The implement should 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 true if configuration valid */
+    bool (*set_configuration)(const uint8_t *selected_configuration, const size_t configuration_size);
+
+};
+
+
+typedef struct a_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..aefafb635
--- /dev/null
+++ b/src/modules/bluetooth/a2dp/a2dp-codecs.h
@@ -0,0 +1,266 @@ 
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+#define A2DP_CODEC_SBC			0x00
+#define A2DP_CODEC_MPEG12		0x01
+#define A2DP_CODEC_MPEG24		0x02
+#define A2DP_CODEC_ATRAC		0x03
+#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 MAX_BITPOOL 64
+#define 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_VBR		0x8000
+#define MPEG_BIT_RATE_320000		0x4000
+#define MPEG_BIT_RATE_256000		0x2000
+#define MPEG_BIT_RATE_224000		0x1000
+#define MPEG_BIT_RATE_192000		0x0800
+#define MPEG_BIT_RATE_160000		0x0400
+#define MPEG_BIT_RATE_128000		0x0200
+#define MPEG_BIT_RATE_112000		0x0100
+#define MPEG_BIT_RATE_96000		0x0080
+#define MPEG_BIT_RATE_80000		0x0040
+#define MPEG_BIT_RATE_64000		0x0020
+#define MPEG_BIT_RATE_56000		0x0010
+#define MPEG_BIT_RATE_48000		0x0008
+#define MPEG_BIT_RATE_40000		0x0004
+#define MPEG_BIT_RATE_32000		0x0002
+#define MPEG_BIT_RATE_FREE		0x0001
+
+#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 LDAC_VENDOR_ID			0x0000012d
+#define LDAC_CODEC_ID			0x00aa
+
+typedef struct {
+    uint32_t vendor_id;
+    uint16_t codec_id;
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
+
+#if __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;
+    uint16_t bitrate;
+} __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 channel_mode:4;
+    uint8_t frequency:4;
+    uint8_t acl_sprint_reserved[4];
+} __attribute__ ((packed)) a2dp_aptxhd_t;
+
+//Codec Specific Information Elements for LDAC(the datasheet is in ldacBT.h)
+typedef struct {
+    a2dp_vendor_codec_t info;
+    uint8_t frequency:6;
+    uint8_t rfa0:2;
+    uint8_t channel_mode:3;
+    uint8_t rfa1:5;
+} __attribute__ ((packed)) a2dp_ldac_t;
+
+#elif __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;
+	uint16_t bitrate;
+} __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 frequency:4;
+	uint8_t channel_mode:4;
+    uint8_t acl_sprint_reserved[4];
+} __attribute__ ((packed)) a2dp_aptxhd_t;
+
+//Codec Specific Information Elements for LDAC(the datasheet is in ldacBT.h)
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t rfa0:2;
+	uint8_t frequency:6;
+	uint8_t rfa1:5;
+	uint8_t channel_mode:3;
+} __attribute__ ((packed)) a2dp_ldac_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/src/modules/bluetooth/a2dp/a2dp_sbc.c b/src/modules/bluetooth/a2dp/a2dp_sbc.c
new file mode 100644
index 000000000..4ad072bc9
--- /dev/null
+++ b/src/modules/bluetooth/a2dp/a2dp_sbc.c
@@ -0,0 +1,659 @@ 
+
+#include <sbc/sbc.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+
+#include <config.h>
+
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/once.h>
+
+#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 = MIN_BITPOOL;
+    capabilities->max_bitpool = 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(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_set_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,
+        .set_configuration = pa_sbc_set_configuration
+};
\ No newline at end of file
diff --git a/src/modules/bluetooth/a2dp/a2dp_util.c b/src/modules/bluetooth/a2dp/a2dp_util.c
new file mode 100644
index 000000000..e73d8c8ff
--- /dev/null
+++ b/src/modules/bluetooth/a2dp/a2dp_util.c
@@ -0,0 +1,298 @@ 
+
+#include <string.h>
+#include <pulse/xmalloc.h>
+
+#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
+#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
+
+
+#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;
+    }
+    codec_index = PA_A2DP_SOURCE_MIN;
+    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;
+        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
+        *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;
+        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_VENDOR:
+            if (!a2dp_codec->vendor_codec) {
+                *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE;
+                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_VENDOR:
+            if (!vendor_codec) {
+                *a2dp_codec = NULL;
+                pa_assert_not_reached();
+            }
+            *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/rtp.h b/src/modules/bluetooth/a2dp/rtp.h
similarity index 100%
rename from src/modules/bluetooth/rtp.h
rename to src/modules/bluetooth/a2dp/rtp.h
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
index 2d8337317..202652712 100644
--- a/src/modules/bluetooth/bluez5-util.c
+++ b/src/modules/bluetooth/bluez5-util.c
@@ -33,7 +33,8 @@ 
 #include <pulsecore/refcnt.h>
 #include <pulsecore/shared.h>
 
-#include "a2dp-codecs.h"
+#include "a2dp/a2dp-api.h"
+
 
 #include "bluez5-util.h"
 
@@ -48,8 +49,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 +88,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 +889,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 +902,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 +931,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 +971,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 +1269,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 +1297,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 +1350,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 +1373,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 +1387,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->set_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 +1411,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 +1425,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 +1445,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 +1462,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);
-
-    /* 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;
-        }
-    }
+    pa_a2dp_endpoint_to_codec_index(endpoint, &index);
+    pa_a2dp_codec_index_to_a2dp_codec(index, &a2dp_codec);
 
-    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 +1542,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 +1552,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 +1582,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 +1594,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 +1656,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 +1769,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 <pulsecore/core.h>
 
+#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 d983efdec..0fcde4941 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -46,9 +46,8 @@ 
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/time-smoother.h>
 
-#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");
@@ -62,13 +61,12 @@  PA_MODULE_USAGE("path=<device object 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
 };
 
@@ -94,17 +92,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;
@@ -146,7 +140,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 {
@@ -418,105 +412,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);
 
@@ -547,9 +491,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;
 
@@ -568,6 +510,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;
@@ -575,22 +519,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) {
 
@@ -607,7 +547,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) {
@@ -615,50 +555,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,
+                                                        &timestamp, &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);
 
@@ -705,40 +617,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. */
@@ -750,27 +632,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);
@@ -860,13 +721,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) {
@@ -906,9 +769,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);
@@ -1095,6 +968,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);
@@ -1270,6 +1145,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);
@@ -1296,111 +1174,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);
     }
 }
 
@@ -1620,6 +1426,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
@@ -1651,8 +1462,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;
@@ -1807,6 +1623,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;
     }
@@ -2415,6 +2239,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 =
@@ -2492,11 +2322,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 b6c8eb050..b5a813968 100644
--- a/src/modules/bluetooth/module-bluez5-discover.c
+++ b/src/modules/bluetooth/module-bluez5-discover.c
@@ -42,6 +42,7 @@  PA_MODULE_USAGE(
 static const char* const valid_modargs[] = {
     "headset",
     "autodetect_mtu",
+    "a2dp_config",
     NULL
 };
 
@@ -52,6 +53,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) {
@@ -72,7 +74,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);
@@ -100,7 +103,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;
 
@@ -129,10 +132,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)))
@@ -169,5 +176,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);
 }

Comments

On Thursday 13 December 2018 19:43:36 EHfive wrote:
> +#define A2DP_CODEC_SBC			0x00
> +#define A2DP_CODEC_MPEG12		0x01
> +#define A2DP_CODEC_MPEG24		0x02
> +#define A2DP_CODEC_ATRAC		0x03

This is incorrect, Atrac codec has A2DP ID 0x04, see:
https://www.bluetooth.com/specifications/assigned-numbers/audio-video

> +#define MAX_BITPOOL 64
> +#define MIN_BITPOOL 2

These two constants are SBC specific, so make them with SBC_ prefix.

> +#define APTX_VENDOR_ID			    0x0000004f

This ID is allocated for APT Ltd. company. Not just for aptX codec. So
maybe better name is A2DP_VENDOR_APT_LIC_LTD?

> +#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 LDAC_VENDOR_ID			0x0000012d

This ID is allocated for Sony Corporation company. Not just for LDAC
codec.

See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers

> +#elif __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;
> +	uint16_t bitrate;

This value for big endian systems seems to be broken. As you need to
store value in little endian. So maybe define as?

uint8_t bitrate_low;
uint8_t bitrate_high;

Or as

uint8_t bitrate[2];

Or better drop big endian support? Broken big endian support is not
useful at all.

> +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;
> +}

Seems that this decode procedure with while loop is similar/same for all
codecs. Months ago I sent to this mailing list different proposal for
codec API which tries to "fix" also this problem. Look into mailing list
archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".

> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"

This would not work correctly. You need for each codec different
endpoint.

Also currently bluez does not allows you to choose which endpoint will
be used... As bluez does not provide API for it yet.
"a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.

I don't want to debate too much about naming.

On 12/13/18 11:35 PM, Pali Rohár wrote:
> On Thursday 13 December 2018 19:43:36 EHfive wrote:
>> +#define A2DP_CODEC_SBC			0x00
>> +#define A2DP_CODEC_MPEG12		0x01
>> +#define A2DP_CODEC_MPEG24		0x02
>> +#define A2DP_CODEC_ATRAC		0x03
> This is incorrect, Atrac codec has A2DP ID 0x04, see:
> https://www.bluetooth.com/specifications/assigned-numbers/audio-video
>
>> +#define MAX_BITPOOL 64
>> +#define MIN_BITPOOL 2
> These two constants are SBC specific, so make them with SBC_ prefix.
>
>> +#define APTX_VENDOR_ID			    0x0000004f
> This ID is allocated for APT Ltd. company. Not just for aptX codec. So
> maybe better name is A2DP_VENDOR_APT_LIC_LTD?
>
>> +#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 LDAC_VENDOR_ID			0x0000012d
> This ID is allocated for Sony Corporation company. Not just for LDAC
> codec.
>
> See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
>
>> +#elif __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;
>> +	uint16_t bitrate;
> This value for big endian systems seems to be broken. As you need to
> store value in little endian. So maybe define as?
>
> uint8_t bitrate_low;
> uint8_t bitrate_high;
>
> Or as
>
> uint8_t bitrate[2];
>
> Or better drop big endian support? Broken big endian support is not
> useful at all.
>
>> +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;
>> +}
> Seems that this decode procedure with while loop is similar/same for all
> codecs. Months ago I sent to this mailing list different proposal for
> codec API which tries to "fix" also this problem. Look into mailing list
> archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".
>
>> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
>> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
> This would not work correctly. You need for each codec different
> endpoint.

Actually, there do have multiple endpoints .

The result is, it support multi-codec, but can't switch to other codec.

Endpoint registered first has higher priority. (see enum
pa_a2dp_codec_index )

>
> Also currently bluez does not allows you to choose which endpoint will
> be used... As bluez does not provide API for it yet.
>
On Friday 14 December 2018 00:07:27 Huang-Huang Bao wrote:
> "a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.

This does not excuse having broken code in pulseaudio. Atrac or big
endian definition seems to be really wrong.

Is not it stupid to copy+paste broken code from one project to another?

> I don't want to debate too much about naming.

Look, if other people comes up with something in the naming, then it
should be also reflected.

Bad naming just increase confusion for other people who start reading
code later. This is the fact.

> On 12/13/18 11:35 PM, Pali Rohár wrote:
> > On Thursday 13 December 2018 19:43:36 EHfive wrote:
> >> +#define A2DP_CODEC_SBC			0x00
> >> +#define A2DP_CODEC_MPEG12		0x01
> >> +#define A2DP_CODEC_MPEG24		0x02
> >> +#define A2DP_CODEC_ATRAC		0x03
> > This is incorrect, Atrac codec has A2DP ID 0x04, see:
> > https://www.bluetooth.com/specifications/assigned-numbers/audio-video
> >
> >> +#define MAX_BITPOOL 64
> >> +#define MIN_BITPOOL 2
> > These two constants are SBC specific, so make them with SBC_ prefix.
> >
> >> +#define APTX_VENDOR_ID			    0x0000004f
> > This ID is allocated for APT Ltd. company. Not just for aptX codec. So
> > maybe better name is A2DP_VENDOR_APT_LIC_LTD?
> >
> >> +#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 LDAC_VENDOR_ID			0x0000012d
> > This ID is allocated for Sony Corporation company. Not just for LDAC
> > codec.
> >
> > See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
> >
> >> +#elif __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;
> >> +	uint16_t bitrate;
> > This value for big endian systems seems to be broken. As you need to
> > store value in little endian. So maybe define as?
> >
> > uint8_t bitrate_low;
> > uint8_t bitrate_high;
> >
> > Or as
> >
> > uint8_t bitrate[2];
> >
> > Or better drop big endian support? Broken big endian support is not
> > useful at all.
> >
> >> +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;
> >> +}
> > Seems that this decode procedure with while loop is similar/same for all
> > codecs. Months ago I sent to this mailing list different proposal for
> > codec API which tries to "fix" also this problem. Look into mailing list
> > archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".
> >
> >> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
> >> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
> > This would not work correctly. You need for each codec different
> > endpoint.
> 
> Actually, there do have multiple endpoints .

Then it is really needed to have "/VENDOR" string in the middle of the
endpoint name? Because SBC codec does not have it. Is not

  A2DP_SOURCE_ENDPOINT "/" <codec_name>

enough for endpoint name?

> The result is, it support multi-codec, but can't switch to other codec.

Yes, bluez is missing API for this switch.

> Endpoint registered first has higher priority. (see enum
> pa_a2dp_codec_index )

Yes, but this priority takes effect only in the case computer creates
connection to headset. NOT when headset initialize connection to
computer.

Therefore that priority list is not always enforced.

> >
> > Also currently bluez does not allows you to choose which endpoint will
> > be used... As bluez does not provide API for it yet.
> >

> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
I don't want to debate too much about naming. In this state, we should pay attention to codes function.

Broken codes and useless codes could be cleaned up after this patches set applied.

If someone want to add another codec,  he/she can just modify "a2dp-codeces.h" or others.


On 12/14/18 5:41 PM, Pali Rohár wrote:
> On Friday 14 December 2018 00:07:27 Huang-Huang Bao wrote:
>> "a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.
> This does not excuse having broken code in pulseaudio. Atrac or big
> endian definition seems to be really wrong.
>
> Is not it stupid to copy+paste broken code from one project to another?
>
>> I don't want to debate too much about naming.
> Look, if other people comes up with something in the naming, then it
> should be also reflected.
>
> Bad naming just increase confusion for other people who start reading
> code later. This is the fact.
>
>> On 12/13/18 11:35 PM, Pali Rohár wrote:
>>> On Thursday 13 December 2018 19:43:36 EHfive wrote:
>>>> +#define A2DP_CODEC_SBC			0x00
>>>> +#define A2DP_CODEC_MPEG12		0x01
>>>> +#define A2DP_CODEC_MPEG24		0x02
>>>> +#define A2DP_CODEC_ATRAC		0x03
>>> This is incorrect, Atrac codec has A2DP ID 0x04, see:
>>> https://www.bluetooth.com/specifications/assigned-numbers/audio-video
>>>
>>>> +#define MAX_BITPOOL 64
>>>> +#define MIN_BITPOOL 2
>>> These two constants are SBC specific, so make them with SBC_ prefix.
>>>
>>>> +#define APTX_VENDOR_ID			    0x0000004f
>>> This ID is allocated for APT Ltd. company. Not just for aptX codec. So
>>> maybe better name is A2DP_VENDOR_APT_LIC_LTD?
>>>
>>>> +#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 LDAC_VENDOR_ID			0x0000012d
>>> This ID is allocated for Sony Corporation company. Not just for LDAC
>>> codec.
>>>
>>> See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
>>>
>>>> +#elif __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;
>>>> +	uint16_t bitrate;
>>> This value for big endian systems seems to be broken. As you need to
>>> store value in little endian. So maybe define as?
>>>
>>> uint8_t bitrate_low;
>>> uint8_t bitrate_high;
>>>
>>> Or as
>>>
>>> uint8_t bitrate[2];
>>>
>>> Or better drop big endian support? Broken big endian support is not
>>> useful at all.
>>>
>>>> +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;
>>>> +}
>>> Seems that this decode procedure with while loop is similar/same for all
>>> codecs. Months ago I sent to this mailing list different proposal for
>>> codec API which tries to "fix" also this problem. Look into mailing list
>>> archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".
>>>
>>>> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
>>>> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
>>> This would not work correctly. You need for each codec different
>>> endpoint.
>> Actually, there do have multiple endpoints .
> Then it is really needed to have "/VENDOR" string in the middle of the
> endpoint name? Because SBC codec does not have it. Is not
>
>   A2DP_SOURCE_ENDPOINT "/" <codec_name>
>
> enough for endpoint name?

Because all vendor codecs share the single codec-id: 0xFF, which is at
the same level with SBC or AAC.

>> The result is, it support multi-codec, but can't switch to other codec.
> Yes, bluez is missing API for this switch.
>
>> Endpoint registered first has higher priority. (see enum
>> pa_a2dp_codec_index )
> Yes, but this priority takes effect only in the case computer creates
> connection to headset. NOT when headset initialize connection to
> computer.
>
> Therefore that priority list is not always enforced.

How do you know? Have you try this patches set ?

Whatever, It works fine for me.

>>> Also currently bluez does not allows you to choose which endpoint will
>>> be used... As bluez does not provide API for it yet.
>>>
>> _______________________________________________
>> pulseaudio-discuss mailing list
>> pulseaudio-discuss@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>
On Friday 14 December 2018 18:48:05 Huang-Huang Bao wrote:
> I don't want to debate too much about naming. In this state, we should pay attention to codes function.
> 
> Broken codes and useless codes could be cleaned up after this patches set applied.
> 
> If someone want to add another codec,  he/she can just modify "a2dp-codeces.h" or others.
> 
> 
> On 12/14/18 5:41 PM, Pali Rohár wrote:
> > On Friday 14 December 2018 00:07:27 Huang-Huang Bao wrote:
> >> "a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.
> > This does not excuse having broken code in pulseaudio. Atrac or big
> > endian definition seems to be really wrong.
> >
> > Is not it stupid to copy+paste broken code from one project to another?
> >
> >> I don't want to debate too much about naming.
> > Look, if other people comes up with something in the naming, then it
> > should be also reflected.
> >
> > Bad naming just increase confusion for other people who start reading
> > code later. This is the fact.
> >
> >> On 12/13/18 11:35 PM, Pali Rohár wrote:
> >>> On Thursday 13 December 2018 19:43:36 EHfive wrote:
> >>>> +#define A2DP_CODEC_SBC			0x00
> >>>> +#define A2DP_CODEC_MPEG12		0x01
> >>>> +#define A2DP_CODEC_MPEG24		0x02
> >>>> +#define A2DP_CODEC_ATRAC		0x03
> >>> This is incorrect, Atrac codec has A2DP ID 0x04, see:
> >>> https://www.bluetooth.com/specifications/assigned-numbers/audio-video
> >>>
> >>>> +#define MAX_BITPOOL 64
> >>>> +#define MIN_BITPOOL 2
> >>> These two constants are SBC specific, so make them with SBC_ prefix.
> >>>
> >>>> +#define APTX_VENDOR_ID			    0x0000004f
> >>> This ID is allocated for APT Ltd. company. Not just for aptX codec. So
> >>> maybe better name is A2DP_VENDOR_APT_LIC_LTD?
> >>>
> >>>> +#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 LDAC_VENDOR_ID			0x0000012d
> >>> This ID is allocated for Sony Corporation company. Not just for LDAC
> >>> codec.
> >>>
> >>> See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
> >>>
> >>>> +#elif __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;
> >>>> +	uint16_t bitrate;
> >>> This value for big endian systems seems to be broken. As you need to
> >>> store value in little endian. So maybe define as?
> >>>
> >>> uint8_t bitrate_low;
> >>> uint8_t bitrate_high;
> >>>
> >>> Or as
> >>>
> >>> uint8_t bitrate[2];
> >>>
> >>> Or better drop big endian support? Broken big endian support is not
> >>> useful at all.
> >>>
> >>>> +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;
> >>>> +}
> >>> Seems that this decode procedure with while loop is similar/same for all
> >>> codecs. Months ago I sent to this mailing list different proposal for
> >>> codec API which tries to "fix" also this problem. Look into mailing list
> >>> archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".
> >>>
> >>>> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
> >>>> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
> >>> This would not work correctly. You need for each codec different
> >>> endpoint.
> >> Actually, there do have multiple endpoints .
> > Then it is really needed to have "/VENDOR" string in the middle of the
> > endpoint name? Because SBC codec does not have it. Is not
> >
> >   A2DP_SOURCE_ENDPOINT "/" <codec_name>
> >
> > enough for endpoint name?
> 
> Because all vendor codecs share the single codec-id: 0xFF, which is at
> the same level with SBC or AAC.

But this does not answer my question. Why we need to have "VENDOR"
string for every 0xFF codec in bluez dbus endpoint name? It does not
bring any value. Bluez endpoint dbus name can be any unique string. So
why not just use simple pattern /MediaEndpoint/A2DPSink/<my_codec_name>?
Normally slash in dbus path is used for sub-resource (or something
similar). But here SBC codec (at /MediaEndpoint/A2DPSink/SBC) has
exactly same meaning as your (/MediaEndpoint/A2DPSink/VENDOR/APTX) aptX
codec. So logically those two endpoints should be at same level
/MediaEndpoint/A2DPSink/<--->.

Bluetooth A2DP specification and its usage of 0xFF for other vendor
codecs is just different layer which bluez/pulseaudio abstract. So it
does not have to leak from one layer to another.

Also pulseaudio source files do not have /.*vendor.*/ in codec names.

> >> The result is, it support multi-codec, but can't switch to other codec.
> > Yes, bluez is missing API for this switch.
> >
> >> Endpoint registered first has higher priority. (see enum
> >> pa_a2dp_codec_index )
> > Yes, but this priority takes effect only in the case computer creates
> > connection to headset. NOT when headset initialize connection to
> > computer.
> >
> > Therefore that priority list is not always enforced.
> 
> How do you know?

Because I wrote my own implementation of aptX and FastStream codec
support for pulseaudio. Also I played with bluez a lot. In summer I sent
those my patches to pulseaudio mailing list, including proposal for
pulseaudio codec API.

And there was discussion about it. Also bluez developer confirmed that
priority list is not applied when headset initialize connection.

So for proper codec selection or fully working priority list, it is
needed to implement this missing API in bluez first.

> Have you try this patches set ?

Note yet. But you are using exactly same bluez API and same logic for
registering endpoints as I used. Therefore it behaves in same way.

> Whatever, It works fine for me.

This is not general argument or prove that something is working or not.

> >>> Also currently bluez does not allows you to choose which endpoint will
> >>> be used... As bluez does not provide API for it yet.
> >>>
> >> _______________________________________________
> >> pulseaudio-discuss mailing list
> >> pulseaudio-discuss@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
> >
>
On 12/14/18 7:01 PM, Pali Rohár wrote:
> On Friday 14 December 2018 18:48:05 Huang-Huang Bao wrote:
>> I don't want to debate too much about naming. In this state, we should pay attention to codes function.
>>
>> Broken codes and useless codes could be cleaned up after this patches set applied.
>>
>> If someone want to add another codec,  he/she can just modify "a2dp-codeces.h" or others.
>>
>>
>> On 12/14/18 5:41 PM, Pali Rohár wrote:
>>> On Friday 14 December 2018 00:07:27 Huang-Huang Bao wrote:
>>>> "a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.
>>> This does not excuse having broken code in pulseaudio. Atrac or big
>>> endian definition seems to be really wrong.
>>>
>>> Is not it stupid to copy+paste broken code from one project to another?
>>>
>>>> I don't want to debate too much about naming.
>>> Look, if other people comes up with something in the naming, then it
>>> should be also reflected.
>>>
>>> Bad naming just increase confusion for other people who start reading
>>> code later. This is the fact.
>>>
>>>> On 12/13/18 11:35 PM, Pali Rohár wrote:
>>>>> On Thursday 13 December 2018 19:43:36 EHfive wrote:
>>>>>> +#define A2DP_CODEC_SBC			0x00
>>>>>> +#define A2DP_CODEC_MPEG12		0x01
>>>>>> +#define A2DP_CODEC_MPEG24		0x02
>>>>>> +#define A2DP_CODEC_ATRAC		0x03
>>>>> This is incorrect, Atrac codec has A2DP ID 0x04, see:
>>>>> https://www.bluetooth.com/specifications/assigned-numbers/audio-video
>>>>>
>>>>>> +#define MAX_BITPOOL 64
>>>>>> +#define MIN_BITPOOL 2
>>>>> These two constants are SBC specific, so make them with SBC_ prefix.
>>>>>
>>>>>> +#define APTX_VENDOR_ID			    0x0000004f
>>>>> This ID is allocated for APT Ltd. company. Not just for aptX codec. So
>>>>> maybe better name is A2DP_VENDOR_APT_LIC_LTD?
>>>>>
>>>>>> +#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 LDAC_VENDOR_ID			0x0000012d
>>>>> This ID is allocated for Sony Corporation company. Not just for LDAC
>>>>> codec.
>>>>>
>>>>> See: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
>>>>>
>>>>>> +#elif __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;
>>>>>> +	uint16_t bitrate;
>>>>> This value for big endian systems seems to be broken. As you need to
>>>>> store value in little endian. So maybe define as?
>>>>>
>>>>> uint8_t bitrate_low;
>>>>> uint8_t bitrate_high;
>>>>>
>>>>> Or as
>>>>>
>>>>> uint8_t bitrate[2];
>>>>>
>>>>> Or better drop big endian support? Broken big endian support is not
>>>>> useful at all.
>>>>>
>>>>>> +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;
>>>>>> +}
>>>>> Seems that this decode procedure with while loop is similar/same for all
>>>>> codecs. Months ago I sent to this mailing list different proposal for
>>>>> codec API which tries to "fix" also this problem. Look into mailing list
>>>>> archive for "[PATCH v2 1/2] Modular API for Bluetooth A2DP codec".
>>>>>
>>>>>> +#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_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR"
>>>>>> +#define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR"
>>>>> This would not work correctly. You need for each codec different
>>>>> endpoint.
>>>> Actually, there do have multiple endpoints .
>>> Then it is really needed to have "/VENDOR" string in the middle of the
>>> endpoint name? Because SBC codec does not have it. Is not
>>>
>>>   A2DP_SOURCE_ENDPOINT "/" <codec_name>
>>>
>>> enough for endpoint name?
>> Because all vendor codecs share the single codec-id: 0xFF, which is at
>> the same level with SBC or AAC.
> But this does not answer my question. Why we need to have "VENDOR"
> string for every 0xFF codec in bluez dbus endpoint name? It does not
> bring any value. Bluez endpoint dbus name can be any unique string. So
> why not just use simple pattern /MediaEndpoint/A2DPSink/<my_codec_name>?
> Normally slash in dbus path is used for sub-resource (or something
> similar). But here SBC codec (at /MediaEndpoint/A2DPSink/SBC) has
> exactly same meaning as your (/MediaEndpoint/A2DPSink/VENDOR/APTX) aptX
> codec. So logically those two endpoints should be at same level
> /MediaEndpoint/A2DPSink/<--->.
>
> Bluetooth A2DP specification and its usage of 0xFF for other vendor
> codecs is just different layer which bluez/pulseaudio abstract. So it
> does not have to leak from one layer to another.
>
> Also pulseaudio source files do not have /.*vendor.*/ in codec names.

Bluez's media api:

Service        unique name
Interface    org.bluez.MediaEndpoint1
Object path    freely definable

Methods    array{byte} SelectConfiguration(array{byte} capabilities)


We can know codec type (SBC, AAC , ... , Vendor codecs) from object path
only.

Add "/VENDOR" make it simpler.

>  
>>>> The result is, it support multi-codec, but can't switch to other codec.
>>> Yes, bluez is missing API for this switch.
>>>
>>>> Endpoint registered first has higher priority. (see enum
>>>> pa_a2dp_codec_index )
>>> Yes, but this priority takes effect only in the case computer creates
>>> connection to headset. NOT when headset initialize connection to
>>> computer.
>>>
>>> Therefore that priority list is not always enforced.
>> How do you know?
> Because I wrote my own implementation of aptX and FastStream codec
> support for pulseaudio. Also I played with bluez a lot. In summer I sent
> those my patches to pulseaudio mailing list, including proposal for
> pulseaudio codec API.
>
> And there was discussion about it. Also bluez developer confirmed that
> priority list is not applied when headset initialize connection.
>
> So for proper codec selection or fully working priority list, it is
> needed to implement this missing API in bluez first.
>
>> Have you try this patches set ?
> Note yet. But you are using exactly same bluez API and same logic for
> registering endpoints as I used. Therefore it behaves in same way.
>
>> Whatever, It works fine for me.
> This is not general argument or prove that something is working or not.
>
>>>>> Also currently bluez does not allows you to choose which endpoint will
>>>>> be used... As bluez does not provide API for it yet.
>>>>>
>>>> _______________________________________________
>>>> pulseaudio-discuss mailing list
>>>> pulseaudio-discuss@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
On Fri, Dec 14, 2018 at 06:48:05PM +0800, Huang-Huang Bao wrote:
> I don't want to debate too much about naming. In this state, we should pay attention to codes function.
> 
> Broken codes and useless codes could be cleaned up after this patches set applied.
>

The common practice in opensource projects is that review comments
need to be addressed *before* the patches can be merged.

The author of the patches might need to post multiple iterations 
of the patches before all the reviewers/maintainers are happy with the patches, 
and the patches can then be merged.


-- Pasi

> If someone want to add another codec,  he/she can just modify "a2dp-codeces.h" or others.
> 
>
On Fri, Dec 14, 2018 at 12:01:57PM +0100, Pali Rohár wrote:
> > >
> > > Therefore that priority list is not always enforced.
> > 
> > How do you know?
> 
> Because I wrote my own implementation of aptX and FastStream codec
> support for pulseaudio. Also I played with bluez a lot. In summer I sent
> those my patches to pulseaudio mailing list, including proposal for
> pulseaudio codec API.
> 
> And there was discussion about it. Also bluez developer confirmed that
> priority list is not applied when headset initialize connection.
>

So what's needed to get the pulseaudio codec api supported in pulseaudio / patches merged? 
Are there still not-yet-implemented items related to the pulseaudio codec api proposal?
More reviews needed?

I understand this is not a simple change, just trying to understand the current status.


Thanks,

-- Pasi
On Thursday 13 December 2018 19:43:36 EHfive wrote:
> diff --git a/src/modules/bluetooth/a2dp/a2dp-api.h b/src/modules/bluetooth/a2dp/a2dp-api.h
> new file mode 100644
> index 000000000..b357555d0
> --- /dev/null
> +++ b/src/modules/bluetooth/a2dp/a2dp-api.h
> @@ -0,0 +1,170 @@
> +#ifndef fooa2dpcodecapifoo
> +#define fooa2dpcodecapifoo
> +
> +#ifdef HAVE_CONFIG_H
> +
> +#include <config.h>
> +
> +#endif
> +
> +#include <pulse/sample.h>
> +#include <pulse/proplist.h>
> +#include <pulsecore/hashmap.h>
> +
> +#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;
> +
> +
> +/* Run from <pa_a2dp_sink_t>.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);
> +
> +
> +// Larger index stands for higher priority by default
> +typedef enum pa_a2dp_codec_index {
> +    PA_A2DP_SINK_MIN,
> +    PA_A2DP_SINK_SBC,
> +    PA_A2DP_SINK_MAX,
> +    PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX,
> +    PA_A2DP_SOURCE_SBC,
> +    PA_A2DP_SOURCE_MAX,
> +    PA_A2DP_CODEC_INDEX_UNAVAILABLE
> +} pa_a2dp_codec_index_t;

This priority enum seems to be strange. One enum mixes both sink and
sources priorities? And why sink max has same value as source min?

> +typedef struct pa_a2dp_sink {
> +    int priority;
> +
> +    /* Load decoder if it's not loaded; Return true if it's loaded */
> +    bool (*decoder_load)();

It is really needed? Cannot be decoder library dynamically linked into
pulseaudio bluez module, and therefore by definition always loaded?

Support for dlopening decoders at runtime just cause problems. Some
users would have installed needed libraries and it work without
problems. Some not and it will not work... And all users have same
pulseaudio version and compiled with same flags..

> +    /* 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 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);
> +
> +    /* The implement should 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);

You can simplify memory management if caller (instead of callee) would
be responsible for passing pointer to allocated memory (e.g. also from
stack) with buffer size. Then API would not need any "free" functions.

> +    /* Return true if configuration valid */
> +    bool (*set_configuration)(const uint8_t *selected_configuration, const size_t configuration_size);

If this function validates configuration, then "validate_configuration"
is better name.

> +
> +};
On Saturday 15 December 2018 23:15:14 Pasi Kärkkäinen wrote:
> On Fri, Dec 14, 2018 at 12:01:57PM +0100, Pali Rohár wrote:
> > > >
> > > > Therefore that priority list is not always enforced.
> > > 
> > > How do you know?
> > 
> > Because I wrote my own implementation of aptX and FastStream codec
> > support for pulseaudio. Also I played with bluez a lot. In summer I sent
> > those my patches to pulseaudio mailing list, including proposal for
> > pulseaudio codec API.
> > 
> > And there was discussion about it. Also bluez developer confirmed that
> > priority list is not applied when headset initialize connection.
> >
> 
> So what's needed to get the pulseaudio codec api supported in pulseaudio / patches merged? 
> Are there still not-yet-implemented items related to the pulseaudio codec api proposal?
> More reviews needed?
> 
> I understand this is not a simple change, just trying to understand the current status.

One of the missing part is bluez API for selecting / switching A2DP
codec. Without it, it is not possible to implement stable selection of
A2DP codec in pulseaudio.

I sent remainder email about this problem to linux-bluetooth ML:
https://www.spinics.net/lists/linux-bluetooth/msg78127.html

This is reason why I paused my work from summer for bringing Modular
codec API into pulseaudio. As all future work will depends on how that
new bluez dbus API would look like. Otherwise I would have finished my
Modular codec API... https://patchwork.freedesktop.org/series/46058/

Luiz proposed, that bluez could device's MediaEndpoint1 with
SelectConfiguration for every supported codec by remote device and
pulseaudio then could choose which one want. See:
https://www.spinics.net/lists/linux-bluetooth/msg76413.html

> 
> Thanks,
> 
> -- Pasi
> 
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
On 12/16/18 5:48 AM, Pali Rohár wrote:
> On Thursday 13 December 2018 19:43:36 EHfive wrote:
>> diff --git a/src/modules/bluetooth/a2dp/a2dp-api.h b/src/modules/bluetooth/a2dp/a2dp-api.h
>> new file mode 100644
>> index 000000000..b357555d0
>> --- /dev/null
>> +++ b/src/modules/bluetooth/a2dp/a2dp-api.h
>> @@ -0,0 +1,170 @@
>> +#ifndef fooa2dpcodecapifoo
>> +#define fooa2dpcodecapifoo
>> +
>> +#ifdef HAVE_CONFIG_H
>> +
>> +#include <config.h>
>> +
>> +#endif
>> +
>> +#include <pulse/sample.h>
>> +#include <pulse/proplist.h>
>> +#include <pulsecore/hashmap.h>
>> +
>> +#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;
>> +
>> +
>> +/* Run from <pa_a2dp_sink_t>.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);
>> +
>> +
>> +// Larger index stands for higher priority by default
>> +typedef enum pa_a2dp_codec_index {
>> +    PA_A2DP_SINK_MIN,
>> +    PA_A2DP_SINK_SBC,
>> +    PA_A2DP_SINK_MAX,
>> +    PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX,
>> +    PA_A2DP_SOURCE_SBC,
>> +    PA_A2DP_SOURCE_MAX,
>> +    PA_A2DP_CODEC_INDEX_UNAVAILABLE
>> +} pa_a2dp_codec_index_t;
> This priority enum seems to be strange. One enum mixes both sink and
> sources priorities? And why sink max has same value as source min?

Because there are some functions in a2dp-codec needs it, one enum let
functions more concise.

"PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX" is no sense.

>> +typedef struct pa_a2dp_sink {
>> +    int priority;
>> +
>> +    /* Load decoder if it's not loaded; Return true if it's loaded */
>> +    bool (*decoder_load)();
> It is really needed? Cannot be decoder library dynamically linked into
> pulseaudio bluez module, and therefore by definition always loaded?

If <pa_a2dp_codec_index>'s encoder/decoder not loaded, the load function
should return false, then "pa_a2dp_init()" would not add that codec
index to indices lists.

In this patch, all codecs libraries dynamically linked so the
"decoder_load()" always return true;

Except ffmpeg, which require a motion for loading aptX(HD) encoder/decoder.

It just support dynamic loading .

> Support for dlopening decoders at runtime just cause problems. Some
> users would have installed needed libraries and it work without
> problems. Some not and it will not work... And all users have same
> pulseaudio version and compiled with same flags..
>
>> +    /* 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 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);
>> +
>> +    /* The implement should 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);
> You can simplify memory management if caller (instead of callee) would
> be responsible for passing pointer to allocated memory (e.g. also from
> stack) with buffer size. Then API would not need any "free" functions.

Well, the api just "hide" all details.

>> +    /* Return true if configuration valid */
>> +    bool (*set_configuration)(const uint8_t *selected_configuration, const size_t configuration_size);
> If this function validates configuration, then "validate_configuration"
> is better name.
Yes. I simply use bluez apis names before.
>> +
>> +};
On Friday 14 December 2018 00:07:27 Huang-Huang Bao wrote:
> "a2dp-codecs.h" copied from bluez

I sent fixes for a2dp-codecs.h from bluez to
linux-bluetooth@vger.kernel.org list.

So wait until it is fixed and we would not need to fix these problems in
two different projects...
Hi,

On Fri, Dec 14, 2018 at 12:07:27AM +0800, Huang-Huang Bao wrote:
>    "a2dp-codecs.h" copied from bluez, then added a2dp_aptxhd_t, a2dp_ldac_t.
> 

Pali sent patches to upstream bluez, and now the codec IDs are in latest bluez git tree,
also the endianess issues are fixed.

Could you please update your patches to use the latest headers from bluez git?


Thanks,

-- Pasi