/src/vlc/modules/codec/opus_header.c
Line | Count | Source |
1 | | /* Copyright (C)2012 Xiph.Org Foundation |
2 | | File: opus_header.c |
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 | | - Redistributions of source code must retain the above copyright |
9 | | notice, this list of conditions and the following disclaimer. |
10 | | |
11 | | - Redistributions in binary form must reproduce the above copyright |
12 | | notice, this list of conditions and the following disclaimer in the |
13 | | documentation and/or other materials provided with the distribution. |
14 | | |
15 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
16 | | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
17 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
18 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR |
19 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
20 | | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 | | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
22 | | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
23 | | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
24 | | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | | */ |
27 | | |
28 | | #ifdef HAVE_CONFIG_H |
29 | | # include "config.h" |
30 | | #endif |
31 | | |
32 | | #include "opus_header.h" |
33 | | #include <string.h> |
34 | | #include <stdio.h> |
35 | | #include <stdlib.h> |
36 | | |
37 | | #include <vlc_common.h> |
38 | | #include <vlc_codec.h> |
39 | | #include "../demux/xiph.h" |
40 | | |
41 | | /* Header contents: |
42 | | - "OpusHead" (64 bits) |
43 | | - version number (8 bits) |
44 | | - Channels C (8 bits) |
45 | | - Pre-skip (16 bits) |
46 | | - Sampling rate (32 bits) |
47 | | - Gain in dB (16 bits, S7.8) |
48 | | - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping, |
49 | | 2..254: reserved, 255: multistream with no mapping) |
50 | | |
51 | | - if (mapping != 0) |
52 | | - N = totel number of streams (8 bits) |
53 | | - M = number of paired streams (8 bits) |
54 | | - C times channel origin |
55 | | - if (C<2*M) |
56 | | - stream = byte/2 |
57 | | - if (byte&0x1 == 0) |
58 | | - left |
59 | | else |
60 | | - right |
61 | | - else |
62 | | - stream = byte-M |
63 | | */ |
64 | | |
65 | | typedef struct { |
66 | | unsigned char *data; |
67 | | int maxlen; |
68 | | int pos; |
69 | | } Packet; |
70 | | |
71 | | typedef struct { |
72 | | const unsigned char *data; |
73 | | int maxlen; |
74 | | int pos; |
75 | | } ROPacket; |
76 | | |
77 | | static int write_uint32(Packet *p, uint32_t val) |
78 | 20.2k | { |
79 | 20.2k | if (p->pos>p->maxlen-4) |
80 | 0 | return 0; |
81 | 20.2k | SetDWLE(&p->data[p->pos], val); |
82 | 20.2k | p->pos += 4; |
83 | 20.2k | return 1; |
84 | 20.2k | } |
85 | | |
86 | | static int write_uint16(Packet *p, uint16_t val) |
87 | 40.4k | { |
88 | 40.4k | if (p->pos>p->maxlen-2) |
89 | 0 | return 0; |
90 | 40.4k | SetWLE(&p->data[p->pos], val); |
91 | 40.4k | p->pos += 2; |
92 | 40.4k | return 1; |
93 | 40.4k | } |
94 | | |
95 | | static int write_chars(Packet *p, const unsigned char *str, int nb_chars) |
96 | 81.1k | { |
97 | 81.1k | if (p->pos>p->maxlen-nb_chars) |
98 | 0 | return 0; |
99 | 81.1k | memcpy(&p->data[p->pos], str, nb_chars); |
100 | 81.1k | p->pos += nb_chars; |
101 | 81.1k | return 1; |
102 | 81.1k | } |
103 | | |
104 | | static int read_uint32(ROPacket *p, uint32_t *val) |
105 | 24.7k | { |
106 | 24.7k | if (p->pos>p->maxlen-4) |
107 | 0 | return 0; |
108 | 24.7k | *val = GetDWLE(&p->data[p->pos]); |
109 | 24.7k | p->pos += 4; |
110 | 24.7k | return 1; |
111 | 24.7k | } |
112 | | |
113 | | static int read_uint16(ROPacket *p, uint16_t *val) |
114 | 49.4k | { |
115 | 49.4k | if (p->pos>p->maxlen-2) |
116 | 0 | return 0; |
117 | 49.4k | *val = GetWLE(&p->data[p->pos]); |
118 | 49.4k | p->pos += 2; |
119 | 49.4k | return 1; |
120 | 49.4k | } |
121 | | |
122 | | static int read_chars(ROPacket *p, unsigned char *str, int nb_chars) |
123 | 102k | { |
124 | 102k | if (p->pos>p->maxlen-nb_chars) |
125 | 3.12k | return 0; |
126 | 99.3k | memcpy(str, &p->data[p->pos], nb_chars); |
127 | 99.3k | p->pos += nb_chars; |
128 | 99.3k | return 1; |
129 | 102k | } |
130 | | |
131 | | int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h) |
132 | 24.8k | { |
133 | 24.8k | char str[9]; |
134 | 24.8k | ROPacket p; |
135 | 24.8k | unsigned char ch; |
136 | 24.8k | uint16_t shortval; |
137 | | |
138 | 24.8k | p.data = packet; |
139 | 24.8k | p.maxlen = len; |
140 | 24.8k | p.pos = 0; |
141 | 24.8k | str[8] = 0; |
142 | 24.8k | if (len<19)return 0; |
143 | 24.8k | if (!read_chars(&p, (unsigned char*)str, 8)) |
144 | 0 | return 0; |
145 | 24.8k | if (memcmp(str, "OpusHead", 8)!=0) |
146 | 0 | return 0; |
147 | | |
148 | 24.8k | if (!read_chars(&p, &ch, 1)) |
149 | 0 | return 0; |
150 | 24.8k | h->version = ch; |
151 | 24.8k | if((h->version&240) != 0) /* Only major version 0 supported. */ |
152 | 82 | return 0; |
153 | | |
154 | 24.7k | if (!read_chars(&p, &ch, 1)) |
155 | 0 | return 0; |
156 | 24.7k | h->channels = ch; |
157 | 24.7k | if (h->channels == 0) |
158 | 27 | return 0; |
159 | | |
160 | 24.7k | if (!read_uint16(&p, &shortval)) |
161 | 0 | return 0; |
162 | 24.7k | h->preskip = shortval; |
163 | | |
164 | 24.7k | if (!read_uint32(&p, &h->input_sample_rate)) |
165 | 0 | return 0; |
166 | | |
167 | 24.7k | if (!read_uint16(&p, &shortval)) |
168 | 0 | return 0; |
169 | 24.7k | h->gain = (short)shortval; |
170 | | |
171 | 24.7k | if (!read_chars(&p, &ch, 1)) |
172 | 0 | return 0; |
173 | 24.7k | h->channel_mapping = ch; |
174 | | |
175 | 24.7k | if(h->channel_mapping == 0) |
176 | 237 | { |
177 | 237 | if(h->channels>2) |
178 | 38 | return 0; |
179 | 199 | h->nb_streams = 1; |
180 | 199 | h->nb_coupled = h->channels>1; |
181 | 199 | h->stream_map[0]=0; |
182 | 199 | h->stream_map[1]=1; |
183 | 199 | } |
184 | 24.4k | else if(h->channel_mapping < 4) |
185 | 3.16k | { |
186 | 3.16k | if (!read_chars(&p, &ch, 1)) |
187 | 3.12k | return 0; |
188 | | |
189 | 41 | if (ch<1) |
190 | 0 | return 0; |
191 | 41 | h->nb_streams = ch; |
192 | | |
193 | 41 | if (!read_chars(&p, &ch, 1)) |
194 | 0 | return 0; |
195 | | |
196 | 41 | if (ch > h->nb_streams) |
197 | 3 | return 0; |
198 | 38 | h->nb_coupled = ch; |
199 | | |
200 | | /* Multi-stream support */ |
201 | 38 | if(h->channel_mapping <= 2) |
202 | 38 | { |
203 | 38 | if (h->nb_coupled + h->nb_streams > 255) |
204 | 2 | return 0; |
205 | 230 | for (int i=0;i<h->channels;i++) |
206 | 197 | { |
207 | 197 | if (!read_chars(&p, &h->stream_map[i], 1)) |
208 | 3 | return 0; |
209 | 194 | if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255) |
210 | 0 | return 0; |
211 | 194 | } |
212 | 36 | } |
213 | 0 | else /* Decoding Matrix */ |
214 | 0 | { |
215 | 0 | if (h->nb_coupled + h->nb_streams > 255) |
216 | 0 | return 0; |
217 | 0 | int matrix_entries = h->channels * (h->nb_streams + h->nb_coupled); |
218 | 0 | int matrix_size = len - p.pos; |
219 | 0 | if(matrix_size < matrix_entries * 2) |
220 | 0 | return 0; |
221 | 0 | h->dmatrix = malloc(matrix_size); |
222 | 0 | if(h->dmatrix == NULL) |
223 | 0 | return 0; |
224 | 0 | if(!read_chars(&p, h->dmatrix, matrix_size)) |
225 | 0 | { |
226 | 0 | free(h->dmatrix); |
227 | 0 | return 0; |
228 | 0 | } |
229 | 0 | h->dmatrix_size = matrix_size; |
230 | 0 | } |
231 | 38 | } |
232 | | |
233 | | /*For version 0/1 we know there won't be any more data |
234 | | so reject any that have data past the end.*/ |
235 | 21.5k | if ((h->version==0 || h->version==1) && p.pos != len) |
236 | 3 | return 0; |
237 | 21.5k | return 1; |
238 | 21.5k | } |
239 | | |
240 | | /* |
241 | | Comments will be stored in the Vorbis style. |
242 | | It is described in the "Structure" section of |
243 | | http://www.xiph.org/ogg/vorbis/doc/v-comment.html |
244 | | |
245 | | However, Opus and other non-vorbis formats omit the "framing_bit". |
246 | | |
247 | | The comment header is decoded as follows: |
248 | | 1) [vendor_length] = unsigned little endian 32 bits integer |
249 | | 2) [vendor_string] = UTF-8 vector as [vendor_length] octets |
250 | | 3) [user_comment_list_length] = unsigned little endian 32 bits integer |
251 | | 4) iterate [user_comment_list_length] times { |
252 | | 5) [length] = unsigned little endian 32 bits integer |
253 | | 6) this iteration's user comment = UTF-8 vector as [length] octets |
254 | | } |
255 | | 7) done. |
256 | | */ |
257 | | |
258 | | static char *comment_init(size_t *length, const char *vendor) |
259 | 20.2k | { |
260 | | /*The 'vendor' field should be the actual encoding library used.*/ |
261 | 20.2k | if (!vendor) |
262 | 0 | vendor = "unknown"; |
263 | 20.2k | size_t vendor_length = strlen(vendor); |
264 | | |
265 | 20.2k | size_t user_comment_list_length = 0; |
266 | 20.2k | size_t len = 8 + 4 + vendor_length + 4; |
267 | 20.2k | char *p = malloc(len); |
268 | 20.2k | if (p == NULL) |
269 | 0 | return NULL; |
270 | | |
271 | 20.2k | memcpy(p, "OpusTags", 8); |
272 | 20.2k | SetDWLE(p + 8, vendor_length); |
273 | 20.2k | memcpy(p + 12, vendor, vendor_length); |
274 | 20.2k | SetDWLE(p + 12 + vendor_length, user_comment_list_length); |
275 | | |
276 | 20.2k | *length = len; |
277 | 20.2k | return p; |
278 | 20.2k | } |
279 | | |
280 | | static int comment_add(char **comments, size_t *length, const char *tag, |
281 | | const char *val) |
282 | 20.2k | { |
283 | 20.2k | char *p = *comments; |
284 | 20.2k | uint32_t vendor_length = GetDWLE(p + 8); |
285 | 20.2k | size_t user_comment_list_length = GetDWLE(p + 8 + 4 + vendor_length); |
286 | 20.2k | size_t tag_len = (tag ? strlen(tag) : 0); |
287 | 20.2k | size_t val_len = strlen(val); |
288 | 20.2k | size_t len = (*length) + 4 + tag_len + val_len; |
289 | | |
290 | 20.2k | char *reaced = realloc(p, len); |
291 | 20.2k | if (reaced == NULL) |
292 | 0 | return 1; |
293 | 20.2k | p = reaced; |
294 | | |
295 | 20.2k | SetDWLE(p + *length, tag_len + val_len); /* length of comment */ |
296 | 20.2k | if (tag) memcpy(p + *length + 4, tag, tag_len); /* comment */ |
297 | 20.2k | memcpy(p + *length + 4 + tag_len, val, val_len); /* comment */ |
298 | 20.2k | SetDWLE(p + 8 + 4 + vendor_length, user_comment_list_length + 1); |
299 | 20.2k | *comments = p; |
300 | 20.2k | *length = len; |
301 | 20.2k | return 0; |
302 | 20.2k | } |
303 | | |
304 | | /* adds padding so that metadata can be updated without rewriting the whole file */ |
305 | | static int comment_pad(char **comments, size_t *length) |
306 | 20.2k | { |
307 | 20.2k | const unsigned padding = 512; /* default from opus-tools */ |
308 | | |
309 | 20.2k | if(SIZE_MAX - *length < padding + 255) |
310 | 0 | return 1; |
311 | | |
312 | 20.2k | char *p = *comments; |
313 | | /* Make sure there is at least "padding" worth of padding free, and |
314 | | round up to the maximum that fits in the current ogg segments. */ |
315 | 20.2k | size_t newlen = ((*length + padding) / 255 + 1) * 255 - 1; |
316 | 20.2k | char *reaced = realloc(p, newlen); |
317 | 20.2k | if (reaced == NULL) |
318 | 0 | return 1; |
319 | 20.2k | p = reaced; |
320 | | |
321 | 20.2k | memset(p + *length, 0, newlen - *length); |
322 | 20.2k | *comments = p; |
323 | 20.2k | *length = newlen; |
324 | 20.2k | return 0; |
325 | 20.2k | } |
326 | | |
327 | | void opus_prepare_header(unsigned channels, unsigned rate, OpusHeader *header) |
328 | 20.2k | { |
329 | 20.2k | header->version = 1; |
330 | 20.2k | header->channels = channels; |
331 | 20.2k | header->nb_streams = header->channels; |
332 | 20.2k | header->nb_coupled = 0; |
333 | 20.2k | header->input_sample_rate = rate; |
334 | 20.2k | header->gain = 0; // 0dB |
335 | 20.2k | header->channel_mapping = header->channels > 8 ? 255 : |
336 | 20.2k | header->channels > 2; |
337 | 20.2k | } |
338 | | |
339 | | static int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len) |
340 | 20.2k | { |
341 | 20.2k | Packet p; |
342 | 20.2k | unsigned char ch; |
343 | | |
344 | 20.2k | p.data = packet; |
345 | 20.2k | p.maxlen = len; |
346 | 20.2k | p.pos = 0; |
347 | 20.2k | if (len<19)return 0; |
348 | 20.2k | if (!write_chars(&p, (const unsigned char*)"OpusHead", 8)) |
349 | 0 | return 0; |
350 | | /* Version is 1 */ |
351 | 20.2k | ch = 1; |
352 | 20.2k | if (!write_chars(&p, &ch, 1)) |
353 | 0 | return 0; |
354 | | |
355 | 20.2k | ch = h->channels; |
356 | 20.2k | if (!write_chars(&p, &ch, 1)) |
357 | 0 | return 0; |
358 | | |
359 | 20.2k | if (!write_uint16(&p, h->preskip)) |
360 | 0 | return 0; |
361 | | |
362 | 20.2k | if (!write_uint32(&p, h->input_sample_rate)) |
363 | 0 | return 0; |
364 | | |
365 | 20.2k | if (!write_uint16(&p, h->gain)) |
366 | 0 | return 0; |
367 | | |
368 | 20.2k | ch = h->channel_mapping; |
369 | 20.2k | if (!write_chars(&p, &ch, 1)) |
370 | 0 | return 0; |
371 | | |
372 | 20.2k | if (h->channel_mapping == 1) |
373 | 33 | { |
374 | 33 | ch = h->nb_streams; |
375 | 33 | if (!write_chars(&p, &ch, 1)) |
376 | 0 | return 0; |
377 | | |
378 | 33 | ch = h->nb_coupled; |
379 | 33 | if (!write_chars(&p, &ch, 1)) |
380 | 0 | return 0; |
381 | | |
382 | | /* Multi-stream support */ |
383 | 227 | for (int i=0;i<h->channels;i++) |
384 | 194 | { |
385 | 194 | if (!write_chars(&p, &h->stream_map[i], 1)) |
386 | 0 | return 0; |
387 | 194 | } |
388 | 33 | } |
389 | | |
390 | 20.2k | return p.pos; |
391 | 20.2k | } |
392 | | |
393 | | int opus_write_header(uint8_t **p_extra, size_t *i_extra, OpusHeader *header, const char *vendor) |
394 | 20.2k | { |
395 | 20.2k | unsigned char header_data[100]; |
396 | 20.2k | const int packet_size = opus_header_to_packet(header, header_data, |
397 | 20.2k | sizeof(header_data)); |
398 | | |
399 | 20.2k | const unsigned char *data[2]; |
400 | 20.2k | size_t size[2]; |
401 | | |
402 | 20.2k | data[0] = header_data; |
403 | 20.2k | size[0] = packet_size; |
404 | | |
405 | 20.2k | size_t comments_length; |
406 | 20.2k | char *comments = comment_init(&comments_length, vendor); |
407 | 20.2k | if (!comments) |
408 | 0 | return 1; |
409 | 20.2k | if (comment_add(&comments, &comments_length, "ENCODER=", |
410 | 20.2k | "VLC media player")) |
411 | 0 | { |
412 | 0 | free(comments); |
413 | 0 | return 1; |
414 | 0 | } |
415 | | |
416 | 20.2k | if (comment_pad(&comments, &comments_length)) |
417 | 0 | { |
418 | 0 | free(comments); |
419 | 0 | return 1; |
420 | 0 | } |
421 | | |
422 | 20.2k | data[1] = (unsigned char *) comments; |
423 | 20.2k | size[1] = comments_length; |
424 | | |
425 | 20.2k | *i_extra = 0; |
426 | 20.2k | *p_extra = NULL; |
427 | | |
428 | 60.6k | for (unsigned i = 0; i < ARRAY_SIZE(data); ++i) |
429 | 40.4k | { |
430 | 40.4k | if (xiph_AppendHeaders(i_extra, (void **) p_extra, size[i], data[i])) |
431 | 0 | { |
432 | 0 | *i_extra = 0; |
433 | 0 | free(*p_extra); |
434 | 0 | *p_extra = NULL; |
435 | 0 | } |
436 | 40.4k | } |
437 | | |
438 | 20.2k | free(comments); |
439 | | |
440 | 20.2k | return 0; |
441 | 20.2k | } |
442 | | |
443 | | void opus_header_init(OpusHeader *h) |
444 | 45.2k | { |
445 | 45.2k | h->version = 0; |
446 | 45.2k | h->channels = 0; |
447 | 45.2k | h->preskip = 3840; /* default is 80 ms */ |
448 | 45.2k | h->input_sample_rate = 0; /* unknown */ |
449 | 45.2k | h->gain = 0; |
450 | 45.2k | h->channel_mapping = 255; /* unknown */ |
451 | 45.2k | h->nb_streams = 0; |
452 | 45.2k | h->nb_coupled = 0; |
453 | 45.2k | h->dmatrix_size = 0; |
454 | 45.2k | h->dmatrix = NULL; |
455 | 45.2k | } |
456 | | |
457 | | void opus_header_clean(OpusHeader *h) |
458 | 45.2k | { |
459 | 45.2k | free(h->dmatrix); |
460 | 45.2k | } |