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