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_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_virtual_memcpy_metadata(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_virtual_memcpy_helper(field_data, 1, *records_offset, buffer, buffer_size, buffer_offset); 64 *records_offset += 1; 65 66 if (metadata_length == 0){ 67 return stored_bytes; 68 } 69 70 for (metadata_type = (uint16_t)LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS; metadata_type < (uint16_t) LE_AUDIO_METADATA_TYPE_RFU; metadata_type++){ 71 if ((metadata->metadata_mask & (1 << metadata_type) ) != 0 ){ 72 // reserve field_data[0] for num butes to store 73 field_data[0] = 1; 74 field_data[1] = metadata_type; 75 76 switch ((le_audio_metadata_type_t)metadata_type){ 77 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS: 78 field_data[0] += 2; 79 little_endian_store_16(field_data, 2, metadata->preferred_audio_contexts_mask); 80 stored_bytes += le_audio_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, buffer, buffer_size, buffer_offset); 81 *records_offset += field_data[0] + 1; 82 break; 83 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS: 84 field_data[0] += 2; 85 little_endian_store_16(field_data, 2, metadata->streaming_audio_contexts_mask); 86 stored_bytes += le_audio_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, buffer, buffer_size, buffer_offset); 87 *records_offset += field_data[0] + 1; 88 break; 89 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO: 90 field_data[0] += metadata->program_info_length; 91 stored_bytes += le_audio_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, buffer_size, buffer_offset); 92 *records_offset += 2; 93 stored_bytes += le_audio_virtual_memcpy_helper(metadata->program_info, metadata->program_info_length, *records_offset, buffer, buffer_size, buffer_offset); 94 *records_offset += metadata->program_info_length; 95 break; 96 case LE_AUDIO_METADATA_TYPE_LANGUAGE: 97 field_data[0] += 3; 98 little_endian_store_24(field_data, 2, metadata->language_code); 99 stored_bytes += le_audio_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, buffer, buffer_size, buffer_offset); 100 *records_offset += field_data[0] + 1; 101 break; 102 case LE_AUDIO_METADATA_TYPE_CCID_LIST: 103 field_data[0] += metadata->ccids_num; 104 stored_bytes += le_audio_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, buffer_size, buffer_offset); 105 *records_offset += 2; 106 stored_bytes += le_audio_virtual_memcpy_helper(metadata->ccids, metadata->ccids_num, *records_offset, buffer, buffer_size, buffer_offset); 107 *records_offset += metadata->ccids_num; 108 break; 109 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING: 110 field_data[0] += 1; 111 field_data[2] = (uint8_t) metadata->parental_rating; 112 stored_bytes += le_audio_virtual_memcpy_helper(field_data, field_data[0] + 1, *records_offset, buffer, buffer_size, buffer_offset); 113 *records_offset += field_data[0] + 1; 114 break; 115 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: 116 field_data[0] += metadata->program_info_uri_length; 117 stored_bytes += le_audio_virtual_memcpy_helper(field_data, 2, *records_offset, buffer, buffer_size, buffer_offset); 118 *records_offset += 2; 119 stored_bytes += le_audio_virtual_memcpy_helper(metadata->program_info_uri, metadata->program_info_uri_length, *records_offset, buffer, buffer_size, buffer_offset); 120 *records_offset += metadata->program_info_uri_length; 121 break; 122 case LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION: 123 field_data[0] += 2 + metadata->extended_metadata_length; 124 field_data[1] = LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA; 125 little_endian_store_16(field_data, 2, metadata->extended_metadata_type); 126 stored_bytes += le_audio_virtual_memcpy_helper(field_data, 4, *records_offset, buffer, buffer_size, buffer_offset); 127 *records_offset += 4; 128 stored_bytes += le_audio_virtual_memcpy_helper(metadata->extended_metadata, metadata->extended_metadata_length, *records_offset, buffer, buffer_size, buffer_offset); 129 *records_offset += metadata->extended_metadata_length; 130 break; 131 case LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION: 132 field_data[0] += 2 + metadata->vendor_specific_metadata_length; 133 field_data[1] = LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA; 134 little_endian_store_16(field_data, 2, metadata->vendor_specific_company_id); 135 stored_bytes += le_audio_virtual_memcpy_helper(field_data, 4, *records_offset, buffer, buffer_size, buffer_offset); 136 *records_offset += 4; 137 stored_bytes += le_audio_virtual_memcpy_helper(metadata->vendor_specific_metadata, metadata->vendor_specific_metadata_length, *records_offset, buffer, buffer_size, buffer_offset); 138 *records_offset += metadata->vendor_specific_metadata_length; 139 break; 140 default: 141 btstack_assert(false); 142 break; 143 } 144 } 145 } 146 147 field_data[0] = *records_offset - metadata_length_pos - 1; 148 le_audio_virtual_memcpy_helper(field_data, 1, metadata_length_pos, buffer, buffer_size, buffer_offset); 149 return stored_bytes; 150 } 151 152 153 uint16_t le_audio_metadata_parse_tlv(uint8_t * buffer, uint8_t buffer_size, le_audio_metadata_t * metadata){ 154 // parse config to get sampling frequency and frame duration 155 uint8_t offset = 0; 156 uint8_t metadata_config_lenght = buffer[offset++]; 157 if (buffer_size < metadata_config_lenght){ 158 return 0; 159 } 160 161 metadata->metadata_mask = 0; 162 163 while ((offset + 1) < metadata_config_lenght){ 164 uint8_t ltv_len = buffer[offset++]; 165 166 le_audio_metadata_type_t ltv_type = (le_audio_metadata_type_t)buffer[offset]; 167 le_audio_parental_rating_t parental_rating; 168 169 switch (ltv_type){ 170 case LE_AUDIO_METADATA_TYPE_PREFERRED_AUDIO_CONTEXTS: 171 metadata->preferred_audio_contexts_mask = little_endian_read_16(buffer, offset+1); 172 metadata->metadata_mask |= (1 << ltv_type); 173 break; 174 case LE_AUDIO_METADATA_TYPE_STREAMING_AUDIO_CONTEXTS: 175 metadata->streaming_audio_contexts_mask = little_endian_read_16(buffer, offset+1); 176 metadata->metadata_mask |= (1 << ltv_type); 177 break; 178 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO: 179 metadata->program_info_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_MAX_LENGTH); 180 memcpy(metadata->program_info, &buffer[offset+1], metadata->program_info_length); 181 metadata->metadata_mask |= (1 << ltv_type); 182 break; 183 case LE_AUDIO_METADATA_TYPE_LANGUAGE: 184 metadata->language_code = little_endian_read_24(buffer, offset+1); 185 metadata->metadata_mask |= (1 << ltv_type); 186 break; 187 case LE_AUDIO_METADATA_TYPE_CCID_LIST: 188 metadata->ccids_num = btstack_min(ltv_len, LE_CCIDS_MAX_NUM); 189 memcpy(metadata->ccids, &buffer[offset+1], metadata->ccids_num); 190 metadata->metadata_mask |= (1 << ltv_type); 191 break; 192 case LE_AUDIO_METADATA_TYPE_PARENTAL_RATING: 193 parental_rating = (le_audio_parental_rating_t)buffer[offset+1]; 194 metadata->parental_rating = parental_rating; 195 metadata->metadata_mask |= (1 << ltv_type); 196 break; 197 case LE_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: 198 metadata->program_info_uri_length = btstack_min(ltv_len, LE_AUDIO_PROGRAM_INFO_URI_MAX_LENGTH); 199 memcpy(metadata->program_info_uri, &buffer[offset+1], metadata->program_info_uri_length); 200 metadata->metadata_mask |= (1 << ltv_type); 201 break; 202 case LE_AUDIO_METADATA_TYPE_EXTENDED_METADATA: 203 if (ltv_len < 2){ 204 break; 205 } 206 metadata->extended_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_EXTENDED_METADATA_MAX_LENGHT); 207 metadata->extended_metadata_type = little_endian_read_16(buffer, offset+1); 208 memcpy(metadata->extended_metadata, &buffer[offset+3], metadata->extended_metadata_length); 209 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_EXTENDED_METADATA_BIT_POSITION); 210 break; 211 case LE_AUDIO_METADATA_TYPE_VENDOR_SPECIFIC_METADATA: 212 if (ltv_len < 2){ 213 break; 214 } 215 metadata->vendor_specific_metadata_length = btstack_min(ltv_len - 2, LE_AUDIO_VENDOR_SPECIFIC_METADATA_MAX_LENGTH); 216 metadata->vendor_specific_company_id = little_endian_read_16(buffer, offset+1); 217 memcpy(metadata->vendor_specific_metadata, &buffer[offset+3], metadata->vendor_specific_metadata_length); 218 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_MAPPED_VENDOR_SPECIFIC_METADATA_BIT_POSITION); 219 break; 220 default: 221 metadata->metadata_mask |= (1 << LE_AUDIO_METADATA_TYPE_RFU); 222 break; 223 } 224 offset += ltv_len; 225 } 226 return offset; 227 } 228 229 uint16_t le_audio_copy_metadata_to_event_buffer(le_audio_metadata_t * metadata, uint8_t * event, uint16_t event_size){ 230 uint8_t pos = 0; 231 232 event[pos++] = (uint8_t)metadata->metadata_mask; 233 little_endian_store_16(event, pos, metadata->preferred_audio_contexts_mask); 234 pos += 2; 235 little_endian_store_16(event, pos, metadata->streaming_audio_contexts_mask); 236 pos += 2; 237 238 event[pos++] = metadata->program_info_length; 239 memcpy(&event[pos], &metadata->program_info[0], metadata->program_info_length); 240 pos += metadata->program_info_length; 241 242 little_endian_store_24(event, pos, metadata->language_code); 243 pos += 3; 244 245 event[pos++] = metadata->ccids_num; 246 memcpy(&event[pos], &metadata->ccids[0], metadata->ccids_num); 247 pos += metadata->ccids_num; 248 249 event[pos++] = (uint8_t)metadata->parental_rating; 250 251 event[pos++] = metadata->program_info_uri_length; 252 memcpy(&event[pos], &metadata->program_info_uri[0], metadata->program_info_uri_length); 253 pos += metadata->program_info_uri_length; 254 255 little_endian_store_16(event, pos, metadata->extended_metadata_type); 256 pos += 2; 257 258 event[pos++] = metadata->extended_metadata_length; 259 memcpy(&event[pos], &metadata->extended_metadata[0], metadata->extended_metadata_length); 260 pos += metadata->extended_metadata_length; 261 262 little_endian_store_16(event, pos, metadata->vendor_specific_company_id); 263 pos += 2; 264 265 event[pos++] = metadata->vendor_specific_metadata_length; 266 memcpy(&event[pos], &metadata->vendor_specific_metadata[0], metadata->vendor_specific_metadata_length); 267 pos += metadata->vendor_specific_metadata_length; 268 269 return pos; 270 } 271