| 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 | 10 | 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 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
|
10 | if (psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) |
| 47 | ✗ | return AVERROR_INVALIDDATA; | |
| 48 | |||
| 49 | 10 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
| 50 | 10 | st->codecpar->codec_id = AV_CODEC_ID_OPUS; | |
| 51 | |||
| 52 | 10 | channels = AV_RL8(packet + 9); | |
| 53 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
|
10 | if (st->codecpar->ch_layout.nb_channels && |
| 54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | 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 | 10 | st->codecpar->ch_layout.nb_channels = channels; | |
| 60 | |||
| 61 | 10 | priv->pre_skip = AV_RL16(packet + 10); | |
| 62 | 10 | st->codecpar->initial_padding = priv->pre_skip; | |
| 63 | 10 | 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 | 10 | ret = ff_alloc_extradata(st->codecpar, os->psize); | |
| 69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (ret < 0) |
| 70 | ✗ | return ret; | |
| 71 | |||
| 72 | 10 | memcpy(st->codecpar->extradata, packet, os->psize); | |
| 73 | |||
| 74 | 10 | st->codecpar->sample_rate = 48000; | |
| 75 | 10 | st->codecpar->seek_preroll = av_rescale(OPUS_SEEK_PREROLL_MS, | |
| 76 | 10 | st->codecpar->sample_rate, 1000); | |
| 77 | 10 | avpriv_set_pts_info(st, 64, 1, 48000); | |
| 78 | |||
| 79 | 10 | priv->need_comments = 1; | |
| 80 | |||
| 81 | 10 | return 1; | |
| 82 | } | ||
| 83 | |||
| 84 | 21 | static int opus_header(AVFormatContext *avf, int idx) | |
| 85 | { | ||
| 86 | 21 | struct ogg *ogg = avf->priv_data; | |
| 87 | 21 | struct ogg_stream *os = &ogg->streams[idx]; | |
| 88 | 21 | AVStream *st = avf->streams[idx]; | |
| 89 | 21 | struct oggopus_private *priv = os->private; | |
| 90 | 21 | uint8_t *packet = os->buf + os->pstart; | |
| 91 | |||
| 92 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
|
21 | if (!priv) { |
| 93 | 7 | priv = os->private = av_mallocz(sizeof(*priv)); | |
| 94 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (!priv) |
| 95 | ✗ | return AVERROR(ENOMEM); | |
| 96 | } | ||
| 97 | |||
| 98 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
|
21 | if (os->flags & OGG_FLAG_BOS) |
| 99 | 7 | return parse_opus_header(avf, st, os, priv, packet, os->psize); | |
| 100 | |||
| 101 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (priv->need_comments) { |
| 102 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
|
7 | if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) |
| 103 | ✗ | return AVERROR_INVALIDDATA; | |
| 104 | 7 | ff_vorbis_stream_comment(avf, st, packet + 8, os->psize - 8); | |
| 105 | 7 | priv->need_comments--; | |
| 106 | 7 | return 1; | |
| 107 | } | ||
| 108 | |||
| 109 | 7 | return 0; | |
| 110 | } | ||
| 111 | |||
| 112 | 5425 | static int opus_duration(uint8_t *src, int size) | |
| 113 | { | ||
| 114 | 5425 | unsigned nb_frames = 1; | |
| 115 | 5425 | unsigned toc = src[0]; | |
| 116 | 5425 | unsigned toc_config = toc >> 3; | |
| 117 | 5425 | unsigned toc_count = toc & 3; | |
| 118 |
2/2✓ Branch 0 taken 5302 times.
✓ Branch 1 taken 123 times.
|
5548 | unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : |
| 119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
|
123 | toc_config < 16 ? 480 << (toc_config & 1) : |
| 120 | 123 | 120 << (toc_config & 3); | |
| 121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5425 times.
|
5425 | 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 5425 times.
|
5425 | } else if (toc_count) { |
| 126 | ✗ | nb_frames = 2; | |
| 127 | } | ||
| 128 | |||
| 129 | 5425 | return frame_size * nb_frames; | |
| 130 | } | ||
| 131 | |||
| 132 | 331 | static int opus_packet(AVFormatContext *avf, int idx) | |
| 133 | { | ||
| 134 | 331 | struct ogg *ogg = avf->priv_data; | |
| 135 | 331 | struct ogg_stream *os = &ogg->streams[idx]; | |
| 136 | 331 | AVStream *st = avf->streams[idx]; | |
| 137 | 331 | struct oggopus_private *priv = os->private; | |
| 138 | 331 | uint8_t *packet = os->buf + os->pstart; | |
| 139 | int ret; | ||
| 140 | |||
| 141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 331 times.
|
331 | if (!os->psize) |
| 142 | ✗ | return AVERROR_INVALIDDATA; | |
| 143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 331 times.
|
331 | 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 129 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 126 times.
|
331 | if (os->psize > 8 && !memcmp(packet, "OpusHead", 8)) { |
| 149 | 3 | ret = parse_opus_header(avf, st, os, priv, packet, os->psize); | |
| 150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret < 0) |
| 151 | ✗ | return ret; | |
| 152 | |||
| 153 | 3 | return 1; | |
| 154 | } | ||
| 155 | |||
| 156 |
4/4✓ Branch 0 taken 126 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 123 times.
|
328 | if (os->psize > 8 && !memcmp(packet, "OpusTags", 8)) { |
| 157 | 3 | ret = ff_vorbis_update_metadata(avf, st, os->buf + os->pstart + 8, | |
| 158 | 3 | os->psize - 8); | |
| 159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret < 0) |
| 160 | ✗ | return ret; | |
| 161 | |||
| 162 | 3 | priv->need_comments = 0; | |
| 163 | 3 | return 1; | |
| 164 | } | ||
| 165 | |||
| 166 |
6/6✓ Branch 0 taken 308 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 306 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 200 times.
✓ Branch 5 taken 123 times.
|
325 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { |
| 167 | int seg, d; | ||
| 168 | int duration; | ||
| 169 | 200 | uint8_t *last_pkt = os->buf + os->pstart; | |
| 170 | 200 | uint8_t *next_pkt = last_pkt; | |
| 171 | |||
| 172 | 200 | duration = 0; | |
| 173 | 200 | seg = os->segp; | |
| 174 | 200 | d = opus_duration(last_pkt, os->psize); | |
| 175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
|
200 | if (d < 0) { |
| 176 | ✗ | os->pflags |= AV_PKT_FLAG_CORRUPT; | |
| 177 | ✗ | return 0; | |
| 178 | } | ||
| 179 | 200 | duration += d; | |
| 180 | 200 | last_pkt = next_pkt = next_pkt + os->psize; | |
| 181 |
2/2✓ Branch 0 taken 4900 times.
✓ Branch 1 taken 200 times.
|
5100 | for (; seg < os->nsegs; seg++) { |
| 182 | 4900 | next_pkt += os->segments[seg]; | |
| 183 |
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) { |
| 184 | 4900 | d = opus_duration(last_pkt, next_pkt - last_pkt); | |
| 185 |
1/2✓ Branch 0 taken 4900 times.
✗ Branch 1 not taken.
|
4900 | if (d > 0) |
| 186 | 4900 | duration += d; | |
| 187 | 4900 | last_pkt = next_pkt; | |
| 188 | } | ||
| 189 | } | ||
| 190 | 200 | os->lastpts = | |
| 191 | 200 | os->lastdts = os->granule - duration; | |
| 192 | } | ||
| 193 | |||
| 194 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 325 times.
|
325 | if ((ret = opus_duration(packet, os->psize)) < 0) |
| 195 | ✗ | return ret; | |
| 196 | |||
| 197 | 325 | os->pduration = ret; | |
| 198 |
2/2✓ Branch 0 taken 215 times.
✓ Branch 1 taken 110 times.
|
325 | if (os->lastpts != AV_NOPTS_VALUE) { |
| 199 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 208 times.
|
215 | if (st->start_time == AV_NOPTS_VALUE) |
| 200 | 7 | st->start_time = os->lastpts; | |
| 201 | 215 | priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; | |
| 202 | } | ||
| 203 | |||
| 204 | 325 | priv->cur_dts += os->pduration; | |
| 205 |
2/2✓ Branch 0 taken 123 times.
✓ Branch 1 taken 202 times.
|
325 | if ((os->flags & OGG_FLAG_EOS)) { |
| 206 | 123 | int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; | |
| 207 | 123 | skip = FFMIN(skip, os->pduration); | |
| 208 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 116 times.
|
123 | if (skip > 0) { |
| 209 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | os->pduration = skip < os->pduration ? os->pduration - skip : 1; |
| 210 | 7 | os->end_trimming = skip; | |
| 211 | 7 | av_log(avf, AV_LOG_DEBUG, | |
| 212 | "Last packet was truncated to %d due to end trimming.\n", | ||
| 213 | os->pduration); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | 325 | return 0; | |
| 218 | } | ||
| 219 | |||
| 220 | const struct ogg_codec ff_opus_codec = { | ||
| 221 | .name = "Opus", | ||
| 222 | .magic = "OpusHead", | ||
| 223 | .magicsize = 8, | ||
| 224 | .header = opus_header, | ||
| 225 | .packet = opus_packet, | ||
| 226 | .nb_header = 1, | ||
| 227 | }; | ||
| 228 |