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 | 12 | static int opus_header(AVFormatContext *avf, int idx) | |
40 | { | ||
41 | 12 | struct ogg *ogg = avf->priv_data; | |
42 | 12 | struct ogg_stream *os = &ogg->streams[idx]; | |
43 | 12 | AVStream *st = avf->streams[idx]; | |
44 | 12 | struct oggopus_private *priv = os->private; | |
45 | 12 | uint8_t *packet = os->buf + os->pstart; | |
46 | int ret; | ||
47 | |||
48 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
|
12 | if (!priv) { |
49 | 4 | priv = os->private = av_mallocz(sizeof(*priv)); | |
50 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!priv) |
51 | ✗ | return AVERROR(ENOMEM); | |
52 | } | ||
53 | |||
54 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
|
12 | if (os->flags & OGG_FLAG_BOS) { |
55 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) |
56 | ✗ | return AVERROR_INVALIDDATA; | |
57 | 4 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
58 | 4 | st->codecpar->codec_id = AV_CODEC_ID_OPUS; | |
59 | 4 | st->codecpar->ch_layout.nb_channels = AV_RL8(packet + 9); | |
60 | |||
61 | 4 | priv->pre_skip = AV_RL16(packet + 10); | |
62 | 4 | st->codecpar->initial_padding = priv->pre_skip; | |
63 | 4 | 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 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if ((ret = ff_alloc_extradata(st->codecpar, os->psize)) < 0) |
69 | ✗ | return ret; | |
70 | |||
71 | 4 | memcpy(st->codecpar->extradata, packet, os->psize); | |
72 | |||
73 | 4 | st->codecpar->sample_rate = 48000; | |
74 | 4 | st->codecpar->seek_preroll = av_rescale(OPUS_SEEK_PREROLL_MS, | |
75 | 4 | st->codecpar->sample_rate, 1000); | |
76 | 4 | avpriv_set_pts_info(st, 64, 1, 48000); | |
77 | 4 | priv->need_comments = 1; | |
78 | 4 | return 1; | |
79 | } | ||
80 | |||
81 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | if (priv->need_comments) { |
82 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) |
83 | ✗ | return AVERROR_INVALIDDATA; | |
84 | 4 | ff_vorbis_stream_comment(avf, st, packet + 8, os->psize - 8); | |
85 | 4 | priv->need_comments--; | |
86 | 4 | return 1; | |
87 | } | ||
88 | |||
89 | 4 | return 0; | |
90 | } | ||
91 | |||
92 | 5386 | static int opus_duration(uint8_t *src, int size) | |
93 | { | ||
94 | 5386 | unsigned nb_frames = 1; | |
95 | 5386 | unsigned toc = src[0]; | |
96 | 5386 | unsigned toc_config = toc >> 3; | |
97 | 5386 | unsigned toc_count = toc & 3; | |
98 |
2/2✓ Branch 0 taken 5302 times.
✓ Branch 1 taken 84 times.
|
5470 | unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : |
99 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
|
84 | toc_config < 16 ? 480 << (toc_config & 1) : |
100 | 84 | 120 << (toc_config & 3); | |
101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5386 times.
|
5386 | if (toc_count == 3) { |
102 | ✗ | if (size<2) | |
103 | ✗ | return AVERROR_INVALIDDATA; | |
104 | ✗ | nb_frames = src[1] & 0x3F; | |
105 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5386 times.
|
5386 | } else if (toc_count) { |
106 | ✗ | nb_frames = 2; | |
107 | } | ||
108 | |||
109 | 5386 | return frame_size * nb_frames; | |
110 | } | ||
111 | |||
112 | 286 | static int opus_packet(AVFormatContext *avf, int idx) | |
113 | { | ||
114 | 286 | struct ogg *ogg = avf->priv_data; | |
115 | 286 | struct ogg_stream *os = &ogg->streams[idx]; | |
116 | 286 | AVStream *st = avf->streams[idx]; | |
117 | 286 | struct oggopus_private *priv = os->private; | |
118 | 286 | uint8_t *packet = os->buf + os->pstart; | |
119 | int ret; | ||
120 | |||
121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 286 times.
|
286 | if (!os->psize) |
122 | ✗ | return AVERROR_INVALIDDATA; | |
123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 286 times.
|
286 | if (os->granule > (1LL << 62)) { |
124 | ✗ | av_log(avf, AV_LOG_ERROR, "Unsupported huge granule pos %"PRId64 "\n", os->granule); | |
125 | ✗ | return AVERROR_INVALIDDATA; | |
126 | } | ||
127 | |||
128 |
6/6✓ Branch 0 taken 278 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 276 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 200 times.
✓ Branch 5 taken 84 times.
|
286 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { |
129 | int seg, d; | ||
130 | int duration; | ||
131 | 200 | uint8_t *last_pkt = os->buf + os->pstart; | |
132 | 200 | uint8_t *next_pkt = last_pkt; | |
133 | |||
134 | 200 | duration = 0; | |
135 | 200 | seg = os->segp; | |
136 | 200 | d = opus_duration(last_pkt, os->psize); | |
137 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
|
200 | if (d < 0) { |
138 | ✗ | os->pflags |= AV_PKT_FLAG_CORRUPT; | |
139 | ✗ | return 0; | |
140 | } | ||
141 | 200 | duration += d; | |
142 | 200 | last_pkt = next_pkt = next_pkt + os->psize; | |
143 |
2/2✓ Branch 0 taken 4900 times.
✓ Branch 1 taken 200 times.
|
5100 | for (; seg < os->nsegs; seg++) { |
144 | 4900 | next_pkt += os->segments[seg]; | |
145 |
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) { |
146 | 4900 | int d = opus_duration(last_pkt, next_pkt - last_pkt); | |
147 |
1/2✓ Branch 0 taken 4900 times.
✗ Branch 1 not taken.
|
4900 | if (d > 0) |
148 | 4900 | duration += d; | |
149 | 4900 | last_pkt = next_pkt; | |
150 | } | ||
151 | } | ||
152 | 200 | os->lastpts = | |
153 | 200 | os->lastdts = os->granule - duration; | |
154 | } | ||
155 | |||
156 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 286 times.
|
286 | if ((ret = opus_duration(packet, os->psize)) < 0) |
157 | ✗ | return ret; | |
158 | |||
159 | 286 | os->pduration = ret; | |
160 |
2/2✓ Branch 0 taken 206 times.
✓ Branch 1 taken 80 times.
|
286 | if (os->lastpts != AV_NOPTS_VALUE) { |
161 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 202 times.
|
206 | if (st->start_time == AV_NOPTS_VALUE) |
162 | 4 | st->start_time = os->lastpts; | |
163 | 206 | priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; | |
164 | } | ||
165 | |||
166 | 286 | priv->cur_dts += os->pduration; | |
167 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 202 times.
|
286 | if ((os->flags & OGG_FLAG_EOS)) { |
168 | 84 | int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; | |
169 | 84 | skip = FFMIN(skip, os->pduration); | |
170 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 82 times.
|
84 | if (skip > 0) { |
171 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | os->pduration = skip < os->pduration ? os->pduration - skip : 1; |
172 | 2 | os->end_trimming = skip; | |
173 | 2 | av_log(avf, AV_LOG_DEBUG, | |
174 | "Last packet was truncated to %d due to end trimming.\n", | ||
175 | os->pduration); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | 286 | return 0; | |
180 | } | ||
181 | |||
182 | const struct ogg_codec ff_opus_codec = { | ||
183 | .name = "Opus", | ||
184 | .magic = "OpusHead", | ||
185 | .magicsize = 8, | ||
186 | .header = opus_header, | ||
187 | .packet = opus_packet, | ||
188 | .nb_header = 1, | ||
189 | }; | ||
190 |