Ogg demuxer ported from tcvp by Luca Barbato <lu_zero at gentoo dot org>,
[libav.git] / libavformat / ogg2.c
CommitLineData
9146ca37
MR
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 "ogg2.h"
35#include "avformat.h"
36
37#define MAX_PAGE_SIZE 65307
38#define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
39
40static ogg_codec_t *ogg_codecs[] = {
41 &vorbis_codec,
42 NULL
43};
44
45#if 0 // CONFIG_ENCODERS
46static int
47ogg_write_header (AVFormatContext * avfcontext)
48{
49}
50
51static int
52ogg_write_packet (AVFormatContext * avfcontext, AVPacket * pkt)
53{
54}
55
56
57static int
58ogg_write_trailer (AVFormatContext * avfcontext)
59{
60}
61
62
63static AVOutputFormat ogg_oformat = {
64 "ogg",
65 "Ogg Vorbis",
66 "audio/x-vorbis",
67 "ogg",
68 sizeof (OggContext),
69 CODEC_ID_VORBIS,
70 0,
71 ogg_write_header,
72 ogg_write_packet,
73 ogg_write_trailer,
74};
75#endif //CONFIG_ENCODERS
76
77//FIXME We could avoid some structure duplication
78static int
79ogg_save (AVFormatContext * s)
80{
81 ogg_t *ogg = s->priv_data;
82 ogg_state_t *ost =
83 av_malloc(sizeof (*ost) + ogg->nstreams * sizeof (*ogg->streams));
84 int i;
85 ost->pos = url_ftell (&s->pb);;
86 ost->curidx = ogg->curidx;
87 ost->next = ogg->state;
88 memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
89
90 for (i = 0; i < ogg->nstreams; i++){
91 ogg_stream_t *os = ogg->streams + i;
92 os->buf = av_malloc (os->bufsize);
93 memset (os->buf, 0, os->bufsize);
94 memcpy (os->buf, ost->streams[i].buf, os->bufpos);
95 }
96
97 ogg->state = ost;
98
99 return 0;
100}
101
102static int
103ogg_restore (AVFormatContext * s, int discard)
104{
105 ogg_t *ogg = s->priv_data;
106 ByteIOContext *bc = &s->pb;
107 ogg_state_t *ost = ogg->state;
108 int i;
109
110 if (!ost)
111 return 0;
112
113 ogg->state = ost->next;
114
115 if (!discard){
116 for (i = 0; i < ogg->nstreams; i++)
117 av_free (ogg->streams[i].buf);
118
119 url_fseek (bc, ost->pos, SEEK_SET);
120 ogg->curidx = ost->curidx;
121 memcpy (ogg->streams, ost->streams,
122 ogg->nstreams * sizeof (*ogg->streams));
123 }
124
125 av_free (ost);
126
127 return 0;
128}
129
130static int
131ogg_reset (ogg_t * ogg)
132{
133 int i;
134
135 for (i = 0; i < ogg->nstreams; i++){
136 ogg_stream_t *os = ogg->streams + i;
137 os->bufpos = 0;
138 os->pstart = 0;
139 os->psize = 0;
140 os->granule = -1;
141 os->lastgp = -1;
142 os->nsegs = 0;
143 os->segp = 0;
144 }
145
146 ogg->curidx = -1;
147
148 return 0;
149}
150
151static ogg_codec_t *
152ogg_find_codec (u_char * buf, int size)
153{
154 int i;
155
156 for (i = 0; ogg_codecs[i]; i++)
157 if (size >= ogg_codecs[i]->magicsize &&
158 !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
159 return ogg_codecs[i];
160
161 return NULL;
162}
163
164static int
165ogg_find_stream (ogg_t * ogg, int serial)
166{
167 int i;
168
169 for (i = 0; i < ogg->nstreams; i++)
170 if (ogg->streams[i].serial == serial)
171 return i;
172
173 return -1;
174}
175
176static int
177ogg_new_stream (AVFormatContext * s, uint32_t serial)
178{
179
180 ogg_t *ogg = s->priv_data;
181 int idx = ogg->nstreams++;
182 AVStream *st;
183 ogg_stream_t *os;
184
185 ogg->streams = av_realloc (ogg->streams,
186 ogg->nstreams * sizeof (*ogg->streams));
187 memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
188 os = ogg->streams + idx;
189 os->serial = serial;
190 os->bufsize = DECODER_BUFFER_SIZE;
191 os->buf = av_malloc (os->bufsize);
192 memset (os->buf, 0, os->bufsize);
193 os->header = -1;
194
195 st = av_new_stream (s, idx);
196 if (!st)
197 return AVERROR_NOMEM;
198
199 av_set_pts_info(st, 64, 1, 1000000);
200 st->start_time = 0;
201
202 return idx;
203}
204
205static int
206ogg_read_page (AVFormatContext * s, int *str)
207{
208 ByteIOContext *bc = &s->pb;
209 ogg_t *ogg = s->priv_data;
210 ogg_stream_t *os;
211 int i = 0;
212 int flags, nsegs;
213 uint64_t gp;
214 uint32_t serial;
215 uint32_t seq;
216 uint32_t crc;
217 int size, idx;
218 char sync[4];
219 int sp = 0;
220
221 if (get_buffer (bc, sync, 4) < 4)
222 return -1;
223
224 do{
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
232 c = url_fgetc (bc);
233 if (c < 0)
234 return -1;
235 sync[sp++ & 3] = c;
236 }while (i++ < MAX_PAGE_SIZE);
237
238 if (i >= MAX_PAGE_SIZE){
239 av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
240 return -1;
241 }
242
243 if (url_fgetc (bc) != 0) /* version */
244 return -1;
245
246 flags = url_fgetc (bc);
247 gp = get_le64 (bc);
248 serial = get_le32 (bc);
249 seq = get_le32 (bc);
250 crc = get_le32 (bc);
251 nsegs = url_fgetc (bc);
252
253 idx = ogg_find_stream (ogg, serial);
254 if (idx < 0){
255 idx = ogg_new_stream (s, serial);
256 if (idx < 0)
257 return -1;
258 }
259
260 os = ogg->streams + idx;
261
262 if (get_buffer (bc, os->segments, nsegs) < nsegs)
263 return -1;
264
265 os->nsegs = nsegs;
266 os->segp = 0;
267
268 size = 0;
269 for (i = 0; i < nsegs; i++)
270 size += os->segments[i];
271
272 if (flags & OGG_FLAG_CONT){
273 if (!os->psize){
274 while (os->segp < os->nsegs){
275 int seg = os->segments[os->segp++];
276 os->pstart += seg;
277 if (seg < 255)
278 break;
279 }
280 }
281 }else{
282 os->psize = 0;
283 }
284
285 if (os->bufsize - os->bufpos < size){
286 u_char *nb = av_malloc (os->bufsize *= 2);
287 memset (nb, 0, os->bufsize);
288 memcpy (nb, os->buf, os->bufpos);
289 av_free (os->buf);
290 os->buf = nb;
291 }
292
293 if (get_buffer (bc, os->buf + os->bufpos, size) < size)
294 return -1;
295
296 os->lastgp = os->granule;
297 os->bufpos += size;
298 os->granule = gp;
299 os->flags = flags;
300
301 if (str)
302 *str = idx;
303
304 return 0;
305}
306
307static int
308ogg_packet (AVFormatContext * s, int *str)
309{
310 ogg_t *ogg = s->priv_data;
311 int idx;
312 ogg_stream_t *os;
313 int complete = 0;
314 int segp = 0, psize = 0;
315
316#if 0
317 av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx);
318#endif
319
320 do{
321 idx = ogg->curidx;
322
323 while (idx < 0){
324 if (ogg_read_page (s, &idx) < 0)
325 return -1;
326 }
327
328 os = ogg->streams + idx;
329
330#if 0
331 av_log (s, AV_LOG_DEBUG,
332 "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
333 idx, os->pstart, os->psize, os->segp, os->nsegs);
334#endif
335
336 if (!os->codec){
337 if (os->header < 0){
338 os->codec = ogg_find_codec (os->buf, os->bufpos);
339 if (!os->codec){
340 os->header = 0;
341 return 0;
342 }
343 }else{
344 return 0;
345 }
346 }
347
348 segp = os->segp;
349 psize = os->psize;
350
351 while (os->segp < os->nsegs){
352 int ss = os->segments[os->segp++];
353 os->psize += ss;
354 if (ss < 255){
355 complete = 1;
356 break;
357 }
358 }
359
360 if (!complete && os->segp == os->nsegs){
361 u_char *nb = av_malloc (os->bufsize);
362 int size = os->bufpos - os->pstart;
363 memset (nb, 0, os->bufsize);
364 memcpy (nb, os->buf + os->pstart, size);
365 av_free (os->buf);
366 os->buf = nb;
367 os->bufpos = size;
368 os->pstart = 0;
369 ogg->curidx = -1;
370 }
371 }while (!complete);
372
373#if 0
374 av_log (s, AV_LOG_DEBUG,
375 "ogg_packet: idx %i, frame size %i, start %i\n",
376 idx, os->psize, os->pstart);
377#endif
378
379 ogg->curidx = idx;
380
381 if (os->header < 0){
382 int hdr = os->codec->header (s, idx);
383 if (!hdr){
384 os->header = os->seq;
385 os->segp = segp;
386 os->psize = psize;
387 ogg->headers = 1;
388 }else{
389 os->pstart += os->psize;
390 os->psize = 0;
391 }
392 }
393
394 if (os->header > -1 && os->seq > os->header){
395 if (os->codec && os->codec->packet)
396 os->codec->packet (s, idx);
397 if (str)
398 *str = idx;
399 }
400
401 os->seq++;
402 if (os->segp == os->nsegs)
403 ogg->curidx = -1;
404
405 return 0;
406}
407
408static int
409ogg_get_headers (AVFormatContext * s)
410{
411 ogg_t *ogg = s->priv_data;
412
413 do{
414 if (ogg_packet (s, NULL) < 0)
415 return -1;
416 }while (!ogg->headers);
417
418#if 0
419 av_log (s, AV_LOG_DEBUG, "found headers\n");
420#endif
421
422 return 0;
423}
424
425static uint64_t
426ogg_gptopts (AVFormatContext * s, int i, uint64_t gp)
427{
428 AVStream *st = s->streams[i];
429 AVCodecContext *codec = &st->codec;
430 uint64_t pts = AV_NOPTS_VALUE;
431
432 if (codec->codec_type == CODEC_TYPE_AUDIO){
433 pts = gp * 1000000LL / codec->sample_rate;
434 }else if (codec->codec_type == CODEC_TYPE_VIDEO){
435//FIXME
436 pts = gp * 1000000LL / codec->sample_rate;
437// pts = gp * st->video.frame_rate.den * 27000000LL /
438// st->video.frame_rate.num;
439 }
440
441 return pts;
442}
443
444
445static int
446ogg_get_length (AVFormatContext * s)
447{
448 ogg_t *ogg = s->priv_data;
449 URLContext *h = url_fileno (&s->pb);
450 int idx = -1, i;
451//FIXME: get the right ctx flag to know if is seekable or not
452// if(ogg->f->flags & URL_FLAG_STREAMED)
453// return 0;
454
455// already set
456 if (s->duration != AV_NOPTS_VALUE)
457 return 0;
458
459 ogg_save (s);
460 url_seek (h, -MAX_PAGE_SIZE, SEEK_END);
461
462 while (!ogg_read_page (s, &i)){
463 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0)
464 idx = i;
465 }
466
467 if (idx != -1){
468 s->streams[idx]->duration =
469 ogg_gptopts (s, idx, ogg->streams[idx].granule);
470 }
471
472 ogg->size = url_filesize(h);
473 ogg_restore (s, 0);
474
475 return 0;
476}
477
478
479static int
480ogg_read_header (AVFormatContext * s, AVFormatParameters * ap)
481{
482 ogg_t *ogg = s->priv_data;
483 ogg->curidx = -1;
484 //linear headers seek from start
485 if (ogg_get_headers (s) < 0){
486 return -1;
487 }
488
489 //linear granulepos seek from end
490 ogg_get_length (s);
491
492 //fill the extradata in the per codec callbacks
493 return 0;
494}
495
496
497static int
498ogg_read_packet (AVFormatContext * s, AVPacket * pkt)
499{
500 ogg_t *ogg;
501 ogg_stream_t *os;
502 int idx = -1;
503
504 //Get an ogg packet
505 do{
506 if (ogg_packet (s, &idx) < 0)
507 return AVERROR_IO;
508 }while (idx < 0 || !s->streams[idx]);
509
510 ogg = s->priv_data;
511 os = ogg->streams + idx;
512
513 //Alloc a pkt
514 if (av_new_packet (pkt, os->psize) < 0)
515 return AVERROR_IO;
516 pkt->stream_index = idx;
517 memcpy (pkt->data, os->buf + os->pstart, os->psize);
518 if (os->lastgp != -1LL){
519 pkt->pts = ogg_gptopts (s, idx, os->lastgp);
520 os->lastgp = -1;
521 }
522 //next
523 os->pstart += os->psize;
524 os->psize = 0;
525 return os->psize;
526}
527
528
529static int
530ogg_read_close (AVFormatContext * s)
531{
532 ogg_t *ogg = s->priv_data;
533 int i;
534
535 for (i = 0; i < ogg->nstreams; i++){
536 av_free (ogg->streams[i].buf);
537 av_freep (&s->streams[i]->codec.extradata);
538 }
539 av_free (ogg->streams);
540 return 0;
541}
542
543
544static int
545ogg_read_seek (AVFormatContext * s, int stream_index, int64_t target_ts,
546 int flags)
547{
548 ogg_t *ogg = s->priv_data;
549 ByteIOContext *bc = &s->pb;
550 uint64_t min = 0, max = ogg->size;
551 uint64_t tmin = 0, tmax = s->duration;
552 int64_t pts = AV_NOPTS_VALUE;
553
554 ogg_save (s);
555
556 while (min <= max){
557 uint64_t p = min + (max - min) * target_ts / (tmax - tmin);
558 int i = -1;
559
560 url_fseek (bc, p, SEEK_SET);
561
562 while (!ogg_read_page (s, &i)){
563 if (ogg->streams[i].granule != 0 && ogg->streams[i].granule != -1)
564 break;
565 }
566
567 if (i == -1)
568 break;
569
570 pts = ogg_gptopts (s, i, ogg->streams[i].granule);
571 p = url_ftell (bc);
572
573 if (ABS (pts - target_ts) < 1000000LL)
574 break;
575
576 if (pts > target_ts){
577 max = p;
578 tmax = pts;
579 }else{
580 min = p;
581 tmin = pts;
582 }
583 }
584
585 if (ABS (pts - target_ts) < 1000000LL){
586 ogg_restore (s, 1);
587 ogg_reset (ogg);
588 }else{
589 ogg_restore (s, 0);
590 pts = AV_NOPTS_VALUE;
591 }
592
593 return pts;
594
595#if 0
596 //later...
597 int64_t pos;
598 if (av_seek_frame_binary (s, stream_index, target_ts, flags) < 0)
599 return -1;
600 pos = url_ftell (&s->pb);
601 ogg_read_timestamp (s, stream_index, &pos, pos - 1);
602#endif
603
604}
605
606#if 0
607static int64_t
608ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg,
609 int64_t pos_limit)
610{
611 ogg_t *ogg = s->priv_data;
612 ByteIOContext *bc = &s->pb;
613 int64_t pos, pts;
614
615 if (*pos_arg < 0)
616 return AV_NOPTS_VALUE;
617
618 pos = *pos_arg;
619}
620#endif
621
622static AVInputFormat ogg_iformat = {
623 "ogg",
624 "Ogg",
625 sizeof (ogg_t),
626 NULL,
627 ogg_read_header,
628 ogg_read_packet,
629 ogg_read_close,
630 ogg_read_seek,
631// ogg_read_timestamp,
632 .extensions = "ogg",
633};
634
635int
636ogg_init (void)
637{
638#if 0 // CONFIG_ENCODERS
639 av_register_output_format (&ogg_oformat);
640#endif
641 av_register_input_format (&ogg_iformat);
642 return 0;
643}