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