Coverage Report

Created: 2026-06-30 07:56

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