contrib: cargo: use cargo/vendored-openssl if needed
[vlc.git] / modules / codec / opus_header.c
bloba19d9c73f75db86e2224798c02534109a182a7f7
1 /* Copyright (C)2012 Xiph.Org Foundation
2 File: opus_header.c
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
6 are met:
8 - Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
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.
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.
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include "opus_header.h"
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h>
37 #include <vlc_common.h>
38 #include <vlc_codec.h>
39 #include "../demux/xiph.h"
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)
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
65 typedef struct {
66 unsigned char *data;
67 int maxlen;
68 int pos;
69 } Packet;
71 typedef struct {
72 const unsigned char *data;
73 int maxlen;
74 int pos;
75 } ROPacket;
77 static int write_uint32(Packet *p, uint32_t val)
79 if (p->pos>p->maxlen-4)
80 return 0;
81 p->data[p->pos ] = (val ) & 0xFF;
82 p->data[p->pos+1] = (val>> 8) & 0xFF;
83 p->data[p->pos+2] = (val>>16) & 0xFF;
84 p->data[p->pos+3] = (val>>24) & 0xFF;
85 p->pos += 4;
86 return 1;
89 static int write_uint16(Packet *p, uint16_t val)
91 if (p->pos>p->maxlen-2)
92 return 0;
93 p->data[p->pos ] = (val ) & 0xFF;
94 p->data[p->pos+1] = (val>> 8) & 0xFF;
95 p->pos += 2;
96 return 1;
99 static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
101 if (p->pos>p->maxlen-nb_chars)
102 return 0;
103 for (int i=0;i<nb_chars;i++)
104 p->data[p->pos++] = str[i];
105 return 1;
108 static int read_uint32(ROPacket *p, uint32_t *val)
110 if (p->pos>p->maxlen-4)
111 return 0;
112 *val = (uint32_t)p->data[p->pos ];
113 *val |= (uint32_t)p->data[p->pos+1]<< 8;
114 *val |= (uint32_t)p->data[p->pos+2]<<16;
115 *val |= (uint32_t)p->data[p->pos+3]<<24;
116 p->pos += 4;
117 return 1;
120 static int read_uint16(ROPacket *p, uint16_t *val)
122 if (p->pos>p->maxlen-2)
123 return 0;
124 *val = (uint16_t)p->data[p->pos ];
125 *val |= (uint16_t)p->data[p->pos+1]<<8;
126 p->pos += 2;
127 return 1;
130 static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
132 if (p->pos>p->maxlen-nb_chars)
133 return 0;
134 for (int i=0;i<nb_chars;i++)
135 str[i] = p->data[p->pos++];
136 return 1;
139 int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
141 char str[9];
142 ROPacket p;
143 unsigned char ch;
144 uint16_t shortval;
146 p.data = packet;
147 p.maxlen = len;
148 p.pos = 0;
149 str[8] = 0;
150 if (len<19)return 0;
151 read_chars(&p, (unsigned char*)str, 8);
152 if (memcmp(str, "OpusHead", 8)!=0)
153 return 0;
155 if (!read_chars(&p, &ch, 1))
156 return 0;
157 h->version = ch;
158 if((h->version&240) != 0) /* Only major version 0 supported. */
159 return 0;
161 if (!read_chars(&p, &ch, 1))
162 return 0;
163 h->channels = ch;
164 if (h->channels == 0)
165 return 0;
167 if (!read_uint16(&p, &shortval))
168 return 0;
169 h->preskip = shortval;
171 if (!read_uint32(&p, &h->input_sample_rate))
172 return 0;
174 if (!read_uint16(&p, &shortval))
175 return 0;
176 h->gain = (short)shortval;
178 if (!read_chars(&p, &ch, 1))
179 return 0;
180 h->channel_mapping = ch;
182 if (h->channel_mapping != 0)
184 if (!read_chars(&p, &ch, 1))
185 return 0;
187 if (ch<1)
188 return 0;
189 h->nb_streams = ch;
191 if (!read_chars(&p, &ch, 1))
192 return 0;
194 if (ch>h->nb_streams || (ch+h->nb_streams)>255)
195 return 0;
196 h->nb_coupled = ch;
198 /* Multi-stream support */
199 for (int i=0;i<h->channels;i++)
201 if (!read_chars(&p, &h->stream_map[i], 1))
202 return 0;
203 if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
204 return 0;
206 } else {
207 if(h->channels>2)
208 return 0;
209 h->nb_streams = 1;
210 h->nb_coupled = h->channels>1;
211 h->stream_map[0]=0;
212 h->stream_map[1]=1;
214 /*For version 0/1 we know there won't be any more data
215 so reject any that have data past the end.*/
216 if ((h->version==0 || h->version==1) && p.pos != len)
217 return 0;
218 return 1;
222 Comments will be stored in the Vorbis style.
223 It is described in the "Structure" section of
224 http://www.xiph.org/ogg/vorbis/doc/v-comment.html
226 However, Opus and other non-vorbis formats omit the "framing_bit".
228 The comment header is decoded as follows:
229 1) [vendor_length] = unsigned little endian 32 bits integer
230 2) [vendor_string] = UTF-8 vector as [vendor_length] octets
231 3) [user_comment_list_length] = unsigned little endian 32 bits integer
232 4) iterate [user_comment_list_length] times {
233 5) [length] = unsigned little endian 32 bits integer
234 6) this iteration's user comment = UTF-8 vector as [length] octets
236 7) done.
239 static char *comment_init(size_t *length, const char *vendor)
241 /*The 'vendor' field should be the actual encoding library used.*/
242 if (!vendor)
243 vendor = "unknown";
244 size_t vendor_length = strlen(vendor);
246 size_t user_comment_list_length = 0;
247 size_t len = 8 + 4 + vendor_length + 4;
248 char *p = malloc(len);
249 if (p == NULL)
250 return NULL;
252 memcpy(p, "OpusTags", 8);
253 SetDWLE(p + 8, vendor_length);
254 memcpy(p + 12, vendor, vendor_length);
255 SetDWLE(p + 12 + vendor_length, user_comment_list_length);
257 *length = len;
258 return p;
261 static int comment_add(char **comments, size_t *length, const char *tag,
262 const char *val)
264 char *p = *comments;
265 uint32_t vendor_length = GetDWLE(p + 8);
266 size_t user_comment_list_length = GetDWLE(p + 8 + 4 + vendor_length);
267 size_t tag_len = (tag ? strlen(tag) : 0);
268 size_t val_len = strlen(val);
269 size_t len = (*length) + 4 + tag_len + val_len;
271 char *reaced = realloc(p, len);
272 if (reaced == NULL)
273 return 1;
274 p = reaced;
276 SetDWLE(p + *length, tag_len + val_len); /* length of comment */
277 if (tag) memcpy(p + *length + 4, tag, tag_len); /* comment */
278 memcpy(p + *length + 4 + tag_len, val, val_len); /* comment */
279 SetDWLE(p + 8 + 4 + vendor_length, user_comment_list_length + 1);
280 *comments = p;
281 *length = len;
282 return 0;
285 /* adds padding so that metadata can be updated without rewriting the whole file */
286 static int comment_pad(char **comments, size_t *length)
288 const unsigned padding = 512; /* default from opus-tools */
290 if(SIZE_MAX - *length < padding + 255)
291 return 1;
293 char *p = *comments;
294 /* Make sure there is at least "padding" worth of padding free, and
295 round up to the maximum that fits in the current ogg segments. */
296 size_t newlen = ((*length + padding) / 255 + 1) * 255 - 1;
297 char *reaced = realloc(p, newlen);
298 if (reaced == NULL)
299 return 1;
300 p = reaced;
302 memset(p + *length, 0, newlen - *length);
303 *comments = p;
304 *length = newlen;
305 return 0;
308 void opus_prepare_header(unsigned channels, unsigned rate, OpusHeader *header)
310 header->version = 1;
311 header->channels = channels;
312 header->nb_streams = header->channels;
313 header->nb_coupled = 0;
314 header->input_sample_rate = rate;
315 header->gain = 0; // 0dB
316 header->channel_mapping = header->channels > 8 ? 255 :
317 header->channels > 2;
320 static int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
322 Packet p;
323 unsigned char ch;
325 p.data = packet;
326 p.maxlen = len;
327 p.pos = 0;
328 if (len<19)return 0;
329 if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
330 return 0;
331 /* Version is 1 */
332 ch = 1;
333 if (!write_chars(&p, &ch, 1))
334 return 0;
336 ch = h->channels;
337 if (!write_chars(&p, &ch, 1))
338 return 0;
340 if (!write_uint16(&p, h->preskip))
341 return 0;
343 if (!write_uint32(&p, h->input_sample_rate))
344 return 0;
346 if (!write_uint16(&p, h->gain))
347 return 0;
349 ch = h->channel_mapping;
350 if (!write_chars(&p, &ch, 1))
351 return 0;
353 if (h->channel_mapping != 0)
355 ch = h->nb_streams;
356 if (!write_chars(&p, &ch, 1))
357 return 0;
359 ch = h->nb_coupled;
360 if (!write_chars(&p, &ch, 1))
361 return 0;
363 /* Multi-stream support */
364 for (int i=0;i<h->channels;i++)
366 if (!write_chars(&p, &h->stream_map[i], 1))
367 return 0;
371 return p.pos;
374 int opus_write_header(uint8_t **p_extra, int *i_extra, OpusHeader *header, const char *vendor)
376 unsigned char header_data[100];
377 const int packet_size = opus_header_to_packet(header, header_data,
378 sizeof(header_data));
380 const unsigned char *data[2];
381 size_t size[2];
383 data[0] = header_data;
384 size[0] = packet_size;
386 size_t comments_length;
387 char *comments = comment_init(&comments_length, vendor);
388 if (!comments)
389 return 1;
390 if (comment_add(&comments, &comments_length, "ENCODER=",
391 "VLC media player"))
393 free(comments);
394 return 1;
397 if (comment_pad(&comments, &comments_length))
399 free(comments);
400 return 1;
403 data[1] = (unsigned char *) comments;
404 size[1] = comments_length;
406 *i_extra = 0;
407 *p_extra = NULL;
409 for (unsigned i = 0; i < ARRAY_SIZE(data); ++i)
411 if (xiph_AppendHeaders(i_extra, (void **) p_extra, size[i], data[i]))
413 *i_extra = 0;
414 free(*p_extra);
415 *p_extra = NULL;
419 free(comments);
421 return 0;