oggdec: Save offset of the page needed to reconstruct the current packet
[libav.git] / libavformat / oggdec.c
1 /*
2 * Ogg bitstream support
3 * Luca Barbato <lu_zero@gentoo.org>
4 * Based on tcvp implementation
5 *
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>
34 #include "oggdec.h"
35 #include "avformat.h"
36
37 #define MAX_PAGE_SIZE 65307
38 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
39
40 static const struct ogg_codec * const ogg_codecs[] = {
41 &ff_dirac_codec,
42 &ff_speex_codec,
43 &ff_vorbis_codec,
44 &ff_theora_codec,
45 &ff_flac_codec,
46 &ff_old_dirac_codec,
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,
52 NULL
53 };
54
55 //FIXME We could avoid some structure duplication
56 static int
57 ogg_save (AVFormatContext * s)
58 {
59 struct ogg *ogg = s->priv_data;
60 struct ogg_state *ost =
61 av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
62 int i;
63 ost->pos = url_ftell (s->pb);
64 ost->curidx = ogg->curidx;
65 ost->next = ogg->state;
66 ost->nstreams = ogg->nstreams;
67 memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
68
69 for (i = 0; i < ogg->nstreams; i++){
70 struct ogg_stream *os = ogg->streams + i;
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
81 static int
82 ogg_restore (AVFormatContext * s, int discard)
83 {
84 struct ogg *ogg = s->priv_data;
85 ByteIOContext *bc = s->pb;
86 struct ogg_state *ost = ogg->state;
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;
100 ogg->nstreams = ost->nstreams;
101 memcpy(ogg->streams, ost->streams,
102 ost->nstreams * sizeof(*ogg->streams));
103 }
104
105 av_free (ost);
106
107 return 0;
108 }
109
110 static int
111 ogg_reset (struct ogg * ogg)
112 {
113 int i;
114
115 for (i = 0; i < ogg->nstreams; i++){
116 struct ogg_stream *os = ogg->streams + i;
117 os->bufpos = 0;
118 os->pstart = 0;
119 os->psize = 0;
120 os->granule = -1;
121 os->lastpts = AV_NOPTS_VALUE;
122 os->lastdts = AV_NOPTS_VALUE;
123 os->sync_pos = -1;
124 os->page_pos = 0;
125 os->nsegs = 0;
126 os->segp = 0;
127 os->incomplete = 0;
128 }
129
130 ogg->curidx = -1;
131
132 return 0;
133 }
134
135 static const struct ogg_codec *
136 ogg_find_codec (uint8_t * buf, int size)
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
148 static int
149 ogg_find_stream (struct ogg * ogg, int serial)
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
160 static int
161 ogg_new_stream (AVFormatContext * s, uint32_t serial)
162 {
163
164 struct ogg *ogg = s->priv_data;
165 int idx = ogg->nstreams++;
166 AVStream *st;
167 struct ogg_stream *os;
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;
175 os->buf = av_malloc(os->bufsize);
176 os->header = -1;
177
178 st = av_new_stream (s, idx);
179 if (!st)
180 return AVERROR(ENOMEM);
181
182 av_set_pts_info(st, 64, 1, 1000000);
183
184 return idx;
185 }
186
187 static int
188 ogg_new_buf(struct ogg *ogg, int idx)
189 {
190 struct ogg_stream *os = ogg->streams + idx;
191 uint8_t *nb = av_malloc(os->bufsize);
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
204 static int
205 ogg_read_page (AVFormatContext * s, int *str)
206 {
207 ByteIOContext *bc = s->pb;
208 struct ogg *ogg = s->priv_data;
209 struct ogg_stream *os;
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;
217 uint8_t sync[4];
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;
260 os->page_pos = url_ftell(bc) - 27;
261
262 if(os->psize > 0)
263 ogg_new_buf(ogg, idx);
264
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
275 if (flags & OGG_FLAG_CONT || os->incomplete){
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)
281 break;
282 }
283 os->sync_pos = os->page_pos;
284 }
285 }else{
286 os->psize = 0;
287 os->sync_pos = os->page_pos;
288 }
289
290 if (os->bufsize - os->bufpos < size){
291 uint8_t *nb = av_malloc (os->bufsize *= 2);
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
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
310 static int
311 ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize, int64_t *fpos)
312 {
313 struct ogg *ogg = s->priv_data;
314 int idx, i;
315 struct ogg_stream *os;
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){
364 ogg->curidx = -1;
365 os->incomplete = 1;
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;
376 os->incomplete = 0;
377
378 if (!ogg->headers){
379 int hdr = os->codec->header (s, idx);
380 os->header = os->seq;
381 if (!hdr){
382 os->segp = segp;
383 os->psize = psize;
384 ogg->headers = 1;
385 }else{
386 os->pstart += os->psize;
387 os->psize = 0;
388 }
389 }
390
391 if (os->header > -1 && os->seq > os->header){
392 os->pflags = 0;
393 os->pduration = 0;
394 if (os->codec && os->codec->packet)
395 os->codec->packet (s, idx);
396 if (str)
397 *str = idx;
398 if (dstart)
399 *dstart = os->pstart;
400 if (dsize)
401 *dsize = os->psize;
402 if (fpos)
403 *fpos = os->sync_pos;
404 os->pstart += os->psize;
405 os->psize = 0;
406 os->sync_pos = os->page_pos;
407 }
408
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
418 os->seq++;
419 if (os->segp == os->nsegs)
420 ogg->curidx = -1;
421
422 return 0;
423 }
424
425 static int
426 ogg_get_headers (AVFormatContext * s)
427 {
428 struct ogg *ogg = s->priv_data;
429
430 do{
431 if (ogg_packet (s, NULL, NULL, NULL, NULL) < 0)
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
442 static uint64_t
443 ogg_gptopts (AVFormatContext * s, int i, uint64_t gp, int64_t *dts)
444 {
445 struct ogg *ogg = s->priv_data;
446 struct ogg_stream *os = ogg->streams + i;
447 uint64_t pts = AV_NOPTS_VALUE;
448
449 if(os->codec->gptopts){
450 pts = os->codec->gptopts(s, i, gp, dts);
451 } else {
452 pts = gp;
453 if (dts)
454 *dts = pts;
455 }
456
457 return pts;
458 }
459
460
461 static int
462 ogg_get_length (AVFormatContext * s)
463 {
464 struct ogg *ogg = s->priv_data;
465 int idx = -1, i;
466 int64_t size, end;
467
468 if(url_is_streamed(s->pb))
469 return 0;
470
471 // already set
472 if (s->duration != AV_NOPTS_VALUE)
473 return 0;
474
475 size = url_fsize(s->pb);
476 if(size < 0)
477 return 0;
478 end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0;
479
480 ogg_save (s);
481 url_fseek (s->pb, end, SEEK_SET);
482
483 while (!ogg_read_page (s, &i)){
484 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
485 ogg->streams[i].codec)
486 idx = i;
487 }
488
489 if (idx != -1){
490 s->streams[idx]->duration =
491 ogg_gptopts (s, idx, ogg->streams[idx].granule, NULL);
492 }
493
494 ogg->size = size;
495 ogg_restore (s, 0);
496
497 return 0;
498 }
499
500
501 static int
502 ogg_read_header (AVFormatContext * s, AVFormatParameters * ap)
503 {
504 struct ogg *ogg = s->priv_data;
505 int i;
506 ogg->curidx = -1;
507 //linear headers seek from start
508 if (ogg_get_headers (s) < 0){
509 return -1;
510 }
511
512 for (i = 0; i < ogg->nstreams; i++)
513 if (ogg->streams[i].header < 0)
514 ogg->streams[i].codec = NULL;
515
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
524 static int
525 ogg_read_packet (AVFormatContext * s, AVPacket * pkt)
526 {
527 struct ogg *ogg;
528 struct ogg_stream *os;
529 int idx = -1;
530 int pstart, psize;
531 int64_t fpos;
532
533 //Get an ogg packet
534 do{
535 if (ogg_packet (s, &idx, &pstart, &psize, &fpos) < 0)
536 return AVERROR(EIO);
537 }while (idx < 0 || !s->streams[idx]);
538
539 ogg = s->priv_data;
540 os = ogg->streams + idx;
541
542 //Alloc a pkt
543 if (av_new_packet (pkt, psize) < 0)
544 return AVERROR(EIO);
545 pkt->stream_index = idx;
546 memcpy (pkt->data, os->buf + pstart, psize);
547
548 if (os->lastpts != AV_NOPTS_VALUE) {
549 pkt->pts = os->lastpts;
550 os->lastpts = AV_NOPTS_VALUE;
551 }
552 if (os->lastdts != AV_NOPTS_VALUE) {
553 pkt->dts = os->lastdts;
554 os->lastdts = AV_NOPTS_VALUE;
555 }
556 if (os->page_end) {
557 if (os->granule != -1LL) {
558 if (os->codec && os->codec->granule_is_start)
559 pkt->pts = ogg_gptopts(s, idx, os->granule, &pkt->dts);
560 else
561 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
562 os->granule = -1LL;
563 } else
564 av_log(s, AV_LOG_WARNING, "Packet is missing granule\n");
565 }
566
567 pkt->flags = os->pflags;
568 pkt->duration = os->pduration;
569 pkt->pos = fpos;
570
571 return psize;
572 }
573
574
575 static int
576 ogg_read_close (AVFormatContext * s)
577 {
578 struct ogg *ogg = s->priv_data;
579 int i;
580
581 for (i = 0; i < ogg->nstreams; i++){
582 av_free (ogg->streams[i].buf);
583 av_free (ogg->streams[i].private);
584 }
585 av_free (ogg->streams);
586 return 0;
587 }
588
589
590 static int64_t
591 ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg,
592 int64_t pos_limit)
593 {
594 struct ogg *ogg = s->priv_data;
595 ByteIOContext *bc = s->pb;
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) {
602 pts = ogg_gptopts(s, i, ogg->streams[i].granule, NULL);
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;
611 }
612
613 static int ogg_probe(AVProbeData *p)
614 {
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
623 AVInputFormat ogg_demuxer = {
624 "ogg",
625 NULL_IF_CONFIG_SMALL("Ogg"),
626 sizeof (struct ogg),
627 ogg_probe,
628 ogg_read_header,
629 ogg_read_packet,
630 ogg_read_close,
631 NULL,
632 ogg_read_timestamp,
633 .extensions = "ogg",
634 .metadata_conv = ff_vorbiscomment_metadata_conv,
635 };