/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 | 148 | { |
79 | 148 | if (p->pos>p->maxlen-4) |
80 | 0 | return 0; |
81 | 148 | SetDWLE(&p->data[p->pos], val); |
82 | 148 | p->pos += 4; |
83 | 148 | return 1; |
84 | 148 | } |
85 | | |
86 | | static int write_uint16(Packet *p, uint16_t val) |
87 | 296 | { |
88 | 296 | if (p->pos>p->maxlen-2) |
89 | 0 | return 0; |
90 | 296 | SetWLE(&p->data[p->pos], val); |
91 | 296 | p->pos += 2; |
92 | 296 | return 1; |
93 | 296 | } |
94 | | |
95 | | static int write_chars(Packet *p, const unsigned char *str, int nb_chars) |
96 | 598 | { |
97 | 598 | if (p->pos>p->maxlen-nb_chars) |
98 | 0 | return 0; |
99 | 598 | memcpy(&p->data[p->pos], str, nb_chars); |
100 | 598 | p->pos += nb_chars; |
101 | 598 | return 1; |
102 | 598 | } |
103 | | |
104 | | static int read_uint32(ROPacket *p, uint32_t *val) |
105 | 610 | { |
106 | 610 | if (p->pos>p->maxlen-4) |
107 | 0 | return 0; |
108 | 610 | *val = GetDWLE(&p->data[p->pos]); |
109 | 610 | p->pos += 4; |
110 | 610 | return 1; |
111 | 610 | } |
112 | | |
113 | | static int read_uint16(ROPacket *p, uint16_t *val) |
114 | 1.22k | { |
115 | 1.22k | if (p->pos>p->maxlen-2) |
116 | 0 | return 0; |
117 | 1.22k | *val = GetWLE(&p->data[p->pos]); |
118 | 1.22k | p->pos += 2; |
119 | 1.22k | return 1; |
120 | 1.22k | } |
121 | | |
122 | | static int read_chars(ROPacket *p, unsigned char *str, int nb_chars) |
123 | 2.87k | { |
124 | 2.87k | if (p->pos>p->maxlen-nb_chars) |
125 | 425 | return 0; |
126 | 2.44k | memcpy(str, &p->data[p->pos], nb_chars); |
127 | 2.44k | p->pos += nb_chars; |
128 | 2.44k | return 1; |
129 | 2.87k | } |
130 | | |
131 | | int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h) |
132 | 610 | { |
133 | 610 | char str[9]; |
134 | 610 | ROPacket p; |
135 | 610 | unsigned char ch; |
136 | 610 | uint16_t shortval; |
137 | | |
138 | 610 | p.data = packet; |
139 | 610 | p.maxlen = len; |
140 | 610 | p.pos = 0; |
141 | 610 | str[8] = 0; |
142 | 610 | if (len<19)return 0; |
143 | 610 | if (!read_chars(&p, (unsigned char*)str, 8)) |
144 | 0 | return 0; |
145 | 610 | if (memcmp(str, "OpusHead", 8)!=0) |
146 | 0 | return 0; |
147 | | |
148 | 610 | if (!read_chars(&p, &ch, 1)) |
149 | 0 | return 0; |
150 | 610 | h->version = ch; |
151 | 610 | if((h->version&240) != 0) /* Only major version 0 supported. */ |
152 | 0 | return 0; |
153 | | |
154 | 610 | if (!read_chars(&p, &ch, 1)) |
155 | 0 | return 0; |
156 | 610 | h->channels = ch; |
157 | 610 | if (h->channels == 0) |
158 | 0 | return 0; |
159 | | |
160 | 610 | if (!read_uint16(&p, &shortval)) |
161 | 0 | return 0; |
162 | 610 | h->preskip = shortval; |
163 | | |
164 | 610 | if (!read_uint32(&p, &h->input_sample_rate)) |
165 | 0 | return 0; |
166 | | |
167 | 610 | if (!read_uint16(&p, &shortval)) |
168 | 0 | return 0; |
169 | 610 | h->gain = (short)shortval; |
170 | | |
171 | 610 | if (!read_chars(&p, &ch, 1)) |
172 | 0 | return 0; |
173 | 610 | h->channel_mapping = ch; |
174 | | |
175 | 610 | if(h->channel_mapping == 0) |
176 | 51 | { |
177 | 51 | if(h->channels>2) |
178 | 0 | return 0; |
179 | 51 | h->nb_streams = 1; |
180 | 51 | h->nb_coupled = h->channels>1; |
181 | 51 | h->stream_map[0]=0; |
182 | 51 | h->stream_map[1]=1; |
183 | 51 | } |
184 | 559 | else if(h->channel_mapping < 4) |
185 | 426 | { |
186 | 426 | if (!read_chars(&p, &ch, 1)) |
187 | 425 | return 0; |
188 | | |
189 | 1 | if (ch<1) |
190 | 0 | return 0; |
191 | 1 | h->nb_streams = ch; |
192 | | |
193 | 1 | if (!read_chars(&p, &ch, 1)) |
194 | 0 | return 0; |
195 | | |
196 | 1 | if (ch > h->nb_streams) |
197 | 0 | return 0; |
198 | 1 | h->nb_coupled = ch; |
199 | | |
200 | | /* Multi-stream support */ |
201 | 1 | if(h->channel_mapping <= 2) |
202 | 1 | { |
203 | 1 | if (h->nb_coupled + h->nb_streams > 255) |
204 | 0 | return 0; |
205 | 5 | for (int i=0;i<h->channels;i++) |
206 | 4 | { |
207 | 4 | if (!read_chars(&p, &h->stream_map[i], 1)) |
208 | 0 | return 0; |
209 | 4 | if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255) |
210 | 0 | return 0; |
211 | 4 | } |
212 | 1 | } |
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 | 1 | } |
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 | 185 | if ((h->version==0 || h->version==1) && p.pos != len) |
236 | 0 | return 0; |
237 | 185 | return 1; |
238 | 185 | } |
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 | 148 | { |
260 | | /*The 'vendor' field should be the actual encoding library used.*/ |
261 | 148 | if (!vendor) |
262 | 0 | vendor = "unknown"; |
263 | 148 | size_t vendor_length = strlen(vendor); |
264 | | |
265 | 148 | size_t user_comment_list_length = 0; |
266 | 148 | size_t len = 8 + 4 + vendor_length + 4; |
267 | 148 | char *p = malloc(len); |
268 | 148 | if (p == NULL) |
269 | 0 | return NULL; |
270 | | |
271 | 148 | memcpy(p, "OpusTags", 8); |
272 | 148 | SetDWLE(p + 8, vendor_length); |
273 | 148 | memcpy(p + 12, vendor, vendor_length); |
274 | 148 | SetDWLE(p + 12 + vendor_length, user_comment_list_length); |
275 | | |
276 | 148 | *length = len; |
277 | 148 | return p; |
278 | 148 | } |
279 | | |
280 | | static int comment_add(char **comments, size_t *length, const char *tag, |
281 | | const char *val) |
282 | 148 | { |
283 | 148 | char *p = *comments; |
284 | 148 | uint32_t vendor_length = GetDWLE(p + 8); |
285 | 148 | size_t user_comment_list_length = GetDWLE(p + 8 + 4 + vendor_length); |
286 | 148 | size_t tag_len = (tag ? strlen(tag) : 0); |
287 | 148 | size_t val_len = strlen(val); |
288 | 148 | size_t len = (*length) + 4 + tag_len + val_len; |
289 | | |
290 | 148 | char *reaced = realloc(p, len); |
291 | 148 | if (reaced == NULL) |
292 | 0 | return 1; |
293 | 148 | p = reaced; |
294 | | |
295 | 148 | SetDWLE(p + *length, tag_len + val_len); /* length of comment */ |
296 | 148 | if (tag) memcpy(p + *length + 4, tag, tag_len); /* comment */ |
297 | 148 | memcpy(p + *length + 4 + tag_len, val, val_len); /* comment */ |
298 | 148 | SetDWLE(p + 8 + 4 + vendor_length, user_comment_list_length + 1); |
299 | 148 | *comments = p; |
300 | 148 | *length = len; |
301 | 148 | return 0; |
302 | 148 | } |
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 | 148 | { |
307 | 148 | const unsigned padding = 512; /* default from opus-tools */ |
308 | | |
309 | 148 | if(SIZE_MAX - *length < padding + 255) |
310 | 0 | return 1; |
311 | | |
312 | 148 | 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 | 148 | size_t newlen = ((*length + padding) / 255 + 1) * 255 - 1; |
316 | 148 | char *reaced = realloc(p, newlen); |
317 | 148 | if (reaced == NULL) |
318 | 0 | return 1; |
319 | 148 | p = reaced; |
320 | | |
321 | 148 | memset(p + *length, 0, newlen - *length); |
322 | 148 | *comments = p; |
323 | 148 | *length = newlen; |
324 | 148 | return 0; |
325 | 148 | } |
326 | | |
327 | | void opus_prepare_header(unsigned channels, unsigned rate, OpusHeader *header) |
328 | 148 | { |
329 | 148 | header->version = 1; |
330 | 148 | header->channels = channels; |
331 | 148 | header->nb_streams = header->channels; |
332 | 148 | header->nb_coupled = 0; |
333 | 148 | header->input_sample_rate = rate; |
334 | 148 | header->gain = 0; // 0dB |
335 | 148 | header->channel_mapping = header->channels > 8 ? 255 : |
336 | 148 | header->channels > 2; |
337 | 148 | } |
338 | | |
339 | | static int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len) |
340 | 148 | { |
341 | 148 | Packet p; |
342 | 148 | unsigned char ch; |
343 | | |
344 | 148 | p.data = packet; |
345 | 148 | p.maxlen = len; |
346 | 148 | p.pos = 0; |
347 | 148 | if (len<19)return 0; |
348 | 148 | if (!write_chars(&p, (const unsigned char*)"OpusHead", 8)) |
349 | 0 | return 0; |
350 | | /* Version is 1 */ |
351 | 148 | ch = 1; |
352 | 148 | if (!write_chars(&p, &ch, 1)) |
353 | 0 | return 0; |
354 | | |
355 | 148 | ch = h->channels; |
356 | 148 | if (!write_chars(&p, &ch, 1)) |
357 | 0 | return 0; |
358 | | |
359 | 148 | if (!write_uint16(&p, h->preskip)) |
360 | 0 | return 0; |
361 | | |
362 | 148 | if (!write_uint32(&p, h->input_sample_rate)) |
363 | 0 | return 0; |
364 | | |
365 | 148 | if (!write_uint16(&p, h->gain)) |
366 | 0 | return 0; |
367 | | |
368 | 148 | ch = h->channel_mapping; |
369 | 148 | if (!write_chars(&p, &ch, 1)) |
370 | 0 | return 0; |
371 | | |
372 | 148 | if (h->channel_mapping == 1) |
373 | 1 | { |
374 | 1 | ch = h->nb_streams; |
375 | 1 | if (!write_chars(&p, &ch, 1)) |
376 | 0 | return 0; |
377 | | |
378 | 1 | ch = h->nb_coupled; |
379 | 1 | if (!write_chars(&p, &ch, 1)) |
380 | 0 | return 0; |
381 | | |
382 | | /* Multi-stream support */ |
383 | 5 | for (int i=0;i<h->channels;i++) |
384 | 4 | { |
385 | 4 | if (!write_chars(&p, &h->stream_map[i], 1)) |
386 | 0 | return 0; |
387 | 4 | } |
388 | 1 | } |
389 | | |
390 | 148 | return p.pos; |
391 | 148 | } |
392 | | |
393 | | int opus_write_header(uint8_t **p_extra, size_t *i_extra, OpusHeader *header, const char *vendor) |
394 | 148 | { |
395 | 148 | unsigned char header_data[100]; |
396 | 148 | const int packet_size = opus_header_to_packet(header, header_data, |
397 | 148 | sizeof(header_data)); |
398 | | |
399 | 148 | const unsigned char *data[2]; |
400 | 148 | size_t size[2]; |
401 | | |
402 | 148 | data[0] = header_data; |
403 | 148 | size[0] = packet_size; |
404 | | |
405 | 148 | size_t comments_length; |
406 | 148 | char *comments = comment_init(&comments_length, vendor); |
407 | 148 | if (!comments) |
408 | 0 | return 1; |
409 | 148 | if (comment_add(&comments, &comments_length, "ENCODER=", |
410 | 148 | "VLC media player")) |
411 | 0 | { |
412 | 0 | free(comments); |
413 | 0 | return 1; |
414 | 0 | } |
415 | | |
416 | 148 | if (comment_pad(&comments, &comments_length)) |
417 | 0 | { |
418 | 0 | free(comments); |
419 | 0 | return 1; |
420 | 0 | } |
421 | | |
422 | 148 | data[1] = (unsigned char *) comments; |
423 | 148 | size[1] = comments_length; |
424 | | |
425 | 148 | *i_extra = 0; |
426 | 148 | *p_extra = NULL; |
427 | | |
428 | 444 | for (unsigned i = 0; i < ARRAY_SIZE(data); ++i) |
429 | 296 | { |
430 | 296 | 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 | 296 | } |
437 | | |
438 | 148 | free(comments); |
439 | | |
440 | 148 | return 0; |
441 | 148 | } |
442 | | |
443 | | void opus_header_init(OpusHeader *h) |
444 | 788 | { |
445 | 788 | h->version = 0; |
446 | 788 | h->channels = 0; |
447 | 788 | h->preskip = 3840; /* default is 80 ms */ |
448 | 788 | h->input_sample_rate = 0; /* unknown */ |
449 | 788 | h->gain = 0; |
450 | 788 | h->channel_mapping = 255; /* unknown */ |
451 | 788 | h->nb_streams = 0; |
452 | 788 | h->nb_coupled = 0; |
453 | 788 | h->dmatrix_size = 0; |
454 | 788 | h->dmatrix = NULL; |
455 | 788 | } |
456 | | |
457 | | void opus_header_clean(OpusHeader *h) |
458 | 788 | { |
459 | 788 | free(h->dmatrix); |
460 | 788 | } |