From 463ab8b01327dc7b2e869197a056ed865b9deab1 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Sun, 22 Dec 2024 23:13:32 +0100 Subject: [PATCH] Wireshark-like output for a2dpconf in verbose mode --- doc/a2dpconf.1.rst | 51 +- utils/a2dpconf.c | 1195 +++++++++++++++++++++++++++++--------------- 2 files changed, 812 insertions(+), 434 deletions(-) diff --git a/doc/a2dpconf.1.rst b/doc/a2dpconf.1.rst index 5d7fb98e3..4c7cd6049 100644 --- a/doc/a2dpconf.1.rst +++ b/doc/a2dpconf.1.rst @@ -6,7 +6,7 @@ a2dpconf Decode A2DP codec capability hex strings ---------------------------------------- -:Date: September 2024 +:Date: December 2024 :Manual section: 1 :Manual group: General Commands Manual :Version: $VERSION$ @@ -35,6 +35,11 @@ OPTIONS -V, --version Print version and exit. +-v, --verbose + Sow verbose bit-stream details. + Display each field as a binary mask with each bit represented by a single + character. + -x, --auto-detect Try to auto-detect the codec. If the name of the codec associated with the configuration string is not known, then give this option and the @@ -48,36 +53,36 @@ EXAMPLES $ a2dpconf sbc:ffff0235 SBC { - sample-rate:4 = 48000 44100 32000 16000 - channel-mode:4 = JointStereo Stereo DualChannel Mono - block-length:4 = 16 12 8 4 - sub-bands:2 = 8 4 - allocation-method:2 = Loudness SNR - min-bit-pool-value:8 = 2 - max-bit-pool-value:8 = 53 + Sample Rate ( 16000 Hz | 32000 Hz | 44100 Hz | 48000 Hz ) + Channel Mode ( Mono | Dual Channel | Stereo | Joint Stereo ) + Block Length ( 4 | 8 | 12 | 16 ) + Sub-bands ( 4 | 8 ) + Allocation Method ( SNR | Loudness ) + Min Bit-pool ( 2 ) + Max Bit-pool ( 53 ) } :: $ a2dpconf -x ffff0235 SBC { - sample-rate:4 = 48000 44100 32000 16000 - channel-mode:4 = JointStereo Stereo DualChannel Mono - block-length:4 = 16 12 8 4 - sub-bands:2 = 8 4 - allocation-method:2 = Loudness SNR - min-bit-pool-value:8 = 2 - max-bit-pool-value:8 = 53 + Sample Rate ( 16000 Hz | 32000 Hz | 44100 Hz | 48000 Hz ) + Channel Mode ( Mono | Dual Channel | Stereo | Joint Stereo ) + Block Length ( 4 | 8 | 12 | 16 ) + Sub-bands ( 4 | 8 ) + Allocation Method ( SNR | Loudness ) + Min Bit-pool ( 2 ) + Max Bit-pool ( 53 ) } MPEG-1,2 Audio { - layer:3 = MP3 MP2 MP1 - crc:1 = true - channel-mode:4 = JointStereo Stereo DualChannel Mono - :1 - media-payload-format:1 = MPF-1 MPF-2 - sample-rate:6 = 48000 44100 32000 24000 22050 16000 - vbr:1 = false - bitrate-index:15 = 0x235 + Layer ( MP1 | MP2 | MP3 ) + CRC ( true ) + Channel Mode ( Mono | Dual Channel | Stereo | Joint Stereo ) + RFA ( 1 ) + Media Payload Format ( MPF-1 | MPF-2 ) + Sample Rate ( 16000 Hz | 22050 Hz | 24000 Hz | 32000 Hz | ... ) + VBR ( false ) + Bitrate Index ( 0 | 2 | 4 | 5 | 9 ) } COPYRIGHT diff --git a/utils/a2dpconf.c b/utils/a2dpconf.c index aea9e7e6d..60ddaf6e9 100644 --- a/utils/a2dpconf.c +++ b/utils/a2dpconf.c @@ -13,6 +13,7 @@ #endif #include +#include #include #include #include @@ -77,391 +78,743 @@ static int check_blob_size(size_t size, size_t value) { return -1; } +static void print_bits(const void *bstream, size_t offset, size_t n, size_t size) { + + char mask[] = ".... .... .... .... .... .... .... ...."; + size_t mask_spaces = size == 0 ? 0 : (size - 1) / 4; + mask[size + mask_spaces] = '\0'; + + for (size_t i = offset; i < offset + n; i++) { + size_t spaces = i == 0 ? 0 : i / 4; + uint8_t byte = ((uint8_t *)bstream)[i / 8]; + uint8_t bit = 1 << (8 - (i % 8) - 1); + mask[i + spaces] = byte & bit ? '1' : '0'; + } + + printf(" %s", mask); + +} + +struct bitfield { + uint32_t value; + const char *label; +}; + +static bool verbose = false; + +static void print_bitfield(const char *name, const void *bstream, + size_t offset, size_t n, size_t size, uint32_t value, + const struct bitfield *fields) { + if (!verbose) { + printf(" %s (", name); + int elems = 0; + if (fields == NULL) + printf(" %s", value ? "true" : "false"); + else + for (const struct bitfield *f = fields; f->label != NULL; f++) + if (f->value == 0 || value & f->value) + printf("%s%s", elems++ == 0 ? " " : " | ", f->label); + printf(" )\n"); + } + else { + for (size_t i = 0; i < n; i++) { + uint32_t bit = 1 << (n - i - 1); + print_bits(bstream, offset + i, 1, size); + printf(" = %s", name); + if (fields != NULL) + for (const struct bitfield *f = fields; f->label != NULL; f++) + if (bit & f->value) + printf(" %s", f->label); + printf(": %s\n", bit & value ? "true" : "false"); + } + } +} + +static void print_value(const char *name, const void *bstream, + size_t offset, size_t n, size_t size, const char *format, ...) { + + va_list ap; + va_start(ap, format); + + if (!verbose) { + printf(" %s ( ", name); + vprintf(format, ap); + printf(" )\n"); + } + else { + print_bits(bstream, offset, n, size); + printf(" = %s: ", name); + vprintf(format, ap); + printf("\n"); + } + + va_end(ap); + +} + +#define print_bitfield8(name, bstream, offset, n, value, fields) \ + print_bitfield(name, bstream, offset, n, 8, value, fields) +#define print_bitfield16(name, bstream, offset, n, value, fields) \ + print_bitfield(name, bstream, offset, n, 16, value, fields) +#define print_bitfield24(name, bstream, offset, n, value, fields) \ + print_bitfield(name, bstream, offset, n, 24, value, fields) +#define print_bitfield32(name, bstream, offset, n, value, fields) \ + print_bitfield(name, bstream, offset, n, 32, value, fields) + +#define print_bool8(name, bstream, offset, n, value) \ + print_bitfield(name, bstream, offset, n, 8, value, NULL) +#define print_bool16(name, bstream, offset, n, value) \ + print_bitfield(name, bstream, offset, n, 16, value, NULL) +#define print_bool24(name, bstream, offset, n, value) \ + print_bitfield(name, bstream, offset, n, 24, value, NULL) +#define print_bool32(name, bstream, offset, n, value) \ + print_bitfield(name, bstream, offset, n, 32, value, NULL) + +#define print_value8(name, bstream, offset, n, format, ...) \ + print_value(name, bstream, offset, n, 8, format, __VA_ARGS__) +#define print_value16(name, bstream, offset, n, format, ...) \ + print_value(name, bstream, offset, n, 16, format, __VA_ARGS__) +#define print_value24(name, bstream, offset, n, format, ...) \ + print_value(name, bstream, offset, n, 24, format, __VA_ARGS__) +#define print_value32(name, bstream, offset, n, format, ...) \ + print_value(name, bstream, offset, n, 32, format, __VA_ARGS__) + static void dump_sbc(const void *blob, size_t size) { + const a2dp_sbc_t *sbc = blob; if (check_blob_size(sizeof(*sbc), size) == -1) return; - printf("SBC {\n" - " sample-rate:4 =%s%s%s%s\n" - " channel-mode:4 =%s%s%s%s\n" - " block-length:4 =%s%s%s%s\n" - " sub-bands:2 =%s%s\n" - " allocation-method:2 =%s%s\n" - " min-bit-pool-value:8 = %u\n" - " max-bit-pool-value:8 = %u\n" - "}\n", - bintohex(sbc, sizeof(*sbc)), - sbc->sampling_freq & SBC_SAMPLING_FREQ_48000 ? " 48000" : "", - sbc->sampling_freq & SBC_SAMPLING_FREQ_44100 ? " 44100" : "", - sbc->sampling_freq & SBC_SAMPLING_FREQ_32000 ? " 32000" : "", - sbc->sampling_freq & SBC_SAMPLING_FREQ_16000 ? " 16000" : "", - sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO ? " JointStereo" : "", - sbc->channel_mode & SBC_CHANNEL_MODE_STEREO ? " Stereo" : "", - sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL ? " DualChannel" : "", - sbc->channel_mode & SBC_CHANNEL_MODE_MONO ? " Mono" : "", - sbc->block_length & SBC_BLOCK_LENGTH_16 ? " 16" : "", - sbc->block_length & SBC_BLOCK_LENGTH_12 ? " 12" : "", - sbc->block_length & SBC_BLOCK_LENGTH_8 ? " 8" : "", - sbc->block_length & SBC_BLOCK_LENGTH_4 ? " 4" : "", - sbc->subbands & SBC_SUBBANDS_8 ? " 8" : "", - sbc->subbands & SBC_SUBBANDS_4 ? " 4" : "", - sbc->allocation_method & SBC_ALLOCATION_LOUDNESS ? " Loudness" : "", - sbc->allocation_method & SBC_ALLOCATION_SNR ? " SNR" : "", - sbc->min_bitpool, sbc->max_bitpool); + + static const struct bitfield ch_modes[] = { + { SBC_CHANNEL_MODE_MONO, "Mono" }, + { SBC_CHANNEL_MODE_DUAL_CHANNEL, "Dual Channel" }, + { SBC_CHANNEL_MODE_STEREO, "Stereo" }, + { SBC_CHANNEL_MODE_JOINT_STEREO, "Joint Stereo" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { SBC_SAMPLING_FREQ_16000, "16000 Hz" }, + { SBC_SAMPLING_FREQ_32000, "32000 Hz" }, + { SBC_SAMPLING_FREQ_44100, "44100 Hz" }, + { SBC_SAMPLING_FREQ_48000, "48000 Hz" }, + { 0 }, + }; + + static const struct bitfield blocks[] = { + { SBC_BLOCK_LENGTH_4, "4" }, + { SBC_BLOCK_LENGTH_8, "8" }, + { SBC_BLOCK_LENGTH_12, "12" }, + { SBC_BLOCK_LENGTH_16, "16" }, + { 0 }, + }; + + static const struct bitfield bands[] = { + { SBC_SUBBANDS_4, "4" }, + { SBC_SUBBANDS_8, "8" }, + { 0 }, + }; + + static const struct bitfield allocs[] = { + { SBC_ALLOCATION_SNR, "SNR" }, + { SBC_ALLOCATION_LOUDNESS, "Loudness" }, + { 0 }, + }; + + const uint8_t *bstream = blob; + printf("SBC {\n", bintohex(sbc, sizeof(*sbc))); + print_bitfield8("Sample Rate", bstream, 0, 4, sbc->sampling_freq, rates); + print_bitfield8("Channel Mode", bstream, 4, 4, sbc->channel_mode, ch_modes); + print_bitfield8("Block Length", bstream += 1, 0, 4, sbc->block_length, blocks); + print_bitfield8("Sub-bands", bstream, 4, 2, sbc->subbands, bands); + print_bitfield8("Allocation Method", bstream, 6, 2, sbc->allocation_method, allocs); + print_value8("Min Bit-pool", bstream += 1, 0, 8, "%u", sbc->min_bitpool); + print_value8("Max Bit-pool", bstream += 1, 0, 8, "%u", sbc->max_bitpool); + printf("}\n"); + } static void dump_mpeg(const void *blob, size_t size) { + const a2dp_mpeg_t *mpeg = blob; if (check_blob_size(sizeof(*mpeg), size) == -1) return; - printf("MPEG-1,2 Audio {\n" - " layer:3 =%s%s%s\n" - " crc:1 = %s\n" - " channel-mode:4 =%s%s%s%s\n" - " :1\n" - " media-payload-format:1 = MPF-1%s\n" - " sample-rate:6 =%s%s%s%s%s%s\n" - " vbr:1 = %s\n" - " bitrate-index:15 = %#x\n" - "}\n", - bintohex(mpeg, sizeof(*mpeg)), - mpeg->layer & MPEG_LAYER_MP3 ? " MP3" : "", - mpeg->layer & MPEG_LAYER_MP2 ? " MP2" : "", - mpeg->layer & MPEG_LAYER_MP1 ? " MP1" : "", - mpeg->crc ? "true" : "false", - mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO ? " JointStereo" : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO ? " Stereo" : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL ? " DualChannel" : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO ? " Mono" : "", - mpeg->mpf ? " MPF-2" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_48000 ? " 48000" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_44100 ? " 44100" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_32000 ? " 32000" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_24000 ? " 24000" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_22050 ? " 22050" : "", - mpeg->sampling_freq & MPEG_SAMPLING_FREQ_16000 ? " 16000" : "", - mpeg->vbr ? "true" : "false", - A2DP_MPEG_GET_BITRATE(*mpeg)); + + static const struct bitfield layers[] = { + { MPEG_LAYER_MP1, "MP1" }, + { MPEG_LAYER_MP2, "MP2" }, + { MPEG_LAYER_MP3, "MP3" }, + { 0 }, + }; + + static const struct bitfield ch_modes[] = { + { MPEG_CHANNEL_MODE_MONO, "Mono" }, + { MPEG_CHANNEL_MODE_DUAL_CHANNEL, "Dual Channel" }, + { MPEG_CHANNEL_MODE_STEREO, "Stereo" }, + { MPEG_CHANNEL_MODE_JOINT_STEREO, "Joint Stereo" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { MPEG_SAMPLING_FREQ_16000, "16000 Hz" }, + { MPEG_SAMPLING_FREQ_22050, "22050 Hz" }, + { MPEG_SAMPLING_FREQ_24000, "24000 Hz" }, + { MPEG_SAMPLING_FREQ_32000, "32000 Hz" }, + { MPEG_SAMPLING_FREQ_44100, "44100 Hz" }, + { MPEG_SAMPLING_FREQ_48000, "48000 Hz" }, + { 0 }, + }; + + static const struct bitfield mpfs[] = { + { 0, "MPF-1" }, + { 1, "MPF-2" }, + { 0 }, + }; + + static const struct bitfield indexes[] = { + { MPEG_BITRATE_INDEX_0, "0" }, + { MPEG_BITRATE_INDEX_1, "1" }, + { MPEG_BITRATE_INDEX_2, "2" }, + { MPEG_BITRATE_INDEX_3, "3" }, + { MPEG_BITRATE_INDEX_4, "4" }, + { MPEG_BITRATE_INDEX_5, "5" }, + { MPEG_BITRATE_INDEX_6, "6" }, + { MPEG_BITRATE_INDEX_7, "7" }, + { MPEG_BITRATE_INDEX_8, "8" }, + { MPEG_BITRATE_INDEX_9, "9" }, + { MPEG_BITRATE_INDEX_10, "10" }, + { MPEG_BITRATE_INDEX_11, "11" }, + { MPEG_BITRATE_INDEX_12, "12" }, + { MPEG_BITRATE_INDEX_13, "13" }, + { MPEG_BITRATE_INDEX_14, "14" }, + { 0 }, + }; + + static const struct bitfield indexes1[] = { + { MPEG_BITRATE_INDEX_8 >> 8, "8" }, + { MPEG_BITRATE_INDEX_9 >> 8, "9" }, + { MPEG_BITRATE_INDEX_10 >> 8, "10" }, + { MPEG_BITRATE_INDEX_11 >> 8, "11" }, + { MPEG_BITRATE_INDEX_12 >> 8, "12" }, + { MPEG_BITRATE_INDEX_13 >> 8, "13" }, + { MPEG_BITRATE_INDEX_14 >> 8, "14" }, + { 0 }, + }; + + const uint8_t *bstream = blob; + printf("MPEG-1,2 Audio {\n", bintohex(mpeg, sizeof(*mpeg))); + print_bitfield8("Layer", bstream, 0, 3, mpeg->layer, layers); + print_bool8("CRC", bstream, 3, 1, mpeg->crc); + print_bitfield8("Channel Mode", bstream, 4, 4, mpeg->channel_mode, ch_modes); + print_value8("RFA", bstream += 1, 0, 1, "%u", mpeg->rfa); + print_bitfield8("Media Payload Format", bstream, 1, 1, mpeg->mpf, mpfs); + print_bitfield8("Sample Rate", bstream, 2, 6, mpeg->sampling_freq, rates); + print_bool8("VBR", bstream += 1, 0, 1, mpeg->vbr); + if (!verbose) { + const uint16_t mpeg_bitrate = A2DP_MPEG_GET_BITRATE(*mpeg); + print_bitfield16("Bitrate Index", bstream, 1, 15, mpeg_bitrate, indexes); + } + else { + print_bitfield8("Bitrate Index", bstream, 1, 7, mpeg->bitrate1, indexes1); + print_bitfield8("Bitrate Index", bstream += 1, 0, 8, mpeg->bitrate2, indexes); + } + printf("}\n"); + } static void dump_aac(const void *blob, size_t size) { + const a2dp_aac_t *aac = blob; if (check_blob_size(sizeof(*aac), size) == -1) return; - const uint16_t aac_sampling_freq = A2DP_AAC_GET_SAMPLING_FREQ(*aac); - printf("MPEG-2,4 AAC {\n" - " object-type:7 =%s%s%s%s%s%s%s\n" - " dynamic-range-control:1 = %s\n" - " sample-rate:12 =%s%s%s%s%s%s%s%s%s%s%s%s\n" - " channel-mode:4 =%s%s%s%s\n" - " vbr:1 = %s\n" - " bitrate:23 = %u\n" - "}\n", - bintohex(aac, sizeof(*aac)), - aac->object_type & AAC_OBJECT_TYPE_MPEG4_ELD2 ? " MPEG4-ELD2" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG4_HE2 ? " MPEG4-HE2" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG4_HE ? " MPEG4-HE" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG4_SCA ? " MPEG4-SCA" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG4_LTP ? " MPEG4-LTP" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG4_LC ? " MPEG4-LC" : "", - aac->object_type & AAC_OBJECT_TYPE_MPEG2_LC ? " MPEG2-LC" : "", - aac->drc ? "true" : "false", - aac_sampling_freq & AAC_SAMPLING_FREQ_96000 ? " 96000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_88200 ? " 88200" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_64000 ? " 64000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_48000 ? " 48000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_44100 ? " 44100" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_32000 ? " 32000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_24000 ? " 24000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_22050 ? " 22050" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_16000 ? " 16000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_12000 ? " 12000" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_11025 ? " 11025" : "", - aac_sampling_freq & AAC_SAMPLING_FREQ_8000 ? " 8000" : "", - aac->channel_mode & AAC_CHANNEL_MODE_7_1 ? " Surround-7.1" : "", - aac->channel_mode & AAC_CHANNEL_MODE_5_1 ? " Surround-5.1" : "", - aac->channel_mode & AAC_CHANNEL_MODE_STEREO ? " Stereo" : "", - aac->channel_mode & AAC_CHANNEL_MODE_MONO ? " Mono" : "", - aac->vbr ? "true" : "false", - A2DP_AAC_GET_BITRATE(*aac)); + + static const struct bitfield objects[] = { + { AAC_OBJECT_TYPE_MPEG2_LC, "MPEG2-LC" }, + { AAC_OBJECT_TYPE_MPEG4_LC, "MPEG4-LC" }, + { AAC_OBJECT_TYPE_MPEG4_LTP, "MPEG4-LTP" }, + { AAC_OBJECT_TYPE_MPEG4_SCA, "MPEG4-SCA" }, + { AAC_OBJECT_TYPE_MPEG4_HE, "MPEG4-HE" }, + { AAC_OBJECT_TYPE_MPEG4_HE2, "MPEG4-HE2" }, + { AAC_OBJECT_TYPE_MPEG4_ELD2, "MPEG4-ELD2" }, + { 0 }, + }; + + static const struct bitfield ch_modes[] = { + { AAC_CHANNEL_MODE_MONO, "Mono" }, + { AAC_CHANNEL_MODE_STEREO, "Stereo" }, + { AAC_CHANNEL_MODE_5_1, "Surround-5.1" }, + { AAC_CHANNEL_MODE_7_1, "Surround-7.1" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { AAC_SAMPLING_FREQ_8000, "8000 Hz" }, + { AAC_SAMPLING_FREQ_11025, "11025 Hz" }, + { AAC_SAMPLING_FREQ_12000, "12000 Hz" }, + { AAC_SAMPLING_FREQ_16000, "16000 Hz" }, + { AAC_SAMPLING_FREQ_22050, "22050 Hz" }, + { AAC_SAMPLING_FREQ_24000, "24000 Hz" }, + { AAC_SAMPLING_FREQ_32000, "32000 Hz" }, + { AAC_SAMPLING_FREQ_44100, "44100 Hz" }, + { AAC_SAMPLING_FREQ_48000, "48000 Hz" }, + { AAC_SAMPLING_FREQ_64000, "64000 Hz" }, + { AAC_SAMPLING_FREQ_88200, "88200 Hz" }, + { AAC_SAMPLING_FREQ_96000, "96000 Hz" }, + { 0 }, + }; + + static const struct bitfield rates1[] = { + { AAC_SAMPLING_FREQ_8000 >> 4, "8000 Hz" }, + { AAC_SAMPLING_FREQ_11025 >> 4, "11025 Hz" }, + { AAC_SAMPLING_FREQ_12000 >> 4, "12000 Hz" }, + { AAC_SAMPLING_FREQ_16000 >> 4, "16000 Hz" }, + { AAC_SAMPLING_FREQ_22050 >> 4, "22050 Hz" }, + { AAC_SAMPLING_FREQ_24000 >> 4, "24000 Hz" }, + { AAC_SAMPLING_FREQ_32000 >> 4, "32000 Hz" }, + { AAC_SAMPLING_FREQ_44100 >> 4, "44100 Hz" }, + { 0 }, + }; + + const uint8_t *bstream = blob; + printf("MPEG-2,4 AAC {\n", bintohex(aac, sizeof(*aac))); + print_bitfield8("Object Type", bstream, 0, 7, aac->object_type, objects); + print_bool8("Dynamic Range Control", bstream, 7, 1, aac->drc); + if (!verbose) { + const uint16_t aac_sampling_freq = A2DP_AAC_GET_SAMPLING_FREQ(*aac); + print_bitfield16("Sample Rate", bstream += 1, 0, 12, aac_sampling_freq, rates); + bstream += 1; + } + else { + print_bitfield8("Sample Rate", bstream += 1, 0, 8, aac->sampling_freq1, rates1); + print_bitfield8("Sample Rate", bstream += 1, 0, 4, aac->sampling_freq2, rates); + } + print_bitfield8("Channel Mode", bstream, 4, 4, aac->channel_mode, ch_modes); + print_bool24("VBR", bstream += 1, 0, 1, aac->vbr); + print_value24("Bitrate", bstream, 1, 23, "%u", A2DP_AAC_GET_BITRATE(*aac)); + printf("}\n"); + } static void dump_usac(const void *blob, size_t size) { + const a2dp_usac_t *usac = blob; if (check_blob_size(sizeof(*usac), size) == -1) return; + + static const struct bitfield objects[] = { + { USAC_OBJECT_TYPE_MPEGD_DRC, "MPEG-D-DRC" }, + { 1 << 0, "RFA" }, + { 0 }, + }; + + static const struct bitfield ch_modes[] = { + { USAC_CHANNEL_MODE_MONO, "Mono" }, + { USAC_CHANNEL_MODE_STEREO, "Stereo" }, + { 1 << 1, "RFA" }, + { 1 << 0, "RFA" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { USAC_SAMPLING_FREQ_7350, "7350 Hz" }, + { USAC_SAMPLING_FREQ_8000, "8000 Hz" }, + { USAC_SAMPLING_FREQ_8820, "8820 Hz" }, + { USAC_SAMPLING_FREQ_9600, "9600 Hz" }, + { USAC_SAMPLING_FREQ_11025, "11025 Hz" }, + { USAC_SAMPLING_FREQ_11760, "11760 Hz" }, + { USAC_SAMPLING_FREQ_12000, "12000 Hz" }, + { USAC_SAMPLING_FREQ_12800, "12800 Hz" }, + { USAC_SAMPLING_FREQ_14700, "14700 Hz" }, + { USAC_SAMPLING_FREQ_16000, "16000 Hz" }, + { USAC_SAMPLING_FREQ_17640, "17640 Hz" }, + { USAC_SAMPLING_FREQ_19200, "19200 Hz" }, + { USAC_SAMPLING_FREQ_22050, "22050 Hz" }, + { USAC_SAMPLING_FREQ_24000, "24000 Hz" }, + { USAC_SAMPLING_FREQ_29400, "29400 Hz" }, + { USAC_SAMPLING_FREQ_32000, "32000 Hz" }, + { USAC_SAMPLING_FREQ_35280, "35280 Hz" }, + { USAC_SAMPLING_FREQ_38400, "38400 Hz" }, + { USAC_SAMPLING_FREQ_44100, "44100 Hz" }, + { USAC_SAMPLING_FREQ_48000, "48000 Hz" }, + { USAC_SAMPLING_FREQ_58800, "58800 Hz" }, + { USAC_SAMPLING_FREQ_64000, "64000 Hz" }, + { USAC_SAMPLING_FREQ_70560, "70560 Hz" }, + { USAC_SAMPLING_FREQ_76800, "76800 Hz" }, + { USAC_SAMPLING_FREQ_88200, "88200 Hz" }, + { USAC_SAMPLING_FREQ_96000, "96000 Hz" }, + { 0 }, + }; + + const uint8_t *bstream = blob; + printf("MPEG-D USAC {\n", bintohex(usac, sizeof(*usac))); + print_bitfield32("Object Type", bstream, 0, 2, usac->object_type, objects); const uint32_t usac_sampling_freq = A2DP_USAC_GET_SAMPLING_FREQ(*usac); - printf("MPEG-D USAC {\n" - " object-type:2 =%s\n" - " sample-rate:26 =%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" - " channel-mode:4 =%s%s\n" - " vbr:1 = %s\n" - " bitrate:23 = %u\n" - "}\n", - bintohex(usac, sizeof(*usac)), - usac->object_type & USAC_OBJECT_TYPE_MPEGD_DRC ? " MPEG-D-DRC" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_96000 ? " 96000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_88200 ? " 88200" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_76800 ? " 76800" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_70560 ? " 70560" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_64000 ? " 64000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_58800 ? " 58800" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_48000 ? " 48000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_44100 ? " 44100" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_38400 ? " 38400" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_35280 ? " 35280" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_32000 ? " 32000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_29400 ? " 29400" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_24000 ? " 24000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_22050 ? " 22050" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_19200 ? " 19200" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_17640 ? " 17640" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_16000 ? " 16000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_14700 ? " 14700" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_12800 ? " 12800" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_12000 ? " 12000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_11760 ? " 11760" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_11025 ? " 11025" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_9600 ? " 9600" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_8820 ? " 8820" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_8000 ? " 8000" : "", - usac_sampling_freq & USAC_SAMPLING_FREQ_7350 ? " 7350" : "", - usac->channel_mode & USAC_CHANNEL_MODE_STEREO ? " Stereo" : "", - usac->channel_mode & USAC_CHANNEL_MODE_MONO ? " Mono" : "", - usac->vbr ? "true" : "false", - A2DP_USAC_GET_BITRATE(*usac)); + print_bitfield32("Sample Rate", bstream, 2, 26, usac_sampling_freq, rates); + print_bitfield32("Channel Mode", bstream, 28, 4, usac->channel_mode, ch_modes); + print_bool24("VBR", bstream += 4, 0, 1, usac->vbr); + print_value24("Bitrate", bstream, 1, 23, "%u", A2DP_USAC_GET_BITRATE(*usac)); + printf("}\n"); + } static void dump_atrac(const void *blob, size_t size) { + const a2dp_atrac_t *atrac = blob; if (check_blob_size(sizeof(*atrac), size) == -1) return; - printf("ATRAC {\n" - " version:3 = ATRAC%u\n" - " channel-mode:3 =%s%s%s\n" - " :4\n" - " sample-rate:2 =%s%s\n" - " vbr:1 = %s\n" - " bitrate-index:19 = %#x\n" - " max-sound-unit-length:16 = %u\n" - " :8\n" - "}\n", - bintohex(atrac, sizeof(*atrac)), - atrac->version, - atrac->channel_mode & ATRAC_CHANNEL_MODE_JOINT_STEREO ? " JointStereo" : "", - atrac->channel_mode & ATRAC_CHANNEL_MODE_DUAL_CHANNEL ? " DualChannel" : "", - atrac->channel_mode & ATRAC_CHANNEL_MODE_MONO ? " Mono" : "", - atrac->sampling_freq & ATRAC_SAMPLING_FREQ_48000 ? " 48000" : "", - atrac->sampling_freq & ATRAC_SAMPLING_FREQ_44100 ? " 44100" : "", - atrac->vbr ? "true" : "false", - A2DP_ATRAC_GET_BITRATE(*atrac), - A2DP_ATRAC_GET_MAX_SUL(*atrac)); + + static const struct bitfield ch_modes[] = { + { ATRAC_CHANNEL_MODE_MONO, "Mono" }, + { ATRAC_CHANNEL_MODE_DUAL_CHANNEL, "Dual Channel" }, + { ATRAC_CHANNEL_MODE_JOINT_STEREO, "Joint Stereo" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { ATRAC_SAMPLING_FREQ_44100, "44100 Hz" }, + { ATRAC_SAMPLING_FREQ_48000, "48000 Hz" }, + { 0 }, + }; + + static const struct bitfield indexes[] = { + { 1 << 18, "0" }, + { 1 << 17, "1" }, + { 1 << 16, "2" }, + { 1 << 15, "3" }, + { 1 << 14, "4" }, + { 1 << 13, "5" }, + { 1 << 12, "6" }, + { 1 << 11, "7" }, + { 1 << 10, "8" }, + { 1 << 9, "9" }, + { 1 << 8, "10" }, + { 1 << 7, "11" }, + { 1 << 6, "12" }, + { 1 << 5, "13" }, + { 1 << 4, "14" }, + { 1 << 3, "15" }, + { 1 << 2, "16" }, + { 1 << 1, "17" }, + { 1 << 0, "18" }, + { 0 }, + }; + + const uint8_t *bstream = blob; + printf("ATRAC {\n", bintohex(atrac, sizeof(*atrac))); + print_value8("Version", bstream, 0, 3, "%u", atrac->version); + print_bitfield8("Channel Mode", bstream, 3, 3, atrac->channel_mode, ch_modes); + print_value8("RFA", bstream, 6, 2, "%#x", atrac->rfa1); + print_value24("RFA", ++bstream, 0, 2, "%#x", atrac->rfa2); + print_bitfield24("Sample Rate", bstream, 2, 2, atrac->sampling_freq, rates); + print_bool24("VBR", bstream, 4, 1, atrac->vbr); + const uint32_t atrac_bitrate = A2DP_ATRAC_GET_BITRATE(*atrac); + print_bitfield24("Bitrate Index", bstream, 5, 19, atrac_bitrate, indexes); + const uint16_t atrac_max_sul = A2DP_ATRAC_GET_MAX_SUL(*atrac); + print_value16("Max Sound Unit Length", bstream += 3, 0, 16, "%u", atrac_max_sul); + print_value8("RFA", bstream += 2, 0, 8, "%#x", atrac->rfa3); + printf("}\n"); + } -static void printf_vendor(const a2dp_vendor_info_t *info) { - printf("" - " vendor-id:32 = %#010x [%s]\n" - " vendor-codec-id:16 = %#06x\n", - A2DP_VENDOR_INFO_GET_VENDOR_ID(*info), - bt_compidtostr(A2DP_VENDOR_INFO_GET_VENDOR_ID(*info)), - A2DP_VENDOR_INFO_GET_CODEC_ID(*info)); +static void print_vendor(const a2dp_vendor_info_t *info) { + const uint32_t vendor_id = A2DP_VENDOR_INFO_GET_VENDOR_ID(*info); + const char *vendor_name = bt_compidtostr(vendor_id); + print_value32("Vendor ID", &info->vendor_id, 0, 32, "%#010x [%s]", vendor_id, vendor_name); + const uint16_t codec_id = A2DP_VENDOR_INFO_GET_CODEC_ID(*info); + print_value16("Vendor Codec ID", &info->codec_id, 0, 16, "%#06x", codec_id); } static void dump_vendor(const void *blob, size_t size) { + const a2dp_vendor_info_t *info = blob; if (size <= sizeof(*info)) return; - const void *data = info + 1; - size_t data_size = size - sizeof(*info); + printf(" {\n", bintohex(blob, size)); - printf_vendor(info); - printf("" - " data:%zu = hex:%s\n" - "}\n", - data_size * 8, - bintohex(data, data_size)); -} + print_vendor(info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + printf(" Data ( hex:%s )\n", bintohex(bstream, size - sizeof(*info))); + printf("}\n"); -static void printf_aptx(const a2dp_aptx_t *aptx) { - printf("" - " 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->channel_mode & APTX_CHANNEL_MODE_STEREO ? " Stereo" : "", - aptx->channel_mode & APTX_CHANNEL_MODE_TWS ? " DualChannel" : "", - aptx->channel_mode & APTX_CHANNEL_MODE_MONO ? " Mono" : ""); } +static const struct bitfield aptx_ch_modes[] = { + { APTX_CHANNEL_MODE_MONO, "Mono" }, + { APTX_CHANNEL_MODE_STEREO, "Stereo" }, + { APTX_CHANNEL_MODE_TWS, "TWS" }, + { 0 }, +}; + +static const struct bitfield aptx_rates[] = { + { APTX_SAMPLING_FREQ_16000, "16000 Hz" }, + { APTX_SAMPLING_FREQ_32000, "32000 Hz" }, + { APTX_SAMPLING_FREQ_44100, "44100 Hz" }, + { APTX_SAMPLING_FREQ_48000, "48000 Hz" }, + { 0 }, +}; + static void dump_aptx(const void *blob, size_t size) { + const a2dp_aptx_t *aptx = blob; if (check_blob_size(sizeof(*aptx), size) == -1) return; + printf("aptX {\n", bintohex(blob, size)); - printf_vendor(&aptx->info); - printf_aptx(aptx); + print_vendor(&aptx->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 4, aptx->sampling_freq, aptx_rates); + print_bitfield8("Channel Mode", bstream, 4, 4, aptx->channel_mode, aptx_ch_modes); printf("}\n"); + } static void dump_aptx_tws(const void *blob, size_t size) { + const a2dp_aptx_t *aptx = blob; if (check_blob_size(sizeof(*aptx), size) == -1) return; printf("aptX-TWS {\n", bintohex(blob, size)); - printf_vendor(&aptx->info); - printf_aptx(aptx); + print_vendor(&aptx->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 4, aptx->sampling_freq, aptx_rates); + print_bitfield8("Channel Mode", bstream, 4, 4, aptx->channel_mode, aptx_ch_modes); 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) + + const a2dp_aptx_ad_t *aptx = blob; + if (check_blob_size(sizeof(*aptx), size) == -1) return; + + static const struct bitfield ch_modes[] = { + { APTX_AD_CHANNEL_MODE_MONO, "Mono" }, + { APTX_AD_CHANNEL_MODE_STEREO, "Stereo" }, + { APTX_AD_CHANNEL_MODE_TWS, "TWS" }, + { APTX_AD_CHANNEL_MODE_JOINT_STEREO, "Joint Stereo" }, + { APTX_AD_CHANNEL_MODE_TWS_MONO, "TWS-Mono" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { APTX_AD_SAMPLING_FREQ_44100, "44100 Hz" }, + { APTX_AD_SAMPLING_FREQ_48000, "48000 Hz" }, + { APTX_AD_SAMPLING_FREQ_88000, "88000 Hz" }, + { APTX_AD_SAMPLING_FREQ_192000, "192000 Hz" }, + { 0 }, + }; + printf("aptX Adaptive {\n", bintohex(blob, size)); - printf_vendor(&aptx_ad->info); - printf("" - " sample-rate:5 =%s%s%s%s\n" - " :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]); + print_vendor(&aptx->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 5, aptx->sampling_freq, rates); + print_value8("RFA", bstream, 5, 3, "%#x", aptx->rfa1); + print_value8("RFA", bstream += 1, 0, 3, "%#x", aptx->rfa2); + print_bitfield8("Channel Mode", bstream, 3, 5, aptx->channel_mode, ch_modes); + print_value8("TTP-LL Low", bstream += 1, 0, 8, "%u", aptx->ttp_ll_low); + print_value8("TTP-LL High", bstream += 1, 0, 8, "%u", aptx->ttp_ll_high); + print_value8("TTP-HQ Low", bstream += 1, 0, 8, "%u", aptx->ttp_hq_low); + print_value8("TTP-HQ High", bstream += 1, 0, 8, "%u", aptx->ttp_hq_high); + print_value8("TTP-TWS Low", bstream += 1, 0, 8, "%u", aptx->ttp_tws_low); + print_value8("TTP-TWS High", bstream += 1, 0, 8, "%u", aptx->ttp_tws_high); + print_value24("EOC", bstream += 1, 0, 24, "hex:%02x%02x%02x", aptx->eoc[0], aptx->eoc[1], aptx->eoc[2]); + printf("}\n"); + } 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) + + const a2dp_aptx_hd_t *aptx = blob; + if (check_blob_size(sizeof(*aptx), size) == -1) return; + printf("aptX HD {\n", bintohex(blob, size)); - printf_vendor(&aptx_hd->aptx.info); - printf_aptx(&aptx_hd->aptx); - printf("" - " :32\n" - "}\n"); + print_vendor(&aptx->aptx.info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 4, aptx->aptx.sampling_freq, aptx_rates); + print_bitfield8("Channel Mode", bstream, 4, 4, aptx->aptx.channel_mode, aptx_ch_modes); + print_value32("RFA", bstream += 1, 0, 32, "%#010x", aptx->rfa); + printf("}\n"); + } static void dump_aptx_ll(const void *blob, size_t size) { - const a2dp_aptx_ll_t *aptx_ll = blob; - const a2dp_aptx_ll_new_t *aptx_ll_new_caps = blob; + const a2dp_aptx_ll_t *aptx = blob; + const a2dp_aptx_ll_new_t *aptx_new_caps = blob; - size_t conf_size = sizeof(*aptx_ll); - if (size >= sizeof(*aptx_ll) && aptx_ll->has_new_caps) - conf_size = sizeof(*aptx_ll_new_caps); + size_t conf_size = sizeof(*aptx); + if (size >= sizeof(*aptx) && aptx->has_new_caps) + conf_size = sizeof(*aptx_new_caps); if (check_blob_size(conf_size, size) == -1) return; printf("aptX LL (Sprint) {\n", bintohex(blob, size)); - printf_vendor(&aptx_ll->aptx.info); - printf_aptx(&aptx_ll->aptx); - printf("" - " :6\n" - " has-new-caps:1 = %s\n" - " bidirectional-link:1 = %s\n", - aptx_ll->has_new_caps ? "true" : "false", - aptx_ll->bidirect_link ? "true" : "false"); - - if (aptx_ll->has_new_caps) - printf("" - " :8\n" - " target-codec-level:16 = %u\n" - " initial-codec-level:16 = %u\n" - " sra-max-rate:8 = %u\n" - " sra-avg-time:8 = %u\n" - " good-working-level:16 = %u\n", - A2DP_APTX_LL_GET_TARGET_CODEC_LEVEL(*aptx_ll_new_caps), - A2DP_APTX_LL_GET_INITIAL_CODEC_LEVEL(*aptx_ll_new_caps), - aptx_ll_new_caps->sra_max_rate, - aptx_ll_new_caps->sra_avg_time, - A2DP_APTX_LL_GET_GOOD_WORKING_LEVEL(*aptx_ll_new_caps)); + print_vendor(&aptx->aptx.info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 4, aptx->aptx.sampling_freq, aptx_rates); + print_bitfield8("Channel Mode", bstream, 4, 4, aptx->aptx.channel_mode, aptx_ch_modes); + print_value8("RFA", bstream += 1, 0, 6, "%#x", aptx->reserved); + print_bool8("Has New Capabilities", bstream, 6, 1, aptx->has_new_caps); + print_bool8("Bidirectional Link", bstream, 7, 1, aptx->bidirect_link); + + if (aptx->has_new_caps) { + print_value8("RFA", bstream += 1, 0, 8, "%#x", aptx_new_caps->reserved); + const uint16_t aptx_target_level = A2DP_APTX_LL_GET_TARGET_CODEC_LEVEL(*aptx_new_caps); + print_value16("Target Codec Level", bstream += 1, 0, 16, "%u", aptx_target_level); + const uint16_t aptx_initial_level = A2DP_APTX_LL_GET_INITIAL_CODEC_LEVEL(*aptx_new_caps); + print_value16("Initial Codec Level", bstream += 2, 0, 16, "%u", aptx_initial_level); + print_value8("SRA Max Rate", bstream += 2, 0, 8, "%u", aptx_new_caps->sra_max_rate); + print_value8("SRA Avg Time", bstream += 1, 0, 8, "%u", aptx_new_caps->sra_avg_time); + const uint16_t aptx_working_level = A2DP_APTX_LL_GET_GOOD_WORKING_LEVEL(*aptx_new_caps); + print_value16("Good Working Level", bstream += 1, 0, 16, "%u", aptx_working_level); + } printf("}\n"); } static void dump_faststream(const void *blob, size_t size) { + const a2dp_faststream_t *fs = blob; if (check_blob_size(sizeof(*fs), size) == -1) return; + + static const struct bitfield directions[] = { + { FASTSTREAM_DIRECTION_MUSIC, "Music" }, + { FASTSTREAM_DIRECTION_VOICE, "Voice" }, + { 0 }, + }; + + static const struct bitfield rates_music[] = { + { FASTSTREAM_SAMPLING_FREQ_MUSIC_48000, "48000 Hz" }, + { FASTSTREAM_SAMPLING_FREQ_MUSIC_44100, "44100 Hz" }, + { 0 }, + }; + + static const struct bitfield rates_voice[] = { + { FASTSTREAM_SAMPLING_FREQ_VOICE_16000, "16000 Hz" }, + { 0 }, + }; + printf("FastStream {\n", bintohex(blob, size)); - printf_vendor(&fs->info); - printf("" - " direction:8 =%s%s\n" - " sample-rate-voice:8 =%s\n" - " sample-rate-music:8 =%s%s\n" - "}\n", - fs->direction & FASTSTREAM_DIRECTION_MUSIC ? " Music" : "", - fs->direction & FASTSTREAM_DIRECTION_VOICE ? " Voice" : "", - fs->sampling_freq_voice & FASTSTREAM_SAMPLING_FREQ_VOICE_16000 ? " 16000" : "", - fs->sampling_freq_music & FASTSTREAM_SAMPLING_FREQ_MUSIC_48000 ? " 48000" : "", - fs->sampling_freq_music & FASTSTREAM_SAMPLING_FREQ_MUSIC_44100 ? " 44100" : ""); + print_vendor(&fs->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_value8("RFA", bstream, 0, 6, "%#x", fs->direction >> 2); + print_bitfield8("Direction", bstream, 6, 2, fs->direction, directions); + print_bitfield8("Sample Rate Voice", bstream += 1, 0, 4, fs->sampling_freq_voice, rates_voice); + print_bitfield8("Sample Rate Music", bstream, 4, 4, fs->sampling_freq_music, rates_music); + printf("}\n"); + } static void dump_lc3plus(const void *blob, size_t size) { + const a2dp_lc3plus_t *lc3plus = blob; if (check_blob_size(sizeof(*lc3plus), size) == -1) return; - const uint16_t lc3plus_sampling_freq = A2DP_LC3PLUS_GET_SAMPLING_FREQ(*lc3plus); + + static const struct bitfield durations[] = { + { LC3PLUS_FRAME_DURATION_025, "2.5 ms" }, + { LC3PLUS_FRAME_DURATION_050, "5 ms" }, + { LC3PLUS_FRAME_DURATION_100, "10 ms" }, + { 0 }, + }; + + static const struct bitfield ch_modes[] = { + { LC3PLUS_CHANNEL_MODE_MONO, "Mono" }, + { LC3PLUS_CHANNEL_MODE_STEREO, "Stereo" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { LC3PLUS_SAMPLING_FREQ_48000, "48000 Hz" }, + { LC3PLUS_SAMPLING_FREQ_96000, "96000 Hz" }, + { 0 }, + }; + printf("LC3plus {\n", bintohex(blob, size)); - printf_vendor(&lc3plus->info); - printf("" - " frame-duration:4 =%s%s%s\n" - " :4\n" - " channel-mode:8 =%s%s\n" - " sample-rate:16 =%s%s\n" - "}\n", - lc3plus->frame_duration & LC3PLUS_FRAME_DURATION_025 ? " 2.5ms" : "", - lc3plus->frame_duration & LC3PLUS_FRAME_DURATION_050 ? " 5ms" : "", - lc3plus->frame_duration & LC3PLUS_FRAME_DURATION_100 ? " 10ms" : "", - lc3plus->channel_mode & LC3PLUS_CHANNEL_MODE_MONO ? " Mono" : "", - lc3plus->channel_mode & LC3PLUS_CHANNEL_MODE_STEREO ? " Stereo" : "", - lc3plus_sampling_freq & LC3PLUS_SAMPLING_FREQ_48000 ? " 48000" : "", - lc3plus_sampling_freq & LC3PLUS_SAMPLING_FREQ_96000 ? " 96000" : ""); + print_vendor(&lc3plus->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Frame Duration", bstream, 0, 4, lc3plus->frame_duration, durations); + print_value8("RFA", bstream, 4, 4, "%#x", lc3plus->rfa); + print_bitfield8("Channel Mode", bstream += 1, 0, 8, lc3plus->channel_mode, ch_modes); + const uint16_t lc3plus_sampling_freq = A2DP_LC3PLUS_GET_SAMPLING_FREQ(*lc3plus); + print_bitfield16("Sample Rate", bstream += 1, 0, 16, lc3plus_sampling_freq, rates); + printf("}\n"); + } static void dump_ldac(const void *blob, size_t size) { + const a2dp_ldac_t *ldac = blob; if (check_blob_size(sizeof(*ldac), size) == -1) return; + + static const struct bitfield ch_modes[] = { + { LDAC_CHANNEL_MODE_MONO, "Mono" }, + { LDAC_CHANNEL_MODE_DUAL, "Dual Channel" }, + { LDAC_CHANNEL_MODE_STEREO, "Stereo" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { LDAC_SAMPLING_FREQ_44100, "44100 Hz" }, + { LDAC_SAMPLING_FREQ_48000, "48000 Hz" }, + { LDAC_SAMPLING_FREQ_88200, "88200 Hz" }, + { LDAC_SAMPLING_FREQ_96000, "96000 Hz" }, + { LDAC_SAMPLING_FREQ_176400, "176400 Hz" }, + { LDAC_SAMPLING_FREQ_192000, "192000 Hz" }, + { 0 }, + }; + printf("LDAC {\n", bintohex(blob, size)); - printf_vendor(&ldac->info); - printf("" - " :2\n" - " sample-rate:6 =%s%s%s%s%s%s\n" - " :5\n" - " channel-mode:3 =%s%s%s\n" - "}\n", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_192000 ? " 192000" : "", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_176400 ? " 176400" : "", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_96000 ? " 96000" : "", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_88200 ? " 88200" : "", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_48000 ? " 48000" : "", - ldac->sampling_freq & LDAC_SAMPLING_FREQ_44100 ? " 44100" : "", - ldac->channel_mode & LDAC_CHANNEL_MODE_STEREO ? " Stereo" : "", - ldac->channel_mode & LDAC_CHANNEL_MODE_DUAL ? " DualChannel" : "", - ldac->channel_mode & LDAC_CHANNEL_MODE_MONO ? " Mono" : ""); + print_vendor(&ldac->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_value8("RFA", bstream, 0, 2, "%#x", ldac->rfa1); + print_bitfield8("Sample Rate", bstream, 2, 6, ldac->sampling_freq, rates); + print_value8("RFA", bstream += 1, 0, 5, "%#x", ldac->rfa2); + print_bitfield8("Channel Mode", bstream, 5, 3, ldac->channel_mode, ch_modes); + printf("}\n"); + } +static const struct bitfield lhdc_rates[] = { + { LHDC_SAMPLING_FREQ_44100, "44100 Hz" }, + { LHDC_SAMPLING_FREQ_48000, "48000 Hz" }, + { LHDC_SAMPLING_FREQ_88200, "88200 Hz" }, + { LHDC_SAMPLING_FREQ_96000, "96000 Hz" }, + { 0 }, +}; + +static const struct bitfield lhdc_bit_depths[] = { + { LHDC_BIT_DEPTH_16, "16 bits" }, + { LHDC_BIT_DEPTH_24, "24 bits" }, + { 0 }, +}; + +static const struct bitfield lhdc_versions[] = { + { LHDC_VER3, "v3" }, + { 0 }, +}; + +static const struct bitfield lhdc_ch_split_modes[] = { + { LHDC_CH_SPLIT_MODE_NONE, "None" }, + { LHDC_CH_SPLIT_MODE_TWS, "TWS" }, + { LHDC_CH_SPLIT_MODE_TWS_PLUS, "TWS+" }, + { 0 }, +}; + static int lhdc_get_max_bitrate(unsigned int value) { switch (value) { case LHDC_MAX_BITRATE_400K: @@ -476,170 +829,185 @@ static int lhdc_get_max_bitrate(unsigned int value) { } static void dump_lhdc_v1(const void *blob, size_t size) { + const a2dp_lhdc_v1_t *lhdc = blob; if (check_blob_size(sizeof(*lhdc), size) == -1) return; + printf("LHDC v1 {\n", bintohex(blob, size)); - printf_vendor(&lhdc->info); - printf("" - " :1\n" - " ch-separation:1 = %s\n" - " bit-depth:2 =%s%s\n" - " sample-rate:4 =%s%s%s%s\n" - "}\n", - lhdc->ch_separation ? "true" : "false", - lhdc->bit_depth & LHDC_BIT_DEPTH_24 ? " 24" : "", - lhdc->bit_depth & LHDC_BIT_DEPTH_16 ? " 16" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_96000 ? " 96000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_88200 ? " 88200" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_48000 ? " 48000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_44100 ? " 44100" : ""); + print_vendor(&lhdc->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_value8("RFA", bstream, 0, 1, "%#x", lhdc->rfa); + print_bool8("Channel Separation", bstream, 1, 1, lhdc->ch_separation); + print_bitfield8("Bit Depth", bstream, 2, 2, lhdc->bit_depth, lhdc_bit_depths); + print_bitfield8("Sample Rate", bstream, 4, 4, lhdc->sampling_freq, lhdc_rates); + printf("}\n"); + } static void dump_lhdc_v2(const void *blob, size_t size) { + const a2dp_lhdc_v2_t *lhdc = blob; if (check_blob_size(sizeof(*lhdc), size) == -1) return; + printf("LHDC v2 {\n", bintohex(blob, size)); - printf_vendor(&lhdc->info); - printf("" - " :2\n" - " bit-depth:2 =%s%s\n" - " sample-rate:4 =%s%s%s%s\n" - " low-latency:1 = %s\n" - " max-bitrate:3 = %d\n" - " version:4 = %u\n" - " :4\n" - " ch-split-mode:4 =%s%s%s\n" - "}\n", - lhdc->bit_depth & LHDC_BIT_DEPTH_24 ? " 24" : "", - lhdc->bit_depth & LHDC_BIT_DEPTH_16 ? " 16" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_96000 ? " 96000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_88200 ? " 88200" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_48000 ? " 48000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_44100 ? " 44100" : "", - lhdc->low_latency ? "true" : "false", - lhdc_get_max_bitrate(lhdc->max_bitrate), - lhdc->version, - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_TWS_PLUS ? " TWS+" : "", - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_TWS ? " TWS" : "", - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_NONE ? " None" : ""); + print_vendor(&lhdc->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_value8("RFA", bstream, 0, 2, "%#x", lhdc->rfa1); + print_bitfield8("Bit Depth", bstream, 2, 2, lhdc->bit_depth, lhdc_bit_depths); + print_bitfield8("Sample Rate", bstream, 4, 4, lhdc->sampling_freq, lhdc_rates); + print_bool8("Low Latency", bstream += 1, 0, 1, lhdc->low_latency); + const uint16_t lhdc_max_bitrate = lhdc_get_max_bitrate(lhdc->max_bitrate); + print_value8("Max Bitrate", bstream, 1, 3, "%#x [%u kbps]", lhdc->max_bitrate, lhdc_max_bitrate); + print_bitfield8("Version", bstream, 4, 4, lhdc->version, lhdc_versions); + print_value8("RFA", bstream += 1, 0, 4, "%#x", lhdc->rfa2); + print_bitfield8("Channel Split Mode", bstream, 4, 4, lhdc->ch_split_mode, lhdc_ch_split_modes); + printf("}\n"); + } static void dump_lhdc_v3(const void *blob, size_t size) { + const a2dp_lhdc_v3_t *lhdc = blob; if (check_blob_size(sizeof(*lhdc), size) == -1) return; + printf("LHDC v3 {\n", bintohex(blob, size)); - printf_vendor(&lhdc->info); - printf("" - " ar:1 = %s\n" - " jas:1 = %s\n" - " bit-depth:2 =%s%s\n" - " sample-rate:4 =%s%s%s%s\n" - " llac:1 = %s\n" - " low-latency:1 = %s\n" - " max-bitrate:2 = %d\n" - " version:4 = %u\n" - " lhdc-v4:1 = %s\n" - " larc:1 = %s\n" - " min-bitrate:1 = %s\n" - " meta:1 = %s\n" - " ch-split-mode:4 =%s%s%s\n" - "}\n", - lhdc->ar ? "true" : "false", - lhdc->jas ? "true" : "false", - lhdc->bit_depth & LHDC_BIT_DEPTH_24 ? " 24" : "", - lhdc->bit_depth & LHDC_BIT_DEPTH_16 ? " 16" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_96000 ? " 96000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_88200 ? " 88200" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_48000 ? " 48000" : "", - lhdc->sampling_freq & LHDC_SAMPLING_FREQ_44100 ? " 44100" : "", - lhdc->llac ? "true" : "false", - lhdc->low_latency ? "true" : "false", - lhdc_get_max_bitrate(lhdc->max_bitrate), - lhdc->version, - lhdc->lhdc_v4 ? "true" : "false", - lhdc->larc ? "true" : "false", - lhdc->min_bitrate ? "true" : "false", - lhdc->meta ? "true" : "false", - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_TWS_PLUS ? " TWS+" : "", - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_TWS ? " TWS" : "", - lhdc->ch_split_mode & LHDC_CH_SPLIT_MODE_NONE ? " None" : ""); + print_vendor(&lhdc->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bool8("AR", bstream, 0, 1, lhdc->ar); + print_bool8("JAS", bstream, 1, 1, lhdc->jas); + print_bitfield8("Bit Depth", bstream, 2, 2, lhdc->bit_depth, lhdc_bit_depths); + print_bitfield8("Sample Rate", bstream, 4, 4, lhdc->sampling_freq, lhdc_rates); + print_bool8("LLAC", bstream += 1, 0, 1, lhdc->llac); + print_bool8("Low Latency", bstream, 1, 1, lhdc->low_latency); + const uint16_t lhdc_max_bitrate = lhdc_get_max_bitrate(lhdc->max_bitrate); + print_value8("Max Bitrate", bstream, 2, 2, "%#x [%u kbps]", lhdc->max_bitrate, lhdc_max_bitrate); + print_bitfield8("Version", bstream, 4, 4, lhdc->version, lhdc_versions); + print_bool8("LHDC v4", bstream += 1, 0, 1, lhdc->lhdc_v4); + print_bool8("LARC", bstream, 1, 1, lhdc->larc); + print_bool8("Min Bitrate", bstream, 2, 1, lhdc->min_bitrate); + print_bool8("Meta", bstream, 3, 1, lhdc->meta); + print_bitfield8("Channel Split Mode", bstream, 4, 4, lhdc->ch_split_mode, lhdc_ch_split_modes); + printf("}\n"); + } static void dump_lhdc_v5(const void *blob, size_t size) { + const a2dp_lhdc_v5_t *lhdc = blob; if (check_blob_size(sizeof(*lhdc), size) == -1) return; + printf("LHDC v5 {\n", bintohex(blob, size)); - printf_vendor(&lhdc->info); - const void *data = (uint8_t *)lhdc + sizeof(lhdc->info); - size_t data_size = sizeof(*lhdc) - sizeof(lhdc->info); - printf("" - " data:%zu = hex:%s\n" - "}\n", - data_size, - bintohex(data, data_size)); + print_vendor(&lhdc->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_value8("RFA", bstream, 0, 3, "%#x", lhdc->rfa1); + print_bitfield8("Sample Rate", bstream, 3, 5, lhdc->sampling_freq, lhdc_rates); + print_value8("Min Bitrate", bstream += 1, 0, 2, "%#x", lhdc->min_bitrate); + const uint16_t lhdc_max_bitrate = lhdc_get_max_bitrate(lhdc->max_bitrate); + print_value8("Max Bitrate", bstream, 2, 2, "%#x [%u kbps]", lhdc->max_bitrate, lhdc_max_bitrate); + print_value8("RFA", bstream, 4, 1, "%#x", lhdc->rfa2); + print_bitfield8("Bit Depth", bstream, 5, 3, lhdc->bit_depth, lhdc_bit_depths); + print_value8("RFA", bstream += 1, 0, 3, "%#x", lhdc->rfa3); + print_bool8("Frame Length 5ms", bstream, 3, 1, lhdc->frame_len_5ms); + print_bitfield8("Version", bstream, 4, 4, lhdc->version, lhdc_versions); + print_bool8("RFA", bstream += 1, 0, 1, lhdc->reserved); + print_bool8("Low Latency", bstream, 1, 1, lhdc->low_latency); + print_value8("RFA", bstream, 2, 3, "%#x", lhdc->rfa4); + print_bool8("Meta", bstream, 5, 1, lhdc->meta); + print_bool8("JAS", bstream, 6, 1, lhdc->jas); + print_bool8("AR", bstream, 7, 1, lhdc->ar); + print_value8("RFA", bstream += 1, 0, 7, "%#x", lhdc->rfa5); + print_bool8("AR On", bstream, 7, 1, lhdc->ar_on); + printf("}\n"); + } static void dump_opus(const void *blob, size_t size) { + const a2dp_opus_t *opus = blob; if (check_blob_size(sizeof(*opus), size) == -1) return; + + static const struct bitfield ch_modes[] = { + { OPUS_CHANNEL_MODE_STEREO, "Stereo" }, + { OPUS_CHANNEL_MODE_DUAL, "Dual Channel" }, + { OPUS_CHANNEL_MODE_MONO, "Mono" }, + { 0 }, + }; + + static const struct bitfield rates[] = { + { OPUS_SAMPLING_FREQ_48000, "48000 Hz" }, + { OPUS_SAMPLING_FREQ_24000, "24000 Hz" }, + { OPUS_SAMPLING_FREQ_16000, "16000 Hz" }, + { 0 }, + }; + + static const struct bitfield durations[] = { + { OPUS_FRAME_DURATION_100, "10 ms" }, + { OPUS_FRAME_DURATION_200, "20 ms" }, + { 0 }, + }; + printf("Opus {\n", bintohex(blob, size)); - printf_vendor(&opus->info); - printf("" - " sample-rate:3 =%s%s%s\n" - " frame-duration:2 =%s%s\n" - " channel-mode:3 =%s%s%s\n" - "}\n", - opus->sampling_freq & OPUS_SAMPLING_FREQ_48000 ? " 48000" : "", - opus->sampling_freq & OPUS_SAMPLING_FREQ_24000 ? " 24000" : "", - opus->sampling_freq & OPUS_SAMPLING_FREQ_16000 ? " 16000" : "", - opus->frame_duration & OPUS_FRAME_DURATION_100 ? " 10ms" : "", - opus->frame_duration & OPUS_FRAME_DURATION_200 ? " 20ms" : "", - opus->channel_mode & OPUS_CHANNEL_MODE_STEREO ? " Stereo" : "", - opus->channel_mode & OPUS_CHANNEL_MODE_DUAL ? " DualChannel" : "", - opus->channel_mode & OPUS_CHANNEL_MODE_MONO ? " Mono" : ""); + print_vendor(&opus->info); + + const uint8_t *bstream = ((uint8_t *)blob) + sizeof(a2dp_vendor_info_t); + print_bitfield8("Sample Rate", bstream, 0, 3, opus->sampling_freq, rates); + print_bitfield8("Frame Duration", bstream, 3, 2, opus->frame_duration, durations); + print_bitfield8("Channel Mode", bstream, 5, 3, opus->channel_mode, ch_modes); + printf("}\n"); + } static void dump_opus_pw(const void *blob, size_t size) { + const a2dp_opus_pw_t *opus = blob; if (check_blob_size(sizeof(*opus), size) == -1) return; + + static const struct bitfield durations[] = { + { OPUS_PW_FRAME_DURATION_025, "2.5 ms" }, + { OPUS_PW_FRAME_DURATION_050, "5 ms" }, + { OPUS_PW_FRAME_DURATION_100, "10 ms" }, + { OPUS_PW_FRAME_DURATION_200, "20 ms" }, + { OPUS_PW_FRAME_DURATION_400, "40 ms" }, + { 0 }, + }; + printf("Opus (PipeWire) {\n", bintohex(blob, size)); - printf_vendor(&opus->info); - printf("" - " music-channels:8 = %u\n" - " music-coupled-streams:8 = %u\n" - " music-location32: = %#x\n" - " music-frame-duration:8 =%s%s%s%s%s\n" - " music-bitrate:16 = %u\n" - " voice-channels:8 = %u\n" - " voice-coupled-streams:8 = %u\n" - " voice-location32: = %#x\n" - " voice-frame-duration:8 =%s%s%s%s%s\n" - " voice-bitrate:16 = %u\n" - "}\n", - opus->music.channels, - opus->music.coupled_streams, - A2DP_OPUS_PW_GET_LOCATION(opus->music), - opus->music.frame_duration & OPUS_PW_FRAME_DURATION_025 ? " 2.5ms" : "", - opus->music.frame_duration & OPUS_PW_FRAME_DURATION_050 ? " 5ms" : "", - opus->music.frame_duration & OPUS_PW_FRAME_DURATION_100 ? " 10ms" : "", - opus->music.frame_duration & OPUS_PW_FRAME_DURATION_200 ? " 20ms" : "", - opus->music.frame_duration & OPUS_PW_FRAME_DURATION_400 ? " 40ms" : "", - A2DP_OPUS_PW_GET_BITRATE(opus->music) * 1024, - opus->voice.channels, - opus->voice.coupled_streams, - A2DP_OPUS_PW_GET_LOCATION(opus->voice), - opus->voice.frame_duration & OPUS_PW_FRAME_DURATION_025 ? " 2.5ms" : "", - opus->voice.frame_duration & OPUS_PW_FRAME_DURATION_050 ? " 5ms" : "", - opus->voice.frame_duration & OPUS_PW_FRAME_DURATION_100 ? " 10ms" : "", - opus->voice.frame_duration & OPUS_PW_FRAME_DURATION_200 ? " 20ms" : "", - opus->voice.frame_duration & OPUS_PW_FRAME_DURATION_400 ? " 40ms" : "", - A2DP_OPUS_PW_GET_BITRATE(opus->voice) * 1024); + print_vendor(&opus->info); + + const uint8_t *bstream; + + bstream = (uint8_t *)&opus->music; + print_value8("Music Channels", bstream, 0, 8, "%u", opus->music.channels); + print_value8("Music Coupled Streams", bstream += 1, 0, 8, "%u", opus->music.coupled_streams); + const uint32_t opus_music_location = A2DP_OPUS_PW_GET_LOCATION(opus->music); + print_value32("Music Location", bstream += 1, 0, 32, "%#x", opus_music_location); + print_bitfield8("Music Frame Duration", bstream += 4, 0, 8, opus->music.frame_duration, durations); + const uint32_t opus_music_bitrate = A2DP_OPUS_PW_GET_BITRATE(opus->music); + print_value16("Music Bitrate", bstream += 1, 0, 16, "%u [%u kbps]", + opus_music_bitrate, opus_music_bitrate * 1024); + + bstream = (uint8_t *)&opus->voice; + print_value8("Voice Channels", bstream, 0, 8, "%u", opus->voice.channels); + print_value8("Voice Coupled Streams", bstream += 1, 0, 8, "%u", opus->voice.coupled_streams); + const uint32_t opus_voice_location = A2DP_OPUS_PW_GET_LOCATION(opus->voice); + print_value32("Voice Location", bstream += 1, 0, 32, "%#x", opus_voice_location); + print_bitfield8("Voice Frame Duration", bstream += 4, 0, 8, opus->voice.frame_duration, durations); + const uint32_t opus_voice_bitrate = A2DP_OPUS_PW_GET_BITRATE(opus->voice); + print_value16("Voice Bitrate", bstream += 1, 0, 16, "%u [%u kbps]", + opus_voice_bitrate, opus_voice_bitrate * 1024); + + printf("}\n"); + } static const struct { @@ -733,10 +1101,11 @@ int dump(const char *config, bool detect) { int main(int argc, char *argv[]) { int opt; - const char *opts = "hVx"; + const char *opts = "hVvx"; const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, { "auto-detect", no_argument, NULL, 'x' }, { 0, 0, 0, 0 }, }; @@ -753,6 +1122,7 @@ int main(int argc, char *argv[]) { "\nOptions:\n" " -h, --help\t\tprint this help and exit\n" " -V, --version\t\tprint version and exit\n" + " -v, --verbose\t\tshow verbose bit-stream details\n" " -x, --auto-detect\ttry to auto-detect codec\n" "\nExamples:\n" " %s sbc:ffff0235\n" @@ -764,6 +1134,9 @@ int main(int argc, char *argv[]) { printf("%s\n", PACKAGE_VERSION); return EXIT_SUCCESS; + case 'v' /* --verbose */ : + verbose = true; + break; case 'x' /* --auto-detect */ : detect = true; break;