Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Opus parser for Ogg | ||
3 | * Copyright (c) 2012 Nicolas George | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include <string.h> | ||
23 | |||
24 | #include "libavutil/intreadwrite.h" | ||
25 | #include "libavutil/mem.h" | ||
26 | #include "avformat.h" | ||
27 | #include "internal.h" | ||
28 | #include "oggdec.h" | ||
29 | |||
30 | struct oggopus_private { | ||
31 | int need_comments; | ||
32 | unsigned pre_skip; | ||
33 | int64_t cur_dts; | ||
34 | }; | ||
35 | |||
36 | #define OPUS_SEEK_PREROLL_MS 80 | ||
37 | #define OPUS_HEAD_SIZE 19 | ||
38 | |||
39 | 6 | static int parse_opus_header(AVFormatContext *avf, AVStream *st, struct ogg_stream *os, | |
40 | struct oggopus_private *priv, uint8_t *packet, | ||
41 | size_t psize) | ||
42 | { | ||
43 | int channels; | ||
44 | int ret; | ||
45 | |||
46 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) |
47 | ✗ | return AVERROR_INVALIDDATA; | |
48 | |||
49 | 6 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
50 | 6 | st->codecpar->codec_id = AV_CODEC_ID_OPUS; | |
51 | |||
52 | 6 | channels = AV_RL8(packet + 9); | |
53 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
|
6 | if (st->codecpar->ch_layout.nb_channels && |
54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | channels != st->codecpar->ch_layout.nb_channels) { |
55 | ✗ | av_log(avf, AV_LOG_ERROR, "Channel change is not supported\n"); | |
56 | ✗ | return AVERROR_PATCHWELCOME; | |
57 | } | ||
58 | |||
59 | 6 | st->codecpar->ch_layout.nb_channels = channels; | |
60 | |||
61 | 6 | priv->pre_skip = AV_RL16(packet + 10); | |
62 | 6 | st->codecpar->initial_padding = priv->pre_skip; | |
63 | 6 | os->start_trimming = priv->pre_skip; | |
64 | /*orig_sample_rate = AV_RL32(packet + 12);*/ | ||
65 | /*gain = AV_RL16(packet + 16);*/ | ||
66 | /*channel_map = AV_RL8 (packet + 18);*/ | ||
67 | |||
68 | 6 | ret = ff_alloc_extradata(st->codecpar, os->psize); | |
69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret < 0) |
70 | ✗ | return ret; | |
71 | |||
72 | 6 | memcpy(st->codecpar->extradata, packet, os->psize); | |
73 | |||
74 | 6 | st->codecpar->sample_rate = 48000; | |
75 | 6 | st->codecpar->seek_preroll = av_rescale(OPUS_SEEK_PREROLL_MS, | |
76 | 6 | st->codecpar->sample_rate, 1000); | |
77 | 6 | avpriv_set_pts_info(st, 64, 1, 48000); | |
78 | |||
79 | 6 | priv->need_comments = 1; | |
80 | |||
81 | 6 | return 1; | |
82 | } | ||
83 | |||
84 | 15 | static int opus_header(AVFormatContext *avf, int idx) | |
85 | { | ||
86 | 15 | struct ogg *ogg = avf->priv_data; | |
87 | 15 | struct ogg_stream *os = &ogg->streams[idx]; | |
88 | 15 | AVStream *st = avf->streams[idx]; | |
89 | 15 | struct oggopus_private *priv = os->private; | |
90 | 15 | uint8_t *packet = os->buf + os->pstart; | |
91 | |||
92 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
|
15 | if (!priv) { |
93 | 5 | priv = os->private = av_mallocz(sizeof(*priv)); | |
94 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (!priv) |
95 | ✗ | return AVERROR(ENOMEM); | |
96 | } | ||
97 | |||
98 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
|
15 | if (os->flags & OGG_FLAG_BOS) |
99 | 5 | return parse_opus_header(avf, st, os, priv, packet, os->psize); | |
100 | |||
101 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | if (priv->need_comments) { |
102 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
5 | if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) |
103 | ✗ | return AVERROR_INVALIDDATA; | |
104 | 5 | ff_vorbis_stream_comment(avf, st, packet + 8, os->psize - 8); | |
105 | 5 | priv->need_comments--; | |
106 | 5 | return 1; | |
107 | } | ||
108 | |||
109 | 5 | return 0; | |
110 | } | ||
111 | |||
112 | 5399 | static int opus_duration(uint8_t *src, int size) | |
113 | { | ||
114 | 5399 | unsigned nb_frames = 1; | |
115 | 5399 | unsigned toc = src[0]; | |
116 | 5399 | unsigned toc_config = toc >> 3; | |
117 | 5399 | unsigned toc_count = toc & 3; | |
118 |
2/2✓ Branch 0 taken 5302 times.
✓ Branch 1 taken 97 times.
|
5496 | unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : |
119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | toc_config < 16 ? 480 << (toc_config & 1) : |
120 | 97 | 120 << (toc_config & 3); | |
121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5399 times.
|
5399 | if (toc_count == 3) { |
122 | ✗ | if (size<2) | |
123 | ✗ | return AVERROR_INVALIDDATA; | |
124 | ✗ | nb_frames = src[1] & 0x3F; | |
125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5399 times.
|
5399 | } else if (toc_count) { |
126 | ✗ | nb_frames = 2; | |
127 | } | ||
128 | |||
129 | 5399 | return frame_size * nb_frames; | |
130 | } | ||
131 | |||
132 | 301 | static int opus_packet(AVFormatContext *avf, int idx) | |
133 | { | ||
134 | 301 | struct ogg *ogg = avf->priv_data; | |
135 | 301 | struct ogg_stream *os = &ogg->streams[idx]; | |
136 | 301 | AVStream *st = avf->streams[idx]; | |
137 | 301 | struct oggopus_private *priv = os->private; | |
138 | 301 | uint8_t *packet = os->buf + os->pstart; | |
139 | int ret; | ||
140 | |||
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 301 times.
|
301 | if (!os->psize) |
142 | ✗ | return AVERROR_INVALIDDATA; | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 301 times.
|
301 | if (os->granule > (1LL << 62)) { |
144 | ✗ | av_log(avf, AV_LOG_ERROR, "Unsupported huge granule pos %"PRId64 "\n", os->granule); | |
145 | ✗ | return AVERROR_INVALIDDATA; | |
146 | } | ||
147 | |||
148 |
4/4✓ Branch 0 taken 99 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 98 times.
|
301 | if (os->psize > 8 && !memcmp(packet, "OpusHead", 8)) { |
149 | 1 | ret = parse_opus_header(avf, st, os, priv, packet, os->psize); | |
150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (ret < 0) |
151 | ✗ | return ret; | |
152 | |||
153 | 1 | return 1; | |
154 | } | ||
155 | |||
156 |
4/4✓ Branch 0 taken 98 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 97 times.
|
300 | if (os->psize > 8 && !memcmp(packet, "OpusTags", 8)) { |
157 | 1 | priv->need_comments = 0; | |
158 | 1 | return 1; | |
159 | } | ||
160 | |||
161 |
6/6✓ Branch 0 taken 288 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 286 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 200 times.
✓ Branch 5 taken 97 times.
|
299 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { |
162 | int seg, d; | ||
163 | int duration; | ||
164 | 200 | uint8_t *last_pkt = os->buf + os->pstart; | |
165 | 200 | uint8_t *next_pkt = last_pkt; | |
166 | |||
167 | 200 | duration = 0; | |
168 | 200 | seg = os->segp; | |
169 | 200 | d = opus_duration(last_pkt, os->psize); | |
170 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
|
200 | if (d < 0) { |
171 | ✗ | os->pflags |= AV_PKT_FLAG_CORRUPT; | |
172 | ✗ | return 0; | |
173 | } | ||
174 | 200 | duration += d; | |
175 | 200 | last_pkt = next_pkt = next_pkt + os->psize; | |
176 |
2/2✓ Branch 0 taken 4900 times.
✓ Branch 1 taken 200 times.
|
5100 | for (; seg < os->nsegs; seg++) { |
177 | 4900 | next_pkt += os->segments[seg]; | |
178 |
2/4✓ Branch 0 taken 4900 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4900 times.
✗ Branch 3 not taken.
|
4900 | if (os->segments[seg] < 255 && next_pkt != last_pkt) { |
179 | 4900 | int d = opus_duration(last_pkt, next_pkt - last_pkt); | |
180 |
1/2✓ Branch 0 taken 4900 times.
✗ Branch 1 not taken.
|
4900 | if (d > 0) |
181 | 4900 | duration += d; | |
182 | 4900 | last_pkt = next_pkt; | |
183 | } | ||
184 | } | ||
185 | 200 | os->lastpts = | |
186 | 200 | os->lastdts = os->granule - duration; | |
187 | } | ||
188 | |||
189 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
|
299 | if ((ret = opus_duration(packet, os->psize)) < 0) |
190 | ✗ | return ret; | |
191 | |||
192 | 299 | os->pduration = ret; | |
193 |
2/2✓ Branch 0 taken 209 times.
✓ Branch 1 taken 90 times.
|
299 | if (os->lastpts != AV_NOPTS_VALUE) { |
194 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 204 times.
|
209 | if (st->start_time == AV_NOPTS_VALUE) |
195 | 5 | st->start_time = os->lastpts; | |
196 | 209 | priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; | |
197 | } | ||
198 | |||
199 | 299 | priv->cur_dts += os->pduration; | |
200 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 202 times.
|
299 | if ((os->flags & OGG_FLAG_EOS)) { |
201 | 97 | int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; | |
202 | 97 | skip = FFMIN(skip, os->pduration); | |
203 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 93 times.
|
97 | if (skip > 0) { |
204 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | os->pduration = skip < os->pduration ? os->pduration - skip : 1; |
205 | 4 | os->end_trimming = skip; | |
206 | 4 | av_log(avf, AV_LOG_DEBUG, | |
207 | "Last packet was truncated to %d due to end trimming.\n", | ||
208 | os->pduration); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | 299 | return 0; | |
213 | } | ||
214 | |||
215 | const struct ogg_codec ff_opus_codec = { | ||
216 | .name = "Opus", | ||
217 | .magic = "OpusHead", | ||
218 | .magicsize = 8, | ||
219 | .header = opus_header, | ||
220 | .packet = opus_packet, | ||
221 | .nb_header = 1, | ||
222 | }; | ||
223 |