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