]> git.sesse.net Git - vlc/blob - modules/codec/opus_header.c
Opus decoder.
[vlc] / modules / codec / opus_header.c
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
36 /* Header contents:
37   - "OpusHead" (64 bits)
38   - version number (8 bits)
39   - Channels C (8 bits)
40   - Pre-skip (16 bits)
41   - Sampling rate (32 bits)
42   - Gain in dB (16 bits, S7.8)
43   - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
44              2..254: reserved, 255: multistream with no mapping)
45
46   - if (mapping != 0)
47      - N = totel number of streams (8 bits)
48      - M = number of paired streams (8 bits)
49      - C times channel origin
50           - if (C<2*M)
51              - stream = byte/2
52              - if (byte&0x1 == 0)
53                  - left
54                else
55                  - right
56           - else
57              - stream = byte-M
58 */
59
60 typedef struct {
61     unsigned char *data;
62     int maxlen;
63     int pos;
64 } Packet;
65
66 typedef struct {
67     const unsigned char *data;
68     int maxlen;
69     int pos;
70 } ROPacket;
71
72 static int write_uint32(Packet *p, ogg_uint32_t val)
73 {
74     if (p->pos>p->maxlen-4)
75         return 0;
76     p->data[p->pos  ] = (val    ) & 0xFF;
77     p->data[p->pos+1] = (val>> 8) & 0xFF;
78     p->data[p->pos+2] = (val>>16) & 0xFF;
79     p->data[p->pos+3] = (val>>24) & 0xFF;
80     p->pos += 4;
81     return 1;
82 }
83
84 static int write_uint16(Packet *p, ogg_uint16_t val)
85 {
86     if (p->pos>p->maxlen-2)
87         return 0;
88     p->data[p->pos  ] = (val    ) & 0xFF;
89     p->data[p->pos+1] = (val>> 8) & 0xFF;
90     p->pos += 2;
91     return 1;
92 }
93
94 static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
95 {
96     if (p->pos>p->maxlen-nb_chars)
97         return 0;
98     for (int i=0;i<nb_chars;i++)
99         p->data[p->pos++] = str[i];
100     return 1;
101 }
102
103 static int read_uint32(ROPacket *p, ogg_uint32_t *val)
104 {
105     if (p->pos>p->maxlen-4)
106         return 0;
107     *val =  (ogg_uint32_t)p->data[p->pos  ];
108     *val |= (ogg_uint32_t)p->data[p->pos+1]<< 8;
109     *val |= (ogg_uint32_t)p->data[p->pos+2]<<16;
110     *val |= (ogg_uint32_t)p->data[p->pos+3]<<24;
111     p->pos += 4;
112     return 1;
113 }
114
115 static int read_uint16(ROPacket *p, ogg_uint16_t *val)
116 {
117     if (p->pos>p->maxlen-2)
118         return 0;
119     *val =  (ogg_uint16_t)p->data[p->pos  ];
120     *val |= (ogg_uint16_t)p->data[p->pos+1]<<8;
121     p->pos += 2;
122     return 1;
123 }
124
125 static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
126 {
127     if (p->pos>p->maxlen-nb_chars)
128         return 0;
129     for (int i=0;i<nb_chars;i++)
130         str[i] = p->data[p->pos++];
131     return 1;
132 }
133
134 int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
135 {
136     char str[9];
137     ROPacket p;
138     unsigned char ch;
139     ogg_uint16_t shortval;
140
141     p.data = packet;
142     p.maxlen = len;
143     p.pos = 0;
144     str[8] = 0;
145     if (len<19)return 0;
146     read_chars(&p, (unsigned char*)str, 8);
147     if (memcmp(str, "OpusHead", 8)!=0)
148         return 0;
149
150     if (!read_chars(&p, &ch, 1))
151         return 0;
152     h->version = ch;
153     if((h->version&240) != 0) /* Only major version 0 supported. */
154         return 0;
155
156     if (!read_chars(&p, &ch, 1))
157         return 0;
158     h->channels = ch;
159     if (h->channels == 0)
160         return 0;
161
162     if (!read_uint16(&p, &shortval))
163         return 0;
164     h->preskip = shortval;
165
166     if (!read_uint32(&p, &h->input_sample_rate))
167         return 0;
168
169     if (!read_uint16(&p, &shortval))
170         return 0;
171     h->gain = (short)shortval;
172
173     if (!read_chars(&p, &ch, 1))
174         return 0;
175     h->channel_mapping = ch;
176
177     if (h->channel_mapping != 0)
178     {
179         if (!read_chars(&p, &ch, 1))
180             return 0;
181
182         if (ch<1)
183             return 0;
184         h->nb_streams = ch;
185
186         if (!read_chars(&p, &ch, 1))
187             return 0;
188
189         if (ch>h->nb_streams || (ch+h->nb_streams)>255)
190             return 0;
191         h->nb_coupled = ch;
192
193         /* Multi-stream support */
194         for (int i=0;i<h->channels;i++)
195         {
196             if (!read_chars(&p, &h->stream_map[i], 1))
197                 return 0;
198             if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
199                 return 0;
200         }
201     } else {
202         if(h->channels>2)
203             return 0;
204         h->nb_streams = 1;
205         h->nb_coupled = h->channels>1;
206         h->stream_map[0]=0;
207         h->stream_map[1]=1;
208     }
209     /*For version 0/1 we know there won't be any more data
210       so reject any that have data past the end.*/
211     if ((h->version==0 || h->version==1) && p.pos != len)
212         return 0;
213     return 1;
214 }
215
216 int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
217 {
218     Packet p;
219     unsigned char ch;
220
221     p.data = packet;
222     p.maxlen = len;
223     p.pos = 0;
224     if (len<19)return 0;
225     if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
226         return 0;
227     /* Version is 1 */
228     ch = 1;
229     if (!write_chars(&p, &ch, 1))
230         return 0;
231
232     ch = h->channels;
233     if (!write_chars(&p, &ch, 1))
234         return 0;
235
236     if (!write_uint16(&p, h->preskip))
237         return 0;
238
239     if (!write_uint32(&p, h->input_sample_rate))
240         return 0;
241
242     if (!write_uint16(&p, h->gain))
243         return 0;
244
245     ch = h->channel_mapping;
246     if (!write_chars(&p, &ch, 1))
247         return 0;
248
249     if (h->channel_mapping != 0)
250     {
251         ch = h->nb_streams;
252         if (!write_chars(&p, &ch, 1))
253             return 0;
254
255         ch = h->nb_coupled;
256         if (!write_chars(&p, &ch, 1))
257             return 0;
258
259         /* Multi-stream support */
260         for (int i=0;i<h->channels;i++)
261         {
262             if (!write_chars(&p, &h->stream_map[i], 1))
263                 return 0;
264         }
265     }
266
267     return p.pos;
268 }