1 /* 2 * Copyright (C) 2022 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "le_audio_util.c" 39 40 #include "btstack_util.h" 41 #include "btstack_debug.h" 42 #include "le-audio/le_audio_util.h" 43 44 // help with buffer == NULL 45 uint16_t le_audio_util_virtual_memcpy_helper( 46 const uint8_t * field_data, uint16_t field_len, uint16_t field_offset, 47 uint8_t * buffer, uint16_t buffer_size, uint16_t buffer_offset){ 48 49 // only calc total size 50 if (buffer == NULL) { 51 return field_len; 52 } 53 return btstack_virtual_memcpy(field_data, field_len, field_offset, buffer, buffer_size, buffer_offset); 54 } 55 56 uint16_t le_audio_util_metadata_virtual_memcpy(const le_audio_metadata_t * metadata, uint8_t metadata_length, uint16_t * records_offset, uint8_t * buffer, uint16_t buffer_size, uint16_t buffer_offset){ 57 uint16_t metadata_type; 58 uint8_t field_data[7]; 59 uint16_t stored_bytes = 0; 60 61 uint16_t metadata_length_pos = *records_offset; 62 field_data[0] = 0; 63 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *records_offset, buffer, buffer_size, 64 buffer_offset); 65 *records_offset += 1; 66 67 if (metadata_length == 0){ 68 return stored_bytes; 69 } 70 71 for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){ 72 if ((metadata->metadata_mask & (1 << metadata_type) ) != 0 ){ 73 // reserve field_data[0] for num butes to store 74 field_data[0] = 1; 75 field_data[1] = metadata_type; 76 77 switch ((le_audio_metadata_type_t)metadata_type){ 78 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS: 79 field_data[0] += 2; 80 little_endian_store_16(field_data, 2, metadata->preferred_audio_contexts_mask); 81 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, 82 buffer, buffer_size, buffer_offset); 83 *records_offset += field_data[0] + 1; 84 break; 85 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS: 86 field_data[0] += 2; 87 little_endian_store_16(field_data, 2, metadata->streaming_audio_contexts_mask); 88 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, 89 buffer, buffer_size, buffer_offset); 90 *records_offset += field_data[0] + 1; 91 break; 92 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO: 93 field_data[0] += metadata->program_info_length; 94 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, 95 buffer_size, buffer_offset); 96 *records_offset += 2; 97 stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info, 98 metadata->program_info_length, *records_offset, 99 buffer, buffer_size, buffer_offset); 100 *records_offset += metadata->program_info_length; 101 break; 102 case LE_AUDIO_METADATA_TYPE_LANGUAGE: 103 field_data[0] += 3; 104 little_endian_store_24(field_data, 2, metadata->language_code); 105 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, 106 buffer, buffer_size, buffer_offset); 107 *records_offset += field_data[0] + 1; 108 break; 109 case LE_AUDIO_METADATA_TYPE_CCID_LIST: 110 field_data[0] += metadata->ccids_num; 111 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, 112 buffer_size, buffer_offset); 113 *records_offset += 2; 114 stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->ccids, metadata->ccids_num, 115 *records_offset, buffer, buffer_size, 116 buffer_offset); 117 *records_offset += metadata->ccids_num; 118 break; 119 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING: 120 field_data[0] += 1; 121 field_data[2] = (uint8_t) metadata->parental_rating; 122 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, 123 buffer, buffer_size, buffer_offset); 124 *records_offset += field_data[0] + 1; 125 break; 126 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: 127 field_data[0] += metadata->program_info_uri_length; 128 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, 129 buffer_size, buffer_offset); 130 *records_offset += 2; 131 stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->program_info_uri, 132 metadata->program_info_uri_length, 133 *records_offset, buffer, buffer_size, 134 buffer_offset); 135 *records_offset += metadata->program_info_uri_length; 136 break; 137 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION: 138 field_data[0] += 2 + metadata->extended_metadata_length; 139 field_data[1] = LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA; 140 little_endian_store_16(field_data, 2, metadata->extended_metadata_type); 141 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer, 142 buffer_size, buffer_offset); 143 *records_offset += 4; 144 stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->extended_metadata, 145 metadata->extended_metadata_length, 146 *records_offset, buffer, buffer_size, 147 buffer_offset); 148 *records_offset += metadata->extended_metadata_length; 149 break; 150 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION: 151 field_data[0] += 2 + metadata->vendor_specific_metadata_length; 152 field_data[1] = LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA; 153 little_endian_store_16(field_data, 2, metadata->vendor_specific_company_id); 154 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *records_offset, buffer, 155 buffer_size, buffer_offset); 156 *records_offset += 4; 157 stored_bytes += le_audio_util_virtual_memcpy_helper(metadata->vendor_specific_metadata, 158 metadata->vendor_specific_metadata_length, 159 *records_offset, buffer, buffer_size, 160 buffer_offset); 161 *records_offset += metadata->vendor_specific_metadata_length; 162 break; 163 default: 164 btstack_assert(false); 165 break; 166 } 167 } 168 } 169 170 field_data[0] = *records_offset - metadata_length_pos - 1; 171 le_audio_util_virtual_memcpy_helper(field_data, 1, metadata_length_pos, buffer, buffer_size, buffer_offset); 172 return stored_bytes; 173 } 174 175 176 uint16_t le_audio_util_metadata_parse(uint8_t * buffer, uint8_t buffer_size, le_audio_metadata_t * metadata){ 177 // parse config to get sampling frequency and frame duration 178 uint8_t offset = 0; 179 uint8_t metadata_config_lenght = buffer[offset++]; 180 if (buffer_size < metadata_config_lenght){ 181 return 0; 182 } 183 184 metadata->metadata_mask = 0; 185 186 while ((offset + 1) < metadata_config_lenght){ 187 uint8_t ltv_len = buffer[offset++]; 188 189 le_audio_metadata_type_t ltv_type = (le_audio_metadata_type_t)buffer[offset]; 190 le_audio_parental_rating_t parental_rating; 191 192 switch (ltv_type){ 193 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS: 194 metadata->preferred_audio_contexts_mask = little_endian_read_16(buffer, offset+1); 195 metadata->metadata_mask |= (1 << ltv_type); 196 break; 197 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS: 198 metadata->streaming_audio_contexts_mask = little_endian_read_16(buffer, offset+1); 199 metadata->metadata_mask |= (1 << ltv_type); 200 break; 201 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO: 202 metadata->program_info_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_MAX_LENGTH); 203 memcpy(metadata->program_info, &buffer[offset+1], metadata->program_info_length); 204 metadata->metadata_mask |= (1 << ltv_type); 205 break; 206 case LE_AUDIO_METADATA_TYPE_LANGUAGE: 207 metadata->language_code = little_endian_read_24(buffer, offset+1); 208 metadata->metadata_mask |= (1 << ltv_type); 209 break; 210 case LE_AUDIO_METADATA_TYPE_CCID_LIST: 211 metadata->ccids_num = btstack_min(ltv_len, LE_CCIDS_MAX_NUM); 212 memcpy(metadata->ccids, &buffer[offset+1], metadata->ccids_num); 213 metadata->metadata_mask |= (1 << ltv_type); 214 break; 215 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING: 216 parental_rating = (le_audio_parental_rating_t)buffer[offset+1]; 217 metadata->parental_rating = parental_rating; 218 metadata->metadata_mask |= (1 << ltv_type); 219 break; 220 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: 221 metadata->program_info_uri_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_URI_MAX_LENGTH); 222 memcpy(metadata->program_info_uri, &buffer[offset+1], metadata->program_info_uri_length); 223 metadata->metadata_mask |= (1 << ltv_type); 224 break; 225 case LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA: 226 if (ltv_len < 2){ 227 break; 228 } 229 metadata->extended_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_EXTENDED_METADATA_MAX_LENGHT); 230 metadata->extended_metadata_type = little_endian_read_16(buffer, offset+1); 231 memcpy(metadata->extended_metadata, &buffer[offset+3], metadata->extended_metadata_length); 232 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION); 233 break; 234 case LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA: 235 if (ltv_len < 2){ 236 break; 237 } 238 metadata->vendor_specific_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_VENDOR_SPECIFIC_METADATA_MAX_LENGTH); 239 metadata->vendor_specific_company_id = little_endian_read_16(buffer, offset+1); 240 memcpy(metadata->vendor_specific_metadata, &buffer[offset+3], metadata->vendor_specific_metadata_length); 241 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION); 242 break; 243 default: 244 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_RFU); 245 break; 246 } 247 offset += ltv_len; 248 } 249 return offset; 250 } 251 252 uint16_t le_audio_util_metadata_serialize(le_audio_metadata_t * metadata, uint8_t * event, uint16_t event_size){ 253 uint8_t pos = 0; 254 255 event[pos++] = (uint8_t)metadata->metadata_mask; 256 little_endian_store_16(event, pos, metadata->preferred_audio_contexts_mask); 257 pos += 2; 258 little_endian_store_16(event, pos, metadata->streaming_audio_contexts_mask); 259 pos += 2; 260 261 event[pos++] = metadata->program_info_length; 262 memcpy(&event[pos], &metadata->program_info[0], metadata->program_info_length); 263 pos += metadata->program_info_length; 264 265 little_endian_store_24(event, pos, metadata->language_code); 266 pos += 3; 267 268 event[pos++] = metadata->ccids_num; 269 memcpy(&event[pos], &metadata->ccids[0], metadata->ccids_num); 270 pos += metadata->ccids_num; 271 272 event[pos++] = (uint8_t)metadata->parental_rating; 273 274 event[pos++] = metadata->program_info_uri_length; 275 memcpy(&event[pos], &metadata->program_info_uri[0], metadata->program_info_uri_length); 276 pos += metadata->program_info_uri_length; 277 278 little_endian_store_16(event, pos, metadata->extended_metadata_type); 279 pos += 2; 280 281 event[pos++] = metadata->extended_metadata_length; 282 memcpy(&event[pos], &metadata->extended_metadata[0], metadata->extended_metadata_length); 283 pos += metadata->extended_metadata_length; 284 285 little_endian_store_16(event, pos, metadata->vendor_specific_company_id); 286 pos += 2; 287 288 event[pos++] = metadata->vendor_specific_metadata_length; 289 memcpy(&event[pos], &metadata->vendor_specific_metadata[0], metadata->vendor_specific_metadata_length); 290 pos += metadata->vendor_specific_metadata_length; 291 292 return pos; 293 } 294