mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: ldac decoding support
Add support for LDAC decoding, if libldac decoder is available.
This commit is contained in:
parent
bbe1587f71
commit
4c51e6518b
5 changed files with 207 additions and 1 deletions
|
|
@ -375,6 +375,7 @@ build_all:
|
|||
-Dbluez5-codec-aptx=disabled
|
||||
-Dbluez5-codec-lc3plus=disabled
|
||||
-Dbluez5-codec-lc3=disabled
|
||||
-Dbluez5-codec-ldac-dec=disabled
|
||||
-Droc=disabled
|
||||
-Dlibcamera=disabled
|
||||
-Dsession-managers=[]
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ option('bluez5-codec-ldac',
|
|||
description: 'Enable LDAC Sony open source codec implementation',
|
||||
type: 'feature',
|
||||
value: 'auto')
|
||||
option('bluez5-codec-ldac-dec',
|
||||
description: 'Enable LDAC Sony open source codec decoding',
|
||||
type: 'feature',
|
||||
value: 'auto')
|
||||
option('bluez5-codec-aac',
|
||||
description: 'Enable Fraunhofer FDK AAC open source codec implementation',
|
||||
type: 'feature',
|
||||
|
|
|
|||
|
|
@ -70,6 +70,23 @@ if get_option('spa-plugins').allowed()
|
|||
summary({'LDAC': ldac_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
|
||||
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')
|
||||
|
||||
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'))
|
||||
summary({'aptX': aptx_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs')
|
||||
fdk_aac_dep = dependency('fdk-aac', required : get_option('bluez5-codec-aac'))
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@
|
|||
#include <ldacBT_abr.h>
|
||||
#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 "media-codecs.h"
|
||||
|
||||
|
|
@ -35,15 +42,25 @@
|
|||
|
||||
#define LDAC_ABR_SOCK_BUFFER_SIZE (LDAC_ABR_THRESHOLD_CRITICAL * LDAC_ABR_MAX_PACKET_NBYTES)
|
||||
|
||||
static struct spa_log *log_;
|
||||
|
||||
|
||||
struct props {
|
||||
int eqmid;
|
||||
};
|
||||
|
||||
struct dec_data {
|
||||
int frames;
|
||||
size_t max_frame_bytes;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
HANDLE_LDAC_BT ldac;
|
||||
#ifdef ENABLE_LDAC_ABR
|
||||
HANDLE_LDAC_ABR ldac_abr;
|
||||
#endif
|
||||
#ifdef ENABLE_LDAC_DEC
|
||||
HANDLE_LDAC_BT ldac_dec;
|
||||
#endif
|
||||
bool enable_abr;
|
||||
|
||||
|
|
@ -57,6 +74,8 @@ struct impl {
|
|||
int codesize;
|
||||
int frame_length;
|
||||
int frame_count;
|
||||
|
||||
struct dec_data d;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
void *config, size_t config_len, const struct spa_audio_info *info,
|
||||
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)
|
||||
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
|
||||
this->ldac_abr = ldac_ABR_get_handle();
|
||||
if (this->ldac_abr == NULL)
|
||||
|
|
@ -424,14 +509,34 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
|
|||
goto error;
|
||||
}
|
||||
|
||||
this->d.max_frame_bytes = (size_t)this->codesize * LDACBT_MAX_LSU / LDACBT_ENC_LSU;
|
||||
|
||||
res = ldacBT_init_handle_encode(this->ldac,
|
||||
this->mtu,
|
||||
this->eqmid,
|
||||
conf->channel_mode,
|
||||
this->fmt,
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
res = ldac_ABR_Init(this->ldac_abr, LDAC_ABR_INTERVAL_MS);
|
||||
|
|
@ -552,6 +657,70 @@ static int codec_encode(void *data,
|
|||
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)
|
||||
{
|
||||
struct impl *this = data;
|
||||
|
|
@ -571,6 +740,12 @@ static void codec_get_delay(void *data, uint32_t *encoder, uint32_t *decoder)
|
|||
*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 = {
|
||||
.id = SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
|
||||
.codec_id = A2DP_CODEC_VENDOR,
|
||||
|
|
@ -595,9 +770,14 @@ const struct media_codec a2dp_codec_ldac = {
|
|||
.abr_process = codec_abr_process,
|
||||
.start_encode = codec_start_encode,
|
||||
.encode = codec_encode,
|
||||
#ifdef ENABLE_LDAC_DEC
|
||||
.start_decode = codec_start_decode,
|
||||
.decode = codec_decode,
|
||||
#endif
|
||||
.reduce_bitpool = codec_reduce_bitpool,
|
||||
.increase_bitpool = codec_increase_bitpool,
|
||||
.get_delay = codec_get_delay,
|
||||
.set_log = codec_set_log,
|
||||
};
|
||||
|
||||
MEDIA_CODEC_EXPORT_DEF(
|
||||
|
|
|
|||
|
|
@ -125,6 +125,10 @@ if ldac_dep.found()
|
|||
if ldac_abr_dep.found()
|
||||
ldac_args += [ '-DENABLE_LDAC_ABR' ]
|
||||
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',
|
||||
[ 'a2dp-codec-ldac.c', 'media-codecs.c' ],
|
||||
include_directories : [ configinc ],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue