1 /* Spa A2DP LDAC codec
2 *
3 * Copyright © 2020 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <unistd.h>
26 #include <stddef.h>
27 #include <errno.h>
28 #include <arpa/inet.h>
29
30 #include <spa/utils/string.h>
31 #include <spa/utils/dict.h>
32 #include <spa/pod/parser.h>
33 #include <spa/param/props.h>
34 #include <spa/param/audio/format.h>
35
36 #include <ldacBT.h>
37
38 #ifdef ENABLE_LDAC_ABR
39 #include <ldacBT_abr.h>
40 #endif
41
42 #include "rtp.h"
43 #include "a2dp-codecs.h"
44
45 #define LDACBT_EQMID_AUTO -1
46
47 #define LDAC_ABR_MAX_PACKET_NBYTES 1280
48
49 #define LDAC_ABR_INTERVAL_MS 5 /* 2 frames * 128 lsu / 48000 */
50
51 /* decrease ABR thresholds to increase stability */
52 #define LDAC_ABR_THRESHOLD_CRITICAL 6
53 #define LDAC_ABR_THRESHOLD_DANGEROUSTREND 4
54 #define LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ 3
55
56 #define LDAC_ABR_SOCK_BUFFER_SIZE (LDAC_ABR_THRESHOLD_CRITICAL * LDAC_ABR_MAX_PACKET_NBYTES)
57
58
59 struct props {
60 int eqmid;
61 };
62
63 struct impl {
64 HANDLE_LDAC_BT ldac;
65 #ifdef ENABLE_LDAC_ABR
66 HANDLE_LDAC_ABR ldac_abr;
67 #endif
68 bool enable_abr;
69
70 struct rtp_header *header;
71 struct rtp_payload *payload;
72
73 int mtu;
74 int eqmid;
75 int frequency;
76 int fmt;
77 int codesize;
78 int frame_length;
79 int frame_count;
80 };
81
codec_fill_caps(const struct a2dp_codec * codec,uint32_t flags,uint8_t caps[A2DP_MAX_CAPS_SIZE])82 static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, uint8_t caps[A2DP_MAX_CAPS_SIZE])
83 {
84 static const a2dp_ldac_t a2dp_ldac = {
85 .info.vendor_id = LDAC_VENDOR_ID,
86 .info.codec_id = LDAC_CODEC_ID,
87 .frequency = LDACBT_SAMPLING_FREQ_044100 |
88 LDACBT_SAMPLING_FREQ_048000 |
89 LDACBT_SAMPLING_FREQ_088200 |
90 LDACBT_SAMPLING_FREQ_096000,
91 .channel_mode = LDACBT_CHANNEL_MODE_MONO |
92 LDACBT_CHANNEL_MODE_DUAL_CHANNEL |
93 LDACBT_CHANNEL_MODE_STEREO,
94 };
95
96 memcpy(caps, &a2dp_ldac, sizeof(a2dp_ldac));
97 return sizeof(a2dp_ldac);
98 }
99
100 static const struct a2dp_codec_config
101 ldac_frequencies[] = {
102 { LDACBT_SAMPLING_FREQ_044100, 44100, 3 },
103 { LDACBT_SAMPLING_FREQ_048000, 48000, 2 },
104 { LDACBT_SAMPLING_FREQ_088200, 88200, 1 },
105 { LDACBT_SAMPLING_FREQ_096000, 96000, 0 },
106 };
107
108 static const struct a2dp_codec_config
109 ldac_channel_modes[] = {
110 { LDACBT_CHANNEL_MODE_STEREO, 2, 2 },
111 { LDACBT_CHANNEL_MODE_DUAL_CHANNEL, 2, 1 },
112 { LDACBT_CHANNEL_MODE_MONO, 1, 0 },
113 };
114
codec_select_config(const struct a2dp_codec * codec,uint32_t flags,const void * caps,size_t caps_size,const struct a2dp_codec_audio_info * info,const struct spa_dict * settings,uint8_t config[A2DP_MAX_CAPS_SIZE])115 static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags,
116 const void *caps, size_t caps_size,
117 const struct a2dp_codec_audio_info *info,
118 const struct spa_dict *settings, uint8_t config[A2DP_MAX_CAPS_SIZE])
119 {
120 a2dp_ldac_t conf;
121 int i;
122
123 if (caps_size < sizeof(conf))
124 return -EINVAL;
125
126 memcpy(&conf, caps, sizeof(conf));
127
128 if (codec->vendor.vendor_id != conf.info.vendor_id ||
129 codec->vendor.codec_id != conf.info.codec_id)
130 return -ENOTSUP;
131
132 if ((i = a2dp_codec_select_config(ldac_frequencies,
133 SPA_N_ELEMENTS(ldac_frequencies),
134 conf.frequency,
135 info ? info->rate : A2DP_CODEC_DEFAULT_RATE
136 )) < 0)
137 return -ENOTSUP;
138 conf.frequency = ldac_frequencies[i].config;
139
140 if ((i = a2dp_codec_select_config(ldac_channel_modes,
141 SPA_N_ELEMENTS(ldac_channel_modes),
142 conf.channel_mode,
143 info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS
144 )) < 0)
145 return -ENOTSUP;
146 conf.channel_mode = ldac_channel_modes[i].config;
147
148 memcpy(config, &conf, sizeof(conf));
149
150 return sizeof(conf);
151 }
152
codec_enum_config(const struct a2dp_codec * codec,const void * caps,size_t caps_size,uint32_t id,uint32_t idx,struct spa_pod_builder * b,struct spa_pod ** param)153 static int codec_enum_config(const struct a2dp_codec *codec,
154 const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
155 struct spa_pod_builder *b, struct spa_pod **param)
156 {
157 a2dp_ldac_t conf;
158 struct spa_pod_frame f[2];
159 struct spa_pod_choice *choice;
160 uint32_t i = 0;
161 uint32_t position[SPA_AUDIO_MAX_CHANNELS];
162
163 if (caps_size < sizeof(conf))
164 return -EINVAL;
165
166 memcpy(&conf, caps, sizeof(conf));
167
168 if (idx > 0)
169 return 0;
170
171 spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
172 spa_pod_builder_add(b,
173 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
174 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
175 SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(5,
176 SPA_AUDIO_FORMAT_F32,
177 SPA_AUDIO_FORMAT_F32,
178 SPA_AUDIO_FORMAT_S32,
179 SPA_AUDIO_FORMAT_S24,
180 SPA_AUDIO_FORMAT_S16),
181 0);
182 spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
183
184 spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
185 choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
186 i = 0;
187 if (conf.frequency & LDACBT_SAMPLING_FREQ_048000) {
188 if (i++ == 0)
189 spa_pod_builder_int(b, 48000);
190 spa_pod_builder_int(b, 48000);
191 }
192 if (conf.frequency & LDACBT_SAMPLING_FREQ_044100) {
193 if (i++ == 0)
194 spa_pod_builder_int(b, 44100);
195 spa_pod_builder_int(b, 44100);
196 }
197 if (conf.frequency & LDACBT_SAMPLING_FREQ_088200) {
198 if (i++ == 0)
199 spa_pod_builder_int(b, 88200);
200 spa_pod_builder_int(b, 88200);
201 }
202 if (conf.frequency & LDACBT_SAMPLING_FREQ_096000) {
203 if (i++ == 0)
204 spa_pod_builder_int(b, 96000);
205 spa_pod_builder_int(b, 96000);
206 }
207 if (i == 0)
208 return -EINVAL;
209 if (i > 1)
210 choice->body.type = SPA_CHOICE_Enum;
211 spa_pod_builder_pop(b, &f[1]);
212
213 if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO &&
214 conf.channel_mode & (LDACBT_CHANNEL_MODE_STEREO |
215 LDACBT_CHANNEL_MODE_DUAL_CHANNEL)) {
216 spa_pod_builder_add(b,
217 SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
218 0);
219 } else if (conf.channel_mode & LDACBT_CHANNEL_MODE_MONO) {
220 position[0] = SPA_AUDIO_CHANNEL_MONO;
221 spa_pod_builder_add(b,
222 SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
223 SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
224 SPA_TYPE_Id, 1, position),
225 0);
226 } else {
227 position[0] = SPA_AUDIO_CHANNEL_FL;
228 position[1] = SPA_AUDIO_CHANNEL_FR;
229 spa_pod_builder_add(b,
230 SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
231 SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
232 SPA_TYPE_Id, 2, position),
233 0);
234 }
235 *param = spa_pod_builder_pop(b, &f[0]);
236 return *param == NULL ? -EIO : 1;
237 }
238
codec_reduce_bitpool(void * data)239 static int codec_reduce_bitpool(void *data)
240 {
241 #ifdef ENABLE_LDAC_ABR
242 return -ENOTSUP;
243 #else
244 struct impl *this = data;
245 int res;
246 if (this->eqmid == LDACBT_EQMID_BITRATE_330000 || !this->enable_abr)
247 return this->eqmid;
248 res = ldacBT_alter_eqmid_priority(this->ldac, LDACBT_EQMID_INC_CONNECTION);
249 return res;
250 #endif
251 }
252
codec_increase_bitpool(void * data)253 static int codec_increase_bitpool(void *data)
254 {
255 #ifdef ENABLE_LDAC_ABR
256 return -ENOTSUP;
257 #else
258 struct impl *this = data;
259 int res;
260 if (!this->enable_abr)
261 return this->eqmid;
262 res = ldacBT_alter_eqmid_priority(this->ldac, LDACBT_EQMID_INC_QUALITY);
263 return res;
264 #endif
265 }
266
codec_get_block_size(void * data)267 static int codec_get_block_size(void *data)
268 {
269 struct impl *this = data;
270 return this->codesize;
271 }
272
string_to_eqmid(const char * eqmid)273 static int string_to_eqmid(const char * eqmid)
274 {
275 if (spa_streq("auto", eqmid))
276 return LDACBT_EQMID_AUTO;
277 else if (spa_streq("hq", eqmid))
278 return LDACBT_EQMID_HQ;
279 else if (spa_streq("sq", eqmid))
280 return LDACBT_EQMID_SQ;
281 else if (spa_streq("mq", eqmid))
282 return LDACBT_EQMID_MQ;
283 else
284 return LDACBT_EQMID_AUTO;
285 }
286
codec_init_props(const struct a2dp_codec * codec,const struct spa_dict * settings)287 static void *codec_init_props(const struct a2dp_codec *codec, const struct spa_dict *settings)
288 {
289 struct props *p = calloc(1, sizeof(struct props));
290 const char *str;
291
292 if (p == NULL)
293 return NULL;
294
295 if (settings == NULL || (str = spa_dict_lookup(settings, "bluez5.a2dp.ldac.quality")) == NULL)
296 str = "auto";
297
298 p->eqmid = string_to_eqmid(str);
299 return p;
300 }
301
codec_clear_props(void * props)302 static void codec_clear_props(void *props)
303 {
304 free(props);
305 }
306
codec_enum_props(void * props,const struct spa_dict * settings,uint32_t id,uint32_t idx,struct spa_pod_builder * b,struct spa_pod ** param)307 static int codec_enum_props(void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx,
308 struct spa_pod_builder *b, struct spa_pod **param)
309 {
310 struct props *p = props;
311 struct spa_pod_frame f[2];
312 switch (id) {
313 case SPA_PARAM_PropInfo:
314 {
315 switch (idx) {
316 case 0:
317 spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
318 spa_pod_builder_prop(b, SPA_PROP_INFO_id, 0);
319 spa_pod_builder_id(b, SPA_PROP_quality);
320 spa_pod_builder_prop(b, SPA_PROP_INFO_name, 0);
321 spa_pod_builder_string(b, "LDAC quality");
322
323 spa_pod_builder_prop(b, SPA_PROP_INFO_type, 0);
324 spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0);
325 spa_pod_builder_int(b, p->eqmid);
326 spa_pod_builder_int(b, LDACBT_EQMID_AUTO);
327 spa_pod_builder_int(b, LDACBT_EQMID_HQ);
328 spa_pod_builder_int(b, LDACBT_EQMID_SQ);
329 spa_pod_builder_int(b, LDACBT_EQMID_MQ);
330 spa_pod_builder_pop(b, &f[1]);
331
332 spa_pod_builder_prop(b, SPA_PROP_INFO_labels, 0);
333 spa_pod_builder_push_struct(b, &f[1]);
334 spa_pod_builder_int(b, LDACBT_EQMID_AUTO);
335 spa_pod_builder_string(b, "auto");
336 spa_pod_builder_int(b, LDACBT_EQMID_HQ);
337 spa_pod_builder_string(b, "hq");
338 spa_pod_builder_int(b, LDACBT_EQMID_SQ);
339 spa_pod_builder_string(b, "sq");
340 spa_pod_builder_int(b, LDACBT_EQMID_MQ);
341 spa_pod_builder_string(b, "mq");
342 spa_pod_builder_pop(b, &f[1]);
343
344 *param = spa_pod_builder_pop(b, &f[0]);
345 break;
346 default:
347 return 0;
348 }
349 break;
350 }
351 case SPA_PARAM_Props:
352 {
353 switch (idx) {
354 case 0:
355 *param = spa_pod_builder_add_object(b,
356 SPA_TYPE_OBJECT_Props, id,
357 SPA_PROP_quality, SPA_POD_Int(p->eqmid));
358 break;
359 default:
360 return 0;
361 }
362 break;
363 }
364 default:
365 return -ENOENT;
366 }
367 return 1;
368 }
369
codec_set_props(void * props,const struct spa_pod * param)370 static int codec_set_props(void *props, const struct spa_pod *param)
371 {
372 struct props *p = props;
373 const int prev_eqmid = p->eqmid;
374 if (param == NULL) {
375 p->eqmid = LDACBT_EQMID_AUTO;
376 } else {
377 spa_pod_parse_object(param,
378 SPA_TYPE_OBJECT_Props, NULL,
379 SPA_PROP_quality, SPA_POD_OPT_Int(&p->eqmid));
380 if (p->eqmid != LDACBT_EQMID_AUTO &&
381 (p->eqmid < LDACBT_EQMID_HQ || p->eqmid > LDACBT_EQMID_MQ))
382 p->eqmid = prev_eqmid;
383 }
384
385 return prev_eqmid != p->eqmid;
386 }
387
codec_init(const struct a2dp_codec * codec,uint32_t flags,void * config,size_t config_len,const struct spa_audio_info * info,void * props,size_t mtu)388 static void *codec_init(const struct a2dp_codec *codec, uint32_t flags,
389 void *config, size_t config_len, const struct spa_audio_info *info,
390 void *props, size_t mtu)
391 {
392 struct impl *this;
393 a2dp_ldac_t *conf = config;
394 int res;
395 struct props *p = props;
396
397 this = calloc(1, sizeof(struct impl));
398 if (this == NULL)
399 goto error_errno;
400
401 this->ldac = ldacBT_get_handle();
402 if (this->ldac == NULL)
403 goto error_errno;
404
405 #ifdef ENABLE_LDAC_ABR
406 this->ldac_abr = ldac_ABR_get_handle();
407 if (this->ldac_abr == NULL)
408 goto error_errno;
409 #endif
410
411 if (p == NULL || p->eqmid == LDACBT_EQMID_AUTO) {
412 this->eqmid = LDACBT_EQMID_SQ;
413 this->enable_abr = true;
414 } else {
415 this->eqmid = p->eqmid;
416 this->enable_abr = false;
417 }
418
419 this->mtu = mtu;
420 this->frequency = info->info.raw.rate;
421 this->codesize = info->info.raw.channels * LDACBT_ENC_LSU;
422
423 switch (info->info.raw.format) {
424 case SPA_AUDIO_FORMAT_F32:
425 this->fmt = LDACBT_SMPL_FMT_F32;
426 this->codesize *= 4;
427 break;
428 case SPA_AUDIO_FORMAT_S32:
429 this->fmt = LDACBT_SMPL_FMT_S32;
430 this->codesize *= 4;
431 break;
432 case SPA_AUDIO_FORMAT_S24:
433 this->fmt = LDACBT_SMPL_FMT_S24;
434 this->codesize *= 3;
435 break;
436 case SPA_AUDIO_FORMAT_S16:
437 this->fmt = LDACBT_SMPL_FMT_S16;
438 this->codesize *= 2;
439 break;
440 default:
441 res = -EINVAL;
442 goto error;
443 }
444
445 res = ldacBT_init_handle_encode(this->ldac,
446 this->mtu,
447 this->eqmid,
448 conf->channel_mode,
449 this->fmt,
450 this->frequency);
451 if (res < 0)
452 goto error;
453
454 #ifdef ENABLE_LDAC_ABR
455 res = ldac_ABR_Init(this->ldac_abr, LDAC_ABR_INTERVAL_MS);
456 if (res < 0)
457 goto error;
458
459 res = ldac_ABR_set_thresholds(this->ldac_abr,
460 LDAC_ABR_THRESHOLD_CRITICAL,
461 LDAC_ABR_THRESHOLD_DANGEROUSTREND,
462 LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ);
463 if (res < 0)
464 goto error;
465 #endif
466
467 return this;
468
469 error_errno:
470 res = -errno;
471 error:
472 if (this->ldac)
473 ldacBT_free_handle(this->ldac);
474 #ifdef ENABLE_LDAC_ABR
475 if (this->ldac_abr)
476 ldac_ABR_free_handle(this->ldac_abr);
477 #endif
478 free(this);
479 errno = -res;
480 return NULL;
481 }
482
codec_deinit(void * data)483 static void codec_deinit(void *data)
484 {
485 struct impl *this = data;
486 if (this->ldac)
487 ldacBT_free_handle(this->ldac);
488 #ifdef ENABLE_LDAC_ABR
489 if (this->ldac_abr)
490 ldac_ABR_free_handle(this->ldac_abr);
491 #endif
492 free(this);
493 }
494
codec_update_props(void * data,void * props)495 static int codec_update_props(void *data, void *props)
496 {
497 struct impl *this = data;
498 struct props *p = props;
499 int res;
500
501 if (p == NULL)
502 return 0;
503
504 if (p->eqmid == LDACBT_EQMID_AUTO) {
505 this->eqmid = LDACBT_EQMID_SQ;
506 this->enable_abr = true;
507 } else {
508 this->eqmid = p->eqmid;
509 this->enable_abr = false;
510 }
511
512 if ((res = ldacBT_set_eqmid(this->ldac, this->eqmid)) < 0)
513 goto error;
514 return 0;
515 error:
516 return res;
517 }
518
codec_abr_process(void * data,size_t unsent)519 static int codec_abr_process(void *data, size_t unsent)
520 {
521 #ifdef ENABLE_LDAC_ABR
522 struct impl *this = data;
523 int res;
524 res = ldac_ABR_Proc(this->ldac, this->ldac_abr,
525 unsent / LDAC_ABR_MAX_PACKET_NBYTES, this->enable_abr);
526 return res;
527 #else
528 return -ENOTSUP;
529 #endif
530 }
531
codec_start_encode(void * data,void * dst,size_t dst_size,uint16_t seqnum,uint32_t timestamp)532 static int codec_start_encode (void *data,
533 void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp)
534 {
535 struct impl *this = data;
536
537 this->header = (struct rtp_header *)dst;
538 this->payload = SPA_PTROFF(dst, sizeof(struct rtp_header), struct rtp_payload);
539 memset(this->header, 0, sizeof(struct rtp_header)+sizeof(struct rtp_payload));
540
541 this->payload->frame_count = 0;
542 this->header->v = 2;
543 this->header->pt = 96;
544 this->header->sequence_number = htons(seqnum);
545 this->header->timestamp = htonl(timestamp);
546 this->header->ssrc = htonl(1);
547 return sizeof(struct rtp_header) + sizeof(struct rtp_payload);
548 }
549
codec_encode(void * data,const void * src,size_t src_size,void * dst,size_t dst_size,size_t * dst_out,int * need_flush)550 static int codec_encode(void *data,
551 const void *src, size_t src_size,
552 void *dst, size_t dst_size,
553 size_t *dst_out, int *need_flush)
554 {
555 struct impl *this = data;
556 int res, src_used, dst_used, frame_num = 0;
557
558 src_used = src_size;
559 dst_used = dst_size;
560
561 res = ldacBT_encode(this->ldac, (void*)src, &src_used, dst, &dst_used, &frame_num);
562 if (SPA_UNLIKELY(res < 0))
563 return -EINVAL;
564
565 *dst_out = dst_used;
566
567 this->payload->frame_count += frame_num;
568 *need_flush = this->payload->frame_count > 0;
569
570 return src_used;
571 }
572
573 const struct a2dp_codec a2dp_codec_ldac = {
574 .id = SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
575 .codec_id = A2DP_CODEC_VENDOR,
576 .vendor = { .vendor_id = LDAC_VENDOR_ID,
577 .codec_id = LDAC_CODEC_ID },
578 .name = "ldac",
579 .description = "LDAC",
580 #ifdef ENABLE_LDAC_ABR
581 .send_buf_size = LDAC_ABR_SOCK_BUFFER_SIZE,
582 #endif
583 .fill_caps = codec_fill_caps,
584 .select_config = codec_select_config,
585 .enum_config = codec_enum_config,
586 .init_props = codec_init_props,
587 .enum_props = codec_enum_props,
588 .set_props = codec_set_props,
589 .clear_props = codec_clear_props,
590 .init = codec_init,
591 .deinit = codec_deinit,
592 .update_props = codec_update_props,
593 .get_block_size = codec_get_block_size,
594 .abr_process = codec_abr_process,
595 .start_encode = codec_start_encode,
596 .encode = codec_encode,
597 .reduce_bitpool = codec_reduce_bitpool,
598 .increase_bitpool = codec_increase_bitpool,
599 };
600
601 A2DP_CODEC_EXPORT_DEF(
602 "ldac",
603 &a2dp_codec_ldac
604 );
605