diff --git a/src/le-audio/broadcast_audio_uri.h b/src/le-audio/broadcast_audio_uri.h new file mode 100644 index 000000000..4cf2ad888 --- /dev/null +++ b/src/le-audio/broadcast_audio_uri.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/** + * @brief TODO + */ + +#ifndef BROADCAST_AUDIO_URI_H +#define BROADCAST_AUDIO_URI_H + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + BROADCAST_AUDIO_URI_BROADCASTNAME, + BROADCAST_AUDIO_URI_ADVERTISER_ADDRESS_TYPE, + BROADCAST_AUDIO_URI_ADVERTISER_ADDRESS, + BROADCAST_AUDIO_URI_BROADCAST_ID, + BROADCAST_AUDIO_URI_BROADCAST_CODE, + BROADCAST_AUDIO_URI_STANDARD_QUALITY, + BROADCAST_AUDIO_URI_HIGH_QUALITY, + BROADCAST_AUDIO_URI_VENDOR_SPECIFIC, + BROADCAST_AUDIO_URI_ADVERTISING_SID, + BROADCAST_AUDIO_URI_PA_INTERVAL, + BROADCAST_AUDIO_URI_NUM_SUBGROUPS, + BROADCAST_AUDIO_URI_BIS_SYNC, + BROADCAST_AUDIO_URI_SG_NUMBER_OF_BISES, + BROADCAST_AUDIO_URI_SG_METADATA, + BROADCAST_AUDIO_URI_PUBLIC_BROADCAST, + BROADCAST_AUDIO_URI_ANNOUNCEMENT, + BROADCAST_AUDIO_URI_METADATA, +} broadcast_audio_uri_id_t; + +#if defined __cplusplus +} +#endif +#endif // BROADCAST_AUDIO_URI_BUILDER_H diff --git a/src/le-audio/broadcast_audio_uri_builder.c b/src/le-audio/broadcast_audio_uri_builder.c new file mode 100644 index 000000000..9b41aade2 --- /dev/null +++ b/src/le-audio/broadcast_audio_uri_builder.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define BTSTACK_FILE__ "broadcast_audio_uri_builder.c" + +#include "le-audio/broadcast_audio_uri_builder.h" +#include "btstack_util.h" +#include +#include +#include + +static void broadcast_audio_uri_builder_string_hexdump(uint8_t * buffer, const uint8_t * data, uint16_t size){ + uint8_t i; + for (i = 0; i < size ; i++) { + uint8_t byte = data[i]; + buffer[2*i+0] = char_for_nibble(byte >> 4); + buffer[2*i+1] = char_for_nibble(byte & 0x0f); + } +} + +static inline bool broadcast_audio_uri_builder_have_space(const broadcast_audio_uri_builder_t * builder, uint16_t len){ + return builder->len + len <= builder->size; +} + +void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size){ + builder->buffer = buffer; + builder->size = size; + builder->len = 0; +} + +uint16_t broadcast_audio_uri_builder_get_remaining_space(const broadcast_audio_uri_builder_t * builder){ + return builder->size - builder->len; +} + +uint16_t broadcast_audio_uri_builder_get_size(const broadcast_audio_uri_builder_t * builder){ + return builder->len; +} + +bool broadcast_audio_uri_builder_append_bytes(broadcast_audio_uri_builder_t * builder, const uint8_t * data, uint16_t len){ + bool ok = broadcast_audio_uri_builder_have_space(builder, len); + if (ok){ + memcpy(&builder->buffer[builder->len], data, len); + builder->len += len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_string(broadcast_audio_uri_builder_t * builder, const char * text){ + uint16_t len = (uint16_t) strlen(text); + return broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) text, len); +} + +bool broadcast_audio_uri_builder_append_broadcast_name(broadcast_audio_uri_builder_t * builder, const char * broadcast_name){ + // TODO: base64 + return broadcast_audio_uri_builder_append_string(builder, "BN:QnJvYWRjYXN0IE5hbWU=;"); +} + +bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_uri_builder_t * builder, bd_addr_type_t advertiser_address_type){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AT:%u;",advertiser_address_type == BD_ADDR_TYPE_LE_RANDOM ? 1 : 0); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_standard_quality(broadcast_audio_uri_builder_t * builder, bool standard_quality){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "SQ:%u;", (int) standard_quality); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_high_quality(broadcast_audio_uri_builder_t * builder, bool high_quality){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "HQ:%u;", (int) high_quality); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_builder_t * builder, bd_addr_t advertiser_address){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder,"AD:"); + if (ok){ + char buffer[13]; + broadcast_audio_uri_builder_string_hexdump((uint8_t *)buffer, (const uint8_t *) advertiser_address, 6); + buffer[12] = ';'; + broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); + } else { + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder,"BI:"); + if (ok){ + uint8_t buffer[7]; + uint8_t big_endian_id[3]; + big_endian_store_24(big_endian_id, 0, broadcast_id); + broadcast_audio_uri_builder_string_hexdump(buffer, big_endian_id, 3); + buffer[6] = ';'; + broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); + } else { + builder->len = len; + } + return ok; + +} + +bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder,"BC:"); + if (ok){ + uint8_t buffer[25]; + // TODO: base64 + memcpy(buffer, "MDEyMzQ1Njc4OWFiY2RlZg==", 24); + buffer[24] = ';'; + broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); + } else { + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_vendor_specific(broadcast_audio_uri_builder_t * builder, uint16_t vendor_id, const uint8_t * data, uint16_t data_len){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder, "VS:"); + if (ok){ + uint8_t vendor_id_big_endian_id[2]; + big_endian_store_16(vendor_id_big_endian_id, 0, vendor_id); + uint8_t vendor_id_hex[4]; + broadcast_audio_uri_builder_string_hexdump(vendor_id_hex, vendor_id_hex, 2); + // TODO: base64(vendor_id_hex + data) + ok = broadcast_audio_uri_builder_append_string(builder, ";"); + } + if (ok == false){ + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_advertising_sid(broadcast_audio_uri_builder_t * builder, uint8_t advertising_sid){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AS:%02X;", advertising_sid); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_pa_interval(broadcast_audio_uri_builder_t * builder, uint16_t pa_interval){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "PI:%04" PRIX32 ";", pa_interval); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_num_subgroups(broadcast_audio_uri_builder_t * builder, uint8_t num_subgroups){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NS:%02X", num_subgroups); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_bis_sync(broadcast_audio_uri_builder_t * builder, uint32_t bis_sync){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "BS:%04" PRIX32 ";", bis_sync); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_sg_number_of_bises(broadcast_audio_uri_builder_t * builder, uint32_t sg_number_of_bises){ + char buffer[10]; + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NB:%04" PRIX32 ";", sg_number_of_bises); + return broadcast_audio_uri_builder_append_string(builder, buffer); +} + +bool broadcast_audio_uri_builder_append_sg_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder, "SM:"); + if (ok && (metadata_len > 0)) { + // TODO: base64(data) + ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); + } + if (ok) { + ok = broadcast_audio_uri_builder_append_string(builder, ";"); + } else { + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_public_broadcast_announcement_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder, "PM:"); + if (ok && metadata_len > 0) { + // TODO: base64(data) + ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); + } + if (ok) { + ok = broadcast_audio_uri_builder_append_string(builder, ";"); + } else { + builder->len = len; + } + return ok; +} diff --git a/src/le-audio/broadcast_audio_uri_builder.h b/src/le-audio/broadcast_audio_uri_builder.h new file mode 100644 index 000000000..41dfcae9d --- /dev/null +++ b/src/le-audio/broadcast_audio_uri_builder.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/** + * @brief TODO + */ + +#ifndef BROADCAST_AUDIO_URI_BUILDER_H +#define BROADCAST_AUDIO_URI_BUILDER_H + +#include +#include "btstack_bool.h" +#include "bluetooth.h" + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t * buffer; + uint16_t size; + uint16_t len; +} broadcast_audio_uri_builder_t; + +/** + * Initialize Broadcast Audio URI builder + * @param builder + * @param buffer to setup BASE + * @param size of buffer + */ +void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size); + +/** + * @brief Query remaining space in buffer + * @param builder + * @return size + */ +uint16_t broadcast_audio_uri_builder_get_remaining_space(const broadcast_audio_uri_builder_t * builder); + +/** + * Get Size of Broadcast Audio URI + * @param builder + * @return size + */ +uint16_t broadcast_audio_uri_builder_get_size(const broadcast_audio_uri_builder_t * builder); + +/** + * Append bytes + * @param builder + * @param data + * @param len + * @return true if there was sufficient space in the underlying buffer + */ +bool broadcast_audio_uri_builder_append_bytes(broadcast_audio_uri_builder_t * builder, const uint8_t * data, uint16_t len); + +/** + * Append String + * @param builder + * @param text as utf-8 + * @return true if there was sufficient space in the underlying buffer + */ +bool broadcast_audio_uri_builder_append_string(broadcast_audio_uri_builder_t * builder, const char * text); + +/** + * Append Broadcast Name (BN) base64 encoded + * @param builder + * @param broadcast_name + * @return + */ +bool broadcast_audio_uri_builder_append_broadcast_name(broadcast_audio_uri_builder_t * builder, const char * broadcast_name); + +/** + * Append Advertiser Address Type + * @param builder + * @param advertiser_address_type + * @return + */ +bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_uri_builder_t * builder, bd_addr_type_t advertiser_address_type); + +/** + * Append Advertiser Address + * @param builder + * @param advertiser_address_type + * @return + */ +bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_builder_t * builder, bd_addr_t advertiser_address); + +/** + * Append Broadcast ID + * @param builder + * @param Broadcast ID (24 bit) + * @return + */ +bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id); + +/** + * Append Broadcast ID + * @param builder + * @param Broadcast Code (128 bit) + * @return + */ +bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code); + +/** + * Append Standard Quality + * @param builder + * @param advertiser_address_type + * @return + */ +bool broadcast_audio_uri_builder_append_standard_quality(broadcast_audio_uri_builder_t * builder, bool standard_quality); + +/** + * Append High Quality + * @param builder + * @param advertiser_address_type + * @return + */ +bool broadcast_audio_uri_builder_append_high_quality(broadcast_audio_uri_builder_t * builder, bool high_quality); + +/** + * Append Vendor Specific data + * @param builder + * @param vendor_id + * @param data + * @param data_len + * @return + */ +bool broadcast_audio_uri_builder_append_vendor_specific(broadcast_audio_uri_builder_t * builder, uint16_t vendor_id, const uint8_t * data, uint16_t data_len); + +/** + * Append Advertising SID + * @param builder + * @param advertising_sid + * @return + */ +bool broadcast_audio_uri_builder_append_advertising_sid(broadcast_audio_uri_builder_t * builder, uint8_t advertising_sid); + +/** + * Append PA Interval + * @param builder + * @param pa_interval + * @return + */ +bool broadcast_audio_uri_builder_append_pa_interval(broadcast_audio_uri_builder_t * builder, uint16_t pa_interval); + +/** + * Append Num Subgroups + * @param builder + * @param num_subgroups + * @return + */ +bool broadcast_audio_uri_builder_append_num_subgroups(broadcast_audio_uri_builder_t * builder, uint8_t num_subgroups); + +/** + * Append BIS_Sync + * @param builder + * @param Broadcast ID (24 bit) + * @return + */ +bool broadcast_audio_uri_builder_append_bis_sync(broadcast_audio_uri_builder_t * builder, uint32_t bis_sync); + +/** + * Append SG Number of BISes + * @param builder + * @param sg_number_of_bises + * @return + */ +bool broadcast_audio_uri_builder_append_sg_number_of_bises(broadcast_audio_uri_builder_t * builder, uint32_t sg_number_of_bises); + +/** + * Append SG_Metadata + * @param builder + * @param metadata + * @param metadata_len + * @return + */ +bool broadcast_audio_uri_builder_append_sg_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len); + +/** + * Append Public Broadcast Announcement Metadata + * @param builder + * @param metadata + * @param metadata_len + * @return + */ +bool broadcast_audio_uri_builder_append_public_broadcast_announcement_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len); + +#if defined __cplusplus +} +#endif +#endif // BROADCAST_AUDIO_URI_BUILDER_H