From f2b3b63f21c8d80b48b0cd8d1a39bf86d5f4aee6 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:03:21 +0300 Subject: [PATCH] bluez5: add LHDC V5 A2DP decoder --- spa/include/spa/param/bluetooth/type-info.h | 1 + spa/meson.build | 18 ++ spa/plugins/bluez5/a2dp-codec-caps.h | 37 ++- spa/plugins/bluez5/a2dp-codec-lhdc.c | 279 +++++++++++++++++++- spa/plugins/bluez5/codec-loader.c | 1 + spa/plugins/bluez5/meson.build | 2 +- 6 files changed, 318 insertions(+), 20 deletions(-) diff --git a/spa/include/spa/param/bluetooth/type-info.h b/spa/include/spa/param/bluetooth/type-info.h index 97f9cf9b3..732463f7b 100644 --- a/spa/include/spa/param/bluetooth/type-info.h +++ b/spa/include/spa/param/bluetooth/type-info.h @@ -30,6 +30,7 @@ static const struct spa_type_info spa_type_bluetooth_audio_codec[] = { { SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_hd", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "ldac", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_LHDC_V3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lhdc_v3", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_LHDC_V5, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lhdc_v5", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll_duplex", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "faststream", NULL }, diff --git a/spa/meson.build b/spa/meson.build index b086d8cc9..08f0af901 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -117,6 +117,24 @@ if get_option('spa-plugins').allowed() endif summary({'LHDC V3 Decoder': lhdc_enc_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + lhdc_v5_enc_dep = dependency('lhdcv5BT-enc', required : false) + if not lhdc_v5_enc_dep.found() + lhdc_v5_enc_lhdc_h_dep = cc.find_library('lhdcv5BT_enc', has_headers: ['lhdcv5BT.h'], required : false) + if lhdc_v5_enc_lhdc_h_dep.found() + lhdc_v5_enc_dep = declare_dependency(dependencies : [ lhdc_v5_enc_lhdc_h_dep ]) + endif + endif + summary({'LHDC V5 Encoder': lhdc_v5_enc_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + + lhdc_v5_dec_dep = dependency('lhdcv5BT-dec', required : false) + if not lhdc_v5_dec_dep.found() + lhdc_v5_dec_lhdc_h_dep = cc.find_library('lhdcv5BT_dec', has_headers: ['lhdcv5BT_dec.h'], required : false) + if lhdc_v5_dec_lhdc_h_dep.found() + lhdc_v5_dec_dep = declare_dependency(dependencies : [ lhdc_v5_dec_lhdc_h_dep ]) + endif + endif + summary({'LHDC V5 Decoder': lhdc_v5_enc_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + if get_option('bluez5-codec-opus').enabled() and not opus_dep.found() error('bluez5-codec-opus enabled, but opus dependency not found') endif diff --git a/spa/plugins/bluez5/a2dp-codec-caps.h b/spa/plugins/bluez5/a2dp-codec-caps.h index a7171cb90..c94b1c54b 100644 --- a/spa/plugins/bluez5/a2dp-codec-caps.h +++ b/spa/plugins/bluez5/a2dp-codec-caps.h @@ -218,6 +218,27 @@ #define LHDC_CH_SPLIT_MODE_TWS 0x02 #define LHDC_CH_SPLIT_MODE_TWS_PLUS 0x04 +#define LHDCV5_SAMPLING_FREQ_44100 (1 << 5) +#define LHDCV5_SAMPLING_FREQ_48000 (1 << 4) +#define LHDCV5_SAMPLING_FREQ_96000 (1 << 2) +#define LHDCV5_SAMPLING_FREQ_192000 (1 << 0) + +#define LHDCV5_BIT_DEPTH_16 (1 << 2) +#define LHDCV5_BIT_DEPTH_24 (1 << 1) +#define LHDCV5_BIT_DEPTH_32 (1 << 0) + +#define LHDCV5_MAX_BITRATE_900K (3) +#define LHDCV5_MAX_BITRATE_500K (2) +#define LHDCV5_MAX_BITRATE_400K (1) +#define LHDCV5_MAX_BITRATE_1000K (0) + +#define LHDCV5_MIN_BITRATE_400K (3) +#define LHDCV5_MIN_BITRATE_256K (2) +#define LHDCV5_MIN_BITRATE_160K (1) +#define LHDCV5_MIN_BITRATE_64K (0) + +#define LHDCV5_VER_1 (1 << 0) + #define FASTSTREAM_VENDOR_ID 0x0000000a #define FASTSTREAM_CODEC_ID 0x0001 @@ -422,23 +443,25 @@ typedef struct { typedef struct { a2dp_vendor_codec_t info; - uint8_t frequency:5; - uint8_t rfa1:3; + uint8_t sampling_freq:6; + uint8_t rfa1:2; uint8_t bit_depth:3; uint8_t rfa2:1; - uint8_t max_bit_rate:2; - uint8_t min_bit_rate:2; + uint8_t max_bitrate:2; + uint8_t min_bitrate:2; uint8_t version:4; uint8_t frame_len_5ms:1; uint8_t rfa3:3; uint8_t ar:1; uint8_t jas:1; uint8_t meta:1; - uint8_t rfa4:3; + uint8_t rfa4:1; + uint8_t lossless_96k:1; + uint8_t lossless_24b:1; uint8_t low_latency:1; - uint8_t reserved:1; // lossless? - uint8_t ar_on:1; + uint8_t lossless_48k:1; uint8_t rfa5:7; + uint8_t lossless_raw_48k:1; } __attribute__ ((packed)) a2dp_lhdc_v5_t; #elif __BYTE_ORDER == __BIG_ENDIAN diff --git a/spa/plugins/bluez5/a2dp-codec-lhdc.c b/spa/plugins/bluez5/a2dp-codec-lhdc.c index df28d0c94..7b0c2f34b 100644 --- a/spa/plugins/bluez5/a2dp-codec-lhdc.c +++ b/spa/plugins/bluez5/a2dp-codec-lhdc.c @@ -19,6 +19,8 @@ #include #include +#include + #include "rtp.h" #include "media-codecs.h" @@ -50,12 +52,25 @@ struct impl_v3 { int bit_depth; int codesize; int block_size; - int frame_length; - int frame_count; uint8_t seq_num; int32_t buf[2][LHDCV2_BT_ENC_BLOCK_SIZE]; }; +struct impl_v5 { + HANDLE_LHDCV5_BT dec; + + struct rtp_header *header; + struct rtp_lhdc_payload *payload; + + int mtu; + int eqmid; + int frequency; + int bit_depth; + int frame_samples; + uint8_t seq_num; + int32_t buf[2][LHDCV5_MAX_SAMPLE_FRAME]; +}; + static int codec_fill_caps_v3(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings, uint8_t caps[A2DP_MAX_CAPS_SIZE]) { @@ -81,6 +96,32 @@ static int codec_fill_caps_v3(const struct media_codec *codec, uint32_t flags, return sizeof(a2dp_lhdc); } +static int codec_fill_caps_v5(const struct media_codec *codec, uint32_t flags, + const struct spa_dict *settings, uint8_t caps[A2DP_MAX_CAPS_SIZE]) +{ + static const a2dp_lhdc_v5_t a2dp_lhdc = { + .info.vendor_id = LHDC_V5_VENDOR_ID, + .info.codec_id = LHDC_V5_CODEC_ID, + .sampling_freq = LHDCV5_SAMPLING_FREQ_44100 | LHDCV5_SAMPLING_FREQ_48000 | LHDCV5_SAMPLING_FREQ_96000, + .bit_depth = LHDCV5_BIT_DEPTH_16 | LHDCV5_BIT_DEPTH_24, + .max_bitrate = LHDCV5_MAX_BITRATE_1000K, + .min_bitrate = LHDCV5_MIN_BITRATE_64K, + .version = LHDCV5_VER_1, + .frame_len_5ms = 1, + .ar = 0, + .jas = 0, + .meta = 0, + .lossless_96k = 0, + .lossless_24b = 0, + .low_latency = 0, + .lossless_48k = 0, + .lossless_raw_48k = 0, + }; + + memcpy(caps, &a2dp_lhdc, sizeof(a2dp_lhdc)); + return sizeof(a2dp_lhdc); +} + static const struct media_codec_config lhdc_frequencies_v3[] = { { LHDC_SAMPLING_FREQ_44100, 44100, 0 }, @@ -88,6 +129,13 @@ lhdc_frequencies_v3[] = { { LHDC_SAMPLING_FREQ_96000, 96000, 1 }, }; +static const struct media_codec_config +lhdc_frequencies_v5[] = { + { LHDCV5_SAMPLING_FREQ_44100, 44100, 0 }, + { LHDCV5_SAMPLING_FREQ_48000, 48000, 2 }, + { LHDCV5_SAMPLING_FREQ_96000, 96000, 1 }, +}; + static int codec_select_config_v3(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, const struct media_codec_audio_info *info, @@ -123,6 +171,38 @@ static int codec_select_config_v3(const struct media_codec *codec, uint32_t flag return sizeof(conf); } +static int codec_select_config_v5(const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, + const struct media_codec_audio_info *info, + const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE]) +{ + a2dp_lhdc_v5_t conf; + int i; + + if (caps_size < sizeof(conf)) + return -EINVAL; + + memcpy(&conf, caps, sizeof(conf)); + + if (codec->vendor.vendor_id != conf.info.vendor_id || + codec->vendor.codec_id != conf.info.codec_id) + return -ENOTSUP; + + if ((i = media_codec_select_config(lhdc_frequencies_v5, + SPA_N_ELEMENTS(lhdc_frequencies_v5), + conf.sampling_freq, + info ? info->rate : A2DP_CODEC_DEFAULT_RATE + )) < 0) + return -ENOTSUP; + conf.sampling_freq = lhdc_frequencies_v5[i].config; + + conf.bit_depth = LHDCV5_BIT_DEPTH_24; + + memcpy(config, &conf, sizeof(conf)); + + return sizeof(conf); +} + static int codec_enum_config_v3(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) @@ -174,7 +254,6 @@ static int codec_enum_config_v3(const struct media_codec *codec, uint32_t flags, if (i == 0) return -EINVAL; - position[0] = SPA_AUDIO_CHANNEL_FL; position[1] = SPA_AUDIO_CHANNEL_FR; spa_pod_builder_add(b, @@ -215,17 +294,17 @@ static int codec_enum_config_v5(const struct media_codec *codec, uint32_t flags, spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); i = 0; - if (conf.frequency & LHDC_SAMPLING_FREQ_48000) { + if (conf.sampling_freq & LHDCV5_SAMPLING_FREQ_48000) { if (i++ == 0) spa_pod_builder_int(b, 48000); spa_pod_builder_int(b, 48000); } - if (conf.frequency & LHDC_SAMPLING_FREQ_44100) { + if (conf.sampling_freq & LHDCV5_SAMPLING_FREQ_44100) { if (i++ == 0) spa_pod_builder_int(b, 44100); spa_pod_builder_int(b, 44100); } - if (conf.frequency & LHDC_SAMPLING_FREQ_96000) { + if (conf.sampling_freq & LHDCV5_SAMPLING_FREQ_96000) { if (i++ == 0) spa_pod_builder_int(b, 96000); spa_pod_builder_int(b, 96000); @@ -237,7 +316,6 @@ static int codec_enum_config_v5(const struct media_codec *codec, uint32_t flags, if (i == 0) return -EINVAL; - position[0] = SPA_AUDIO_CHANNEL_FL; position[1] = SPA_AUDIO_CHANNEL_FR; spa_pod_builder_add(b, @@ -255,6 +333,12 @@ static int codec_get_block_size_v3(void *data) return this->codesize; } +static int codec_get_block_size_v5(void *data) +{ + struct impl_v5 *this = data; + return this->frame_samples * 4 * 2; +} + static const struct { const char *name; int v; } eqmids_v3[] = { { "low0", .v = LHDCBT_QUALITY_LOW0 }, { "low1", .v = LHDCBT_QUALITY_LOW1 }, @@ -292,9 +376,13 @@ static void *codec_init_props_v3(const struct media_codec *codec, uint32_t flags return p; } +static void *codec_init_props_v5(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings) +{ + return malloc(1); +} + static void codec_clear_props_v3(void *props) { - free(props); } static int codec_enum_props_v3(void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx, @@ -355,6 +443,11 @@ static int codec_enum_props_v3(void *props, const struct spa_dict *settings, uin return 1; } +static int codec_enum_props_v5(void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx, + struct spa_pod_builder *b, struct spa_pod **param) { + return 0; +} + static int codec_set_props_v3(void *props, const struct spa_pod *param) { struct props_v3 *p = props; @@ -372,6 +465,11 @@ static int codec_set_props_v3(void *props, const struct spa_pod *param) return prev_eqmid != p->eqmid; } +static int codec_set_props_v5(void *props, const struct spa_pod *param) +{ + return 0; +} + static LHDC_VERSION_SETUP get_version_v3(const a2dp_lhdc_v3_t *configuration) { if (configuration->llac) { return LLAC; @@ -398,6 +496,14 @@ static int get_bit_depth_v3(const a2dp_lhdc_v3_t *configuration) { } } +static int get_bit_depth_v5(const a2dp_lhdc_v5_t *configuration) { + if (configuration->bit_depth == LHDCV5_BIT_DEPTH_16) { + return 16; + } else { + return 24; + } +} + static LHDCBT_QUALITY_T get_max_bitrate_v3(const a2dp_lhdc_v3_t *configuration) { if (configuration->max_bit_rate == LHDC_MAX_BIT_RATE_400K) { return LHDCBT_QUALITY_LOW; @@ -496,6 +602,47 @@ error: return NULL; } +static void *codec_init_v5(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) +{ + struct impl_v5 *this; + a2dp_lhdc_v5_t *conf = config; + int res; + + this = calloc(1, sizeof(struct impl_v5)); + if (this == NULL) + goto error_errno; + + this->mtu = mtu; + this->frequency = info->info.raw.rate; + this->bit_depth = get_bit_depth_v5(conf); + + tLHDCV5_DEC_CONFIG dec_config = { + .version = VERSION_5, + .sample_rate = this->frequency, + .bits_depth = this->bit_depth, + .bit_rate = LHDCV5BT_BIT_RATE_1000K, + .lossless_enable = 0, + .lossless_raw_enable = 0, + }; + + if ((res = lhdcv5BT_dec_init_decoder(&this->dec, &dec_config)) < 0) + goto error; + + this->frame_samples = (50 * (this->frequency == 44100 ? 48000 : this->frequency)) / 10000; + + return this; +error_errno: + res = -errno; +error: + if (this && this->dec) + lhdcv5BT_dec_deinit_decoder(this->dec); + free(this); + errno = -res; + return NULL; +} + static void codec_deinit_v3(void *data) { struct impl_v3 *this = data; @@ -506,6 +653,14 @@ static void codec_deinit_v3(void *data) free(this); } +static void codec_deinit_v5(void *data) +{ + struct impl_v5 *this = data; + if (this->dec) + lhdcv5BT_dec_deinit_decoder(this->dec); + free(this); +} + static int codec_update_props_v3(void *data, void *props) { struct impl_v3 *this = data; @@ -524,12 +679,23 @@ error: return res; } + +static int codec_update_props_v5(void *data, void *props) +{ + return 0; +} + static int codec_abr_process_v3(void *data, size_t unsent) { struct impl_v3 *this = data; return this->eqmid == LHDCBT_QUALITY_AUTO ? lhdcBT_adjust_bitrate(this->lhdc, unsent / this->mtu) : -ENOTSUP; } +static int codec_abr_process_v5(void *data, size_t unsent) +{ + return -ENOTSUP; +} + static int codec_start_encode_v3(void *data, void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp) { @@ -548,7 +714,11 @@ static int codec_start_encode_v3(void *data, return sizeof(struct rtp_header) + sizeof(struct rtp_lhdc_payload); } - +static int codec_start_encode_v5(void *data, + void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp) +{ + return -ENOTSUP; +} static void deinterleave_32_c2(int32_t * SPA_RESTRICT * SPA_RESTRICT dst, const int32_t * SPA_RESTRICT src, size_t n_samples) { @@ -590,6 +760,13 @@ static int codec_encode_v3(void *data, return src_used; } +static int codec_encode_v5(void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out, int *need_flush) +{ + return -ENOTSUP; +} static int codec_start_decode(void *data, const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp) @@ -607,7 +784,7 @@ static int codec_start_decode(void *data, return header_size; } -static const char *dec_errors[] = { +static const char *dec_errors_v3[] = { [-LHDCBT_DEC_FUNC_SUCCEED] = "OK", [-LHDCBT_DEC_FUNC_FAIL] = "General error", [-LHDCBT_DEC_FUNC_INPUT_NOT_ENOUGH] = "Not enough input data", @@ -643,7 +820,56 @@ static int codec_decode_v3(void *data, return consumed; error: - spa_log_error(log, "lhdcBT_dec_decode: %s (%d)!", dec_errors[-err], err); + spa_log_error(log, "lhdcBT_dec_decode: %s (%d)!", dec_errors_v3[-err], err); + return -1; +} + +static const char *dec_errors_v5[] = { + [-LHDCV5BT_DEC_API_SUCCEED] = "OK", + [-LHDCV5BT_DEC_API_FAIL] = "General error", + [-LHDCV5BT_DEC_API_INVALID_INPUT] = "Invalid input", + [-LHDCV5BT_DEC_API_INVALID_OUTPUT] = "Invalid output", + [-LHDCV5BT_DEC_API_INVALID_SEQ_NO] = "Invalid sequence number", + [-LHDCV5BT_DEC_API_INIT_DECODER_FAIL] = "Decoder initialization error", + [-LHDCV5BT_DEC_API_CHANNEL_SETUP_FAIL] = "Channel setup error", + [-LHDCV5BT_DEC_API_FRAME_INFO_FAIL] = "Failed to fetch frame info", + [-LHDCV5BT_DEC_API_INPUT_NOT_ENOUGH] = "Not enough input data", + [-LHDCV5BT_DEC_API_OUTPUT_NOT_ENOUGH] = "Invalid sequence number", + [-LHDCV5BT_DEC_API_DECODE_FAIL] = "Decode error", + [-LHDCV5BT_DEC_API_ALLOC_MEM_FAIL] = "Out of memory", +}; + +static int codec_decode_v5(void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out) +{ + struct impl_v5 *this = data; + + uint32_t decoded = dst_size; + uint32_t consumed = 0; + + int err = 0; + + if ((err = lhdcv5BT_dec_check_frame_data_enough(this->dec, src, src_size, &consumed)) < 0) + goto error; + + consumed += sizeof(struct rtp_lhdc_payload); + + if ((err = lhdcv5BT_dec_decode(this->dec, src, consumed, dst, &decoded, 24)) < 0) + goto error; + + int32_t *samples = dst; + for (size_t i = 0; i < decoded / 4; i++) + samples[i] *= (1 << 8); + + if (dst_out) + *dst_out = decoded; + + return consumed; + +error: + spa_log_error(log, "lhdcv5BT_dec_decode: %s (%d)!", dec_errors_v5[-err], err); return -1; } @@ -691,7 +917,36 @@ const struct media_codec a2dp_codec_lhdc_v3 = { .set_log = codec_set_log, }; +const struct media_codec a2dp_codec_lhdc_v5 = { + .id = SPA_BLUETOOTH_AUDIO_CODEC_LHDC_V5, + .codec_id = A2DP_CODEC_VENDOR, + .vendor = { .vendor_id = LHDC_V5_VENDOR_ID, + .codec_id = LHDC_V5_CODEC_ID }, + .name = "lhdc_v5", + .description = "LHDC V5", + .fill_caps = codec_fill_caps_v5, + .select_config = codec_select_config_v5, + .enum_config = codec_enum_config_v5, + .init_props = codec_init_props_v5, + .enum_props = codec_enum_props_v5, + .set_props = codec_set_props_v5, + .clear_props = codec_clear_props_v3, + .init = codec_init_v5, + .deinit = codec_deinit_v5, + .update_props = codec_update_props_v5, + .get_block_size = codec_get_block_size_v5, + .abr_process = codec_abr_process_v5, + .start_encode = codec_start_encode_v5, + .encode = codec_encode_v5, + .start_decode = codec_start_decode, + .decode = codec_decode_v5, + .reduce_bitpool = codec_reduce_bitpool, + .increase_bitpool = codec_increase_bitpool, + .set_log = codec_set_log, +}; + MEDIA_CODEC_EXPORT_DEF( "lhdc", - &a2dp_codec_lhdc_v3 + &a2dp_codec_lhdc_v3, + &a2dp_codec_lhdc_v5 ); diff --git a/spa/plugins/bluez5/codec-loader.c b/spa/plugins/bluez5/codec-loader.c index 18539c825..ffaf21ede 100644 --- a/spa/plugins/bluez5/codec-loader.c +++ b/spa/plugins/bluez5/codec-loader.c @@ -32,6 +32,7 @@ static int codec_order(const struct media_codec *c) { static const enum spa_bluetooth_audio_codec order[] = { SPA_BLUETOOTH_AUDIO_CODEC_LC3, + SPA_BLUETOOTH_AUDIO_CODEC_LHDC_V5, SPA_BLUETOOTH_AUDIO_CODEC_LHDC_V3, SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, diff --git a/spa/plugins/bluez5/meson.build b/spa/plugins/bluez5/meson.build index 73f382ae4..1fbd551bd 100644 --- a/spa/plugins/bluez5/meson.build +++ b/spa/plugins/bluez5/meson.build @@ -144,7 +144,7 @@ if lhdc_enc_dep.found() [ 'a2dp-codec-lhdc.c', 'media-codecs.c' ], include_directories : [ configinc ], c_args : lhdc_args, - dependencies : [ spa_dep, lhdc_enc_dep, lhdc_dec_dep ], + dependencies : [ spa_dep, lhdc_enc_dep, lhdc_dec_dep, lhdc_v5_enc_dep, lhdc_v5_dec_dep ], install : true, install_dir : spa_plugindir / 'bluez5') endif