FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/rtpdec_opus.c
Date: 2026-10-19 20:11:44
Exec Total Coverage
Lines: 0 57 0.0%
Functions: 0 6 0.0%
Branches: 0 30 0.0%

Line Branch Exec 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 {
31 ✗ unsigned nb_frames = 1;
32 ✗ unsigned toc = src[0];
33 ✗ unsigned toc_config = toc >> 3;
34 ✗ unsigned toc_count = toc & 3;
35 ✗ unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) :
36 ✗ toc_config < 16 ? 480 << (toc_config & 1) :
37 ✗ 120 << (toc_config & 3);
38 ✗ if (toc_count == 3) {
39 ✗ if (size<2)
40 ✗ return AVERROR_INVALIDDATA;
41 ✗ nb_frames = src[1] & 0x3F;
42 ✗ } else if (toc_count) {
43 ✗ nb_frames = 2;
44 }
45
46 ✗ return frame_size * nb_frames;
47 }
48
49 ✗ static int opus_write_extradata(AVCodecParameters *codecpar)
50 {
51 uint8_t *bs;
52 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 ✗ if (codecpar->ch_layout.nb_channels > 2) {
59 ✗ return AVERROR_INVALIDDATA;
60 }
61
62 ✗ ret = ff_alloc_extradata(codecpar, 19);
63 ✗ if (ret < 0)
64 ✗ return ret;
65
66 ✗ bs = (uint8_t *)codecpar->extradata;
67
68 /* Opus magic */
69 ✗ bytestream_put_buffer(&bs, "OpusHead", 8);
70 /* Version */
71 ✗ bytestream_put_byte (&bs, 0x1);
72 /* Channel count */
73 ✗ bytestream_put_byte (&bs, codecpar->ch_layout.nb_channels);
74 /* Pre skip */
75 ✗ bytestream_put_le16 (&bs, 0);
76 /* Input sample rate */
77 ✗ bytestream_put_le32 (&bs, 48000);
78 /* Output gain */
79 ✗ bytestream_put_le16 (&bs, 0x0);
80 /* Mapping family */
81 ✗ bytestream_put_byte (&bs, 0x0);
82
83 ✗ return 0;
84 }
85
86 ✗ static int opus_init(AVFormatContext *s, int st_index, PayloadContext *priv_data)
87 {
88 ✗ return opus_write_extradata(s->streams[st_index]->codecpar);
89 }
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 {
96 int rv;
97 int duration;
98
99 ✗ if (!len)
100 ✗ return AVERROR_INVALIDDATA;
101
102 ✗ if ((rv = av_new_packet(pkt, len)) < 0)
103 ✗ return rv;
104
105 ✗ memcpy(pkt->data, buf, len);
106 ✗ pkt->stream_index = st->index;
107
108 ✗ duration = opus_duration(buf, len);
109 ✗ if (duration != AVERROR_INVALIDDATA) {
110 ✗ pkt->duration = duration;
111 }
112
113 ✗ return 0;
114 }
115
116 ✗ static int parse_fmtp(AVFormatContext *s,
117 AVStream *stream, PayloadContext *data,
118 const char *attr, const char *value)
119 {
120 ✗ if (!strcmp(attr, "sprop-maxcapturerate")) {
121 ✗ int rate = atoi(value);
122 ✗ if (rate < 8000 || rate > 48000) {
123 ✗ av_log(s, AV_LOG_ERROR,
124 "fmtp field 'sprop-maxcapturerate' must be between 8000 to 48000 (provided value: %s)",
125 value);
126 ✗ return AVERROR_INVALIDDATA;
127 }
128 ✗ stream->codecpar->sample_rate = rate;
129 }
130 ✗ return 0;
131 }
132
133 ✗ static int opus_parse_sdp_line(AVFormatContext *s, int st_index,
134 PayloadContext *data, const char *line)
135 {
136 const char *p;
137
138 ✗ if (st_index < 0)
139 ✗ return 0;
140
141 ✗ if (av_strstart(line, "fmtp:", &p)) {
142 ✗ return ff_parse_fmtp(s, s->streams[st_index], data, p, parse_fmtp);
143 }
144 ✗ return 0;
145 }
146
147 const RTPDynamicProtocolHandler ff_opus_dynamic_handler = {
148 .enc_name = "opus",
149 .codec_type = AVMEDIA_TYPE_AUDIO,
150 .codec_id = AV_CODEC_ID_OPUS,
151 .parse_packet = opus_parse_packet,
152 .init = opus_init,
153 .parse_sdp_a_line = opus_parse_sdp_line,
154 };
155