Skip to content

Commit 5f2bdc0

Browse files
committed
Keep A2DP configuration selection in codec files
Also, this commit improves A2DP codec selection logic in case where provided configuration is invalid in terms of match with peer and BlueALSA capabilities. Now, such codec selection request will be rejected by BlueALSA instead of being rejected by peer device, which might result in Bluetooth disconnection.
1 parent bc5d8d3 commit 5f2bdc0

File tree

13 files changed

+490
-437
lines changed

13 files changed

+490
-437
lines changed

.github/spellcheck-wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ conf
8181
const
8282
dereferenced
8383
duplications
84+
durations
8485
endian
8586
endianness
8687
enum

src/a2dp-aac.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "a2dp-aac.h"
1212

1313
#include <errno.h>
14+
#include <limits.h>
1415
#include <pthread.h>
1516
#include <stdbool.h>
1617
#include <stddef.h>
@@ -23,6 +24,8 @@
2324
#define AACENCODER_LIB_VERSION LIB_VERSION( \
2425
AACENCODER_LIB_VL0, AACENCODER_LIB_VL1, AACENCODER_LIB_VL2)
2526

27+
#include <glib.h>
28+
2629
#include "a2dp.h"
2730
#include "ba-transport.h"
2831
#include "ba-transport-pcm.h"
@@ -475,6 +478,80 @@ static const struct a2dp_sampling a2dp_aac_samplings[] = {
475478
{ 0 },
476479
};
477480

481+
static int a2dp_aac_capabilities_filter(
482+
const struct a2dp_codec *codec,
483+
const void *capabilities_mask,
484+
void *capabilities) {
485+
486+
(void)codec;
487+
const a2dp_aac_t *caps_mask = capabilities_mask;
488+
a2dp_aac_t *caps = capabilities;
489+
490+
int rate = MIN(AAC_GET_BITRATE(*caps), AAC_GET_BITRATE(*caps_mask));
491+
492+
for (size_t i = 0; i < sizeof(*caps); i++)
493+
((uint8_t *)caps)[i] = ((uint8_t *)caps)[i] & ((uint8_t *)caps_mask)[i];
494+
495+
AAC_SET_BITRATE(*caps, rate);
496+
497+
return 0;
498+
}
499+
500+
static int a2dp_aac_configuration_select(
501+
const struct a2dp_codec *codec,
502+
void *capabilities) {
503+
504+
a2dp_aac_t *caps = capabilities;
505+
const a2dp_aac_t saved = *caps;
506+
507+
/* narrow capabilities to values supported by BlueALSA */
508+
if (a2dp_filter_capabilities(codec, &codec->capabilities,
509+
caps, sizeof(*caps)) != 0)
510+
return -1;
511+
512+
if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
513+
caps->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_SCA;
514+
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
515+
caps->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LTP;
516+
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
517+
caps->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC;
518+
else if (caps->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
519+
caps->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC;
520+
else {
521+
error("AAC: No supported object types: %#x", saved.object_type);
522+
return errno = ENOTSUP, -1;
523+
}
524+
525+
const struct a2dp_sampling *sampling;
526+
const uint16_t caps_frequency = AAC_GET_FREQUENCY(*caps);
527+
if ((sampling = a2dp_sampling_select(a2dp_aac_samplings, caps_frequency)) != NULL)
528+
AAC_SET_FREQUENCY(*caps, sampling->value);
529+
else {
530+
error("AAC: No supported sampling frequencies: %#x", AAC_GET_FREQUENCY(saved));
531+
return errno = ENOTSUP, -1;
532+
}
533+
534+
const struct a2dp_channel_mode *chm;
535+
if ((chm = a2dp_channel_mode_select(a2dp_aac_channels, caps->channels)) != NULL)
536+
caps->channels = chm->value;
537+
else {
538+
error("AAC: No supported channels: %#x", saved.channels);
539+
return errno = ENOTSUP, -1;
540+
}
541+
542+
unsigned int ba_bitrate = AAC_GET_BITRATE(codec->capabilities.aac);
543+
unsigned int cap_bitrate = AAC_GET_BITRATE(*caps);
544+
if (cap_bitrate == 0)
545+
/* fix bitrate value if it was not set */
546+
cap_bitrate = UINT_MAX;
547+
AAC_SET_BITRATE(*caps, MIN(cap_bitrate, ba_bitrate));
548+
549+
if (!config.aac_prefer_vbr)
550+
caps->vbr = 0;
551+
552+
return 0;
553+
}
554+
478555
static int a2dp_aac_transport_init(struct ba_transport *t) {
479556

480557
const struct a2dp_channel_mode *chm;
@@ -556,6 +633,8 @@ struct a2dp_codec a2dp_aac_source = {
556633
.channels[0] = a2dp_aac_channels,
557634
.samplings[0] = a2dp_aac_samplings,
558635
.init = a2dp_aac_source_init,
636+
.capabilities_filter = a2dp_aac_capabilities_filter,
637+
.configuration_select = a2dp_aac_configuration_select,
559638
.transport_init = a2dp_aac_transport_init,
560639
.transport_start = a2dp_aac_source_transport_start,
561640
.enabled = true,
@@ -615,6 +694,8 @@ struct a2dp_codec a2dp_aac_sink = {
615694
.channels[0] = a2dp_aac_channels,
616695
.samplings[0] = a2dp_aac_samplings,
617696
.init = a2dp_aac_sink_init,
697+
.capabilities_filter = a2dp_aac_capabilities_filter,
698+
.configuration_select = a2dp_aac_configuration_select,
618699
.transport_init = a2dp_aac_transport_init,
619700
.transport_start = a2dp_aac_sink_transport_start,
620701
.enabled = true,

src/a2dp-aptx-hd.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,37 @@ static const struct a2dp_sampling a2dp_aptx_hd_samplings[] = {
282282
{ 0 },
283283
};
284284

285+
static int a2dp_aptx_hd_configuration_select(
286+
const struct a2dp_codec *codec,
287+
void *capabilities) {
288+
289+
a2dp_aptx_hd_t *caps = capabilities;
290+
const a2dp_aptx_hd_t saved = *caps;
291+
292+
/* narrow capabilities to values supported by BlueALSA */
293+
if (a2dp_filter_capabilities(codec, &codec->capabilities,
294+
caps, sizeof(*caps)) != 0)
295+
return -1;
296+
297+
const struct a2dp_sampling *sampling;
298+
if ((sampling = a2dp_sampling_select(a2dp_aptx_hd_samplings, caps->aptx.frequency)) != NULL)
299+
caps->aptx.frequency = sampling->value;
300+
else {
301+
error("apt-X HD: No supported sampling frequencies: %#x", saved.aptx.frequency);
302+
return errno = ENOTSUP, -1;
303+
}
304+
305+
const struct a2dp_channel_mode *chm;
306+
if ((chm = a2dp_channel_mode_select(a2dp_aptx_hd_channels, caps->aptx.channel_mode)) != NULL)
307+
caps->aptx.channel_mode = chm->value;
308+
else {
309+
error("apt-X HD: No supported channel modes: %#x", saved.aptx.channel_mode);
310+
return errno = ENOTSUP, -1;
311+
}
312+
313+
return 0;
314+
}
315+
285316
static int a2dp_aptx_hd_transport_init(struct ba_transport *t) {
286317

287318
const struct a2dp_channel_mode *chm;
@@ -303,7 +334,7 @@ static int a2dp_aptx_hd_transport_init(struct ba_transport *t) {
303334

304335
static int a2dp_aptx_hd_source_init(struct a2dp_codec *codec) {
305336
if (config.a2dp.force_mono)
306-
warn("Apt-X HD mono channel mode not supported");
337+
warn("apt-X HD: Mono channel mode not supported");
307338
if (config.a2dp.force_44100)
308339
codec->capabilities.aptx_hd.aptx.frequency = APTX_SAMPLING_FREQ_44100;
309340
return 0;
@@ -333,6 +364,7 @@ struct a2dp_codec a2dp_aptx_hd_source = {
333364
.channels[0] = a2dp_aptx_hd_channels,
334365
.samplings[0] = a2dp_aptx_hd_samplings,
335366
.init = a2dp_aptx_hd_source_init,
367+
.configuration_select = a2dp_aptx_hd_configuration_select,
336368
.transport_init = a2dp_aptx_hd_transport_init,
337369
.transport_start = a2dp_aptx_hd_source_transport_start,
338370
};
@@ -362,6 +394,7 @@ struct a2dp_codec a2dp_aptx_hd_sink = {
362394
.capabilities_size = sizeof(a2dp_aptx_hd_t),
363395
.channels[0] = a2dp_aptx_hd_channels,
364396
.samplings[0] = a2dp_aptx_hd_samplings,
397+
.configuration_select = a2dp_aptx_hd_configuration_select,
365398
.transport_init = a2dp_aptx_hd_transport_init,
366399
.transport_start = a2dp_aptx_hd_sink_transport_start,
367400
};

src/a2dp-aptx.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,37 @@ static const struct a2dp_sampling a2dp_aptx_samplings[] = {
245245
{ 0 },
246246
};
247247

248+
static int a2dp_aptx_configuration_select(
249+
const struct a2dp_codec *codec,
250+
void *capabilities) {
251+
252+
a2dp_aptx_t *caps = capabilities;
253+
const a2dp_aptx_t saved = *caps;
254+
255+
/* narrow capabilities to values supported by BlueALSA */
256+
if (a2dp_filter_capabilities(codec, &codec->capabilities,
257+
caps, sizeof(*caps)) != 0)
258+
return -1;
259+
260+
const struct a2dp_sampling *sampling;
261+
if ((sampling = a2dp_sampling_select(a2dp_aptx_samplings, caps->frequency)) != NULL)
262+
caps->frequency = sampling->value;
263+
else {
264+
error("apt-X: No supported sampling frequencies: %#x", saved.frequency);
265+
return errno = ENOTSUP, -1;
266+
}
267+
268+
const struct a2dp_channel_mode *chm;
269+
if ((chm = a2dp_channel_mode_select(a2dp_aptx_channels, caps->channel_mode)) != NULL)
270+
caps->channel_mode = chm->value;
271+
else {
272+
error("apt-X: No supported channel modes: %#x", saved.channel_mode);
273+
return errno = ENOTSUP, -1;
274+
}
275+
276+
return 0;
277+
}
278+
248279
static int a2dp_aptx_transport_init(struct ba_transport *t) {
249280

250281
const struct a2dp_channel_mode *chm;
@@ -266,7 +297,7 @@ static int a2dp_aptx_transport_init(struct ba_transport *t) {
266297

267298
static int a2dp_aptx_source_init(struct a2dp_codec *codec) {
268299
if (config.a2dp.force_mono)
269-
warn("Apt-X mono channel mode not supported");
300+
warn("apt-X: Mono channel mode not supported");
270301
if (config.a2dp.force_44100)
271302
codec->capabilities.aptx.frequency = APTX_SAMPLING_FREQ_44100;
272303
return 0;
@@ -296,6 +327,7 @@ struct a2dp_codec a2dp_aptx_source = {
296327
.channels[0] = a2dp_aptx_channels,
297328
.samplings[0] = a2dp_aptx_samplings,
298329
.init = a2dp_aptx_source_init,
330+
.configuration_select = a2dp_aptx_configuration_select,
299331
.transport_init = a2dp_aptx_transport_init,
300332
.transport_start = a2dp_aptx_source_transport_start,
301333
};
@@ -325,6 +357,7 @@ struct a2dp_codec a2dp_aptx_sink = {
325357
.capabilities_size = sizeof(a2dp_aptx_t),
326358
.channels[0] = a2dp_aptx_channels,
327359
.samplings[0] = a2dp_aptx_samplings,
360+
.configuration_select = a2dp_aptx_configuration_select,
328361
.transport_init = a2dp_aptx_transport_init,
329362
.transport_start = a2dp_aptx_sink_transport_start,
330363
};

src/a2dp-faststream.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,44 @@ static const struct a2dp_sampling a2dp_faststream_samplings_voice[] = {
251251
{ 0 },
252252
};
253253

254+
static int a2dp_faststream_configuration_select(
255+
const struct a2dp_codec *codec,
256+
void *capabilities) {
257+
258+
a2dp_faststream_t *caps = capabilities;
259+
const a2dp_faststream_t saved = *caps;
260+
261+
/* narrow capabilities to values supported by BlueALSA */
262+
if (a2dp_filter_capabilities(codec, &codec->capabilities,
263+
caps, sizeof(*caps)) != 0)
264+
return -1;
265+
266+
if ((caps->direction & (FASTSTREAM_DIRECTION_MUSIC | FASTSTREAM_DIRECTION_VOICE)) == 0) {
267+
error("FastStream: No supported directions: %#x", saved.direction);
268+
return errno = ENOTSUP, -1;
269+
}
270+
271+
const struct a2dp_sampling *sampling_v;
272+
if (caps->direction & FASTSTREAM_DIRECTION_VOICE &&
273+
(sampling_v = a2dp_sampling_select(a2dp_faststream_samplings_voice, caps->frequency_voice)) != NULL)
274+
caps->frequency_voice = sampling_v->value;
275+
else {
276+
error("FastStream: No supported voice sampling frequencies: %#x", saved.frequency_voice);
277+
return errno = ENOTSUP, -1;
278+
}
279+
280+
const struct a2dp_sampling *sampling_m;
281+
if (caps->direction & FASTSTREAM_DIRECTION_MUSIC &&
282+
(sampling_m = a2dp_sampling_select(a2dp_faststream_samplings_music, caps->frequency_music)) != NULL)
283+
caps->frequency_music = sampling_m->value;
284+
else {
285+
error("FastStream: No supported music sampling frequencies: %#x", saved.frequency_music);
286+
return errno = ENOTSUP, -1;
287+
}
288+
289+
return 0;
290+
}
291+
254292
static int a2dp_faststream_transport_init(struct ba_transport *t) {
255293

256294
if (t->a2dp.configuration.faststream.direction & FASTSTREAM_DIRECTION_MUSIC) {
@@ -284,7 +322,7 @@ static int a2dp_faststream_transport_init(struct ba_transport *t) {
284322

285323
static int a2dp_faststream_source_init(struct a2dp_codec *codec) {
286324
if (config.a2dp.force_mono)
287-
warn("FastStream mono channel mode not supported");
325+
warn("FastStream: Mono channel mode not supported");
288326
if (config.a2dp.force_44100)
289327
codec->capabilities.faststream.frequency_music = FASTSTREAM_SAMPLING_FREQ_MUSIC_44100;
290328
return 0;
@@ -321,6 +359,7 @@ struct a2dp_codec a2dp_faststream_source = {
321359
.samplings[0] = a2dp_faststream_samplings_music,
322360
.samplings[1] = a2dp_faststream_samplings_voice,
323361
.init = a2dp_faststream_source_init,
362+
.configuration_select = a2dp_faststream_configuration_select,
324363
.transport_init = a2dp_faststream_transport_init,
325364
.transport_start = a2dp_faststream_source_transport_start,
326365
};
@@ -355,6 +394,7 @@ struct a2dp_codec a2dp_faststream_sink = {
355394
.capabilities_size = sizeof(a2dp_faststream_t),
356395
.samplings[0] = a2dp_faststream_samplings_music,
357396
.samplings[1] = a2dp_faststream_samplings_voice,
397+
.configuration_select = a2dp_faststream_configuration_select,
358398
.transport_init = a2dp_faststream_transport_init,
359399
.transport_start = a2dp_faststream_sink_transport_start,
360400
};

src/a2dp-lc3plus.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,49 @@ static const struct a2dp_sampling a2dp_lc3plus_samplings[] = {
547547
{ 0 },
548548
};
549549

550+
static int a2dp_lc3plus_configuration_select(
551+
const struct a2dp_codec *codec,
552+
void *capabilities) {
553+
554+
a2dp_lc3plus_t *caps = capabilities;
555+
const a2dp_lc3plus_t saved = *caps;
556+
557+
/* narrow capabilities to values supported by BlueALSA */
558+
if (a2dp_filter_capabilities(codec, &codec->capabilities,
559+
caps, sizeof(*caps)) != 0)
560+
return -1;
561+
562+
if (caps->frame_duration & LC3PLUS_FRAME_DURATION_100)
563+
caps->frame_duration = LC3PLUS_FRAME_DURATION_100;
564+
else if (caps->frame_duration & LC3PLUS_FRAME_DURATION_050)
565+
caps->frame_duration = LC3PLUS_FRAME_DURATION_050;
566+
else if (caps->frame_duration & LC3PLUS_FRAME_DURATION_025)
567+
caps->frame_duration = LC3PLUS_FRAME_DURATION_025;
568+
else {
569+
error("LC3plus: No supported frame durations: %#x", saved.frame_duration);
570+
return errno = ENOTSUP, -1;
571+
}
572+
573+
const struct a2dp_channel_mode *chm;
574+
if ((chm = a2dp_channel_mode_select(a2dp_lc3plus_channels, caps->channels)) != NULL)
575+
caps->channels = chm->value;
576+
else {
577+
error("LC3plus: No supported channels: %#x", saved.channels);
578+
return errno = ENOTSUP, -1;
579+
}
580+
581+
const struct a2dp_sampling *sampling;
582+
const uint16_t caps_frequency = LC3PLUS_GET_FREQUENCY(*caps);
583+
if ((sampling = a2dp_sampling_select(a2dp_lc3plus_samplings, caps_frequency)) != NULL)
584+
LC3PLUS_SET_FREQUENCY(*caps, sampling->value);
585+
else {
586+
error("LC3plus: No supported sampling frequencies: %#x", LC3PLUS_GET_FREQUENCY(saved));
587+
return errno = ENOTSUP, -1;
588+
}
589+
590+
return 0;
591+
}
592+
550593
static int a2dp_lc3plus_transport_init(struct ba_transport *t) {
551594

552595
const struct a2dp_channel_mode *chm;
@@ -570,7 +613,7 @@ static int a2dp_lc3plus_source_init(struct a2dp_codec *codec) {
570613
if (config.a2dp.force_mono)
571614
codec->capabilities.lc3plus.channels = LC3PLUS_CHANNELS_1;
572615
if (config.a2dp.force_44100)
573-
warn("LC3plus 44.1 kHz sampling frequency not supported");
616+
warn("LC3plus: 44.1 kHz sampling frequency not supported");
574617
return 0;
575618
}
576619

@@ -599,6 +642,7 @@ struct a2dp_codec a2dp_lc3plus_source = {
599642
.channels[0] = a2dp_lc3plus_channels,
600643
.samplings[0] = a2dp_lc3plus_samplings,
601644
.init = a2dp_lc3plus_source_init,
645+
.configuration_select = a2dp_lc3plus_configuration_select,
602646
.transport_init = a2dp_lc3plus_transport_init,
603647
.transport_start = a2dp_lc3plus_source_transport_start,
604648
};
@@ -627,6 +671,7 @@ struct a2dp_codec a2dp_lc3plus_sink = {
627671
.capabilities_size = sizeof(a2dp_lc3plus_t),
628672
.channels[0] = a2dp_lc3plus_channels,
629673
.samplings[0] = a2dp_lc3plus_samplings,
674+
.configuration_select = a2dp_lc3plus_configuration_select,
630675
.transport_init = a2dp_lc3plus_transport_init,
631676
.transport_start = a2dp_lc3plus_sink_transport_start,
632677
};

0 commit comments

Comments
 (0)