[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 6 commits: bluetooth: policy: Remove BlueZ 4 related code
Tanu Kaskinen
gitlab at gitlab.freedesktop.org
Fri Apr 12 12:17:11 UTC 2019
Tanu Kaskinen pushed to branch master at PulseAudio / pulseaudio
Commits:
e81e7a2c by Pali Rohár at 2019-04-12T10:56:25Z
bluetooth: policy: Remove BlueZ 4 related code
- - - - -
e8c4638f by Pali Rohár at 2019-04-12T10:56:28Z
bluetooth: Update a2dp-codecs.h from upstream bluez project
- - - - -
106aa914 by Pali Rohár at 2019-04-12T12:09:08Z
bluetooth: Modular API for A2DP codecs
This patch introduce new modular API for bluetooth A2DP codecs. Its
benefits are:
* bluez5-util and module-bluez5-device does not contain any codec specific
code, they are codec independent.
* For adding new A2DP codec it is needed just to adjust one table in
a2dp-codec-util.c file. All codec specific functions are in separate
codec file.
* Support for backchannel (microphone voice). Some A2DP codecs (like
FastStream or aptX Low Latency) are bi-directional and can be used for
both music playback and audio call.
* Support for more configurations per codec. This allows to implement low
quality mode of some codec together with high quality.
Current SBC codec implementation was moved from bluez5-util and
module-bluez5-device to its own file and converted to this new A2DP API.
- - - - -
745c161c by Pali Rohár at 2019-04-12T12:09:16Z
bluetooth: Add missing validations for SBC codec parameters
- - - - -
63add82c by Pali Rohár at 2019-04-12T12:09:33Z
bluetooth: Clean up SBC bitpool calculation
Remove dead code and replace numeric bitpool values by macro definitions.
Maximal bitpool value in fill_capabilities() was reduced from 64 to 53
(SBC_BITPOOL_HQ_JOINT_STEREO_44100) because default_bitpool() already set
maximal value to 53.
This patch does not change SBC behavior as maximal bitpool was already
limited to 53. So it is just clean up.
- - - - -
1b6e5b85 by Pali Rohár at 2019-04-12T12:09:49Z
bluetooth: Set correct endianity of audio samples for SBC codec
Pulseaudio SBC codec defines that audio samples are in PA_SAMPLE_S16LE
format which is little endian. But libsbc library expects audio samples by
default in host endianity which is big endian on big endian system. So SBC
support on big endian system is broken. To fix this problem tell libsbc
library that audio samples are in little endian to match PA_SIMPLE_S16LE
sample format.
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=91359
- - - - -
10 changed files:
- src/Makefile.am
- + src/modules/bluetooth/a2dp-codec-api.h
- + src/modules/bluetooth/a2dp-codec-sbc.c
- + src/modules/bluetooth/a2dp-codec-util.c
- + src/modules/bluetooth/a2dp-codec-util.h
- src/modules/bluetooth/a2dp-codecs.h
- src/modules/bluetooth/bluez5-util.c
- src/modules/bluetooth/bluez5-util.h
- src/modules/bluetooth/module-bluetooth-policy.c
- src/modules/bluetooth/module-bluez5-device.c
Changes:
=====================================
src/Makefile.am
=====================================
@@ -2131,6 +2131,9 @@ 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-codec-api.h \
+ modules/bluetooth/a2dp-codec-util.c \
+ modules/bluetooth/a2dp-codec-util.h \
modules/bluetooth/a2dp-codecs.h \
modules/bluetooth/rtp.h
if HAVE_BLUEZ_5_OFONO_HEADSET
@@ -2145,6 +2148,11 @@ 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_CPPFLAGS = $(AM_CPPFLAGS)
+
+libbluez5_util_la_SOURCES += modules/bluetooth/a2dp-codec-sbc.c
+libbluez5_util_la_LIBADD += $(SBC_LIBS)
+libbluez5_util_la_CFLAGS += $(SBC_CFLAGS)
module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
@@ -2153,8 +2161,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
=====================================
src/modules/bluetooth/a2dp-codec-api.h
=====================================
@@ -0,0 +1,95 @@
+#ifndef fooa2dpcodechfoo
+#define fooa2dpcodechfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2018-2019 Pali Rohár <pali.rohar at gmail.com>
+
+ PulseAudio 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.
+
+ PulseAudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulsecore/core.h>
+
+#define MAX_A2DP_CAPS_SIZE 254
+
+typedef struct pa_a2dp_codec_capabilities {
+ uint8_t size;
+ uint8_t buffer[]; /* max size is 254 bytes */
+} pa_a2dp_codec_capabilities;
+
+typedef struct pa_a2dp_codec_id {
+ uint8_t codec_id;
+ uint32_t vendor_id;
+ uint16_t vendor_codec_id;
+} pa_a2dp_codec_id;
+
+typedef struct pa_a2dp_codec {
+ /* Unique name of the codec, lowercase and without whitespaces, used for
+ * constructing identifier, D-Bus paths, ... */
+ const char *name;
+ /* Human readable codec description */
+ const char *description;
+
+ /* A2DP codec id */
+ pa_a2dp_codec_id id;
+
+ /* True if codec is bi-directional and supports backchannel */
+ bool support_backchannel;
+
+ /* Returns true if codec accepts capabilities, for_encoding is true when
+ * capabilities are used for encoding */
+ bool (*can_accept_capabilities)(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding);
+ /* Choose remote endpoint based on capabilities from hash map
+ * (const char *endpoint -> const pa_a2dp_codec_capabilities *capability)
+ * and returns corresponding endpoint key (or NULL when there is no valid),
+ * for_encoder is true when capabilities hash map is used for encoding */
+ const char *(*choose_remote_endpoint)(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding);
+ /* Fill codec capabilities, returns size of filled buffer */
+ uint8_t (*fill_capabilities)(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]);
+ /* Validate codec configuration, returns true on success */
+ bool (*is_configuration_valid)(const uint8_t *config_buffer, uint8_t config_size);
+ /* Fill preferred codec configuration, returns size of filled buffer or 0 on failure */
+ uint8_t (*fill_preferred_configuration)(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]);
+
+ /* Initialize codec, returns codec info data and set sample_spec,
+ * for_encoding is true when codec_info is used for encoding,
+ * for_backchannel is true when codec_info is used for backchannel */
+ void *(*init)(bool for_encoding, bool for_backchannel, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *sample_spec);
+ /* Deinitialize and release codec info data in codec_info */
+ void (*deinit)(void *codec_info);
+ /* Reset internal state of codec info data in codec_info */
+ void (*reset)(void *codec_info);
+
+ /* Get read block size for codec */
+ size_t (*get_read_block_size)(void *codec_info, size_t read_link_mtu);
+ /* Get write block size for codec */
+ size_t (*get_write_block_size)(void *codec_info, size_t write_link_mtu);
+
+ /* Reduce encoder bitrate for codec, returns new write block size or zero
+ * if not changed, called when socket is not accepting encoded data fast
+ * enough */
+ size_t (*reduce_encoder_bitrate)(void *codec_info, size_t write_link_mtu);
+
+ /* Encode input_buffer of input_size to output_buffer of output_size,
+ * returns size of filled ouput_buffer and set processed to size of
+ * processed input_buffer */
+ size_t (*encode_buffer)(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed);
+ /* Decode input_buffer of input_size to output_buffer of output_size,
+ * returns size of filled ouput_buffer and set processed to size of
+ * processed input_buffer */
+ size_t (*decode_buffer)(void *codec_info, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed);
+} pa_a2dp_codec;
+
+#endif
=====================================
src/modules/bluetooth/a2dp-codec-sbc.c
=====================================
@@ -0,0 +1,647 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2018-2019 Pali Rohár <pali.rohar at gmail.com>
+
+ PulseAudio 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.
+
+ PulseAudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/once.h>
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <arpa/inet.h>
+
+#include <sbc/sbc.h>
+
+#include "a2dp-codecs.h"
+#include "a2dp-codec-api.h"
+#include "rtp.h"
+
+#define SBC_BITPOOL_DEC_LIMIT 32
+#define SBC_BITPOOL_DEC_STEP 5
+
+struct sbc_info {
+ sbc_t sbc; /* Codec data */
+ size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */
+ uint16_t seq_num; /* Cumulative packet sequence */
+ uint8_t frequency;
+ uint8_t blocks;
+ uint8_t subbands;
+ uint8_t mode;
+ uint8_t allocation;
+ uint8_t initial_bitpool;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+};
+
+static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding) {
+ const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;
+
+ if (capabilities_size != sizeof(*capabilities))
+ return false;
+
+ if (!(capabilities->frequency & (SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000)))
+ return false;
+
+ if (!(capabilities->channel_mode & (SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO)))
+ return false;
+
+ if (!(capabilities->allocation_method & (SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS)))
+ return false;
+
+ if (!(capabilities->subbands & (SBC_SUBBANDS_4 | SBC_SUBBANDS_8)))
+ return false;
+
+ if (!(capabilities->block_length & (SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16)))
+ return false;
+
+ return true;
+}
+
+static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) {
+ const pa_a2dp_codec_capabilities *a2dp_capabilities;
+ const char *key;
+ void *state;
+
+ /* There is no preference, just choose random valid entry */
+ PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, capabilities_hashmap, state) {
+ if (can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, for_encoding))
+ return key;
+ }
+
+ return NULL;
+}
+
+static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]) {
+ a2dp_sbc_t *capabilities = (a2dp_sbc_t *) capabilities_buffer;
+
+ pa_zero(*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 = SBC_MIN_BITPOOL;
+ capabilities->max_bitpool = SBC_BITPOOL_HQ_JOINT_STEREO_44100;
+
+ return sizeof(*capabilities);
+}
+
+static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_size) {
+ const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer;
+
+ if (config_size != sizeof(*config)) {
+ pa_log_error("Invalid size of config buffer");
+ return false;
+ }
+
+ if (config->frequency != SBC_SAMPLING_FREQ_16000 && config->frequency != SBC_SAMPLING_FREQ_32000 &&
+ config->frequency != SBC_SAMPLING_FREQ_44100 && config->frequency != SBC_SAMPLING_FREQ_48000) {
+ pa_log_error("Invalid sampling frequency in configuration");
+ return false;
+ }
+
+ if (config->channel_mode != SBC_CHANNEL_MODE_MONO && config->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL &&
+ config->channel_mode != SBC_CHANNEL_MODE_STEREO && config->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) {
+ pa_log_error("Invalid channel mode in configuration");
+ return false;
+ }
+
+ if (config->allocation_method != SBC_ALLOCATION_SNR && config->allocation_method != SBC_ALLOCATION_LOUDNESS) {
+ pa_log_error("Invalid allocation method in configuration");
+ return false;
+ }
+
+ if (config->subbands != SBC_SUBBANDS_4 && config->subbands != SBC_SUBBANDS_8) {
+ pa_log_error("Invalid SBC subbands in configuration");
+ return false;
+ }
+
+ if (config->block_length != SBC_BLOCK_LENGTH_4 && config->block_length != SBC_BLOCK_LENGTH_8 &&
+ config->block_length != SBC_BLOCK_LENGTH_12 && config->block_length != SBC_BLOCK_LENGTH_16) {
+ pa_log_error("Invalid block length in configuration");
+ return false;
+ }
+
+ if (config->min_bitpool > config->max_bitpool) {
+ pa_log_error("Invalid bitpool in configuration");
+ return false;
+ }
+
+ return true;
+}
+
+static uint8_t 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:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return SBC_BITPOOL_HQ_JOINT_STEREO_44100;
+ }
+
+ case SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return SBC_BITPOOL_HQ_MONO_44100;
+
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return SBC_BITPOOL_HQ_JOINT_STEREO_44100;
+ }
+
+ case SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return SBC_BITPOOL_HQ_MONO_48000;
+
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return SBC_BITPOOL_HQ_JOINT_STEREO_48000;
+ }
+ }
+
+ pa_assert_not_reached();
+}
+
+static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
+ a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer;
+ const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;
+ int i;
+
+ 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 }
+ };
+
+ if (capabilities_size != sizeof(*capabilities)) {
+ pa_log_error("Invalid size of capabilities buffer");
+ return 0;
+ }
+
+ 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 >= default_sample_spec->rate && (capabilities->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 (capabilities->frequency & freq_table[i].cap) {
+ config->frequency = freq_table[i].cap;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ pa_log_error("Not suitable sample rate");
+ return 0;
+ }
+ }
+
+ pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
+
+ if (default_sample_spec->channels <= 1) {
+ if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO)
+ config->channel_mode = SBC_CHANNEL_MODE_MONO;
+ else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ config->channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (capabilities->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;
+ }
+ } else {
+ if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ config->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (capabilities->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ config->channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (capabilities->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ config->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO)
+ config->channel_mode = SBC_CHANNEL_MODE_MONO;
+ else {
+ pa_log_error("No supported channel modes");
+ return 0;
+ }
+ }
+
+ if (capabilities->block_length & SBC_BLOCK_LENGTH_16)
+ config->block_length = SBC_BLOCK_LENGTH_16;
+ else if (capabilities->block_length & SBC_BLOCK_LENGTH_12)
+ config->block_length = SBC_BLOCK_LENGTH_12;
+ else if (capabilities->block_length & SBC_BLOCK_LENGTH_8)
+ config->block_length = SBC_BLOCK_LENGTH_8;
+ else if (capabilities->block_length & SBC_BLOCK_LENGTH_4)
+ config->block_length = SBC_BLOCK_LENGTH_4;
+ else {
+ pa_log_error("No supported block lengths");
+ return 0;
+ }
+
+ if (capabilities->subbands & SBC_SUBBANDS_8)
+ config->subbands = SBC_SUBBANDS_8;
+ else if (capabilities->subbands & SBC_SUBBANDS_4)
+ config->subbands = SBC_SUBBANDS_4;
+ else {
+ pa_log_error("No supported subbands");
+ return 0;
+ }
+
+ if (capabilities->allocation_method & SBC_ALLOCATION_LOUDNESS)
+ config->allocation_method = SBC_ALLOCATION_LOUDNESS;
+ else if (capabilities->allocation_method & SBC_ALLOCATION_SNR)
+ config->allocation_method = SBC_ALLOCATION_SNR;
+ else {
+ pa_log_error("No supported allocation method");
+ return 0;
+ }
+
+ config->min_bitpool = (uint8_t) PA_MAX(SBC_MIN_BITPOOL, capabilities->min_bitpool);
+ config->max_bitpool = (uint8_t) PA_MIN(default_bitpool(config->frequency, config->channel_mode), capabilities->max_bitpool);
+
+ if (config->min_bitpool > config->max_bitpool) {
+ pa_log_error("No supported bitpool");
+ return 0;
+ }
+
+ return sizeof(*config);
+}
+
+static void set_params(struct sbc_info *sbc_info) {
+ sbc_info->sbc.frequency = sbc_info->frequency;
+ sbc_info->sbc.blocks = sbc_info->blocks;
+ sbc_info->sbc.subbands = sbc_info->subbands;
+ sbc_info->sbc.mode = sbc_info->mode;
+ sbc_info->sbc.allocation = sbc_info->allocation;
+ sbc_info->sbc.bitpool = sbc_info->initial_bitpool;
+ sbc_info->sbc.endian = SBC_LE;
+
+ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+}
+
+static void *init(bool for_encoding, bool for_backchannel, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *sample_spec) {
+ struct sbc_info *sbc_info;
+ const a2dp_sbc_t *config = (const a2dp_sbc_t *) config_buffer;
+ int ret;
+
+ pa_assert(config_size == sizeof(*config));
+ pa_assert(!for_backchannel);
+
+ sbc_info = pa_xnew0(struct sbc_info, 1);
+
+ ret = sbc_init(&sbc_info->sbc, 0);
+ if (ret != 0) {
+ pa_xfree(sbc_info);
+ pa_log_error("SBC initialization failed: %d", ret);
+ return NULL;
+ }
+
+ sample_spec->format = PA_SAMPLE_S16LE;
+
+ switch (config->frequency) {
+ case SBC_SAMPLING_FREQ_16000:
+ sbc_info->frequency = SBC_FREQ_16000;
+ sample_spec->rate = 16000U;
+ break;
+ case SBC_SAMPLING_FREQ_32000:
+ sbc_info->frequency = SBC_FREQ_32000;
+ sample_spec->rate = 32000U;
+ break;
+ case SBC_SAMPLING_FREQ_44100:
+ sbc_info->frequency = SBC_FREQ_44100;
+ sample_spec->rate = 44100U;
+ break;
+ case SBC_SAMPLING_FREQ_48000:
+ sbc_info->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->mode = SBC_MODE_MONO;
+ sample_spec->channels = 1;
+ break;
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ sbc_info->mode = SBC_MODE_DUAL_CHANNEL;
+ sample_spec->channels = 2;
+ break;
+ case SBC_CHANNEL_MODE_STEREO:
+ sbc_info->mode = SBC_MODE_STEREO;
+ sample_spec->channels = 2;
+ break;
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ sbc_info->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->allocation = SBC_AM_SNR;
+ break;
+ case SBC_ALLOCATION_LOUDNESS:
+ sbc_info->allocation = SBC_AM_LOUDNESS;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ switch (config->subbands) {
+ case SBC_SUBBANDS_4:
+ sbc_info->subbands = SBC_SB_4;
+ break;
+ case SBC_SUBBANDS_8:
+ sbc_info->subbands = SBC_SB_8;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ switch (config->block_length) {
+ case SBC_BLOCK_LENGTH_4:
+ sbc_info->blocks = SBC_BLK_4;
+ break;
+ case SBC_BLOCK_LENGTH_8:
+ sbc_info->blocks = SBC_BLK_8;
+ break;
+ case SBC_BLOCK_LENGTH_12:
+ sbc_info->blocks = SBC_BLK_12;
+ break;
+ case SBC_BLOCK_LENGTH_16:
+ sbc_info->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->initial_bitpool = for_encoding ? sbc_info->max_bitpool : sbc_info->min_bitpool;
+
+ set_params(sbc_info);
+
+ pa_log_info("SBC parameters: allocation=%s, subbands=%u, blocks=%u, mode=%s bitpool=%u codesize=%u frame_length=%u",
+ sbc_info->sbc.allocation ? "SNR" : "Loudness", sbc_info->sbc.subbands ? 8 : 4,
+ (sbc_info->sbc.blocks+1)*4, sbc_info->sbc.mode == SBC_MODE_MONO ? "Mono" :
+ sbc_info->sbc.mode == SBC_MODE_DUAL_CHANNEL ? "DualChannel" :
+ sbc_info->sbc.mode == SBC_MODE_STEREO ? "Stereo" : "JointStereo",
+ sbc_info->sbc.bitpool, (unsigned)sbc_info->codesize, (unsigned)sbc_info->frame_length);
+
+ return sbc_info;
+}
+
+static void deinit(void *codec_info) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+
+ sbc_finish(&sbc_info->sbc);
+ pa_xfree(sbc_info);
+}
+
+static void set_bitpool(struct sbc_info *sbc_info, uint8_t bitpool) {
+ 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 reset(void *codec_info) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+ int ret;
+
+ ret = sbc_reinit(&sbc_info->sbc, 0);
+ if (ret != 0) {
+ pa_log_error("SBC reinitialization failed: %d", ret);
+ return;
+ }
+
+ /* sbc_reinit() sets also default parameters, so reset them back */
+ set_params(sbc_info);
+
+ sbc_info->seq_num = 0;
+}
+
+static size_t get_block_size(void *codec_info, size_t link_mtu) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+
+ return (link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+ / sbc_info->frame_length * sbc_info->codesize;
+}
+
+static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+ uint8_t bitpool;
+
+ /* Check if bitpool is already at its limit */
+ if (sbc_info->sbc.bitpool <= SBC_BITPOOL_DEC_LIMIT)
+ return 0;
+
+ bitpool = sbc_info->sbc.bitpool - SBC_BITPOOL_DEC_STEP;
+
+ if (bitpool < SBC_BITPOOL_DEC_LIMIT)
+ bitpool = SBC_BITPOOL_DEC_LIMIT;
+
+ if (sbc_info->sbc.bitpool == bitpool)
+ return 0;
+
+ set_bitpool(sbc_info, bitpool);
+ return get_block_size(codec_info, write_link_mtu);
+}
+
+static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+ struct rtp_header *header;
+ struct rtp_payload *payload;
+ uint8_t *d;
+ const uint8_t *p;
+ size_t to_write, to_encode;
+ unsigned frame_count;
+
+ header = (struct rtp_header*) output_buffer;
+ payload = (struct rtp_payload*) (output_buffer + sizeof(*header));
+
+ frame_count = 0;
+
+ p = input_buffer;
+ to_encode = input_size;
+
+ d = output_buffer + sizeof(*header) + sizeof(*payload);
+ to_write = output_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);
+ *processed = p - input_buffer;
+ 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 += encoded;
+ to_encode -= encoded;
+
+ d += written;
+ to_write -= written;
+
+ frame_count++;
+ }
+
+ PA_ONCE_BEGIN {
+ pa_log_debug("Using SBC codec implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc)));
+ } PA_ONCE_END;
+
+ /* write it to the fifo */
+ memset(output_buffer, 0, sizeof(*header) + sizeof(*payload));
+ header->v = 2;
+
+ /* A2DP spec: "A payload type in the RTP dynamic range shall be chosen".
+ * RFC3551 defines the dynamic range to span from 96 to 127, and 96 appears
+ * to be the most common choice in A2DP implementations. */
+ header->pt = 96;
+
+ header->sequence_number = htons(sbc_info->seq_num++);
+ header->timestamp = htonl(timestamp);
+ header->ssrc = htonl(1);
+ payload->frame_count = frame_count;
+
+ *processed = p - input_buffer;
+ return d - output_buffer;
+}
+
+static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) {
+ struct sbc_info *sbc_info = (struct sbc_info *) codec_info;
+
+ struct rtp_header *header;
+ struct rtp_payload *payload;
+ const uint8_t *p;
+ uint8_t *d;
+ size_t to_write, to_decode;
+
+ header = (struct rtp_header *) input_buffer;
+ payload = (struct rtp_payload*) (input_buffer + sizeof(*header));
+
+ p = input_buffer + sizeof(*header) + sizeof(*payload);
+ to_decode = input_size - sizeof(*header) - sizeof(*payload);
+
+ d = output_buffer;
+ to_write = output_size;
+
+ 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);
+ *processed = p - input_buffer;
+ return 0;
+ }
+
+ /* 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 += decoded;
+ to_decode -= decoded;
+
+ d += written;
+ to_write -= written;
+ }
+
+ *processed = p - input_buffer;
+ return d - output_buffer;
+}
+
+const pa_a2dp_codec pa_a2dp_codec_sbc = {
+ .name = "sbc",
+ .description = "SBC",
+ .id = { A2DP_CODEC_SBC, 0, 0 },
+ .support_backchannel = false,
+ .can_accept_capabilities = can_accept_capabilities,
+ .choose_remote_endpoint = choose_remote_endpoint,
+ .fill_capabilities = fill_capabilities,
+ .is_configuration_valid = is_configuration_valid,
+ .fill_preferred_configuration = fill_preferred_configuration,
+ .init = init,
+ .deinit = deinit,
+ .reset = reset,
+ .get_read_block_size = get_block_size,
+ .get_write_block_size = get_block_size,
+ .reduce_encoder_bitrate = reduce_encoder_bitrate,
+ .encode_buffer = encode_buffer,
+ .decode_buffer = decode_buffer,
+};
=====================================
src/modules/bluetooth/a2dp-codec-util.c
=====================================
@@ -0,0 +1,56 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2019 Pali Rohár <pali.rohar at gmail.com>
+
+ PulseAudio 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.
+
+ PulseAudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+
+#include "a2dp-codec-util.h"
+
+extern const pa_a2dp_codec pa_a2dp_codec_sbc;
+
+/* This is list of supported codecs. Their order is important.
+ * Codec with higher index has higher priority. */
+const pa_a2dp_codec *pa_a2dp_codecs[] = {
+ &pa_a2dp_codec_sbc,
+};
+
+unsigned int pa_bluetooth_a2dp_codec_count(void) {
+ return PA_ELEMENTSOF(pa_a2dp_codecs);
+}
+
+const pa_a2dp_codec *pa_bluetooth_a2dp_codec_iter(unsigned int i) {
+ pa_assert(i < pa_bluetooth_a2dp_codec_count());
+ return pa_a2dp_codecs[i];
+}
+
+const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name) {
+ unsigned int i;
+ unsigned int count = pa_bluetooth_a2dp_codec_count();
+
+ for (i = 0; i < count; i++) {
+ if (pa_streq(pa_a2dp_codecs[i]->name, name))
+ return pa_a2dp_codecs[i];
+ }
+
+ return NULL;
+}
=====================================
src/modules/bluetooth/a2dp-codec-util.h
=====================================
@@ -0,0 +1,34 @@
+#ifndef fooa2dpcodecutilhfoo
+#define fooa2dpcodecutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2019 Pali Rohár <pali.rohar at gmail.com>
+
+ PulseAudio 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.
+
+ PulseAudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "a2dp-codec-api.h"
+
+/* Get number of supported A2DP codecs */
+unsigned int pa_bluetooth_a2dp_codec_count(void);
+
+/* Get i-th codec. Codec with higher number has higher priority */
+const pa_a2dp_codec *pa_bluetooth_a2dp_codec_iter(unsigned int i);
+
+/* Get codec by name */
+const pa_a2dp_codec *pa_bluetooth_get_a2dp_codec(const char *name);
+
+#endif
=====================================
src/modules/bluetooth/a2dp-codecs.h
=====================================
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel at holtmann.org>
+ * Copyright (C) 2018 Pali Rohár <pali.rohar at gmail.com>
*
*
* This library is free software; you can redistribute it and/or
@@ -17,14 +18,19 @@
* 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/>.
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+#include <endian.h>
+#include <stdint.h>
+
#define A2DP_CODEC_SBC 0x00
#define A2DP_CODEC_MPEG12 0x01
#define A2DP_CODEC_MPEG24 0x02
-#define A2DP_CODEC_ATRAC 0x03
+#define A2DP_CODEC_ATRAC 0x04
+#define A2DP_CODEC_VENDOR 0xFF
#define SBC_SAMPLING_FREQ_16000 (1 << 3)
#define SBC_SAMPLING_FREQ_32000 (1 << 2)
@@ -47,6 +53,23 @@
#define SBC_ALLOCATION_SNR (1 << 1)
#define SBC_ALLOCATION_LOUDNESS 1
+#define SBC_MIN_BITPOOL 2
+#define SBC_MAX_BITPOOL 250
+
+/* Other settings:
+ * Block length = 16
+ * Allocation method = Loudness
+ * Subbands = 8
+ */
+#define SBC_BITPOOL_MQ_MONO_44100 19
+#define SBC_BITPOOL_MQ_MONO_48000 18
+#define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35
+#define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33
+#define SBC_BITPOOL_HQ_MONO_44100 31
+#define SBC_BITPOOL_HQ_MONO_48000 29
+#define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53
+#define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51
+
#define MPEG_CHANNEL_MODE_MONO (1 << 3)
#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
@@ -63,10 +86,228 @@
#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
#define MPEG_SAMPLING_FREQ_48000 1
-#define MAX_BITPOOL 64
-#define MIN_BITPOOL 2
+#define MPEG_BIT_RATE_INDEX_0 (1 << 0)
+#define MPEG_BIT_RATE_INDEX_1 (1 << 1)
+#define MPEG_BIT_RATE_INDEX_2 (1 << 2)
+#define MPEG_BIT_RATE_INDEX_3 (1 << 3)
+#define MPEG_BIT_RATE_INDEX_4 (1 << 4)
+#define MPEG_BIT_RATE_INDEX_5 (1 << 5)
+#define MPEG_BIT_RATE_INDEX_6 (1 << 6)
+#define MPEG_BIT_RATE_INDEX_7 (1 << 7)
+#define MPEG_BIT_RATE_INDEX_8 (1 << 8)
+#define MPEG_BIT_RATE_INDEX_9 (1 << 9)
+#define MPEG_BIT_RATE_INDEX_10 (1 << 10)
+#define MPEG_BIT_RATE_INDEX_11 (1 << 11)
+#define MPEG_BIT_RATE_INDEX_12 (1 << 12)
+#define MPEG_BIT_RATE_INDEX_13 (1 << 13)
+#define MPEG_BIT_RATE_INDEX_14 (1 << 14)
+
+#define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+#define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2
+#define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3
+#define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4
+#define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5
+#define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6
+#define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7
+#define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8
+#define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9
+#define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10
+#define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11
+#define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12
+#define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13
+#define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14
+
+#define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0
+
+#define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2)
+#define MPEG_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = ((b) >> 8) & 0x7f; \
+ (a).bitrate2 = (b) & 0xff; \
+ } while (0)
+
+#define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20
+#define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10
+
+#define AAC_SAMPLING_FREQ_8000 0x0800
+#define AAC_SAMPLING_FREQ_11025 0x0400
+#define AAC_SAMPLING_FREQ_12000 0x0200
+#define AAC_SAMPLING_FREQ_16000 0x0100
+#define AAC_SAMPLING_FREQ_22050 0x0080
+#define AAC_SAMPLING_FREQ_24000 0x0040
+#define AAC_SAMPLING_FREQ_32000 0x0020
+#define AAC_SAMPLING_FREQ_44100 0x0010
+#define AAC_SAMPLING_FREQ_48000 0x0008
+#define AAC_SAMPLING_FREQ_64000 0x0004
+#define AAC_SAMPLING_FREQ_88200 0x0002
+#define AAC_SAMPLING_FREQ_96000 0x0001
+
+#define AAC_CHANNELS_1 0x02
+#define AAC_CHANNELS_2 0x01
+
+#define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \
+ (a).bitrate2 << 8 | (a).bitrate3)
+#define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2)
+
+#define AAC_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = (b >> 16) & 0x7f; \
+ (a).bitrate2 = (b >> 8) & 0xff; \
+ (a).bitrate3 = b & 0xff; \
+ } while (0)
+#define AAC_SET_FREQUENCY(a, f) \
+ do { \
+ (a).frequency1 = (f >> 4) & 0xff; \
+ (a).frequency2 = f & 0x0f; \
+ } while (0)
+
+#define AAC_INIT_BITRATE(b) \
+ .bitrate1 = (b >> 16) & 0x7f, \
+ .bitrate2 = (b >> 8) & 0xff, \
+ .bitrate3 = b & 0xff,
+#define AAC_INIT_FREQUENCY(f) \
+ .frequency1 = (f >> 4) & 0xff, \
+ .frequency2 = f & 0x0f,
+
+#define APTX_VENDOR_ID 0x0000004f
+#define APTX_CODEC_ID 0x0001
+
+#define APTX_CHANNEL_MODE_MONO 0x01
+#define APTX_CHANNEL_MODE_STEREO 0x02
+
+#define APTX_SAMPLING_FREQ_16000 0x08
+#define APTX_SAMPLING_FREQ_32000 0x04
+#define APTX_SAMPLING_FREQ_44100 0x02
+#define APTX_SAMPLING_FREQ_48000 0x01
+
+#define FASTSTREAM_VENDOR_ID 0x0000000a
+#define FASTSTREAM_CODEC_ID 0x0001
+
+#define FASTSTREAM_DIRECTION_SINK 0x1
+#define FASTSTREAM_DIRECTION_SOURCE 0x2
+
+#define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2
+#define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1
+
+#define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2
+
+#define APTX_LL_VENDOR_ID 0x0000000a
+#define APTX_LL_CODEC_ID 0x0002
+
+/* Default parameters for aptX Low Latency encoder */
+
+/* Target codec buffer level = 180 */
+#define APTX_LL_TARGET_LEVEL2 0xb4
+#define APTX_LL_TARGET_LEVEL1 0x00
+
+/* Initial codec buffer level = 360 */
+#define APTX_LL_INITIAL_LEVEL2 0x68
+#define APTX_LL_INITIAL_LEVEL1 0x01
+
+/* SRA max rate 0.005 * 10000 = 50 */
+#define APTX_LL_SRA_MAX_RATE 0x32
+
+/* SRA averaging time = 1s */
+#define APTX_LL_SRA_AVG_TIME 0x01
+
+/* Good working codec buffer level = 180 */
+#define APTX_LL_GOOD_WORKING_LEVEL2 0xB4
+#define APTX_LL_GOOD_WORKING_LEVEL1 0x00
+
+#define APTX_HD_VENDOR_ID 0x000000D7
+#define APTX_HD_CODEC_ID 0x0024
+
+#define LDAC_VENDOR_ID 0x0000012d
+#define LDAC_CODEC_ID 0x00aa
-#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define LDAC_SAMPLING_FREQ_44100 0x20
+#define LDAC_SAMPLING_FREQ_48000 0x10
+#define LDAC_SAMPLING_FREQ_88200 0x08
+#define LDAC_SAMPLING_FREQ_96000 0x04
+#define LDAC_SAMPLING_FREQ_176400 0x02
+#define LDAC_SAMPLING_FREQ_192000 0x01
+
+#define LDAC_CHANNEL_MODE_MONO 0x04
+#define LDAC_CHANNEL_MODE_DUAL 0x02
+#define LDAC_CHANNEL_MODE_STEREO 0x01
+
+typedef struct {
+ uint8_t vendor_id4;
+ uint8_t vendor_id3;
+ uint8_t vendor_id2;
+ uint8_t vendor_id1;
+ uint8_t codec_id2;
+ uint8_t codec_id1;
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
+
+#define A2DP_GET_VENDOR_ID(a) ( \
+ (((uint32_t)(a).vendor_id4) << 0) | \
+ (((uint32_t)(a).vendor_id3) << 8) | \
+ (((uint32_t)(a).vendor_id2) << 16) | \
+ (((uint32_t)(a).vendor_id1) << 24) \
+ )
+#define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8))
+#define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \
+ .vendor_id4 = (((v) >> 0) & 0xff), \
+ .vendor_id3 = (((v) >> 8) & 0xff), \
+ .vendor_id2 = (((v) >> 16) & 0xff), \
+ .vendor_id1 = (((v) >> 24) & 0xff), \
+ .codec_id2 = (((c) >> 0) & 0xff), \
+ .codec_id1 = (((c) >> 8) & 0xff), \
+ })
+
+typedef struct {
+ uint8_t reserved;
+ uint8_t target_level2;
+ uint8_t target_level1;
+ uint8_t initial_level2;
+ uint8_t initial_level1;
+ uint8_t sra_max_rate;
+ uint8_t sra_avg_time;
+ uint8_t good_working_level2;
+ uint8_t good_working_level1;
+} __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency;
+ uint8_t channel_mode;
+} __attribute__ ((packed)) a2dp_ldac_t;
+
+#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN
typedef struct {
uint8_t channel_mode:4;
@@ -85,10 +326,46 @@ typedef struct {
uint8_t frequency:6;
uint8_t mpf:1;
uint8_t rfa:1;
- uint16_t bitrate;
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
} __attribute__ ((packed)) a2dp_mpeg_t;
-#elif __BYTE_ORDER == __BIG_ENDIAN
+typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t rfa:2;
+ uint8_t channels:2;
+ uint8_t frequency2:4;
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t sink_frequency:4;
+ uint8_t source_frequency:4;
+} __attribute__ ((packed)) a2dp_faststream_t;
+
+typedef struct {
+ a2dp_aptx_t aptx;
+ uint8_t bidirect_link:1;
+ uint8_t has_new_caps:1;
+ uint8_t reserved:6;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+} __attribute__ ((packed)) a2dp_aptx_ll_t;
+
+#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN
typedef struct {
uint8_t frequency:4;
@@ -107,9 +384,52 @@ typedef struct {
uint8_t rfa:1;
uint8_t mpf:1;
uint8_t frequency:6;
- uint16_t bitrate;
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
} __attribute__ ((packed)) a2dp_mpeg_t;
+typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t frequency2:4;
+ uint8_t channels:2;
+ uint8_t rfa:2;
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
+typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t source_frequency:4;
+ uint8_t sink_frequency:4;
+} __attribute__ ((packed)) a2dp_faststream_t;
+
+typedef struct {
+ a2dp_aptx_t aptx;
+ uint8_t reserved:6;
+ uint8_t has_new_caps:1;
+ uint8_t bidirect_link:1;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+} __attribute__ ((packed)) a2dp_aptx_ll_t;
+
#else
#error "Unknown byte order"
#endif
+
+typedef struct {
+ a2dp_aptx_t aptx;
+ uint8_t reserved0;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t reserved3;
+} __attribute__ ((packed)) a2dp_aptx_hd_t;
=====================================
src/modules/bluetooth/bluez5-util.c
=====================================
@@ -2,6 +2,7 @@
This file is part of PulseAudio.
Copyright 2008-2013 João Paulo Rechi Vita
+ Copyrigth 2018-2019 Pali Rohár <pali.rohar at gmail.com>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -33,6 +34,7 @@
#include <pulsecore/refcnt.h>
#include <pulsecore/shared.h>
+#include "a2dp-codec-util.h"
#include "a2dp-codecs.h"
#include "bluez5-util.h"
@@ -885,13 +887,19 @@ finish:
pa_xfree(endpoint);
}
-static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
+static void register_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_codec *a2dp_codec, const char *path, const char *endpoint, const char *uuid) {
DBusMessage *m;
DBusMessageIter i, d;
- uint8_t codec = 0;
+ uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
+ size_t capabilities_size;
+ uint8_t codec_id;
pa_log_debug("Registering %s on adapter %s", endpoint, path);
+ codec_id = a2dp_codec->id.codec_id;
+ capabilities_size = a2dp_codec->fill_capabilities(capabilities);
+ pa_assert(capabilities_size != 0);
+
pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
dbus_message_iter_init_append(m, &i);
@@ -899,23 +907,8 @@ 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_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));
- }
+ pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
+ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
dbus_message_iter_close_container(&i, &d);
@@ -950,6 +943,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
pa_bluetooth_adapter *a;
+ unsigned a2dp_codec_i;
if ((a = pa_hashmap_get(y->adapters, path))) {
pa_log_error("Found duplicated D-Bus path for adapter %s", path);
@@ -964,8 +958,20 @@ 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);
+ /* Order is important. bluez prefers endpoints registered earlier.
+ * And codec with higher number has higher priority. So iterate in reverse order. */
+ for (a2dp_codec_i = pa_bluetooth_a2dp_codec_count(); a2dp_codec_i > 0; a2dp_codec_i--) {
+ const pa_a2dp_codec *a2dp_codec = pa_bluetooth_a2dp_codec_iter(a2dp_codec_i-1);
+ char *endpoint;
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+ pa_xfree(endpoint);
+ }
} else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
@@ -1257,48 +1263,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) {
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
@@ -1316,10 +1280,24 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
return NULL;
}
+static const pa_a2dp_codec *a2dp_endpoint_to_a2dp_codec(const char *endpoint) {
+ const char *codec_name;
+
+ if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
+ codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
+ else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
+ codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
+ else
+ return NULL;
+
+ return pa_bluetooth_get_a2dp_codec(codec_name);
+}
+
static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
pa_bluetooth_discovery *y = userdata;
pa_bluetooth_device *d;
pa_bluetooth_transport *t;
+ const pa_a2dp_codec *a2dp_codec = NULL;
const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
const uint8_t *config = NULL;
int size = 0;
@@ -1345,6 +1323,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
goto fail;
+ endpoint_path = dbus_message_get_path(m);
+
/* Read transport properties */
while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
const char *key;
@@ -1367,16 +1347,13 @@ 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_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
- p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
- } else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) {
- if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
- p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
- }
+ if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+ else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
- if (p == PA_BLUETOOTH_PROFILE_OFF) {
+ if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
+ (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
goto fail;
}
@@ -1389,7 +1366,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,45 +1380,20 @@ 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;
+ a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
+ pa_assert(a2dp_codec);
- 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->is_configuration_valid(config, 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);
}
+ if (!a2dp_codec)
+ goto fail2;
+
if ((d = pa_hashmap_get(y->devices, dev_path))) {
if (!d->valid) {
pa_log_error("Information about device %s is invalid", dev_path);
@@ -1466,6 +1417,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
dbus_message_unref(r);
t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
+ t->a2dp_codec = a2dp_codec;
t->acquire = bluez5_transport_acquire_cb;
t->release = bluez5_transport_release_cb;
pa_bluetooth_transport_put(t);
@@ -1484,21 +1436,17 @@ 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;
+ const char *endpoint_path;
+ uint8_t *cap;
+ int size;
+ const pa_a2dp_codec *a2dp_codec;
+ uint8_t config[MAX_A2DP_CAPS_SIZE];
+ uint8_t *config_ptr = config;
+ size_t config_size;
DBusMessage *r;
DBusError err;
- 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 }
- };
+ endpoint_path = dbus_message_get_path(m);
dbus_error_init(&err);
@@ -1508,101 +1456,15 @@ 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;
- }
- }
-
- 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");
- 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");
- goto fail;
- }
+ a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
+ pa_assert(a2dp_codec);
- 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)
+ config_size = a2dp_codec->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
+ if (config_size == 0)
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));
+ pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
return r;
@@ -1677,7 +1539,7 @@ 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))
+ if (!a2dp_endpoint_to_a2dp_codec(path))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
@@ -1705,49 +1567,32 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi
return DBUS_HANDLER_RESULT_HANDLED;
}
-static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
+static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
static const DBusObjectPathVTable vtable_endpoint = {
.message_function = endpoint_handler,
};
pa_assert(y);
+ pa_assert(endpoint);
- 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));
- 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));
- break;
- default:
- pa_assert_not_reached();
- break;
- }
+ 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) {
+static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
pa_assert(y);
+ pa_assert(endpoint);
- switch(profile) {
- case PA_BLUETOOTH_PROFILE_A2DP_SINK:
- dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
- break;
- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
- dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
- break;
- default:
- pa_assert_not_reached();
- break;
- }
+ 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) {
pa_bluetooth_discovery *y;
DBusError err;
DBusConnection *conn;
- unsigned i;
+ unsigned i, count;
+ const pa_a2dp_codec *a2dp_codec;
+ char *endpoint;
y = pa_xnew0(pa_bluetooth_discovery, 1);
PA_REFCNT_INIT(y);
@@ -1799,8 +1644,18 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe
}
y->matches_added = true;
- endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
- endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ count = pa_bluetooth_a2dp_codec_count();
+ for (i = 0; i < count; i++) {
+ a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ endpoint_init(y, endpoint);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ endpoint_init(y, endpoint);
+ pa_xfree(endpoint);
+ }
get_managed_objects(y);
@@ -1823,6 +1678,10 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
}
void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
+ unsigned i, count;
+ const pa_a2dp_codec *a2dp_codec;
+ char *endpoint;
+
pa_assert(y);
pa_assert(PA_REFCNT_VALUE(y) > 0);
@@ -1868,8 +1727,18 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
if (y->filter_added)
dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
- endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
- endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ count = pa_bluetooth_a2dp_codec_count();
+ for (i = 0; i < count; i++) {
+ a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ endpoint_done(y, endpoint);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ endpoint_done(y, endpoint);
+ pa_xfree(endpoint);
+ }
pa_dbus_connection_unref(y->connection);
}
=====================================
src/modules/bluetooth/bluez5-util.h
=====================================
@@ -5,6 +5,7 @@
This file is part of PulseAudio.
Copyright 2008-2013 João Paulo Rechi Vita
+ Copyrigth 2018-2019 Pali Rohár <pali.rohar at gmail.com>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -22,6 +23,8 @@
#include <pulsecore/core.h>
+#include "a2dp-codec-util.h"
+
#define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb"
#define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb"
@@ -82,6 +85,8 @@ struct pa_bluetooth_transport {
uint8_t *config;
size_t config_size;
+ const pa_a2dp_codec *a2dp_codec;
+
uint16_t microphone_gain;
uint16_t speaker_gain;
=====================================
src/modules/bluetooth/module-bluetooth-policy.c
=====================================
@@ -154,10 +154,10 @@ static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a
/* Check for correct profile based on revert_to_a2dp */
if (revert_to_a2dp) {
- if (!pa_streq(profile->name, "a2dp") && !pa_streq(profile->name, "a2dp_sink"))
+ if (!pa_streq(profile->name, "a2dp_sink"))
continue;
} else {
- if (!pa_streq(profile->name, "hsp") && !pa_streq(profile->name, "headset_head_unit"))
+ if (!pa_streq(profile->name, "headset_head_unit"))
continue;
}
@@ -192,19 +192,19 @@ static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
return;
/* Skip card if does not have active hsp profile */
- if (!pa_streq(card->active_profile->name, "hsp") && !pa_streq(card->active_profile->name, "headset_head_unit"))
+ if (!pa_streq(card->active_profile->name, "headset_head_unit"))
return;
/* Skip card if already has active a2dp profile */
- if (pa_streq(card->active_profile->name, "a2dp") || pa_streq(card->active_profile->name, "a2dp_sink"))
+ if (pa_streq(card->active_profile->name, "a2dp_sink"))
return;
} else {
/* Skip card if does not have active a2dp profile */
- if (!pa_streq(card->active_profile->name, "a2dp") && !pa_streq(card->active_profile->name, "a2dp_sink"))
+ if (!pa_streq(card->active_profile->name, "a2dp_sink"))
return;
/* Skip card if already has active hsp profile */
- if (pa_streq(card->active_profile->name, "hsp") || pa_streq(card->active_profile->name, "headset_head_unit"))
+ if (pa_streq(card->active_profile->name, "headset_head_unit"))
return;
}
@@ -307,7 +307,6 @@ static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *car
/* Ignore card if has already set other initial profile than a2dp */
if (card->active_profile &&
- !pa_streq(card->active_profile->name, "a2dp") &&
!pa_streq(card->active_profile->name, "a2dp_sink"))
return PA_HOOK_OK;
=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -3,6 +3,7 @@
Copyright 2008-2013 João Paulo Rechi Vita
Copyright 2011-2013 BMW Car IT GmbH.
+ Copyright 2018-2019 Pali Rohár <pali.rohar at gmail.com>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -25,7 +26,6 @@
#include <errno.h>
#include <arpa/inet.h>
-#include <sbc/sbc.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
@@ -47,8 +47,8 @@
#include <pulsecore/time-smoother.h>
#include "a2dp-codecs.h"
+#include "a2dp-codec-util.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,8 +62,6 @@ 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[] = {
@@ -94,18 +92,6 @@ 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;
-
struct userdata {
pa_module *module;
pa_core *core;
@@ -145,8 +131,18 @@ struct userdata {
pa_usec_t started_at;
pa_smoother *read_smoother;
pa_memchunk write_memchunk;
- pa_sample_spec sample_spec;
- struct sbc_info sbc_info;
+
+ const pa_a2dp_codec *a2dp_codec;
+
+ void *encoder_info;
+ pa_sample_spec encoder_sample_spec;
+ void *encoder_buffer; /* Codec transfer buffer */
+ size_t encoder_buffer_size; /* Size of the buffer */
+
+ void *decoder_info;
+ pa_sample_spec decoder_sample_spec;
+ void *decoder_buffer; /* Codec transfer buffer */
+ size_t decoder_buffer_size; /* Size of the buffer */
};
typedef enum pa_bluetooth_form_factor {
@@ -380,7 +376,7 @@ static int sco_process_push(struct userdata *u) {
* issues in our Bluetooth adapter. In these cases, in order to avoid
* an assertion failure due to unaligned data, just discard the whole
* packet */
- if (!pa_frame_aligned(l, &u->sample_spec)) {
+ if (!pa_frame_aligned(l, &u->decoder_sample_spec)) {
pa_log_warn("SCO packet received of unaligned size: %zu", l);
pa_memblock_unref(memchunk.memblock);
return -1;
@@ -403,7 +399,7 @@ static int sco_process_push(struct userdata *u) {
tstamp = pa_rtclock_now();
}
- pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
pa_smoother_resume(u->read_smoother, tstamp, true);
pa_source_post(u->source, &memchunk);
@@ -413,115 +409,37 @@ static int sco_process_push(struct userdata *u) {
}
/* Run from IO thread */
-static void a2dp_prepare_buffer(struct userdata *u) {
- size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
-
+static void a2dp_prepare_encoder_buffer(struct userdata *u) {
pa_assert(u);
- if (u->sbc_info.buffer_size >= min_buffer_size)
+ if (u->encoder_buffer_size >= u->write_link_mtu)
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->encoder_buffer_size = 2 * u->write_link_mtu;
+ pa_xfree(u->encoder_buffer);
+ u->encoder_buffer = pa_xmalloc(u->encoder_buffer_size);
}
/* 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;
- int ret = 0;
-
+static void a2dp_prepare_decoder_buffer(struct userdata *u) {
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);
-
- 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;
-
- /* A2DP spec: "A payload type in the RTP dynamic range shall be chosen".
- * RFC3551 defines the dynamic range to span from 96 to 127, and 96 appears
- * to be the most common choice in A2DP implementations. */
- header->pt = 96;
+ if (u->decoder_buffer_size >= u->read_link_mtu)
+ return;
- 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;
+ u->decoder_buffer_size = 2 * u->read_link_mtu;
+ pa_xfree(u->decoder_buffer);
+ u->decoder_buffer = pa_xmalloc(u->decoder_buffer_size);
+}
- nbytes = (uint8_t*) d - (uint8_t*) sbc_info->buffer;
+/* Run from IO thread */
+static int a2dp_write_buffer(struct userdata *u, size_t nbytes) {
+ int ret = 0;
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->encoder_buffer, nbytes, &u->stream_write_type);
pa_assert(l != 0);
@@ -564,6 +482,40 @@ static int a2dp_process_render(struct userdata *u) {
return ret;
}
+/* Run from IO thread */
+static int a2dp_process_render(struct userdata *u) {
+ const uint8_t *ptr;
+ size_t processed;
+ size_t length;
+
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ pa_assert(u->sink);
+ pa_assert(u->a2dp_codec);
+
+ /* 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);
+
+ a2dp_prepare_encoder_buffer(u);
+
+ /* Try to create a packet of the full MTU */
+ ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+
+ length = u->a2dp_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer, u->encoder_buffer_size, &processed);
+
+ pa_memblock_release(u->write_memchunk.memblock);
+
+ if (length == 0)
+ return -1;
+
+ pa_assert(processed == u->write_memchunk.length);
+
+ return a2dp_write_buffer(u, length);
+}
+
/* Run from IO thread */
static int a2dp_process_push(struct userdata *u) {
int ret = 0;
@@ -573,6 +525,7 @@ 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->a2dp_codec);
memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
memchunk.index = memchunk.length = 0;
@@ -580,22 +533,13 @@ 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;
- void *d;
+ uint8_t *ptr;
ssize_t l;
- size_t to_write, to_decode;
- size_t total_written = 0;
-
- a2dp_prepare_buffer(u);
+ size_t processed;
- sbc_info = &u->sbc_info;
- header = sbc_info->buffer;
- payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
+ a2dp_prepare_decoder_buffer(u);
- l = pa_read(u->stream_fd, sbc_info->buffer, sbc_info->buffer_size, &u->stream_write_type);
+ l = pa_read(u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &u->stream_write_type);
if (l <= 0) {
@@ -612,7 +556,7 @@ static int a2dp_process_push(struct userdata *u) {
break;
}
- pa_assert((size_t) l <= sbc_info->buffer_size);
+ pa_assert((size_t) l <= u->decoder_buffer_size);
/* TODO: get timestamp from rtp */
if (!found_tstamp) {
@@ -620,51 +564,20 @@ 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);
+ ptr = pa_memblock_acquire(memchunk.memblock);
+ 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;
- }
-
- 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;
+ memchunk.length = u->a2dp_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed);
+ if (memchunk.length == 0) {
+ pa_memblock_release(memchunk.memblock);
+ ret = 0;
+ break;
}
- u->read_index += (uint64_t) total_written;
- pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+ u->read_index += (uint64_t) memchunk.length;
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
pa_smoother_resume(u->read_smoother, tstamp, true);
- memchunk.length -= to_write;
-
pa_memblock_release(memchunk.memblock);
pa_source_post(u->source, &memchunk);
@@ -678,7 +591,7 @@ static int a2dp_process_push(struct userdata *u) {
return ret;
}
-static void update_buffer_size(struct userdata *u) {
+static void update_sink_buffer_size(struct userdata *u) {
int old_bufsize;
socklen_t len = sizeof(int);
int ret;
@@ -710,72 +623,6 @@ 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;
-
- 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));
-
- /* If there is still data in the memchunk, we have to discard it
- * because the write_block_size may have changed. */
- if (u->write_memchunk.memblock) {
- pa_memblock_unref(u->write_memchunk.memblock);
- pa_memchunk_reset(&u->write_memchunk);
- }
-
- 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);
@@ -849,6 +696,24 @@ static void transport_release(struct userdata *u) {
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
}
+/* Run from I/O thread */
+static void handle_sink_block_size_change(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,
+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ?
+ FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +
+ pa_bytes_to_usec(u->write_block_size, &u->encoder_sample_spec));
+
+ /* If there is still data in the memchunk, we have to discard it
+ * because the write_block_size may have changed. */
+ if (u->write_memchunk.memblock) {
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ }
+
+ update_sink_buffer_size(u);
+}
+
/* Run from I/O thread */
static void transport_config_mtu(struct userdata *u) {
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
@@ -865,28 +730,22 @@ 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;
+ pa_assert(u->a2dp_codec);
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ u->write_block_size = u->a2dp_codec->get_write_block_size(u->encoder_info, u->write_link_mtu);
+ } else {
+ u->read_block_size = u->a2dp_codec->get_read_block_size(u->decoder_info, u->read_link_mtu);
+ }
}
- if (u->sink) {
- pa_sink_set_max_request_within_thread(u->sink, u->write_block_size);
- pa_sink_set_fixed_latency_within_thread(u->sink,
- (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ?
- FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) +
- pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
- }
+ if (u->sink)
+ handle_sink_block_size_change(u);
if (u->source)
pa_source_set_fixed_latency_within_thread(u->source,
(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?
FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +
- pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
+ pa_bytes_to_usec(u->read_block_size, &u->decoder_sample_spec));
}
/* Run from I/O thread */
@@ -900,6 +759,14 @@ static void setup_stream(struct userdata *u) {
pa_log_info("Transport %s resuming", u->transport->path);
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ pa_assert(u->a2dp_codec);
+ u->a2dp_codec->reset(u->encoder_info);
+ } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
+ pa_assert(u->a2dp_codec);
+ u->a2dp_codec->reset(u->decoder_info);
+ }
+
transport_config_mtu(u);
pa_make_fd_nonblock(u->stream_fd);
@@ -911,11 +778,6 @@ 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);
- }
-
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->stream_fd;
@@ -957,7 +819,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (u->read_smoother) {
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
- ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
+ ri = pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec);
*((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
} else
@@ -1050,11 +912,11 @@ static void source_set_volume_cb(pa_source *s) {
if (volume < PA_VOLUME_NORM)
volume++;
- pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+ pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
/* Set soft volume when in headset role */
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
- pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
+ pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume);
/* If we are in the AG role, we send a command to the head set to change
* the microphone gain. In the HS role, source and sink are swapped, so
@@ -1075,7 +937,7 @@ static int add_source(struct userdata *u) {
data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
- pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec);
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
@@ -1133,10 +995,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->read_smoother) {
ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
- wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->sample_spec);
+ wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->encoder_sample_spec);
} else if (u->started_at) {
ri = pa_rtclock_now() - u->started_at;
- wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+ wi = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
}
*((int64_t*) data) = u->sink->thread_info.fixed_latency + wi - ri;
@@ -1224,11 +1086,11 @@ static void sink_set_volume_cb(pa_sink *s) {
if (volume < PA_VOLUME_NORM)
volume++;
- pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
+ pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
/* Set soft volume when in headset role */
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
- pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
+ pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume);
/* If we are in the AG role, we send a command to the head set to change
* the speaker gain. In the HS role, source and sink are swapped, so
@@ -1249,7 +1111,7 @@ static int add_sink(struct userdata *u) {
data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
- pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec);
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
@@ -1295,117 +1157,38 @@ static int add_sink(struct userdata *u) {
}
/* Run from main thread */
-static void transport_config(struct userdata *u) {
+static int transport_config(struct userdata *u) {
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
- u->sample_spec.format = PA_SAMPLE_S16LE;
- u->sample_spec.channels = 1;
- u->sample_spec.rate = 8000;
+ u->encoder_sample_spec.format = PA_SAMPLE_S16LE;
+ u->encoder_sample_spec.channels = 1;
+ u->encoder_sample_spec.rate = 8000;
+ u->decoder_sample_spec.format = PA_SAMPLE_S16LE;
+ u->decoder_sample_spec.channels = 1;
+ u->decoder_sample_spec.rate = 8000;
+ return 0;
} else {
- sbc_info_t *sbc_info = &u->sbc_info;
- a2dp_sbc_t *config;
+ bool is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
+ void *info;
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();
- }
-
- 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();
- }
+ pa_assert(!u->a2dp_codec);
+ pa_assert(!u->encoder_info);
+ pa_assert(!u->decoder_info);
- 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->a2dp_codec = u->transport->a2dp_codec;
+ pa_assert(u->a2dp_codec);
- sbc_info->min_bitpool = config->min_bitpool;
- sbc_info->max_bitpool = config->max_bitpool;
+ info = u->a2dp_codec->init(is_a2dp_sink, false, u->transport->config, u->transport->config_size, is_a2dp_sink ? &u->encoder_sample_spec : &u->decoder_sample_spec);
+ if (is_a2dp_sink)
+ u->encoder_info = info;
+ else
+ u->decoder_info = info;
- /* 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);
+ if (!info)
+ return -1;
- 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);
+ return 0;
}
}
@@ -1436,9 +1219,7 @@ static int setup_transport(struct userdata *u) {
return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
}
- transport_config(u);
-
- return 0;
+ return transport_config(u);
}
/* Run from main thread */
@@ -1618,12 +1399,12 @@ static void thread_func(void *userdata) {
if (u->started_at) {
time_passed = pa_rtclock_now() - u->started_at;
- audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+ audio_sent = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
}
/* A new block needs to be sent. */
if (audio_sent <= time_passed) {
- size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->sample_spec);
+ size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->encoder_sample_spec);
/* 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
@@ -1636,7 +1417,7 @@ static void thread_func(void *userdata) {
pa_usec_t skip_usec;
skip_bytes = bytes_to_send - 2 * u->write_block_size;
- skip_usec = pa_bytes_to_usec(skip_bytes, &u->sample_spec);
+ skip_usec = pa_bytes_to_usec(skip_bytes, &u->encoder_sample_spec);
pa_log_debug("Skipping %llu us (= %llu bytes) in audio stream",
(unsigned long long) skip_usec,
@@ -1656,8 +1437,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->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ size_t new_write_block_size = u->a2dp_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu);
+ if (new_write_block_size) {
+ u->write_block_size = new_write_block_size;
+ handle_sink_block_size_change(u);
+ }
+ }
}
blocks_to_write = 1;
@@ -1686,7 +1472,7 @@ static void thread_func(void *userdata) {
if (writable) {
/* There was no write pending on this iteration of the loop.
* Let's estimate when we need to wake up next */
- next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
/* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
} else
@@ -1830,6 +1616,20 @@ static void stop_thread(struct userdata *u) {
pa_smoother_free(u->read_smoother);
u->read_smoother = NULL;
}
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
+ if (u->encoder_info) {
+ u->a2dp_codec->deinit(u->encoder_info);
+ u->encoder_info = NULL;
+ }
+
+ if (u->decoder_info) {
+ u->a2dp_codec->deinit(u->decoder_info);
+ u->decoder_info = NULL;
+ }
+
+ u->a2dp_codec = NULL;
+ }
}
/* Run from main thread */
@@ -2309,7 +2109,7 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery
if (volume < PA_VOLUME_NORM)
volume++;
- pa_cvolume_set(&v, u->sample_spec.channels, volume);
+ pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_sink_volume_changed(u->sink, &v);
else
@@ -2336,7 +2136,7 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov
if (volume < PA_VOLUME_NORM)
volume++;
- pa_cvolume_set(&v, u->sample_spec.channels, volume);
+ pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_source_volume_changed(u->source, &v);
@@ -2497,11 +2297,11 @@ 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->encoder_buffer)
+ pa_xfree(u->encoder_buffer);
- if (u->sbc_info.sbc_initialized)
- sbc_finish(&u->sbc_info.sbc);
+ if (u->decoder_buffer)
+ pa_xfree(u->decoder_buffer);
if (u->msg)
pa_xfree(u->msg);
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/compare/f08443e18688f1dae2d2431112e59a388782b44d...1b6e5b8554c46c7a228a9c0d65d287ac0e823b83
--
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/compare/f08443e18688f1dae2d2431112e59a388782b44d...1b6e5b8554c46c7a228a9c0d65d287ac0e823b83
You're receiving this email because of your account on gitlab.freedesktop.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20190412/787cb49c/attachment-0001.html>
More information about the pulseaudio-commits
mailing list