Commit | Line | Data |
---|---|---|
9146ca37 MR |
1 | /* |
2 | * Ogg bitstream support | |
3 | * Luca Barbato <lu_zero@gentoo.org> | |
4 | * Based on tcvp implementation | |
9146ca37 MR |
5 | */ |
6 | ||
e873c03a | 7 | /* |
9146ca37 MR |
8 | Copyright (C) 2005 Michael Ahlberg, Måns Rullgård |
9 | ||
10 | Permission is hereby granted, free of charge, to any person | |
11 | obtaining a copy of this software and associated documentation | |
12 | files (the "Software"), to deal in the Software without | |
13 | restriction, including without limitation the rights to use, copy, | |
14 | modify, merge, publish, distribute, sublicense, and/or sell copies | |
15 | of the Software, and to permit persons to whom the Software is | |
16 | furnished to do so, subject to the following conditions: | |
17 | ||
18 | The above copyright notice and this permission notice shall be | |
19 | included in all copies or substantial portions of the Software. | |
20 | ||
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
25 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
26 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
28 | DEALINGS IN THE SOFTWARE. | |
e873c03a | 29 | */ |
9146ca37 MR |
30 | |
31 | ||
32 | #include <stdio.h> | |
a0ddef24 | 33 | #include "oggdec.h" |
9146ca37 | 34 | #include "avformat.h" |
a2faa951 | 35 | #include "internal.h" |
66061a12 | 36 | #include "vorbiscomment.h" |
9146ca37 MR |
37 | |
38 | #define MAX_PAGE_SIZE 65307 | |
39 | #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE | |
40 | ||
77be08ee | 41 | static const struct ogg_codec * const ogg_codecs[] = { |
32ad8692 | 42 | &ff_skeleton_codec, |
24ca518b | 43 | &ff_dirac_codec, |
547ea47d RD |
44 | &ff_speex_codec, |
45 | &ff_vorbis_codec, | |
46 | &ff_theora_codec, | |
47 | &ff_flac_codec, | |
4ca59d19 | 48 | &ff_celt_codec, |
ecab1c77 | 49 | &ff_opus_codec, |
430a8168 | 50 | &ff_vp8_codec, |
24ca518b | 51 | &ff_old_dirac_codec, |
547ea47d RD |
52 | &ff_old_flac_codec, |
53 | &ff_ogm_video_codec, | |
54 | &ff_ogm_audio_codec, | |
55 | &ff_ogm_text_codec, | |
56 | &ff_ogm_old_codec, | |
9146ca37 MR |
57 | NULL |
58 | }; | |
59 | ||
9146ca37 | 60 | //FIXME We could avoid some structure duplication |
e575685f | 61 | static int ogg_save(AVFormatContext *s) |
9146ca37 | 62 | { |
77be08ee MR |
63 | struct ogg *ogg = s->priv_data; |
64 | struct ogg_state *ost = | |
f5f1cf52 | 65 | av_malloc(sizeof(*ost) + (ogg->nstreams - 1) * sizeof(*ogg->streams)); |
9146ca37 | 66 | int i; |
f5f1cf52 LB |
67 | ost->pos = avio_tell(s->pb); |
68 | ost->curidx = ogg->curidx; | |
69 | ost->next = ogg->state; | |
20be72c8 | 70 | ost->nstreams = ogg->nstreams; |
9146ca37 MR |
71 | memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); |
72 | ||
f5f1cf52 | 73 | for (i = 0; i < ogg->nstreams; i++) { |
77be08ee | 74 | struct ogg_stream *os = ogg->streams + i; |
f5f1cf52 LB |
75 | os->buf = av_mallocz(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); |
76 | memcpy(os->buf, ost->streams[i].buf, os->bufpos); | |
9146ca37 MR |
77 | } |
78 | ||
79 | ogg->state = ost; | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
e575685f | 84 | static int ogg_restore(AVFormatContext *s, int discard) |
9146ca37 | 85 | { |
77be08ee | 86 | struct ogg *ogg = s->priv_data; |
ae628ec1 | 87 | AVIOContext *bc = s->pb; |
77be08ee | 88 | struct ogg_state *ost = ogg->state; |
f369b935 | 89 | int i, err; |
9146ca37 MR |
90 | |
91 | if (!ost) | |
92 | return 0; | |
93 | ||
94 | ogg->state = ost->next; | |
95 | ||
f5f1cf52 | 96 | if (!discard) { |
0e7efb9d | 97 | |
9146ca37 | 98 | for (i = 0; i < ogg->nstreams; i++) |
f5f1cf52 | 99 | av_free(ogg->streams[i].buf); |
9146ca37 | 100 | |
f5f1cf52 LB |
101 | avio_seek(bc, ost->pos, SEEK_SET); |
102 | ogg->curidx = ost->curidx; | |
20be72c8 | 103 | ogg->nstreams = ost->nstreams; |
f369b935 AK |
104 | if ((err = av_reallocp_array(&ogg->streams, ogg->nstreams, |
105 | sizeof(*ogg->streams))) < 0) { | |
106 | ogg->nstreams = 0; | |
107 | return err; | |
108 | } else | |
0e7efb9d LA |
109 | memcpy(ogg->streams, ost->streams, |
110 | ost->nstreams * sizeof(*ogg->streams)); | |
9146ca37 MR |
111 | } |
112 | ||
f5f1cf52 | 113 | av_free(ost); |
9146ca37 MR |
114 | |
115 | return 0; | |
116 | } | |
117 | ||
e575685f | 118 | static int ogg_reset(struct ogg *ogg) |
9146ca37 MR |
119 | { |
120 | int i; | |
121 | ||
f5f1cf52 | 122 | for (i = 0; i < ogg->nstreams; i++) { |
77be08ee | 123 | struct ogg_stream *os = ogg->streams + i; |
f5f1cf52 LB |
124 | os->bufpos = 0; |
125 | os->pstart = 0; | |
126 | os->psize = 0; | |
127 | os->granule = -1; | |
128 | os->lastpts = AV_NOPTS_VALUE; | |
129 | os->lastdts = AV_NOPTS_VALUE; | |
130 | os->sync_pos = -1; | |
131 | os->page_pos = 0; | |
132 | os->nsegs = 0; | |
133 | os->segp = 0; | |
ecc0027b | 134 | os->incomplete = 0; |
9146ca37 MR |
135 | } |
136 | ||
137 | ogg->curidx = -1; | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
e575685f | 142 | static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size) |
9146ca37 MR |
143 | { |
144 | int i; | |
145 | ||
146 | for (i = 0; ogg_codecs[i]; i++) | |
147 | if (size >= ogg_codecs[i]->magicsize && | |
f5f1cf52 | 148 | !memcmp(buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize)) |
9146ca37 MR |
149 | return ogg_codecs[i]; |
150 | ||
151 | return NULL; | |
152 | } | |
153 | ||
e575685f | 154 | static int ogg_new_stream(AVFormatContext *s, uint32_t serial, int new_avstream) |
9146ca37 | 155 | { |
77be08ee | 156 | struct ogg *ogg = s->priv_data; |
f5f1cf52 | 157 | int idx = ogg->nstreams++; |
9146ca37 | 158 | AVStream *st; |
77be08ee | 159 | struct ogg_stream *os; |
9146ca37 | 160 | |
ba064ebe LB |
161 | os = av_realloc(ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); |
162 | ||
163 | if (!os) | |
164 | return AVERROR(ENOMEM); | |
165 | ||
166 | ogg->streams = os; | |
167 | ||
f5f1cf52 LB |
168 | memset(ogg->streams + idx, 0, sizeof(*ogg->streams)); |
169 | ||
170 | os = ogg->streams + idx; | |
171 | os->serial = serial; | |
172 | os->bufsize = DECODER_BUFFER_SIZE; | |
173 | os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); | |
174 | os->header = -1; | |
d1f05dd1 | 175 | os->start_granule = OGG_NOGRANULE_VALUE; |
9146ca37 | 176 | |
5780f41a | 177 | if (new_avstream) { |
84ad31ff | 178 | st = avformat_new_stream(s, NULL); |
5780f41a CB |
179 | if (!st) |
180 | return AVERROR(ENOMEM); | |
9146ca37 | 181 | |
84ad31ff | 182 | st->id = idx; |
c3f9ebf7 | 183 | avpriv_set_pts_info(st, 64, 1, 1000000); |
5780f41a | 184 | } |
9146ca37 MR |
185 | |
186 | return idx; | |
187 | } | |
188 | ||
e575685f | 189 | static int ogg_new_buf(struct ogg *ogg, int idx) |
12a195e3 | 190 | { |
77be08ee | 191 | struct ogg_stream *os = ogg->streams + idx; |
ef0d7797 | 192 | uint8_t *nb = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); |
12a195e3 | 193 | int size = os->bufpos - os->pstart; |
f5f1cf52 LB |
194 | |
195 | if (os->buf) { | |
12a195e3 MR |
196 | memcpy(nb, os->buf + os->pstart, size); |
197 | av_free(os->buf); | |
198 | } | |
f5f1cf52 LB |
199 | |
200 | os->buf = nb; | |
12a195e3 MR |
201 | os->bufpos = size; |
202 | os->pstart = 0; | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
e575685f | 207 | static int ogg_read_page(AVFormatContext *s, int *str) |
9146ca37 | 208 | { |
ae628ec1 | 209 | AVIOContext *bc = s->pb; |
77be08ee MR |
210 | struct ogg *ogg = s->priv_data; |
211 | struct ogg_stream *os; | |
9cec1bbd | 212 | int ret, i = 0; |
9146ca37 MR |
213 | int flags, nsegs; |
214 | uint64_t gp; | |
215 | uint32_t serial; | |
9146ca37 | 216 | int size, idx; |
191e8ca7 | 217 | uint8_t sync[4]; |
9146ca37 MR |
218 | int sp = 0; |
219 | ||
9cec1bbd NG |
220 | ret = avio_read(bc, sync, 4); |
221 | if (ret < 4) | |
222 | return ret < 0 ? ret : AVERROR_EOF; | |
9146ca37 | 223 | |
f5f1cf52 | 224 | do { |
9146ca37 MR |
225 | int c; |
226 | ||
227 | if (sync[sp & 3] == 'O' && | |
228 | sync[(sp + 1) & 3] == 'g' && | |
229 | sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') | |
230 | break; | |
231 | ||
e5197539 | 232 | c = avio_r8(bc); |
f5f1cf52 | 233 | |
66e5b1df | 234 | if (bc->eof_reached) |
9cec1bbd | 235 | return AVERROR_EOF; |
f5f1cf52 | 236 | |
9146ca37 | 237 | sync[sp++ & 3] = c; |
f5f1cf52 | 238 | } while (i++ < MAX_PAGE_SIZE); |
9146ca37 | 239 | |
f5f1cf52 LB |
240 | if (i >= MAX_PAGE_SIZE) { |
241 | av_log(s, AV_LOG_INFO, "cannot find sync word\n"); | |
9cec1bbd | 242 | return AVERROR_INVALIDDATA; |
9146ca37 MR |
243 | } |
244 | ||
e5197539 | 245 | if (avio_r8(bc) != 0) /* version */ |
9cec1bbd | 246 | return AVERROR_INVALIDDATA; |
9146ca37 | 247 | |
f5f1cf52 LB |
248 | flags = avio_r8(bc); |
249 | gp = avio_rl64(bc); | |
250 | serial = avio_rl32(bc); | |
e65ab9d9 | 251 | avio_skip(bc, 8); /* seq, crc */ |
f5f1cf52 | 252 | nsegs = avio_r8(bc); |
9146ca37 | 253 | |
f5f1cf52 LB |
254 | idx = ogg_find_stream(ogg, serial); |
255 | if (idx < 0) { | |
8f3eebd6 | 256 | if (ogg->headers) { |
dc713546 CB |
257 | int n; |
258 | ||
259 | for (n = 0; n < ogg->nstreams; n++) { | |
260 | av_freep(&ogg->streams[n].buf); | |
f5f1cf52 LB |
261 | if (!ogg->state || |
262 | ogg->state->streams[n].private != ogg->streams[n].private) | |
9ed6cbc3 | 263 | av_freep(&ogg->streams[n].private); |
dc713546 | 264 | } |
f5f1cf52 | 265 | |
dc713546 CB |
266 | ogg->curidx = -1; |
267 | ogg->nstreams = 0; | |
f5f1cf52 | 268 | |
5780f41a CB |
269 | idx = ogg_new_stream(s, serial, 0); |
270 | } else { | |
271 | idx = ogg_new_stream(s, serial, 1); | |
47dec30e | 272 | } |
9146ca37 | 273 | if (idx < 0) |
9cec1bbd | 274 | return idx; |
9146ca37 MR |
275 | } |
276 | ||
277 | os = ogg->streams + idx; | |
a2704c97 | 278 | os->page_pos = avio_tell(bc) - 27; |
9146ca37 | 279 | |
f5f1cf52 | 280 | if (os->psize > 0) |
12a195e3 MR |
281 | ogg_new_buf(ogg, idx); |
282 | ||
9cec1bbd NG |
283 | ret = avio_read(bc, os->segments, nsegs); |
284 | if (ret < nsegs) | |
285 | return ret < 0 ? ret : AVERROR_EOF; | |
9146ca37 MR |
286 | |
287 | os->nsegs = nsegs; | |
f5f1cf52 | 288 | os->segp = 0; |
9146ca37 MR |
289 | |
290 | size = 0; | |
291 | for (i = 0; i < nsegs; i++) | |
292 | size += os->segments[i]; | |
293 | ||
f5f1cf52 LB |
294 | if (flags & OGG_FLAG_CONT || os->incomplete) { |
295 | if (!os->psize) { | |
296 | while (os->segp < os->nsegs) { | |
9146ca37 MR |
297 | int seg = os->segments[os->segp++]; |
298 | os->pstart += seg; | |
299 | if (seg < 255) | |
bad4a6bb | 300 | break; |
9146ca37 | 301 | } |
73823cb9 | 302 | os->sync_pos = os->page_pos; |
9146ca37 | 303 | } |
f5f1cf52 LB |
304 | } else { |
305 | os->psize = 0; | |
73823cb9 | 306 | os->sync_pos = os->page_pos; |
9146ca37 MR |
307 | } |
308 | ||
f5f1cf52 LB |
309 | if (os->bufsize - os->bufpos < size) { |
310 | uint8_t *nb = av_malloc((os->bufsize *= 2) + FF_INPUT_BUFFER_PADDING_SIZE); | |
ba064ebe LB |
311 | if (!nb) |
312 | return AVERROR(ENOMEM); | |
f5f1cf52 LB |
313 | memcpy(nb, os->buf, os->bufpos); |
314 | av_free(os->buf); | |
9146ca37 MR |
315 | os->buf = nb; |
316 | } | |
317 | ||
9cec1bbd NG |
318 | ret = avio_read(bc, os->buf + os->bufpos, size); |
319 | if (ret < size) | |
320 | return ret < 0 ? ret : AVERROR_EOF; | |
9146ca37 | 321 | |
9146ca37 MR |
322 | os->bufpos += size; |
323 | os->granule = gp; | |
f5f1cf52 | 324 | os->flags = flags; |
9146ca37 | 325 | |
ef0d7797 | 326 | memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE); |
9146ca37 MR |
327 | if (str) |
328 | *str = idx; | |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
e575685f CB |
333 | static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize, |
334 | int64_t *fpos) | |
9146ca37 | 335 | { |
77be08ee | 336 | struct ogg *ogg = s->priv_data; |
9cec1bbd | 337 | int idx, i, ret; |
77be08ee | 338 | struct ogg_stream *os; |
9146ca37 | 339 | int complete = 0; |
f5f1cf52 | 340 | int segp = 0, psize = 0; |
9146ca37 | 341 | |
b751f611 | 342 | av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx); |
9146ca37 | 343 | |
f5f1cf52 | 344 | do { |
9146ca37 MR |
345 | idx = ogg->curidx; |
346 | ||
f5f1cf52 | 347 | while (idx < 0) { |
9cec1bbd NG |
348 | ret = ogg_read_page(s, &idx); |
349 | if (ret < 0) | |
350 | return ret; | |
9146ca37 MR |
351 | } |
352 | ||
353 | os = ogg->streams + idx; | |
354 | ||
b751f611 | 355 | av_dlog(s, "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n", |
9146ca37 | 356 | idx, os->pstart, os->psize, os->segp, os->nsegs); |
9146ca37 | 357 | |
f5f1cf52 LB |
358 | if (!os->codec) { |
359 | if (os->header < 0) { | |
360 | os->codec = ogg_find_codec(os->buf, os->bufpos); | |
361 | if (!os->codec) { | |
9cec1bbd | 362 | av_log(s, AV_LOG_WARNING, "Codec not found\n"); |
9146ca37 MR |
363 | os->header = 0; |
364 | return 0; | |
365 | } | |
f5f1cf52 | 366 | } else { |
9146ca37 MR |
367 | return 0; |
368 | } | |
369 | } | |
370 | ||
f5f1cf52 | 371 | segp = os->segp; |
9146ca37 MR |
372 | psize = os->psize; |
373 | ||
f5f1cf52 | 374 | while (os->segp < os->nsegs) { |
9146ca37 MR |
375 | int ss = os->segments[os->segp++]; |
376 | os->psize += ss; | |
f5f1cf52 | 377 | if (ss < 255) { |
9146ca37 MR |
378 | complete = 1; |
379 | break; | |
380 | } | |
381 | } | |
382 | ||
f5f1cf52 LB |
383 | if (!complete && os->segp == os->nsegs) { |
384 | ogg->curidx = -1; | |
9a27acae RD |
385 | // Do not set incomplete for empty packets. |
386 | // Together with the code in ogg_read_page | |
387 | // that discards all continuation of empty packets | |
388 | // we would get an infinite loop. | |
389 | os->incomplete = !!os->psize; | |
9146ca37 | 390 | } |
f5f1cf52 | 391 | } while (!complete); |
9146ca37 | 392 | |
b751f611 | 393 | av_dlog(s, "ogg_packet: idx %i, frame size %i, start %i\n", |
9146ca37 | 394 | idx, os->psize, os->pstart); |
9146ca37 | 395 | |
adc725b5 | 396 | if (os->granule == -1) |
f5f1cf52 LB |
397 | av_log(s, AV_LOG_WARNING, |
398 | "Page at %"PRId64" is missing granule\n", | |
399 | os->page_pos); | |
adc725b5 | 400 | |
f5f1cf52 | 401 | ogg->curidx = idx; |
ecc0027b | 402 | os->incomplete = 0; |
9146ca37 | 403 | |
81b743eb | 404 | if (os->header) { |
f5f1cf52 LB |
405 | os->header = os->codec->header(s, idx); |
406 | if (!os->header) { | |
407 | os->segp = segp; | |
bad4a6bb | 408 | os->psize = psize; |
365d8e47 | 409 | |
6bd69e6a RD |
410 | // We have reached the first non-header packet in this stream. |
411 | // Unfortunately more header packets may still follow for others, | |
0a94020b | 412 | // but if we continue with header parsing we may lose data packets. |
bad4a6bb | 413 | ogg->headers = 1; |
365d8e47 AC |
414 | |
415 | // Update the header state for all streams and | |
416 | // compute the data_offset. | |
6bd69e6a RD |
417 | if (!s->data_offset) |
418 | s->data_offset = os->sync_pos; | |
f5f1cf52 | 419 | |
365d8e47 AC |
420 | for (i = 0; i < ogg->nstreams; i++) { |
421 | struct ogg_stream *cur_os = ogg->streams + i; | |
365d8e47 AC |
422 | |
423 | // if we have a partial non-header packet, its start is | |
424 | // obviously at or after the data start | |
425 | if (cur_os->incomplete) | |
426 | s->data_offset = FFMIN(s->data_offset, cur_os->sync_pos); | |
427 | } | |
f5f1cf52 | 428 | } else { |
7751e469 | 429 | os->nb_header++; |
bad4a6bb | 430 | os->pstart += os->psize; |
f5f1cf52 | 431 | os->psize = 0; |
9146ca37 | 432 | } |
81b743eb | 433 | } else { |
f5f1cf52 | 434 | os->pflags = 0; |
15299b38 | 435 | os->pduration = 0; |
9146ca37 | 436 | if (os->codec && os->codec->packet) |
f5f1cf52 | 437 | os->codec->packet(s, idx); |
9146ca37 MR |
438 | if (str) |
439 | *str = idx; | |
12a195e3 MR |
440 | if (dstart) |
441 | *dstart = os->pstart; | |
442 | if (dsize) | |
443 | *dsize = os->psize; | |
73823cb9 DC |
444 | if (fpos) |
445 | *fpos = os->sync_pos; | |
f5f1cf52 LB |
446 | os->pstart += os->psize; |
447 | os->psize = 0; | |
73823cb9 | 448 | os->sync_pos = os->page_pos; |
9146ca37 MR |
449 | } |
450 | ||
5e15c7d9 DC |
451 | // determine whether there are more complete packets in this page |
452 | // if not, the page's granule will apply to this packet | |
453 | os->page_end = 1; | |
454 | for (i = os->segp; i < os->nsegs; i++) | |
455 | if (os->segments[i] < 255) { | |
456 | os->page_end = 0; | |
457 | break; | |
458 | } | |
459 | ||
9146ca37 MR |
460 | if (os->segp == os->nsegs) |
461 | ogg->curidx = -1; | |
462 | ||
463 | return 0; | |
464 | } | |
465 | ||
e575685f | 466 | static int ogg_get_headers(AVFormatContext *s) |
9146ca37 | 467 | { |
77be08ee | 468 | struct ogg *ogg = s->priv_data; |
7751e469 | 469 | int ret, i; |
9146ca37 | 470 | |
f5f1cf52 | 471 | do { |
9cec1bbd NG |
472 | ret = ogg_packet(s, NULL, NULL, NULL, NULL); |
473 | if (ret < 0) | |
474 | return ret; | |
f5f1cf52 | 475 | } while (!ogg->headers); |
9146ca37 | 476 | |
7751e469 LB |
477 | for (i = 0; i < ogg->nstreams; i++) { |
478 | struct ogg_stream *os = ogg->streams + i; | |
479 | ||
480 | if (os->codec && os->codec->nb_header && | |
481 | os->nb_header < os->codec->nb_header) { | |
482 | av_log(s, AV_LOG_ERROR, | |
f963f701 LB |
483 | "Headers mismatch for stream %d: " |
484 | "expected %d received %d.\n", | |
485 | i, os->codec->nb_header, os->nb_header); | |
486 | if (s->error_recognition & AV_EF_EXPLODE) | |
487 | return AVERROR_INVALIDDATA; | |
7751e469 | 488 | } |
d1f05dd1 LB |
489 | if (os->start_granule != OGG_NOGRANULE_VALUE) |
490 | os->lastpts = s->streams[i]->start_time = | |
491 | ogg_gptopts(s, i, os->start_granule, NULL); | |
7751e469 | 492 | } |
b751f611 | 493 | av_dlog(s, "found headers\n"); |
9146ca37 MR |
494 | |
495 | return 0; | |
496 | } | |
497 | ||
e575685f | 498 | static int ogg_get_length(AVFormatContext *s) |
9146ca37 | 499 | { |
77be08ee | 500 | struct ogg *ogg = s->priv_data; |
44a088ea | 501 | int i; |
bc5c918e | 502 | int64_t size, end; |
69599eea | 503 | |
f5f1cf52 | 504 | if (!s->pb->seekable) |
69599eea | 505 | return 0; |
9146ca37 MR |
506 | |
507 | // already set | |
508 | if (s->duration != AV_NOPTS_VALUE) | |
509 | return 0; | |
510 | ||
76aa876e | 511 | size = avio_size(s->pb); |
f5f1cf52 | 512 | if (size < 0) |
56466d7b | 513 | return 0; |
f5f1cf52 | 514 | end = size > MAX_PAGE_SIZE ? size - MAX_PAGE_SIZE : 0; |
56466d7b | 515 | |
f5f1cf52 LB |
516 | ogg_save(s); |
517 | avio_seek(s->pb, end, SEEK_SET); | |
9146ca37 | 518 | |
f5f1cf52 | 519 | while (!ogg_read_page(s, &i)) { |
e22f2aaf | 520 | if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && |
44a088ea DC |
521 | ogg->streams[i].codec) { |
522 | s->streams[i]->duration = | |
f5f1cf52 | 523 | ogg_gptopts(s, i, ogg->streams[i].granule, NULL); |
44a088ea DC |
524 | if (s->streams[i]->start_time != AV_NOPTS_VALUE) |
525 | s->streams[i]->duration -= s->streams[i]->start_time; | |
526 | } | |
9146ca37 MR |
527 | } |
528 | ||
f5f1cf52 | 529 | ogg_restore(s, 0); |
9146ca37 MR |
530 | |
531 | return 0; | |
532 | } | |
533 | ||
89b51b57 LB |
534 | static int ogg_read_close(AVFormatContext *s) |
535 | { | |
536 | struct ogg *ogg = s->priv_data; | |
537 | int i; | |
538 | ||
539 | for (i = 0; i < ogg->nstreams; i++) { | |
540 | av_free(ogg->streams[i].buf); | |
d894f747 LB |
541 | if (ogg->streams[i].codec && |
542 | ogg->streams[i].codec->cleanup) { | |
543 | ogg->streams[i].codec->cleanup(s, i); | |
544 | } | |
89b51b57 LB |
545 | av_free(ogg->streams[i].private); |
546 | } | |
547 | av_free(ogg->streams); | |
548 | return 0; | |
549 | } | |
550 | ||
6e9651d1 | 551 | static int ogg_read_header(AVFormatContext *s) |
9146ca37 | 552 | { |
77be08ee | 553 | struct ogg *ogg = s->priv_data; |
9cec1bbd | 554 | int ret, i; |
9146ca37 MR |
555 | ogg->curidx = -1; |
556 | //linear headers seek from start | |
9cec1bbd | 557 | ret = ogg_get_headers(s); |
89b51b57 LB |
558 | if (ret < 0) { |
559 | ogg_read_close(s); | |
9cec1bbd | 560 | return ret; |
89b51b57 | 561 | } |
9146ca37 | 562 | |
c9da676d RD |
563 | for (i = 0; i < ogg->nstreams; i++) |
564 | if (ogg->streams[i].header < 0) | |
565 | ogg->streams[i].codec = NULL; | |
566 | ||
9146ca37 | 567 | //linear granulepos seek from end |
f5f1cf52 | 568 | ogg_get_length(s); |
9146ca37 MR |
569 | |
570 | //fill the extradata in the per codec callbacks | |
571 | return 0; | |
572 | } | |
573 | ||
6abaa272 DC |
574 | static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts) |
575 | { | |
f5f1cf52 | 576 | struct ogg *ogg = s->priv_data; |
6abaa272 | 577 | struct ogg_stream *os = ogg->streams + idx; |
f5f1cf52 | 578 | int64_t pts = AV_NOPTS_VALUE; |
6abaa272 DC |
579 | |
580 | if (dts) | |
581 | *dts = AV_NOPTS_VALUE; | |
582 | ||
583 | if (os->lastpts != AV_NOPTS_VALUE) { | |
f5f1cf52 | 584 | pts = os->lastpts; |
6abaa272 DC |
585 | os->lastpts = AV_NOPTS_VALUE; |
586 | } | |
587 | if (os->lastdts != AV_NOPTS_VALUE) { | |
588 | if (dts) | |
589 | *dts = os->lastdts; | |
590 | os->lastdts = AV_NOPTS_VALUE; | |
591 | } | |
592 | if (os->page_end) { | |
593 | if (os->granule != -1LL) { | |
594 | if (os->codec && os->codec->granule_is_start) | |
595 | pts = ogg_gptopts(s, idx, os->granule, dts); | |
596 | else | |
597 | os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts); | |
598 | os->granule = -1LL; | |
adc725b5 | 599 | } |
6abaa272 DC |
600 | } |
601 | return pts; | |
602 | } | |
9146ca37 | 603 | |
e575685f | 604 | static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt) |
9146ca37 | 605 | { |
77be08ee MR |
606 | struct ogg *ogg; |
607 | struct ogg_stream *os; | |
9cec1bbd | 608 | int idx = -1, ret; |
12a195e3 | 609 | int pstart, psize; |
d8b91fae | 610 | int64_t fpos, pts, dts; |
9146ca37 | 611 | |
115329f1 | 612 | //Get an ogg packet |
d8b91fae | 613 | retry: |
f5f1cf52 | 614 | do { |
9cec1bbd NG |
615 | ret = ogg_packet(s, &idx, &pstart, &psize, &fpos); |
616 | if (ret < 0) | |
617 | return ret; | |
f5f1cf52 | 618 | } while (idx < 0 || !s->streams[idx]); |
9146ca37 MR |
619 | |
620 | ogg = s->priv_data; | |
f5f1cf52 | 621 | os = ogg->streams + idx; |
9146ca37 | 622 | |
d8b91fae DC |
623 | // pflags might not be set until after this |
624 | pts = ogg_calc_pts(s, idx, &dts); | |
625 | ||
cc947f04 | 626 | if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) |
d8b91fae DC |
627 | goto retry; |
628 | os->keyframe_seek = 0; | |
629 | ||
9146ca37 | 630 | //Alloc a pkt |
9cec1bbd NG |
631 | ret = av_new_packet(pkt, psize); |
632 | if (ret < 0) | |
633 | return ret; | |
9146ca37 | 634 | pkt->stream_index = idx; |
f5f1cf52 | 635 | memcpy(pkt->data, os->buf + pstart, psize); |
5e15c7d9 | 636 | |
f5f1cf52 LB |
637 | pkt->pts = pts; |
638 | pkt->dts = dts; | |
639 | pkt->flags = os->pflags; | |
15299b38 | 640 | pkt->duration = os->pduration; |
f5f1cf52 | 641 | pkt->pos = fpos; |
e1a794b2 | 642 | |
12a195e3 | 643 | return psize; |
9146ca37 MR |
644 | } |
645 | ||
e575685f CB |
646 | static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, |
647 | int64_t *pos_arg, int64_t pos_limit) | |
9146ca37 | 648 | { |
77be08ee | 649 | struct ogg *ogg = s->priv_data; |
ae628ec1 | 650 | AVIOContext *bc = s->pb; |
f5f1cf52 LB |
651 | int64_t pts = AV_NOPTS_VALUE; |
652 | int i = -1; | |
6b4aa5da | 653 | avio_seek(bc, *pos_arg, SEEK_SET); |
873d117e DC |
654 | ogg_reset(ogg); |
655 | ||
f5f1cf52 LB |
656 | while (avio_tell(bc) < pos_limit && |
657 | !ogg_packet(s, &i, NULL, NULL, pos_arg)) { | |
873d117e | 658 | if (i == stream_index) { |
4cc3467e | 659 | struct ogg_stream *os = ogg->streams + stream_index; |
873d117e | 660 | pts = ogg_calc_pts(s, i, NULL); |
cc947f04 | 661 | if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) |
d8b91fae | 662 | pts = AV_NOPTS_VALUE; |
a1f29b95 | 663 | } |
873d117e DC |
664 | if (pts != AV_NOPTS_VALUE) |
665 | break; | |
a1f29b95 RD |
666 | } |
667 | ogg_reset(ogg); | |
668 | return pts; | |
9146ca37 | 669 | } |
9146ca37 | 670 | |
e575685f CB |
671 | static int ogg_read_seek(AVFormatContext *s, int stream_index, |
672 | int64_t timestamp, int flags) | |
d8b91fae | 673 | { |
f5f1cf52 | 674 | struct ogg *ogg = s->priv_data; |
d8b91fae DC |
675 | struct ogg_stream *os = ogg->streams + stream_index; |
676 | int ret; | |
677 | ||
678 | // Try seeking to a keyframe first. If this fails (very possible), | |
679 | // av_seek_frame will fall back to ignoring keyframes | |
72415b2a | 680 | if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO |
d8b91fae DC |
681 | && !(flags & AVSEEK_FLAG_ANY)) |
682 | os->keyframe_seek = 1; | |
683 | ||
a2faa951 | 684 | ret = ff_seek_frame_binary(s, stream_index, timestamp, flags); |
f5f1cf52 | 685 | os = ogg->streams + stream_index; |
d8b91fae DC |
686 | if (ret < 0) |
687 | os->keyframe_seek = 0; | |
688 | return ret; | |
689 | } | |
690 | ||
2e70e4aa MR |
691 | static int ogg_probe(AVProbeData *p) |
692 | { | |
f95257d2 | 693 | if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7) |
2e70e4aa | 694 | return AVPROBE_SCORE_MAX; |
f95257d2 | 695 | return 0; |
2e70e4aa MR |
696 | } |
697 | ||
c6610a21 | 698 | AVInputFormat ff_ogg_demuxer = { |
b3bbc6fd CB |
699 | .name = "ogg", |
700 | .long_name = NULL_IF_CONFIG_SMALL("Ogg"), | |
701 | .priv_data_size = sizeof(struct ogg), | |
702 | .read_probe = ogg_probe, | |
703 | .read_header = ogg_read_header, | |
704 | .read_packet = ogg_read_packet, | |
705 | .read_close = ogg_read_close, | |
706 | .read_seek = ogg_read_seek, | |
707 | .read_timestamp = ogg_read_timestamp, | |
708 | .extensions = "ogg", | |
8ad3267c | 709 | .flags = AVFMT_GENERIC_INDEX | AVFMT_NOBINSEARCH, |
9146ca37 | 710 | }; |