Coverage Report

Created: 2026-08-14 07:16

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