Coverage Report

Created: 2026-05-30 08:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}