xref: /btstack/src/le-audio/le_audio_util.c (revision c824d78c0a34df89b57d535abafcc7dacf30bb06)
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