]> git.sesse.net Git - ffmpeg/blob - libavformat/ogg2.c
fix seeking bug
[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     NULL
43 };
44
45 #if 0                           // CONFIG_ENCODERS
46 static int
47 ogg_write_header (AVFormatContext * avfcontext)
48 {
49 }
50
51 static int
52 ogg_write_packet (AVFormatContext * avfcontext, AVPacket * pkt)
53 {
54 }
55
56
57 static int
58 ogg_write_trailer (AVFormatContext * avfcontext)
59 {
60 }
61
62
63 static 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
78 static int
79 ogg_save (AVFormatContext * s)
80 {
81     ogg_t *ogg = s->priv_data;
82     ogg_state_t *ost =
83         av_malloc(sizeof (*ost) + (ogg->nstreams-1) * 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
102 static int
103 ogg_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
130 static int
131 ogg_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
151 static ogg_codec_t *
152 ogg_find_codec (uint8_t * 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
164 static int
165 ogg_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
176 static int
177 ogg_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
205 static int
206 ogg_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         uint8_t *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
307 static int
308 ogg_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             uint8_t *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
408 static int
409 ogg_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
425 static uint64_t
426 ogg_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
445 static int
446 ogg_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
479 static int
480 ogg_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
497 static int
498 ogg_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
529 static int
530 ogg_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
544 static int
545 ogg_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 - tmin) / (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
607 static int64_t
608 ogg_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
622 static 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
635 int
636 ogg_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 }