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-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=[]
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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'))
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 ],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue