Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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 "avformat.h" | |
26 | #include "internal.h" | |
27 | #include "oggdec.h" | |
28 | ||
29 | struct oggopus_private { | |
30 | int need_comments; | |
31 | unsigned pre_skip; | |
32 | int64_t cur_dts; | |
33 | }; | |
34 | ||
35 | #define OPUS_SEEK_PREROLL_MS 80 | |
36 | #define OPUS_HEAD_SIZE 19 | |
37 | ||
38 | static int opus_header(AVFormatContext *avf, int idx) | |
39 | { | |
40 | struct ogg *ogg = avf->priv_data; | |
41 | struct ogg_stream *os = &ogg->streams[idx]; | |
42 | AVStream *st = avf->streams[idx]; | |
43 | struct oggopus_private *priv = os->private; | |
44 | uint8_t *packet = os->buf + os->pstart; | |
45 | ||
46 | if (!priv) { | |
47 | priv = os->private = av_mallocz(sizeof(*priv)); | |
48 | if (!priv) | |
49 | return AVERROR(ENOMEM); | |
50 | } | |
51 | ||
52 | if (os->flags & OGG_FLAG_BOS) { | |
53 | if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) | |
54 | return AVERROR_INVALIDDATA; | |
55 | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; | |
56 | st->codec->codec_id = AV_CODEC_ID_OPUS; | |
57 | st->codec->channels = AV_RL8 (packet + 9); | |
58 | priv->pre_skip = AV_RL16(packet + 10); | |
59 | st->codec->delay = priv->pre_skip; | |
60 | /*orig_sample_rate = AV_RL32(packet + 12);*/ | |
61 | /*gain = AV_RL16(packet + 16);*/ | |
62 | /*channel_map = AV_RL8 (packet + 18);*/ | |
63 | ||
64 | if (ff_alloc_extradata(st->codec, os->psize)) | |
65 | return AVERROR(ENOMEM); | |
66 | ||
67 | memcpy(st->codec->extradata, packet, os->psize); | |
68 | ||
69 | st->codec->sample_rate = 48000; | |
70 | av_codec_set_seek_preroll(st->codec, | |
71 | av_rescale(OPUS_SEEK_PREROLL_MS, | |
72 | st->codec->sample_rate, 1000)); | |
73 | avpriv_set_pts_info(st, 64, 1, 48000); | |
74 | priv->need_comments = 1; | |
75 | return 1; | |
76 | } | |
77 | ||
78 | if (priv->need_comments) { | |
79 | if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) | |
80 | return AVERROR_INVALIDDATA; | |
81 | ff_vorbis_stream_comment(avf, st, packet + 8, os->psize - 8); | |
82 | priv->need_comments--; | |
83 | return 1; | |
84 | } | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | static int opus_duration(uint8_t *src, int size) | |
90 | { | |
91 | unsigned nb_frames = 1; | |
92 | unsigned toc = src[0]; | |
93 | unsigned toc_config = toc >> 3; | |
94 | unsigned toc_count = toc & 3; | |
95 | unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : | |
96 | toc_config < 16 ? 480 << (toc_config & 1) : | |
97 | 120 << (toc_config & 3); | |
98 | if (toc_count == 3) { | |
99 | if (size<2) | |
100 | return AVERROR_INVALIDDATA; | |
101 | nb_frames = src[1] & 0x3F; | |
102 | } else if (toc_count) { | |
103 | nb_frames = 2; | |
104 | } | |
105 | ||
106 | return frame_size * nb_frames; | |
107 | } | |
108 | ||
109 | static int opus_packet(AVFormatContext *avf, int idx) | |
110 | { | |
111 | struct ogg *ogg = avf->priv_data; | |
112 | struct ogg_stream *os = &ogg->streams[idx]; | |
113 | AVStream *st = avf->streams[idx]; | |
114 | struct oggopus_private *priv = os->private; | |
115 | uint8_t *packet = os->buf + os->pstart; | |
116 | int ret; | |
117 | ||
118 | if (!os->psize) | |
119 | return AVERROR_INVALIDDATA; | |
120 | ||
121 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { | |
122 | int seg, d; | |
123 | int duration; | |
124 | uint8_t *last_pkt = os->buf + os->pstart; | |
125 | uint8_t *next_pkt = last_pkt; | |
126 | ||
127 | duration = 0; | |
128 | seg = os->segp; | |
129 | d = opus_duration(last_pkt, os->psize); | |
130 | if (d < 0) { | |
131 | os->pflags |= AV_PKT_FLAG_CORRUPT; | |
132 | return 0; | |
133 | } | |
134 | duration += d; | |
135 | last_pkt = next_pkt = next_pkt + os->psize; | |
136 | for (; seg < os->nsegs; seg++) { | |
137 | next_pkt += os->segments[seg]; | |
138 | if (os->segments[seg] < 255 && next_pkt != last_pkt) { | |
139 | int d = opus_duration(last_pkt, next_pkt - last_pkt); | |
140 | if (d > 0) | |
141 | duration += d; | |
142 | last_pkt = next_pkt; | |
143 | } | |
144 | } | |
145 | os->lastpts = | |
146 | os->lastdts = os->granule - duration; | |
147 | } | |
148 | ||
149 | if ((ret = opus_duration(packet, os->psize)) < 0) | |
150 | return ret; | |
151 | ||
152 | os->pduration = ret; | |
153 | if (os->lastpts != AV_NOPTS_VALUE) { | |
154 | if (st->start_time == AV_NOPTS_VALUE) | |
155 | st->start_time = os->lastpts; | |
156 | priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; | |
157 | } | |
158 | ||
159 | priv->cur_dts += os->pduration; | |
160 | if ((os->flags & OGG_FLAG_EOS)) { | |
161 | int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; | |
162 | skip = FFMIN(skip, os->pduration); | |
163 | if (skip > 0) { | |
164 | os->pduration = skip < os->pduration ? os->pduration - skip : 1; | |
165 | os->end_trimming = skip; | |
166 | av_log(avf, AV_LOG_DEBUG, | |
167 | "Last packet was truncated to %d due to end trimming.\n", | |
168 | os->pduration); | |
169 | } | |
170 | } | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | const struct ogg_codec ff_opus_codec = { | |
176 | .name = "Opus", | |
177 | .magic = "OpusHead", | |
178 | .magicsize = 8, | |
179 | .header = opus_header, | |
180 | .packet = opus_packet, | |
181 | .nb_header = 1, | |
182 | }; |