Skip to content

Commit

Permalink
Parse A2DP capabilities blob for aptX Adaptive
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Dec 19, 2024
1 parent 2357d98 commit 951d31b
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .github/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ EDE
EINTR
EINVAL
ENODEV
eoc
EP
EPIPE
EPMR
Expand Down Expand Up @@ -263,6 +264,7 @@ syslogd
systemd
TIOCOUTQ
TNS
ttp
TWS
ud
uint
Expand Down
8 changes: 1 addition & 7 deletions src/a2dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,9 @@ const struct a2dp_sep *a2dp_sep_lookup(enum a2dp_type type, uint32_t codec_id) {
* @param size A2DP vendor codec capabilities size.
* @return On success this function returns A2DP 32-bit vendor codec ID. */
uint32_t a2dp_get_vendor_codec_id(const void *capabilities, size_t size) {

if (size < sizeof(a2dp_vendor_info_t))
return errno = EINVAL, 0xFFFFFFFF;

const a2dp_vendor_info_t *info = capabilities;
const uint32_t vendor_id = A2DP_VENDOR_INFO_GET_VENDOR_ID(*info);
const uint16_t codec_id = A2DP_VENDOR_INFO_GET_CODEC_ID(*info);

return A2DP_CODEC_VENDOR_ID(vendor_id, codec_id);
return a2dp_codecs_vendor_codec_id(capabilities);
}

/**
Expand Down
13 changes: 12 additions & 1 deletion src/shared/a2dp-codecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static const struct {
* Get BlueALSA A2DP codec ID from string representation.
*
* @param alias Alias of an A2DP audio codec name.
* @return BlueALSA audio codec ID or 0xFFFFFFFF if there was no match. */
* @return BlueALSA A2DP codec ID or 0xFFFFFFFF if there was no match. */
uint32_t a2dp_codecs_codec_id_from_string(const char *alias) {
for (size_t i = 0; i < ARRAYSIZE(codecs); i++)
for (size_t n = 0; n < ARRAYSIZE(codecs[i].aliases); n++)
Expand All @@ -58,6 +58,17 @@ uint32_t a2dp_codecs_codec_id_from_string(const char *alias) {
return 0xFFFFFFFF;
}

/**
* Get BlueALSA A2DP codec ID from vendor codec information.
*
* @param info A2DP vendor codec capabilities.
* @return BlueALSA A2DP codec ID. */
uint32_t a2dp_codecs_vendor_codec_id(const a2dp_vendor_info_t *info) {
const uint32_t vendor_id = A2DP_VENDOR_INFO_GET_VENDOR_ID(*info);
const uint16_t codec_id = A2DP_VENDOR_INFO_GET_CODEC_ID(*info);
return A2DP_CODEC_VENDOR_ID(vendor_id, codec_id);
}

/**
* Convert BlueALSA A2DP codec ID into a human-readable string.
*
Expand Down
39 changes: 37 additions & 2 deletions src/shared/a2dp-codecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,39 @@ typedef struct a2dp_aptx_hd {
#define APTX_AD_VENDOR_ID BT_COMPID_QUALCOMM_TECH
#define APTX_AD_CODEC_ID 0x00AD

#define APTX_AD_CHANNEL_MODE_MONO (1 << 0)
#define APTX_AD_CHANNEL_MODE_STEREO (1 << 1)
#define APTX_AD_CHANNEL_MODE_TWS (1 << 2)
#define APTX_AD_CHANNEL_MODE_JOINT_STEREO (1 << 3)
#define APTX_AD_CHANNEL_MODE_TWS_MONO (1 << 4)

#define APTX_AD_SAMPLING_FREQ_44100 (1 << 0)
#define APTX_AD_SAMPLING_FREQ_48000 (1 << 1)
#define APTX_AD_SAMPLING_FREQ_88000 (1 << 2)
#define APTX_AD_SAMPLING_FREQ_192000 (1 << 3)

typedef struct {
a2dp_vendor_info_t info;
#if __BYTE_ORDER == __BIG_ENDIAN
uint8_t sampling_freq:5;
uint8_t rfa1:3;
uint8_t rfa2:3;
uint8_t channel_mode:5;
#else
uint8_t rfa1:3;
uint8_t sampling_freq:5;
uint8_t channel_mode:5;
uint8_t rfa2:3;
#endif
uint8_t ttp_ll_low;
uint8_t ttp_ll_high;
uint8_t ttp_hq_low;
uint8_t ttp_hq_high;
uint8_t ttp_tws_low;
uint8_t ttp_tws_high;
uint8_t eoc[3];
} __attribute__ ((packed)) a2dp_aptx_ad_t;

#define LC3PLUS_VENDOR_ID BT_COMPID_FRAUNHOFER_IIS
#define LC3PLUS_CODEC_ID 0x0001

Expand Down Expand Up @@ -764,11 +797,12 @@ typedef union a2dp {
a2dp_aac_t aac;
a2dp_usac_t usac;
a2dp_atrac_t atrac;
a2dp_aptx_t aptx;
a2dp_faststream_t faststream;
a2dp_aptx_t aptx;
a2dp_aptx_ad_t aptx_ad;
a2dp_aptx_hd_t aptx_hd;
a2dp_aptx_ll_t aptx_ll;
a2dp_aptx_ll_new_t aptx_ll_new;
a2dp_aptx_hd_t aptx_hd;
a2dp_lc3plus_t lc3plus;
a2dp_ldac_t ldac;
a2dp_lhdc_v1_t lhdc_v1;
Expand All @@ -780,6 +814,7 @@ typedef union a2dp {
} a2dp_t;

uint32_t a2dp_codecs_codec_id_from_string(const char *alias);
uint32_t a2dp_codecs_vendor_codec_id(const a2dp_vendor_info_t *info);
const char *a2dp_codecs_codec_id_to_string(uint32_t codec_id);
const char *a2dp_codecs_get_canonical_name(const char *alias);

Expand Down
57 changes: 49 additions & 8 deletions utils/a2dpconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>

Expand All @@ -34,6 +35,9 @@ static uint32_t get_codec(const char *s) {
if ((tmp = strchr(buffer, ':')) != NULL)
tmp[0] = '\0';

if (strcasecmp(buffer, "vendor") == 0)
return A2DP_CODEC_VENDOR;

return a2dp_codecs_codec_id_from_string(buffer);
}

Expand Down Expand Up @@ -280,15 +284,15 @@ static void dump_vendor(const void *blob, size_t size) {

static void printf_aptx(const a2dp_aptx_t *aptx) {
printf(""
" channel-mode:4 =%s%s%s\n"
" sample-rate:4 =%s%s%s%s\n",
aptx->channel_mode & APTX_CHANNEL_MODE_STEREO ? " Stereo" : "",
aptx->channel_mode & APTX_CHANNEL_MODE_TWS ? " DualChannel" : "",
aptx->channel_mode & APTX_CHANNEL_MODE_MONO ? " Mono" : "",
" sample-rate:4 =%s%s%s%s\n"
" channel-mode:4 =%s%s%s\n",
aptx->sampling_freq & APTX_SAMPLING_FREQ_48000 ? " 48000" : "",
aptx->sampling_freq & APTX_SAMPLING_FREQ_44100 ? " 44100" : "",
aptx->sampling_freq & APTX_SAMPLING_FREQ_32000 ? " 32000" : "",
aptx->sampling_freq & APTX_SAMPLING_FREQ_16000 ? " 16000" : "");
aptx->sampling_freq & APTX_SAMPLING_FREQ_16000 ? " 16000" : "",
aptx->channel_mode & APTX_CHANNEL_MODE_STEREO ? " Stereo" : "",
aptx->channel_mode & APTX_CHANNEL_MODE_TWS ? " DualChannel" : "",
aptx->channel_mode & APTX_CHANNEL_MODE_MONO ? " Mono" : "");
}

static void dump_aptx(const void *blob, size_t size) {
Expand All @@ -311,6 +315,39 @@ static void dump_aptx_tws(const void *blob, size_t size) {
printf("}\n");
}

static void dump_aptx_ad(const void *blob, size_t size) {
const a2dp_aptx_ad_t *aptx_ad = blob;
if (check_blob_size(sizeof(*aptx_ad), size) == -1)
return;
printf("aptX Adaptive <hex:%s> {\n", bintohex(blob, size));
printf_vendor(&aptx_ad->info);
printf(""
" sample-rate:5 =%s%s%s%s\n"
" <reserved>:6\n"
" channel-mode:5 =%s%s%s%s%s\n"
" ttp-ll-low:8 = %u\n"
" ttp-ll-high:8 = %u\n"
" ttp-hq-low:8 = %u\n"
" ttp-hq-high:8 = %u\n"
" ttp-tws-low:8 = %u\n"
" ttp-tws-high:8 = %u\n"
" eoc:24 = hex:%02x%02x%02x\n"
"}\n",
aptx_ad->sampling_freq & APTX_AD_SAMPLING_FREQ_192000 ? " 192000" : "",
aptx_ad->sampling_freq & APTX_AD_SAMPLING_FREQ_88000 ? " 88000" : "",
aptx_ad->sampling_freq & APTX_AD_SAMPLING_FREQ_48000 ? " 48000" : "",
aptx_ad->sampling_freq & APTX_AD_SAMPLING_FREQ_44100 ? " 44100" : "",
aptx_ad->channel_mode & APTX_AD_CHANNEL_MODE_TWS_MONO ? " TWS-Mono" : "",
aptx_ad->channel_mode & APTX_AD_CHANNEL_MODE_JOINT_STEREO ? " JointStereo" : "",
aptx_ad->channel_mode & APTX_AD_CHANNEL_MODE_STEREO ? " Stereo" : "",
aptx_ad->channel_mode & APTX_AD_CHANNEL_MODE_TWS ? " DualChannel" : "",
aptx_ad->channel_mode & APTX_AD_CHANNEL_MODE_MONO ? " Mono" : "",
aptx_ad->ttp_ll_low, aptx_ad->ttp_ll_high,
aptx_ad->ttp_hq_low, aptx_ad->ttp_hq_high,
aptx_ad->ttp_tws_low, aptx_ad->ttp_tws_high,
aptx_ad->eoc[0], aptx_ad->eoc[1], aptx_ad->eoc[2]);
}

static void dump_aptx_hd(const void *blob, size_t size) {
const a2dp_aptx_hd_t *aptx_hd = blob;
if (check_blob_size(sizeof(*aptx_hd), size) == -1)
Expand Down Expand Up @@ -620,7 +657,7 @@ static const struct {
{ A2DP_CODEC_VENDOR_ID(APTX_TWS_VENDOR_ID, APTX_TWS_CODEC_ID),
sizeof(a2dp_aptx_t), dump_aptx_tws },
{ A2DP_CODEC_VENDOR_ID(APTX_AD_VENDOR_ID, APTX_AD_CODEC_ID),
-1, dump_vendor },
sizeof(a2dp_aptx_ad_t), dump_aptx_ad },
{ A2DP_CODEC_VENDOR_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID),
sizeof(a2dp_aptx_hd_t), dump_aptx_hd },
{ A2DP_CODEC_VENDOR_ID(APTX_LL_VENDOR_ID, APTX_LL_CODEC_ID),
Expand Down Expand Up @@ -666,6 +703,10 @@ int dump(const char *config, bool detect) {
if (get_codec_blob(config, blob, blob_size) == -1)
goto final;

if (codec_id == A2DP_CODEC_VENDOR &&
(size_t)blob_size >= sizeof(a2dp_vendor_info_t))
codec_id = a2dp_codecs_vendor_codec_id(blob);

rv = 0;
for (size_t i = 0; i < ARRAYSIZE(dumps); i++)
if (dumps[i].codec_id == codec_id) {
Expand Down Expand Up @@ -715,7 +756,7 @@ int main(int argc, char *argv[]) {
" -x, --auto-detect\ttry to auto-detect codec\n"
"\nExamples:\n"
" %s sbc:ffff0235\n"
" %s aptx:4f0000000100ff\n",
" %s vendor:4f0000000100ff\n",
argv[0], argv[0], argv[0]);
return EXIT_SUCCESS;

Expand Down

0 comments on commit 951d31b

Please sign in to comment.