blob: a373b41b4cac4222af544e59e531cc5814ef9789
1 | /* |
2 | * Ogg Daala parser |
3 | * Copyright (C) 2015 Rostislav Pehlivanov <atomnuker gmail com> |
4 | * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara gmail com> |
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 <stdlib.h> |
24 | #include "libavcodec/bytestream.h" |
25 | #include "avformat.h" |
26 | #include "internal.h" |
27 | #include "oggdec.h" |
28 | |
29 | struct DaalaPixFmtMap { |
30 | enum AVPixelFormat ffmpeg_fmt; |
31 | int depth; |
32 | int planes; |
33 | int xdec[4]; |
34 | int ydec[4]; |
35 | }; |
36 | |
37 | /* Currently supported formats only */ |
38 | static const struct DaalaPixFmtMap list_fmts[] = { |
39 | { AV_PIX_FMT_YUV420P, 8, 3, {0, 1, 1, 0}, {0, 1, 1, 0} }, |
40 | { AV_PIX_FMT_YUV444P, 8, 3, {0, 0, 0, 0}, {0, 0, 0, 0} } |
41 | }; |
42 | |
43 | typedef struct DaalaInfoHeader { |
44 | int init_d; |
45 | int fpr; |
46 | int gpshift; |
47 | int gpmask; |
48 | int version_maj; |
49 | int version_min; |
50 | int version_sub; |
51 | int frame_duration; |
52 | int keyframe_granule_shift; |
53 | struct DaalaPixFmtMap format; |
54 | } DaalaInfoHeader; |
55 | |
56 | static inline int daala_match_pix_fmt(struct DaalaPixFmtMap *fmt) |
57 | { |
58 | int i, j; |
59 | for (i = 0; i < FF_ARRAY_ELEMS(list_fmts); i++) { |
60 | int match = 0; |
61 | if (fmt->depth != list_fmts[i].depth) |
62 | continue; |
63 | if (fmt->planes != list_fmts[i].planes) |
64 | continue; |
65 | for (j = 0; j < fmt->planes; j++) { |
66 | if (fmt->xdec[j] != list_fmts[i].xdec[j]) |
67 | continue; |
68 | if (fmt->ydec[j] != list_fmts[i].ydec[j]) |
69 | continue; |
70 | match++; |
71 | } |
72 | if (match == fmt->planes) |
73 | return list_fmts[i].ffmpeg_fmt; |
74 | } |
75 | return -1; |
76 | } |
77 | |
78 | static int daala_header(AVFormatContext *s, int idx) |
79 | { |
80 | int i, err; |
81 | uint8_t *cdp; |
82 | GetByteContext gb; |
83 | AVRational timebase; |
84 | struct ogg *ogg = s->priv_data; |
85 | struct ogg_stream *os = ogg->streams + idx; |
86 | AVStream *st = s->streams[idx]; |
87 | int cds = st->codecpar->extradata_size + os->psize + 2; |
88 | DaalaInfoHeader *hdr = os->private; |
89 | |
90 | if (!(os->buf[os->pstart] & 0x80)) |
91 | return 0; |
92 | |
93 | if (!hdr) { |
94 | hdr = av_mallocz(sizeof(*hdr)); |
95 | if (!hdr) |
96 | return AVERROR(ENOMEM); |
97 | os->private = hdr; |
98 | } |
99 | |
100 | switch (os->buf[os->pstart]) { |
101 | case 0x80: |
102 | bytestream2_init(&gb, os->buf + os->pstart, os->psize); |
103 | bytestream2_skip(&gb, ff_daala_codec.magicsize); |
104 | |
105 | hdr->version_maj = bytestream2_get_byte(&gb); |
106 | hdr->version_min = bytestream2_get_byte(&gb); |
107 | hdr->version_sub = bytestream2_get_byte(&gb); |
108 | |
109 | st->codecpar->width = bytestream2_get_ne32(&gb); |
110 | st->codecpar->height = bytestream2_get_ne32(&gb); |
111 | |
112 | st->sample_aspect_ratio.num = bytestream2_get_ne32(&gb); |
113 | st->sample_aspect_ratio.den = bytestream2_get_ne32(&gb); |
114 | |
115 | timebase.num = bytestream2_get_ne32(&gb); |
116 | timebase.den = bytestream2_get_ne32(&gb); |
117 | if (timebase.num < 0 && timebase.den < 0) { |
118 | av_log(s, AV_LOG_WARNING, "Invalid timebase, assuming 30 FPS\n"); |
119 | timebase.num = 1; |
120 | timebase.den = 30; |
121 | } |
122 | avpriv_set_pts_info(st, 64, timebase.den, timebase.num); |
123 | |
124 | hdr->frame_duration = bytestream2_get_ne32(&gb); |
125 | hdr->gpshift = bytestream2_get_byte(&gb); |
126 | if (hdr->gpshift >= 32) { |
127 | av_log(s, AV_LOG_ERROR, "Too large gpshift %d (>= 32).\n", |
128 | hdr->gpshift); |
129 | hdr->gpshift = 0; |
130 | return AVERROR_INVALIDDATA; |
131 | } |
132 | hdr->gpmask = (1U << hdr->gpshift) - 1; |
133 | |
134 | hdr->format.depth = 8 + 2*(bytestream2_get_byte(&gb)-1); |
135 | |
136 | hdr->fpr = bytestream2_get_byte(&gb); |
137 | |
138 | hdr->format.planes = bytestream2_get_byte(&gb); |
139 | if (hdr->format.planes > 4) { |
140 | av_log(s, AV_LOG_ERROR, |
141 | "Invalid number of planes %d in daala pixel format map.\n", |
142 | hdr->format.planes); |
143 | return AVERROR_INVALIDDATA; |
144 | } |
145 | for (i = 0; i < hdr->format.planes; i++) { |
146 | hdr->format.xdec[i] = bytestream2_get_byte(&gb); |
147 | hdr->format.ydec[i] = bytestream2_get_byte(&gb); |
148 | } |
149 | |
150 | if ((st->codecpar->format = daala_match_pix_fmt(&hdr->format)) < 0) |
151 | av_log(s, AV_LOG_ERROR, "Unsupported pixel format - %i %i\n", |
152 | hdr->format.depth, hdr->format.planes); |
153 | |
154 | st->codecpar->codec_id = AV_CODEC_ID_DAALA; |
155 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
156 | st->need_parsing = AVSTREAM_PARSE_HEADERS; |
157 | |
158 | hdr->init_d = 1; |
159 | break; |
160 | case 0x81: |
161 | if (!hdr->init_d) |
162 | return AVERROR_INVALIDDATA; |
163 | ff_vorbis_stream_comment(s, st, |
164 | os->buf + os->pstart + ff_daala_codec.magicsize, |
165 | os->psize - ff_daala_codec.magicsize); |
166 | break; |
167 | case 0x82: |
168 | if (!hdr->init_d) |
169 | return AVERROR_INVALIDDATA; |
170 | break; |
171 | default: |
172 | av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); |
173 | return AVERROR_INVALIDDATA; |
174 | break; |
175 | } |
176 | |
177 | if ((err = av_reallocp(&st->codecpar->extradata, |
178 | cds + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { |
179 | st->codecpar->extradata_size = 0; |
180 | return err; |
181 | } |
182 | |
183 | memset(st->codecpar->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE); |
184 | cdp = st->codecpar->extradata + st->codecpar->extradata_size; |
185 | *cdp++ = os->psize >> 8; |
186 | *cdp++ = os->psize & 0xff; |
187 | memcpy(cdp, os->buf + os->pstart, os->psize); |
188 | st->codecpar->extradata_size = cds; |
189 | |
190 | return 1; |
191 | } |
192 | |
193 | static uint64_t daala_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, |
194 | int64_t *dts) |
195 | { |
196 | uint64_t iframe, pframe; |
197 | struct ogg *ogg = ctx->priv_data; |
198 | struct ogg_stream *os = ogg->streams + idx; |
199 | DaalaInfoHeader *hdr = os->private; |
200 | |
201 | if (!hdr) |
202 | return AV_NOPTS_VALUE; |
203 | |
204 | iframe = gp >> hdr->gpshift; |
205 | pframe = gp & hdr->gpmask; |
206 | |
207 | if (!pframe) |
208 | os->pflags |= AV_PKT_FLAG_KEY; |
209 | |
210 | if (dts) |
211 | *dts = iframe + pframe; |
212 | |
213 | return iframe + pframe; |
214 | } |
215 | |
216 | static int daala_packet(AVFormatContext *s, int idx) |
217 | { |
218 | int seg, duration = 1; |
219 | struct ogg *ogg = s->priv_data; |
220 | struct ogg_stream *os = ogg->streams + idx; |
221 | |
222 | /* |
223 | * first packet handling: here we parse the duration of each packet in the |
224 | * first page and compare the total duration to the page granule to find the |
225 | * encoder delay and set the first timestamp |
226 | */ |
227 | |
228 | if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { |
229 | for (seg = os->segp; seg < os->nsegs; seg++) |
230 | if (os->segments[seg] < 255) |
231 | duration++; |
232 | |
233 | os->lastpts = os->lastdts = daala_gptopts(s, idx, os->granule, NULL) - duration; |
234 | if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { |
235 | s->streams[idx]->start_time = os->lastpts; |
236 | if (s->streams[idx]->duration != AV_NOPTS_VALUE) |
237 | s->streams[idx]->duration -= s->streams[idx]->start_time; |
238 | } |
239 | } |
240 | |
241 | /* parse packet duration */ |
242 | if (os->psize > 0) |
243 | os->pduration = 1; |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | const struct ogg_codec ff_daala_codec = { |
249 | .name = "Daala", |
250 | .magic = "\200daala", |
251 | .magicsize = 6, |
252 | .header = daala_header, |
253 | .packet = daala_packet, |
254 | .gptopts = daala_gptopts, |
255 | .granule_is_start = 1, |
256 | .nb_header = 3, |
257 | }; |
258 |