bluez5: ldac decoding support

Add support for LDAC decoding, if libldac decoder is available.
This commit is contained in:
Pauli Virtanen 2022-08-07 17:58:39 +03:00 committed by Wim Taymans
parent bbe1587f71
commit 4c51e6518b
5 changed files with 207 additions and 1 deletions

View file

@ -375,6 +375,7 @@ build_all:
-Dbluez5-codec-aptx=disabled -Dbluez5-codec-aptx=disabled
-Dbluez5-codec-lc3plus=disabled -Dbluez5-codec-lc3plus=disabled
-Dbluez5-codec-lc3=disabled -Dbluez5-codec-lc3=disabled
-Dbluez5-codec-ldac-dec=disabled
-Droc=disabled -Droc=disabled
-Dlibcamera=disabled -Dlibcamera=disabled
-Dsession-managers=[] -Dsession-managers=[]

View file

@ -129,6 +129,10 @@ option('bluez5-codec-ldac',
description: 'Enable LDAC Sony open source codec implementation', description: 'Enable LDAC Sony open source codec implementation',
type: 'feature', type: 'feature',
value: 'auto') value: 'auto')
option('bluez5-codec-ldac-dec',
description: 'Enable LDAC Sony open source codec decoding',
type: 'feature',
value: 'auto')
option('bluez5-codec-aac', option('bluez5-codec-aac',
description: 'Enable Fraunhofer FDK AAC open source codec implementation', description: 'Enable Fraunhofer FDK AAC open source codec implementation',
type: 'feature', type: 'feature',

View file

@ -70,6 +70,23 @@ if get_option('spa-plugins').allowed()
summary({'LDAC': ldac_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') summary({'LDAC': ldac_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
ldac_abr_dep = dependency('ldacBT-abr', required : get_option('bluez5-codec-ldac')) ldac_abr_dep = dependency('ldacBT-abr', required : get_option('bluez5-codec-ldac'))
summary({'LDAC ABR': ldac_abr_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') summary({'LDAC ABR': ldac_abr_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
if get_option('bluez5-codec-ldac-dec').allowed()
ldac_dec_dep = dependency('ldacBT-dec', required : false)
if not ldac_dec_dep.found()
dep = cc.find_library('ldacBT_dec', required : false)
if dep.found() and cc.has_function('ldacBT_decode', dependencies : dep)
ldac_dec_dep = dep
endif
endif
if not ldac_dec_dep.found() and get_option('bluez5-codec-ldac-dec').enabled()
error('LDAC decoder library not found')
endif
else
ldac_dec_dep = dependency('', required: false)
endif
summary({'LDAC DEC': ldac_dec_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
aptx_dep = dependency('libfreeaptx', required : get_option('bluez5-codec-aptx')) aptx_dep = dependency('libfreeaptx', required : get_option('bluez5-codec-aptx'))
summary({'aptX': aptx_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') summary({'aptX': aptx_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
fdk_aac_dep = dependency('fdk-aac', required : get_option('bluez5-codec-aac')) fdk_aac_dep = dependency('fdk-aac', required : get_option('bluez5-codec-aac'))

View file

@ -19,6 +19,13 @@
#include <ldacBT_abr.h> #include <ldacBT_abr.h>
#endif #endif
#if defined(ENABLE_LDAC_DEC)
int ldacBT_decode(HANDLE_LDAC_BT handle, unsigned char *src, unsigned char *dst,
LDACBT_SMPL_FMT_T fmt, int src_size, int *consumed, int *dst_out);
int ldacBT_init_handle_decode(HANDLE_LDAC_BT handle, int channel_mode, int frequency,
int dummy1, int dummy2, int dummy3);
#endif
#include "rtp.h" #include "rtp.h"
#include "media-codecs.h" #include "media-codecs.h"
@ -35,15 +42,25 @@
#define LDAC_ABR_SOCK_BUFFER_SIZE (LDAC_ABR_THRESHOLD_CRITICAL * LDAC_ABR_MAX_PACKET_NBYTES) #define LDAC_ABR_SOCK_BUFFER_SIZE (LDAC_ABR_THRESHOLD_CRITICAL * LDAC_ABR_MAX_PACKET_NBYTES)
static struct spa_log *log_;
struct props { struct props {
int eqmid; int eqmid;
}; };
struct dec_data {
int frames;
size_t max_frame_bytes;
};
struct impl { struct impl {
HANDLE_LDAC_BT ldac; HANDLE_LDAC_BT ldac;
#ifdef ENABLE_LDAC_ABR #ifdef ENABLE_LDAC_ABR
HANDLE_LDAC_ABR ldac_abr; HANDLE_LDAC_ABR ldac_abr;
#endif
#ifdef ENABLE_LDAC_DEC
HANDLE_LDAC_BT ldac_dec;
#endif #endif
bool enable_abr; bool enable_abr;
@ -57,6 +74,8 @@ struct impl {
int codesize; int codesize;
int frame_length; int frame_length;
int frame_count; int frame_count;
struct dec_data d;
}; };
static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
@ -367,6 +386,66 @@ static int codec_set_props(void *props, const struct spa_pod *param)
return prev_eqmid != p->eqmid; return prev_eqmid != p->eqmid;
} }
static const char *ldac_strerror(int ldac_error)
{
#define LDAC_BT_ERROR_CASE(name) case name: return #name
switch (ldac_error) {
LDAC_BT_ERROR_CASE(LDACBT_ERR_NONE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_NON_FATAL);
LDAC_BT_ERROR_CASE(LDACBT_ERR_BIT_ALLOCATION);
LDAC_BT_ERROR_CASE(LDACBT_ERR_NOT_IMPLEMENTED);
LDAC_BT_ERROR_CASE(LDACBT_ERR_NON_FATAL_ENCODE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_FATAL);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_BAND);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_GRAD_A);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_GRAD_B);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_GRAD_C);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_GRAD_D);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_GRAD_E);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_IDSF);
LDAC_BT_ERROR_CASE(LDACBT_ERR_SYNTAX_SPEC);
LDAC_BT_ERROR_CASE(LDACBT_ERR_BIT_PACKING);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ALLOC_MEMORY);
LDAC_BT_ERROR_CASE(LDACBT_ERR_FATAL_HANDLE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_SYNCWORD);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_SMPL_FORMAT);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_PARAM);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_SAMPLING_FREQ);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_SUP_SAMPLING_FREQ);
LDAC_BT_ERROR_CASE(LDACBT_ERR_CHECK_SAMPLING_FREQ);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_CHANNEL_CONFIG);
LDAC_BT_ERROR_CASE(LDACBT_ERR_CHECK_CHANNEL_CONFIG);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_FRAME_LENGTH);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_SUP_FRAME_LENGTH);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_FRAME_STATUS);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_NSHIFT);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ASSERT_CHANNEL_MODE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_INIT_ALLOC);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_GRADMODE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_GRADPAR_A);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_GRADPAR_B);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_GRADPAR_C);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_GRADPAR_D);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ENC_ILL_NBANDS);
LDAC_BT_ERROR_CASE(LDACBT_ERR_PACK_BLOCK_FAILED);
LDAC_BT_ERROR_CASE(LDACBT_ERR_DEC_INIT_ALLOC);
LDAC_BT_ERROR_CASE(LDACBT_ERR_INPUT_BUFFER_SIZE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_UNPACK_BLOCK_FAILED);
LDAC_BT_ERROR_CASE(LDACBT_ERR_UNPACK_BLOCK_ALIGN);
LDAC_BT_ERROR_CASE(LDACBT_ERR_UNPACK_FRAME_ALIGN);
LDAC_BT_ERROR_CASE(LDACBT_ERR_FRAME_LENGTH_OVER);
LDAC_BT_ERROR_CASE(LDACBT_ERR_FRAME_ALIGN_OVER);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ALTER_EQMID_LIMITED);
LDAC_BT_ERROR_CASE(LDACBT_ERR_HANDLE_NOT_INIT);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_EQMID);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_SAMPLING_FREQ);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_NUM_CHANNEL);
LDAC_BT_ERROR_CASE(LDACBT_ERR_ILL_MTU_SIZE);
LDAC_BT_ERROR_CASE(LDACBT_ERR_DEC_CONFIG_UPDATED);
default: return "other error";
}
}
static void *codec_init(const struct media_codec *codec, uint32_t flags, static void *codec_init(const struct media_codec *codec, uint32_t flags,
void *config, size_t config_len, const struct spa_audio_info *info, void *config, size_t config_len, const struct spa_audio_info *info,
void *props, size_t mtu) void *props, size_t mtu)
@ -384,6 +463,12 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
if (this->ldac == NULL) if (this->ldac == NULL)
goto error_errno; goto error_errno;
#ifdef ENABLE_LDAC_DEC
this->ldac_dec = ldacBT_get_handle();
if (this->ldac_dec == NULL)
goto error_errno;
#endif
#ifdef ENABLE_LDAC_ABR #ifdef ENABLE_LDAC_ABR
this->ldac_abr = ldac_ABR_get_handle(); this->ldac_abr = ldac_ABR_get_handle();
if (this->ldac_abr == NULL) if (this->ldac_abr == NULL)
@ -424,14 +509,34 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
goto error; goto error;
} }
this->d.max_frame_bytes = (size_t)this->codesize * LDACBT_MAX_LSU / LDACBT_ENC_LSU;
res = ldacBT_init_handle_encode(this->ldac, res = ldacBT_init_handle_encode(this->ldac,
this->mtu, this->mtu,
this->eqmid, this->eqmid,
conf->channel_mode, conf->channel_mode,
this->fmt, this->fmt,
this->frequency); this->frequency);
if (res < 0) if (res < 0) {
res = ldacBT_get_error_code(this->ldac);
spa_log_error(log_, "LDAC encoder initialization failed: %s (%d)",
ldac_strerror(LDACBT_API_ERR(res)), res);
res = -EIO;
goto error; goto error;
}
#ifdef ENABLE_LDAC_DEC
res = ldacBT_init_handle_decode(this->ldac_dec,
conf->channel_mode,
this->frequency, 0, 0, 0);
if (res < 0) {
res = ldacBT_get_error_code(this->ldac_dec);
spa_log_error(log_, "LDAC decoder initialization failed: %s (%d)",
ldac_strerror(LDACBT_API_ERR(res)), res);
res = -EIO;
goto error;
}
#endif
#ifdef ENABLE_LDAC_ABR #ifdef ENABLE_LDAC_ABR
res = ldac_ABR_Init(this->ldac_abr, LDAC_ABR_INTERVAL_MS); res = ldac_ABR_Init(this->ldac_abr, LDAC_ABR_INTERVAL_MS);
@ -552,6 +657,70 @@ static int codec_encode(void *data,
return src_used; return src_used;
} }
#ifdef ENABLE_LDAC_DEC
static int codec_start_decode (void *data,
const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp)
{
struct impl *this = data;
const struct rtp_header *header = src;
const struct rtp_payload *payload = SPA_PTROFF(src, sizeof(struct rtp_header), struct rtp_payload);
size_t header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
spa_return_val_if_fail (src_size > header_size, -EINVAL);
if (seqnum)
*seqnum = ntohs(header->sequence_number);
if (timestamp)
*timestamp = ntohl(header->timestamp);
this->d.frames = payload->frame_count;
return header_size;
}
static int codec_decode(void *data,
const void *src, size_t src_size,
void *dst, size_t dst_size,
size_t *dst_out)
{
struct impl *this = data;
void *to = dst;
size_t avail = dst_size;
size_t processed = 0;
*dst_out = 0;
for (; this->d.frames > 0; --this->d.frames) {
int consumed;
int written;
if (avail < this->d.max_frame_bytes)
return -EINVAL;
if (ldacBT_decode(this->ldac_dec, (void *)src, to, this->fmt, src_size,
&consumed, &written) != 0) {
return -EINVAL;
}
if (consumed < 0 || (size_t)consumed > src_size)
return -EINVAL;
if (written < 0 || (size_t)written > avail)
return -EINVAL;
src = SPA_PTROFF(src, consumed, const void);
src_size -= consumed;
processed += consumed;
to = SPA_PTROFF(to, written, void);
avail -= written;
*dst_out += written;
}
return processed;
}
#endif
static void codec_get_delay(void *data, uint32_t *encoder, uint32_t *decoder) static void codec_get_delay(void *data, uint32_t *encoder, uint32_t *decoder)
{ {
struct impl *this = data; struct impl *this = data;
@ -571,6 +740,12 @@ static void codec_get_delay(void *data, uint32_t *encoder, uint32_t *decoder)
*decoder = 0; *decoder = 0;
} }
static void codec_set_log(struct spa_log *global_log)
{
log_ = global_log;
spa_log_topic_init(log_, &codec_plugin_log_topic);
}
const struct media_codec a2dp_codec_ldac = { const struct media_codec a2dp_codec_ldac = {
.id = SPA_BLUETOOTH_AUDIO_CODEC_LDAC, .id = SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
.codec_id = A2DP_CODEC_VENDOR, .codec_id = A2DP_CODEC_VENDOR,
@ -595,9 +770,14 @@ const struct media_codec a2dp_codec_ldac = {
.abr_process = codec_abr_process, .abr_process = codec_abr_process,
.start_encode = codec_start_encode, .start_encode = codec_start_encode,
.encode = codec_encode, .encode = codec_encode,
#ifdef ENABLE_LDAC_DEC
.start_decode = codec_start_decode,
.decode = codec_decode,
#endif
.reduce_bitpool = codec_reduce_bitpool, .reduce_bitpool = codec_reduce_bitpool,
.increase_bitpool = codec_increase_bitpool, .increase_bitpool = codec_increase_bitpool,
.get_delay = codec_get_delay, .get_delay = codec_get_delay,
.set_log = codec_set_log,
}; };
MEDIA_CODEC_EXPORT_DEF( MEDIA_CODEC_EXPORT_DEF(

View file

@ -125,6 +125,10 @@ if ldac_dep.found()
if ldac_abr_dep.found() if ldac_abr_dep.found()
ldac_args += [ '-DENABLE_LDAC_ABR' ] ldac_args += [ '-DENABLE_LDAC_ABR' ]
endif endif
if get_option('bluez5-codec-ldac-dec').allowed() and ldac_dec_dep.found()
ldac_args += [ '-DENABLE_LDAC_DEC' ]
ldac_dep = [ldac_dep, ldac_dec_dep]
endif
bluez_codec_ldac = shared_library('spa-codec-bluez5-ldac', bluez_codec_ldac = shared_library('spa-codec-bluez5-ldac',
[ 'a2dp-codec-ldac.c', 'media-codecs.c' ], [ 'a2dp-codec-ldac.c', 'media-codecs.c' ],
include_directories : [ configinc ], include_directories : [ configinc ],