broadcast_audio_uri_builder: improve compliance

This commit is contained in:
Dirk Helbig 2025-03-17 11:46:57 +01:00 committed by Matthias Ringwald
parent 063e806bae
commit 058e13bc89
2 changed files with 230 additions and 73 deletions

View File

@ -41,7 +41,6 @@
#include "btstack_base64_encoder.h"
#include "btstack_util.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <inttypes.h>
@ -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) && (len<remaining)) {
builder->len += 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) && (olen<remaining));
}
if (ok){
builder->len += 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;

View File

@ -45,15 +45,17 @@
#include <stdint.h>
#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