FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/rtpdec_opus.c
Date: 2025-06-05 01:40:37
Exec Total Coverage
Lines: 0 55 0.0%
Functions: 0 6 0.0%
Branches: 0 28 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 ((rv = av_new_packet(pkt, len)) < 0)
100 return rv;
101
102 memcpy(pkt->data, buf, len);
103 pkt->stream_index = st->index;
104
105 duration = opus_duration(buf, len);
106 if (duration != AVERROR_INVALIDDATA) {
107 pkt->duration = duration;
108 }
109
110 return 0;
111 }
112
113 static int parse_fmtp(AVFormatContext *s,
114 AVStream *stream, PayloadContext *data,
115 const char *attr, const char *value)
116 {
117 if (!strcmp(attr, "sprop-maxcapturerate")) {
118 int rate = atoi(value);
119 if (rate < 8000 || rate > 48000) {
120 av_log(s, AV_LOG_ERROR,
121 "fmtp field 'sprop-maxcapturerate' must be between 8000 to 48000 (provided value: %s)",
122 value);
123 return AVERROR_INVALIDDATA;
124 }
125 stream->codecpar->sample_rate = rate;
126 }
127 return 0;
128 }
129
130 static int opus_parse_sdp_line(AVFormatContext *s, int st_index,
131 PayloadContext *data, const char *line)
132 {
133 const char *p;
134
135 if (st_index < 0)
136 return 0;
137
138 if (av_strstart(line, "fmtp:", &p)) {
139 return ff_parse_fmtp(s, s->streams[st_index], data, p, parse_fmtp);
140 }
141 return 0;
142 }
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 };
152