Coverage Report

Created: 2026-02-14 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/rtpdec_opus.c
Line
Count
Source
1
/*
2
 * RTP Depacketization of Opus, RFC 7587
3
 * Copyright (c) 2025 Jonathan Baudanza <jon@jonb.org>
4
 * Copyright (c) 2022 Erik Linge
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#include "libavcodec/bytestream.h"
24
#include "libavutil/mem.h"
25
#include "libavutil/avstring.h"
26
#include "rtpdec_formats.h"
27
#include "internal.h"
28
29
static int opus_duration(const uint8_t *src, int size)
30
0
{
31
0
    unsigned nb_frames  = 1;
32
0
    unsigned toc        = src[0];
33
0
    unsigned toc_config = toc >> 3;
34
0
    unsigned toc_count  = toc & 3;
35
0
    unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) :
36
0
                          toc_config < 16 ? 480 << (toc_config & 1) :
37
0
                                            120 << (toc_config & 3);
38
0
    if (toc_count == 3) {
39
0
        if (size<2)
40
0
            return AVERROR_INVALIDDATA;
41
0
        nb_frames = src[1] & 0x3F;
42
0
    } else if (toc_count) {
43
0
        nb_frames = 2;
44
0
    }
45
46
0
    return frame_size * nb_frames;
47
0
}
48
49
static int opus_write_extradata(AVCodecParameters *codecpar)
50
0
{
51
0
    uint8_t *bs;
52
0
    int ret;
53
54
    /* This function writes an extradata with a channel mapping family of 0.
55
     * This mapping family only supports mono and stereo layouts. And RFC7587
56
     * specifies that the number of channels in the SDP must be 2.
57
     */
58
0
    if (codecpar->ch_layout.nb_channels > 2) {
59
0
        return AVERROR_INVALIDDATA;
60
0
    }
61
62
0
    ret = ff_alloc_extradata(codecpar, 19);
63
0
    if (ret < 0)
64
0
        return ret;
65
66
0
    bs = (uint8_t *)codecpar->extradata;
67
68
    /* Opus magic */
69
0
    bytestream_put_buffer(&bs, "OpusHead", 8);
70
    /* Version */
71
0
    bytestream_put_byte  (&bs, 0x1);
72
    /* Channel count */
73
0
    bytestream_put_byte  (&bs, codecpar->ch_layout.nb_channels);
74
    /* Pre skip */
75
0
    bytestream_put_le16  (&bs, 0);
76
    /* Input sample rate */
77
0
    bytestream_put_le32  (&bs, 48000);
78
    /* Output gain */
79
0
    bytestream_put_le16  (&bs, 0x0);
80
    /* Mapping family */
81
0
    bytestream_put_byte  (&bs, 0x0);
82
83
0
    return 0;
84
0
}
85
86
static int opus_init(AVFormatContext *s, int st_index, PayloadContext *priv_data)
87
0
{
88
0
    return opus_write_extradata(s->streams[st_index]->codecpar);
89
0
}
90
91
static int opus_parse_packet(AVFormatContext *ctx, PayloadContext *data,
92
                            AVStream *st, AVPacket *pkt, uint32_t *timestamp,
93
                            const uint8_t *buf, int len, uint16_t seq,
94
                            int flags)
95
0
{
96
0
    int rv;
97
0
    int duration;
98
99
0
    if ((rv = av_new_packet(pkt, len)) < 0)
100
0
        return rv;
101
102
0
    memcpy(pkt->data, buf, len);
103
0
    pkt->stream_index = st->index;
104
105
0
    duration = opus_duration(buf, len);
106
0
    if (duration != AVERROR_INVALIDDATA) {
107
0
        pkt->duration = duration;
108
0
    }
109
110
0
    return 0;
111
0
}
112
113
static int parse_fmtp(AVFormatContext *s,
114
                      AVStream *stream, PayloadContext *data,
115
                      const char *attr, const char *value)
116
0
{
117
0
    if (!strcmp(attr, "sprop-maxcapturerate")) {
118
0
        int rate = atoi(value);
119
0
        if (rate < 8000 || rate > 48000) {
120
0
            av_log(s, AV_LOG_ERROR,
121
0
                   "fmtp field 'sprop-maxcapturerate' must be between 8000 to 48000 (provided value: %s)",
122
0
                   value);
123
0
            return AVERROR_INVALIDDATA;
124
0
        }
125
0
        stream->codecpar->sample_rate = rate;
126
0
    }
127
0
    return 0;
128
0
}
129
130
static int opus_parse_sdp_line(AVFormatContext *s, int st_index,
131
                               PayloadContext *data, const char *line)
132
0
{
133
0
    const char *p;
134
135
0
    if (st_index < 0)
136
0
        return 0;
137
138
0
    if (av_strstart(line, "fmtp:", &p)) {
139
0
        return ff_parse_fmtp(s, s->streams[st_index], data, p, parse_fmtp);
140
0
    }
141
0
    return 0;
142
0
}
143
144
const RTPDynamicProtocolHandler ff_opus_dynamic_handler = {
145
    .enc_name     = "opus",
146
    .codec_type   = AVMEDIA_TYPE_AUDIO,
147
    .codec_id     = AV_CODEC_ID_OPUS,
148
    .parse_packet = opus_parse_packet,
149
    .init         = opus_init,
150
    .parse_sdp_a_line = opus_parse_sdp_line,
151
};