diff --git a/src/le-audio/broadcast_audio_uri_builder.c b/src/le-audio/broadcast_audio_uri_builder.c index 3ade53996..978e47413 100644 --- a/src/le-audio/broadcast_audio_uri_builder.c +++ b/src/le-audio/broadcast_audio_uri_builder.c @@ -41,7 +41,6 @@ #include "btstack_base64_encoder.h" #include "btstack_util.h" -#include #include #include #include @@ -59,10 +58,30 @@ static inline bool broadcast_audio_uri_builder_have_space(const broadcast_audio_ return builder->len + len <= builder->size; } -void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size){ +static inline char * broadcast_audio_uri_builder_get_ptr( const broadcast_audio_uri_builder_t * builder ) { + return &builder->buffer[builder->len]; +} + +bool broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, char * buffer, uint16_t size){ builder->buffer = buffer; builder->size = size; builder->len = 0; + return broadcast_audio_uri_builder_append_string(builder, "BLUETOOTH:UUID:184F;"); +} + +bool broadcast_audio_uri_builder_finalize(broadcast_audio_uri_builder_t * builder){ + bool ok = broadcast_audio_uri_builder_append_string(builder, ";"); + uint16_t remaining; + void *ptr; + if(ok) { + remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + ptr = broadcast_audio_uri_builder_get_ptr(builder); + ok = (remaining > 0); + } + if(ok) { + memset(ptr, 0, remaining); + } + return ok; } uint16_t broadcast_audio_uri_builder_get_remaining_space(const broadcast_audio_uri_builder_t * builder){ @@ -93,20 +112,17 @@ bool broadcast_audio_uri_builder_append_broadcast_name(broadcast_audio_uri_build if( !ok ) { return ok; } - uint8_t *ptr =&builder->buffer[builder->len]; + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); size_t remaining = broadcast_audio_uri_builder_get_remaining_space( builder ); - ptr = btstack_base64_encoder(broadcast_name, strlen(broadcast_name), ptr, &remaining); - if( ptr == NULL ) { - goto error_return; + ok = btstack_base64_encoder_process_block(broadcast_name, strlen(broadcast_name), ptr, &remaining); + if( ok ) { + builder->len += remaining; + ok = broadcast_audio_uri_builder_append_string( builder, ";" ); } - ok = broadcast_audio_uri_builder_append_string( builder, ";" ); if( !ok ) { - goto error_return; + builder->len = len; } - return true; -error_return: - builder->len = len; - return false; + return ok; } bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_uri_builder_t * builder, bd_addr_type_t advertiser_address_type){ @@ -116,14 +132,9 @@ bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_ } bool broadcast_audio_uri_builder_append_standard_quality(broadcast_audio_uri_builder_t * builder, bool standard_quality){ - uint8_t *ptr = &builder->buffer[builder->len]; - uint16_t remaining = broadcast_audio_uri_builder_get_remaining_space( builder ); - int len = snprintf((char*)ptr, remaining, "SQ:%u;", (int) standard_quality); - if((len>0) && (lenlen += len-1; - return true; - } - return false; + 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){ @@ -133,47 +144,73 @@ bool broadcast_audio_uri_builder_append_high_quality(broadcast_audio_uri_builder } bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_builder_t * builder, bd_addr_t advertiser_address){ + char buffer[13]; 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 { + broadcast_audio_uri_builder_string_hexdump((uint8_t *)buffer, (const uint8_t *) advertiser_address, 6); + buffer[12] = ';'; + if(ok){ + ok = broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); + } + if(!ok){ builder->len = len; } return ok; } bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id){ + uint8_t buffer[7]; 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 { + + 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] = ';'; + + if(ok){ + ok = broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); + } + if(!ok) { builder->len = len; } return ok; - } -bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code){ - UNUSED(broadcast_code); +bool broadcast_audio_uri_builder_append_broadcast_code_as_bytes(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 { + if (!ok){ + return ok; + } + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + size_t remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + ok = btstack_base64_encoder_process_block(broadcast_code, 16, ptr, &remaining); + if(ok) { + builder->len += remaining; + ok = broadcast_audio_uri_builder_append_string( builder, ";" ); + } + if(!ok){ + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_append_broadcast_code_as_string(broadcast_audio_uri_builder_t * builder, const char *broadcast_code){ + uint16_t len = builder->len; + bool ok = broadcast_audio_uri_builder_append_string(builder,"BC:"); + if (!ok){ + return ok; + } + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + size_t remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + size_t block_size = btstack_min(strlen(broadcast_code), 16); + ok = btstack_base64_encoder_process_block(broadcast_code, block_size, ptr, &remaining); + if(ok) { + builder->len += remaining; + ok = broadcast_audio_uri_builder_append_string( builder, ";" ); + } + if(!ok){ builder->len = len; } return ok; @@ -184,16 +221,41 @@ bool broadcast_audio_uri_builder_append_vendor_specific(broadcast_audio_uri_buil UNUSED(data_len); uint16_t len = builder->len; bool ok = broadcast_audio_uri_builder_append_string(builder, "VS:"); - if (ok){ + size_t remaining, olen; + btstack_base64_state_t state; + if(ok) { + char buffer[9] = "ID:0000;"; 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]; - memset(vendor_id_hex, 0, sizeof(vendor_id_hex)); - broadcast_audio_uri_builder_string_hexdump(vendor_id_hex, vendor_id_hex, 2); - // TODO: base64(vendor_id_hex + data) + broadcast_audio_uri_builder_string_hexdump((uint8_t*)&buffer[3], vendor_id_big_endian_id, 2); + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + olen = remaining; + btstack_base64_encoder_stream_init( &state ); + ssize_t ret = btstack_base64_encoder_stream( &state, buffer, strlen(buffer), ptr, &olen ); + ok = ((ret>0) && !(olen>remaining)); + } + if(ok) { + builder->len += olen; + remaining -= olen; + olen = remaining; + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + ssize_t ret = btstack_base64_encoder_stream( &state, data, data_len, ptr, &olen ); + ok = ((ret>0) && !(olen>remaining)); + } + if(ok) { + builder->len += olen; + remaining -= olen; + olen = remaining; + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + ssize_t ret = btstack_base64_encoder_stream_final(&state, ptr, &olen); + ok = ((ret>=0) && (olenlen += olen; ok = broadcast_audio_uri_builder_append_string(builder, ";"); } - if (ok == false){ + if (!ok){ builder->len = len; } return ok; @@ -201,59 +263,115 @@ bool broadcast_audio_uri_builder_append_vendor_specific(broadcast_audio_uri_buil 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); + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AS:%X;", 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" PRIX16 ";", pa_interval); + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "PI:%" PRIX16 ";", 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); + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NS:%X;", 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); + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "BS:%" 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); + btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NB:%" 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){ +bool broadcast_audio_uri_builder_sg_metadata_begin(broadcast_audio_uri_builder_t * builder){ 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) { + btstack_base64_encoder_stream_init(&builder->state); + } + if(!ok){ + builder->len = len; + } + return ok; +} + +bool broadcast_audio_uri_builder_sg_metadata_append_ltv(broadcast_audio_uri_builder_t * builder, uint8_t metadata_len, const uint8_t type, const uint8_t * metadata){ + if(metadata_len == 0) { + return true; + } + uint16_t len = builder->len; + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + size_t remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + size_t olen = remaining; + uint8_t l = metadata_len + 1; // account for type octet + ssize_t ret = btstack_base64_encoder_stream(&builder->state, &l, 1, ptr, &olen); + bool ok = (ret>0) && !(olen>remaining); + if (ok) { + builder->len += olen; + ptr = broadcast_audio_uri_builder_get_ptr(builder); + remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + olen = remaining; + ret = btstack_base64_encoder_stream(&builder->state, &type, 1, ptr, &olen); + ok = (ret>0) && !(olen>remaining); } if (ok) { - ok = broadcast_audio_uri_builder_append_string(builder, ";"); + builder->len += olen; + ptr = broadcast_audio_uri_builder_get_ptr(builder); + remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + olen = remaining; + ret = btstack_base64_encoder_stream(&builder->state, metadata, metadata_len, ptr, &olen); + ok = (ret>0) && !(olen>remaining); + } + if(ok) { + builder->len += olen; } else { builder->len = len; } return ok; } +bool broadcast_audio_uri_builder_sg_metadata_end(broadcast_audio_uri_builder_t * builder){ + uint16_t len = builder->len; + + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + size_t remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + size_t olen = remaining; + ssize_t ret = btstack_base64_encoder_stream_final(&builder->state, ptr, &olen); + bool ok = (ret>=0) && !(olen>remaining); + + if (ok) { + builder->len += olen; + ok = broadcast_audio_uri_builder_append_string(builder, ";"); + } + if(!ok){ + 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){ + if(metadata_len == 0) { + return true; + } 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) { + char *ptr = broadcast_audio_uri_builder_get_ptr(builder); + size_t remaining = broadcast_audio_uri_builder_get_remaining_space(builder); + ok = btstack_base64_encoder_process_block(metadata, metadata_len, ptr, &remaining); } if (ok) { ok = broadcast_audio_uri_builder_append_string(builder, ";"); - } else { + } + if(!ok){ 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 index 41dfcae9d..febfe137e 100644 --- a/src/le-audio/broadcast_audio_uri_builder.h +++ b/src/le-audio/broadcast_audio_uri_builder.h @@ -45,15 +45,17 @@ #include #include "btstack_bool.h" #include "bluetooth.h" +#include "btstack_base64_encoder.h" #if defined __cplusplus extern "C" { #endif typedef struct { - uint8_t * buffer; + char * buffer; uint16_t size; uint16_t len; + btstack_base64_state_t state; } broadcast_audio_uri_builder_t; /** @@ -61,8 +63,18 @@ typedef struct { * @param builder * @param buffer to setup BASE * @param size of buffer + * @return true if there was sufficient space in the underlying buffer */ -void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size); +bool broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, char * buffer, uint16_t size); + +/** + * Finalize Broadcast Audio URI + * @param builder + * @param buffer to setup BASE + * @param size of buffer + * @return true if there was sufficient space in the underlying buffer + */ +bool broadcast_audio_uri_builder_finalize(broadcast_audio_uri_builder_t * builder); /** * @brief Query remaining space in buffer @@ -128,12 +140,24 @@ bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_b bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id); /** - * Append Broadcast ID + * Append Broadcast Code * @param builder - * @param Broadcast Code (128 bit) + * @param Broadcast Code (128 bit) as byte array * @return */ -bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code); +bool broadcast_audio_uri_builder_append_broadcast_code_as_bytes(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code); + +/** + * Append Broadcast Code + * @param builder + * @param Broadcast Code (128 bit) as string + * @return + * + * The broadcast code will include the first 16 bytes of the given string. + * If the string is smaller only the bytes till the null termination are included. + * Bigger strings are truncated after 16 bytes. + */ +bool broadcast_audio_uri_builder_append_broadcast_code_as_string(broadcast_audio_uri_builder_t * builder, const char * broadcast_code); /** * Append Standard Quality @@ -202,13 +226,28 @@ bool broadcast_audio_uri_builder_append_bis_sync(broadcast_audio_uri_builder_t * 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 + * Start a SG_Metadata block * @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); +bool broadcast_audio_uri_builder_sg_metadata_begin(broadcast_audio_uri_builder_t * builder); + +/** + * Append ltv to SG_Metadata block + * @param builder + * @param metadata_len + * @param type + * @param metadata + * @return + */ +bool broadcast_audio_uri_builder_sg_metadata_append_ltv(broadcast_audio_uri_builder_t * builder, uint8_t metadata_len, uint8_t type, const uint8_t * metadata); + +/** + * Finalize SG_Metadata block + * @param builder + * @return + */ +bool broadcast_audio_uri_builder_sg_metadata_end(broadcast_audio_uri_builder_t * builder); /** * Append Public Broadcast Announcement Metadata