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