oggdec: Save offset of the page needed to reconstruct the current packet
[libav.git] / libavformat / oggdec.c
CommitLineData
9146ca37
MR
1/*
2 * Ogg bitstream support
3 * Luca Barbato <lu_zero@gentoo.org>
4 * Based on tcvp implementation
115329f1 5 *
9146ca37
MR
6 */
7
8/**
9 Copyright (C) 2005 Michael Ahlberg, Måns Rullgård
10
11 Permission is hereby granted, free of charge, to any person
12 obtaining a copy of this software and associated documentation
13 files (the "Software"), to deal in the Software without
14 restriction, including without limitation the rights to use, copy,
15 modify, merge, publish, distribute, sublicense, and/or sell copies
16 of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be
20 included in all copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 DEALINGS IN THE SOFTWARE.
30**/
31
32
33#include <stdio.h>
a0ddef24 34#include "oggdec.h"
9146ca37
MR
35#include "avformat.h"
36
37#define MAX_PAGE_SIZE 65307
38#define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
39
77be08ee 40static const struct ogg_codec * const ogg_codecs[] = {
24ca518b 41 &ff_dirac_codec,
547ea47d
RD
42 &ff_speex_codec,
43 &ff_vorbis_codec,
44 &ff_theora_codec,
45 &ff_flac_codec,
24ca518b 46 &ff_old_dirac_codec,
547ea47d
RD
47 &ff_old_flac_codec,
48 &ff_ogm_video_codec,
49 &ff_ogm_audio_codec,
50 &ff_ogm_text_codec,
51 &ff_ogm_old_codec,
9146ca37
MR
52 NULL
53};
54
9146ca37
MR
55//FIXME We could avoid some structure duplication
56static int
57ogg_save (AVFormatContext * s)
58{
77be08ee
MR
59 struct ogg *ogg = s->priv_data;
60 struct ogg_state *ost =
ad3aa874 61 av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
9146ca37 62 int i;
899681cd 63 ost->pos = url_ftell (s->pb);
9146ca37
MR
64 ost->curidx = ogg->curidx;
65 ost->next = ogg->state;
20be72c8 66 ost->nstreams = ogg->nstreams;
9146ca37
MR
67 memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
68
69 for (i = 0; i < ogg->nstreams; i++){
77be08ee 70 struct ogg_stream *os = ogg->streams + i;
9146ca37
MR
71 os->buf = av_malloc (os->bufsize);
72 memset (os->buf, 0, os->bufsize);
73 memcpy (os->buf, ost->streams[i].buf, os->bufpos);
74 }
75
76 ogg->state = ost;
77
78 return 0;
79}
80
81static int
82ogg_restore (AVFormatContext * s, int discard)
83{
77be08ee 84 struct ogg *ogg = s->priv_data;
899681cd 85 ByteIOContext *bc = s->pb;
77be08ee 86 struct ogg_state *ost = ogg->state;
9146ca37
MR
87 int i;
88
89 if (!ost)
90 return 0;
91
92 ogg->state = ost->next;
93
94 if (!discard){
95 for (i = 0; i < ogg->nstreams; i++)
96 av_free (ogg->streams[i].buf);
97
98 url_fseek (bc, ost->pos, SEEK_SET);
99 ogg->curidx = ost->curidx;
20be72c8
MR
100 ogg->nstreams = ost->nstreams;
101 memcpy(ogg->streams, ost->streams,
102 ost->nstreams * sizeof(*ogg->streams));
9146ca37
MR
103 }
104
105 av_free (ost);
106
107 return 0;
108}
109
110static int
77be08ee 111ogg_reset (struct ogg * ogg)
9146ca37
MR
112{
113 int i;
114
115 for (i = 0; i < ogg->nstreams; i++){
77be08ee 116 struct ogg_stream *os = ogg->streams + i;
9146ca37
MR
117 os->bufpos = 0;
118 os->pstart = 0;
119 os->psize = 0;
120 os->granule = -1;
5e15c7d9 121 os->lastpts = AV_NOPTS_VALUE;
2d4970d8 122 os->lastdts = AV_NOPTS_VALUE;
73823cb9
DC
123 os->sync_pos = -1;
124 os->page_pos = 0;
9146ca37
MR
125 os->nsegs = 0;
126 os->segp = 0;
ecc0027b 127 os->incomplete = 0;
9146ca37
MR
128 }
129
130 ogg->curidx = -1;
131
132 return 0;
133}
134
77be08ee 135static const struct ogg_codec *
2d2f443d 136ogg_find_codec (uint8_t * buf, int size)
9146ca37
MR
137{
138 int i;
139
140 for (i = 0; ogg_codecs[i]; i++)
141 if (size >= ogg_codecs[i]->magicsize &&
142 !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
143 return ogg_codecs[i];
144
145 return NULL;
146}
147
148static int
77be08ee 149ogg_find_stream (struct ogg * ogg, int serial)
9146ca37
MR
150{
151 int i;
152
153 for (i = 0; i < ogg->nstreams; i++)
154 if (ogg->streams[i].serial == serial)
155 return i;
156
157 return -1;
158}
159
160static int
161ogg_new_stream (AVFormatContext * s, uint32_t serial)
162{
163
77be08ee 164 struct ogg *ogg = s->priv_data;
9146ca37
MR
165 int idx = ogg->nstreams++;
166 AVStream *st;
77be08ee 167 struct ogg_stream *os;
9146ca37
MR
168
169 ogg->streams = av_realloc (ogg->streams,
170 ogg->nstreams * sizeof (*ogg->streams));
171 memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
172 os = ogg->streams + idx;
173 os->serial = serial;
174 os->bufsize = DECODER_BUFFER_SIZE;
40c5e1fa 175 os->buf = av_malloc(os->bufsize);
9146ca37
MR
176 os->header = -1;
177
178 st = av_new_stream (s, idx);
179 if (!st)
769e10f0 180 return AVERROR(ENOMEM);
9146ca37
MR
181
182 av_set_pts_info(st, 64, 1, 1000000);
9146ca37
MR
183
184 return idx;
185}
186
187static int
77be08ee 188ogg_new_buf(struct ogg *ogg, int idx)
12a195e3 189{
77be08ee 190 struct ogg_stream *os = ogg->streams + idx;
ea02862a 191 uint8_t *nb = av_malloc(os->bufsize);
12a195e3
MR
192 int size = os->bufpos - os->pstart;
193 if(os->buf){
194 memcpy(nb, os->buf + os->pstart, size);
195 av_free(os->buf);
196 }
197 os->buf = nb;
198 os->bufpos = size;
199 os->pstart = 0;
200
201 return 0;
202}
203
204static int
9146ca37
MR
205ogg_read_page (AVFormatContext * s, int *str)
206{
899681cd 207 ByteIOContext *bc = s->pb;
77be08ee
MR
208 struct ogg *ogg = s->priv_data;
209 struct ogg_stream *os;
9146ca37
MR
210 int i = 0;
211 int flags, nsegs;
212 uint64_t gp;
213 uint32_t serial;
214 uint32_t seq;
215 uint32_t crc;
216 int size, idx;
191e8ca7 217 uint8_t sync[4];
9146ca37
MR
218 int sp = 0;
219
220 if (get_buffer (bc, sync, 4) < 4)
221 return -1;
222
223 do{
224 int c;
225
226 if (sync[sp & 3] == 'O' &&
227 sync[(sp + 1) & 3] == 'g' &&
228 sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
229 break;
230
231 c = url_fgetc (bc);
232 if (c < 0)
233 return -1;
234 sync[sp++ & 3] = c;
235 }while (i++ < MAX_PAGE_SIZE);
236
237 if (i >= MAX_PAGE_SIZE){
238 av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
239 return -1;
240 }
241
242 if (url_fgetc (bc) != 0) /* version */
243 return -1;
244
245 flags = url_fgetc (bc);
246 gp = get_le64 (bc);
247 serial = get_le32 (bc);
248 seq = get_le32 (bc);
249 crc = get_le32 (bc);
250 nsegs = url_fgetc (bc);
251
252 idx = ogg_find_stream (ogg, serial);
253 if (idx < 0){
254 idx = ogg_new_stream (s, serial);
255 if (idx < 0)
256 return -1;
257 }
258
259 os = ogg->streams + idx;
73823cb9 260 os->page_pos = url_ftell(bc) - 27;
9146ca37 261
40c5e1fa 262 if(os->psize > 0)
12a195e3
MR
263 ogg_new_buf(ogg, idx);
264
9146ca37
MR
265 if (get_buffer (bc, os->segments, nsegs) < nsegs)
266 return -1;
267
268 os->nsegs = nsegs;
269 os->segp = 0;
270
271 size = 0;
272 for (i = 0; i < nsegs; i++)
273 size += os->segments[i];
274
ecc0027b 275 if (flags & OGG_FLAG_CONT || os->incomplete){
9146ca37
MR
276 if (!os->psize){
277 while (os->segp < os->nsegs){
278 int seg = os->segments[os->segp++];
279 os->pstart += seg;
280 if (seg < 255)
bad4a6bb 281 break;
9146ca37 282 }
73823cb9 283 os->sync_pos = os->page_pos;
9146ca37
MR
284 }
285 }else{
bad4a6bb 286 os->psize = 0;
73823cb9 287 os->sync_pos = os->page_pos;
9146ca37
MR
288 }
289
290 if (os->bufsize - os->bufpos < size){
2d2f443d 291 uint8_t *nb = av_malloc (os->bufsize *= 2);
9146ca37
MR
292 memcpy (nb, os->buf, os->bufpos);
293 av_free (os->buf);
294 os->buf = nb;
295 }
296
297 if (get_buffer (bc, os->buf + os->bufpos, size) < size)
298 return -1;
299
9146ca37
MR
300 os->bufpos += size;
301 os->granule = gp;
302 os->flags = flags;
303
304 if (str)
305 *str = idx;
306
307 return 0;
308}
309
310static int
73823cb9 311ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize, int64_t *fpos)
9146ca37 312{
77be08ee 313 struct ogg *ogg = s->priv_data;
5e15c7d9 314 int idx, i;
77be08ee 315 struct ogg_stream *os;
9146ca37
MR
316 int complete = 0;
317 int segp = 0, psize = 0;
318
319#if 0
320 av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx);
321#endif
322
323 do{
324 idx = ogg->curidx;
325
326 while (idx < 0){
327 if (ogg_read_page (s, &idx) < 0)
328 return -1;
329 }
330
331 os = ogg->streams + idx;
332
333#if 0
334 av_log (s, AV_LOG_DEBUG,
335 "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
336 idx, os->pstart, os->psize, os->segp, os->nsegs);
337#endif
338
339 if (!os->codec){
340 if (os->header < 0){
341 os->codec = ogg_find_codec (os->buf, os->bufpos);
342 if (!os->codec){
343 os->header = 0;
344 return 0;
345 }
346 }else{
347 return 0;
348 }
349 }
350
351 segp = os->segp;
352 psize = os->psize;
353
354 while (os->segp < os->nsegs){
355 int ss = os->segments[os->segp++];
356 os->psize += ss;
357 if (ss < 255){
358 complete = 1;
359 break;
360 }
361 }
362
363 if (!complete && os->segp == os->nsegs){
9146ca37 364 ogg->curidx = -1;
ecc0027b 365 os->incomplete = 1;
9146ca37
MR
366 }
367 }while (!complete);
368
369#if 0
370 av_log (s, AV_LOG_DEBUG,
371 "ogg_packet: idx %i, frame size %i, start %i\n",
372 idx, os->psize, os->pstart);
373#endif
374
375 ogg->curidx = idx;
ecc0027b 376 os->incomplete = 0;
9146ca37 377
be4a1132 378 if (!ogg->headers){
9146ca37 379 int hdr = os->codec->header (s, idx);
be4a1132 380 os->header = os->seq;
9146ca37 381 if (!hdr){
bad4a6bb
PR
382 os->segp = segp;
383 os->psize = psize;
384 ogg->headers = 1;
9146ca37 385 }else{
bad4a6bb
PR
386 os->pstart += os->psize;
387 os->psize = 0;
9146ca37
MR
388 }
389 }
390
391 if (os->header > -1 && os->seq > os->header){
e1a794b2 392 os->pflags = 0;
15299b38 393 os->pduration = 0;
9146ca37
MR
394 if (os->codec && os->codec->packet)
395 os->codec->packet (s, idx);
396 if (str)
397 *str = idx;
12a195e3
MR
398 if (dstart)
399 *dstart = os->pstart;
400 if (dsize)
401 *dsize = os->psize;
73823cb9
DC
402 if (fpos)
403 *fpos = os->sync_pos;
12a195e3
MR
404 os->pstart += os->psize;
405 os->psize = 0;
73823cb9 406 os->sync_pos = os->page_pos;
9146ca37
MR
407 }
408
5e15c7d9
DC
409 // determine whether there are more complete packets in this page
410 // if not, the page's granule will apply to this packet
411 os->page_end = 1;
412 for (i = os->segp; i < os->nsegs; i++)
413 if (os->segments[i] < 255) {
414 os->page_end = 0;
415 break;
416 }
417
9146ca37
MR
418 os->seq++;
419 if (os->segp == os->nsegs)
420 ogg->curidx = -1;
421
422 return 0;
423}
424
425static int
426ogg_get_headers (AVFormatContext * s)
427{
77be08ee 428 struct ogg *ogg = s->priv_data;
9146ca37
MR
429
430 do{
73823cb9 431 if (ogg_packet (s, NULL, NULL, NULL, NULL) < 0)
9146ca37
MR
432 return -1;
433 }while (!ogg->headers);
434
435#if 0
436 av_log (s, AV_LOG_DEBUG, "found headers\n");
437#endif
438
439 return 0;
440}
441
442static uint64_t
2d4970d8 443ogg_gptopts (AVFormatContext * s, int i, uint64_t gp, int64_t *dts)
9146ca37 444{
77be08ee
MR
445 struct ogg *ogg = s->priv_data;
446 struct ogg_stream *os = ogg->streams + i;
9146ca37
MR
447 uint64_t pts = AV_NOPTS_VALUE;
448
1ed923ea 449 if(os->codec->gptopts){
2d4970d8 450 pts = os->codec->gptopts(s, i, gp, dts);
3644cb8f 451 } else {
1ed923ea 452 pts = gp;
2d4970d8
DC
453 if (dts)
454 *dts = pts;
9146ca37
MR
455 }
456
457 return pts;
458}
459
460
461static int
462ogg_get_length (AVFormatContext * s)
463{
77be08ee 464 struct ogg *ogg = s->priv_data;
9146ca37 465 int idx = -1, i;
bc5c918e 466 int64_t size, end;
69599eea 467
ceeacce6 468 if(url_is_streamed(s->pb))
69599eea 469 return 0;
9146ca37
MR
470
471// already set
472 if (s->duration != AV_NOPTS_VALUE)
473 return 0;
474
899681cd 475 size = url_fsize(s->pb);
56466d7b
MR
476 if(size < 0)
477 return 0;
ddd94932 478 end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0;
56466d7b 479
9146ca37 480 ogg_save (s);
899681cd 481 url_fseek (s->pb, end, SEEK_SET);
9146ca37
MR
482
483 while (!ogg_read_page (s, &i)){
e22f2aaf
MR
484 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
485 ogg->streams[i].codec)
9146ca37
MR
486 idx = i;
487 }
488
489 if (idx != -1){
490 s->streams[idx]->duration =
2d4970d8 491 ogg_gptopts (s, idx, ogg->streams[idx].granule, NULL);
9146ca37
MR
492 }
493
56466d7b 494 ogg->size = size;
9146ca37
MR
495 ogg_restore (s, 0);
496
497 return 0;
498}
499
500
501static int
502ogg_read_header (AVFormatContext * s, AVFormatParameters * ap)
503{
77be08ee 504 struct ogg *ogg = s->priv_data;
c9da676d 505 int i;
9146ca37
MR
506 ogg->curidx = -1;
507 //linear headers seek from start
508 if (ogg_get_headers (s) < 0){
bad4a6bb 509 return -1;
9146ca37
MR
510 }
511
c9da676d
RD
512 for (i = 0; i < ogg->nstreams; i++)
513 if (ogg->streams[i].header < 0)
514 ogg->streams[i].codec = NULL;
515
9146ca37
MR
516 //linear granulepos seek from end
517 ogg_get_length (s);
518
519 //fill the extradata in the per codec callbacks
520 return 0;
521}
522
523
524static int
525ogg_read_packet (AVFormatContext * s, AVPacket * pkt)
526{
77be08ee
MR
527 struct ogg *ogg;
528 struct ogg_stream *os;
9146ca37 529 int idx = -1;
12a195e3 530 int pstart, psize;
73823cb9 531 int64_t fpos;
9146ca37 532
115329f1 533 //Get an ogg packet
9146ca37 534 do{
73823cb9 535 if (ogg_packet (s, &idx, &pstart, &psize, &fpos) < 0)
6f3e0b21 536 return AVERROR(EIO);
9146ca37
MR
537 }while (idx < 0 || !s->streams[idx]);
538
539 ogg = s->priv_data;
540 os = ogg->streams + idx;
541
542 //Alloc a pkt
12a195e3 543 if (av_new_packet (pkt, psize) < 0)
6f3e0b21 544 return AVERROR(EIO);
9146ca37 545 pkt->stream_index = idx;
12a195e3 546 memcpy (pkt->data, os->buf + pstart, psize);
5e15c7d9
DC
547
548 if (os->lastpts != AV_NOPTS_VALUE) {
549 pkt->pts = os->lastpts;
550 os->lastpts = AV_NOPTS_VALUE;
551 }
2d4970d8
DC
552 if (os->lastdts != AV_NOPTS_VALUE) {
553 pkt->dts = os->lastdts;
554 os->lastdts = AV_NOPTS_VALUE;
555 }
5e15c7d9
DC
556 if (os->page_end) {
557 if (os->granule != -1LL) {
558 if (os->codec && os->codec->granule_is_start)
2d4970d8 559 pkt->pts = ogg_gptopts(s, idx, os->granule, &pkt->dts);
5e15c7d9 560 else
2d4970d8 561 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
5e15c7d9
DC
562 os->granule = -1LL;
563 } else
564 av_log(s, AV_LOG_WARNING, "Packet is missing granule\n");
9146ca37 565 }
12a195e3 566
e1a794b2 567 pkt->flags = os->pflags;
15299b38 568 pkt->duration = os->pduration;
73823cb9 569 pkt->pos = fpos;
e1a794b2 570
12a195e3 571 return psize;
9146ca37
MR
572}
573
574
575static int
576ogg_read_close (AVFormatContext * s)
577{
77be08ee 578 struct ogg *ogg = s->priv_data;
9146ca37
MR
579 int i;
580
581 for (i = 0; i < ogg->nstreams; i++){
582 av_free (ogg->streams[i].buf);
1ed923ea 583 av_free (ogg->streams[i].private);
9146ca37
MR
584 }
585 av_free (ogg->streams);
586 return 0;
587}
588
589
9146ca37
MR
590static int64_t
591ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg,
592 int64_t pos_limit)
593{
77be08ee 594 struct ogg *ogg = s->priv_data;
899681cd 595 ByteIOContext *bc = s->pb;
a1f29b95
RD
596 int64_t pts = AV_NOPTS_VALUE;
597 int i;
598 url_fseek(bc, *pos_arg, SEEK_SET);
599 while (url_ftell(bc) < pos_limit && !ogg_read_page (s, &i)) {
600 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
601 ogg->streams[i].codec && i == stream_index) {
2d4970d8 602 pts = ogg_gptopts(s, i, ogg->streams[i].granule, NULL);
a1f29b95
RD
603 // FIXME: this is the position of the packet after the one with above
604 // pts.
605 *pos_arg = url_ftell(bc);
606 break;
607 }
608 }
609 ogg_reset(ogg);
610 return pts;
9146ca37 611}
9146ca37 612
2e70e4aa
MR
613static int ogg_probe(AVProbeData *p)
614{
2e70e4aa
MR
615 if (p->buf[0] == 'O' && p->buf[1] == 'g' &&
616 p->buf[2] == 'g' && p->buf[3] == 'S' &&
617 p->buf[4] == 0x0 && p->buf[5] <= 0x7 )
618 return AVPROBE_SCORE_MAX;
619 else
620 return 0;
621}
622
ff70e601 623AVInputFormat ogg_demuxer = {
9146ca37 624 "ogg",
bde15e74 625 NULL_IF_CONFIG_SMALL("Ogg"),
77be08ee 626 sizeof (struct ogg),
2e70e4aa 627 ogg_probe,
9146ca37
MR
628 ogg_read_header,
629 ogg_read_packet,
630 ogg_read_close,
ce3132be 631 NULL,
a1f29b95 632 ogg_read_timestamp,
9146ca37 633 .extensions = "ogg",
d7bb185f 634 .metadata_conv = ff_vorbiscomment_metadata_conv,
9146ca37 635};