lavf/srtdec: simplify start/end computation.
[ffmpeg.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     Copyright (C) 2005  Michael Ahlberg, Måns Rullgård
9
10     Permission is hereby granted, free of charge, to any person
11     obtaining a copy of this software and associated documentation
12     files (the "Software"), to deal in the Software without
13     restriction, including without limitation the rights to use, copy,
14     modify, merge, publish, distribute, sublicense, and/or sell copies
15     of the Software, and to permit persons to whom the Software is
16     furnished to do so, subject to the following conditions:
17
18     The above copyright notice and this permission notice shall be
19     included in all copies or substantial portions of the Software.
20
21     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28     DEALINGS IN THE SOFTWARE.
29  */
30
31 #include <stdio.h>
32 #include "libavutil/avassert.h"
33 #include "oggdec.h"
34 #include "avformat.h"
35 #include "internal.h"
36 #include "vorbiscomment.h"
37
38 #define MAX_PAGE_SIZE 65307
39 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
40
41 static const struct ogg_codec * const ogg_codecs[] = {
42     &ff_skeleton_codec,
43     &ff_dirac_codec,
44     &ff_speex_codec,
45     &ff_vorbis_codec,
46     &ff_theora_codec,
47     &ff_flac_codec,
48     &ff_celt_codec,
49     &ff_opus_codec,
50     &ff_old_dirac_codec,
51     &ff_old_flac_codec,
52     &ff_ogm_video_codec,
53     &ff_ogm_audio_codec,
54     &ff_ogm_text_codec,
55     &ff_ogm_old_codec,
56     NULL
57 };
58
59 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts);
60
61 //FIXME We could avoid some structure duplication
62 static int ogg_save(AVFormatContext *s)
63 {
64     struct ogg *ogg = s->priv_data;
65     struct ogg_state *ost =
66         av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
67     int i;
68     ost->pos = avio_tell (s->pb);
69     ost->curidx = ogg->curidx;
70     ost->next = ogg->state;
71     ost->nstreams = ogg->nstreams;
72     memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
73
74     for (i = 0; i < ogg->nstreams; i++){
75         struct ogg_stream *os = ogg->streams + i;
76         os->buf = av_mallocz (os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
77         memcpy (os->buf, ost->streams[i].buf, os->bufpos);
78     }
79
80     ogg->state = ost;
81
82     return 0;
83 }
84
85 static int ogg_restore(AVFormatContext *s, int discard)
86 {
87     struct ogg *ogg = s->priv_data;
88     AVIOContext *bc = s->pb;
89     struct ogg_state *ost = ogg->state;
90     int i;
91
92     if (!ost)
93         return 0;
94
95     ogg->state = ost->next;
96
97     if (!discard){
98         struct ogg_stream *old_streams = ogg->streams;
99
100         for (i = 0; i < ogg->nstreams; i++)
101             av_free (ogg->streams[i].buf);
102
103         avio_seek (bc, ost->pos, SEEK_SET);
104         ogg->curidx = ost->curidx;
105         ogg->nstreams = ost->nstreams;
106         ogg->streams = av_realloc (ogg->streams,
107                                    ogg->nstreams * sizeof (*ogg->streams));
108
109         if (ogg->streams) {
110             memcpy(ogg->streams, ost->streams,
111                    ost->nstreams * sizeof(*ogg->streams));
112         } else {
113             av_free(old_streams);
114             ogg->nstreams = 0;
115         }
116     }
117
118     av_free (ost);
119
120     return 0;
121 }
122
123 static int ogg_reset(AVFormatContext *s)
124 {
125     struct ogg *ogg = s->priv_data;
126     int i;
127     int64_t start_pos = avio_tell(s->pb);
128
129     for (i = 0; i < ogg->nstreams; i++){
130         struct ogg_stream *os = ogg->streams + i;
131         os->bufpos = 0;
132         os->pstart = 0;
133         os->psize = 0;
134         os->granule = -1;
135         os->lastpts = AV_NOPTS_VALUE;
136         os->lastdts = AV_NOPTS_VALUE;
137         os->sync_pos = -1;
138         os->page_pos = 0;
139         os->nsegs = 0;
140         os->segp = 0;
141         os->incomplete = 0;
142         os->got_data = 0;
143         if (start_pos <= s->data_offset) {
144             os->lastpts = 0;
145         }
146     }
147
148     ogg->curidx = -1;
149
150     return 0;
151 }
152
153 static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size)
154 {
155     int i;
156
157     for (i = 0; ogg_codecs[i]; i++)
158         if (size >= ogg_codecs[i]->magicsize &&
159             !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
160             return ogg_codecs[i];
161
162     return NULL;
163 }
164
165 /**
166  * Replace the current stream with a new one. This is a typical webradio
167  * situation where a new audio stream spawn (identified with a new serial) and
168  * must replace the previous one (track switch).
169  */
170 static int ogg_replace_stream(AVFormatContext *s, uint32_t serial)
171 {
172     struct ogg *ogg = s->priv_data;
173     struct ogg_stream *os;
174     unsigned bufsize;
175     uint8_t *buf;
176
177     if (ogg->nstreams != 1) {
178         av_log_missing_feature(s, "Changing stream parameters in multistream ogg", 0);
179         return AVERROR_PATCHWELCOME;
180     }
181
182     os = &ogg->streams[0];
183
184     buf = os->buf;
185     bufsize = os->bufsize;
186
187     if (!ogg->state || ogg->state->streams[0].private != os->private)
188         av_freep(&ogg->streams[0].private);
189
190     /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We
191      * also re-use the ogg_stream allocated buffer */
192     memset(os, 0, sizeof(*os));
193     os->serial = serial;
194     os->bufsize = bufsize;
195     os->buf = buf;
196     os->header = -1;
197
198     return 0;
199 }
200
201 static int ogg_new_stream(AVFormatContext *s, uint32_t serial)
202 {
203     struct ogg *ogg = s->priv_data;
204     int idx = ogg->nstreams;
205     AVStream *st;
206     struct ogg_stream *os;
207     size_t size;
208
209     if (ogg->state) {
210         av_log(s, AV_LOG_ERROR, "New streams are not supposed to be added "
211                "in between Ogg context save/restore operations.\n");
212         return AVERROR_BUG;
213     }
214
215     /* Allocate and init a new Ogg Stream */
216     if (av_size_mult(ogg->nstreams + 1, sizeof(*ogg->streams), &size) < 0 ||
217         !(os = av_realloc(ogg->streams, size)))
218         return AVERROR(ENOMEM);
219     ogg->streams = os;
220     os = ogg->streams + idx;
221     memset(os, 0, sizeof(*os));
222     os->serial = serial;
223     os->bufsize = DECODER_BUFFER_SIZE;
224     os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
225     os->header = -1;
226     os->start_granule = OGG_NOGRANULE_VALUE;
227     if (!os->buf)
228         return AVERROR(ENOMEM);
229
230     /* Create the associated AVStream */
231     st = avformat_new_stream(s, NULL);
232     if (!st) {
233         av_freep(&os->buf);
234         return AVERROR(ENOMEM);
235     }
236     st->id = idx;
237     avpriv_set_pts_info(st, 64, 1, 1000000);
238
239     ogg->nstreams++;
240     return idx;
241 }
242
243 static int ogg_new_buf(struct ogg *ogg, int idx)
244 {
245     struct ogg_stream *os = ogg->streams + idx;
246     uint8_t *nb = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
247     int size = os->bufpos - os->pstart;
248     if(os->buf){
249         memcpy(nb, os->buf + os->pstart, size);
250         av_free(os->buf);
251     }
252     os->buf = nb;
253     os->bufpos = size;
254     os->pstart = 0;
255
256     return 0;
257 }
258
259 static int data_packets_seen(const struct ogg *ogg)
260 {
261     int i;
262
263     for (i = 0; i < ogg->nstreams; i++)
264         if (ogg->streams[i].got_data)
265             return 1;
266     return 0;
267 }
268
269 static int ogg_read_page(AVFormatContext *s, int *sid)
270 {
271     AVIOContext *bc = s->pb;
272     struct ogg *ogg = s->priv_data;
273     struct ogg_stream *os;
274     int ret, i = 0;
275     int flags, nsegs;
276     uint64_t gp;
277     uint32_t serial;
278     int size, idx;
279     uint8_t sync[4];
280     int sp = 0;
281
282     ret = avio_read(bc, sync, 4);
283     if (ret < 4)
284         return ret < 0 ? ret : AVERROR_EOF;
285
286     do{
287         int c;
288
289         if (sync[sp & 3] == 'O' &&
290             sync[(sp + 1) & 3] == 'g' &&
291             sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
292             break;
293
294         c = avio_r8(bc);
295         if (url_feof(bc))
296             return AVERROR_EOF;
297         sync[sp++ & 3] = c;
298     }while (i++ < MAX_PAGE_SIZE);
299
300     if (i >= MAX_PAGE_SIZE){
301         av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
302         return AVERROR_INVALIDDATA;
303     }
304
305     if (avio_r8(bc) != 0){      /* version */
306         av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n");
307         return AVERROR_INVALIDDATA;
308     }
309
310     flags = avio_r8(bc);
311     gp = avio_rl64 (bc);
312     serial = avio_rl32 (bc);
313     avio_skip(bc, 8); /* seq, crc */
314     nsegs = avio_r8(bc);
315
316     idx = ogg_find_stream (ogg, serial);
317     if (idx < 0){
318         if (data_packets_seen(ogg))
319             idx = ogg_replace_stream(s, serial);
320         else
321             idx = ogg_new_stream(s, serial);
322
323         if (idx < 0) {
324             av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n");
325             return idx;
326         }
327     }
328
329     os = ogg->streams + idx;
330     os->page_pos = avio_tell(bc) - 27;
331
332     if(os->psize > 0)
333         ogg_new_buf(ogg, idx);
334
335     ret = avio_read(bc, os->segments, nsegs);
336     if (ret < nsegs)
337         return ret < 0 ? ret : AVERROR_EOF;
338
339     os->nsegs = nsegs;
340     os->segp = 0;
341
342     size = 0;
343     for (i = 0; i < nsegs; i++)
344         size += os->segments[i];
345
346     if (!(flags & OGG_FLAG_BOS))
347         os->got_data = 1;
348
349     if (flags & OGG_FLAG_CONT || os->incomplete){
350         if (!os->psize){
351             // If this is the very first segment we started
352             // playback in the middle of a continuation packet.
353             // Discard it since we missed the start of it.
354             while (os->segp < os->nsegs){
355                 int seg = os->segments[os->segp++];
356                 os->pstart += seg;
357                 if (seg < 255)
358                     break;
359             }
360             os->sync_pos = os->page_pos;
361         }
362     }else{
363         os->psize = 0;
364         os->sync_pos = os->page_pos;
365     }
366
367     if (os->bufsize - os->bufpos < size){
368         uint8_t *nb = av_malloc ((os->bufsize *= 2) + FF_INPUT_BUFFER_PADDING_SIZE);
369         memcpy (nb, os->buf, os->bufpos);
370         av_free (os->buf);
371         os->buf = nb;
372     }
373
374     ret = avio_read(bc, os->buf + os->bufpos, size);
375     if (ret < size)
376         return ret < 0 ? ret : AVERROR_EOF;
377
378     os->bufpos += size;
379     os->granule = gp;
380     os->flags = flags;
381
382     memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE);
383     if (sid)
384         *sid = idx;
385
386     return 0;
387 }
388
389 /**
390  * @brief find the next Ogg packet
391  * @param *sid is set to the stream for the packet or -1 if there is
392  *             no matching stream, in that case assume all other return
393  *             values to be uninitialized.
394  * @return negative value on error or EOF.
395  */
396 static int ogg_packet(AVFormatContext *s, int *sid, int *dstart, int *dsize,
397                       int64_t *fpos)
398 {
399     struct ogg *ogg = s->priv_data;
400     int idx, i, ret;
401     struct ogg_stream *os;
402     int complete = 0;
403     int segp = 0, psize = 0;
404
405     av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx);
406     if (sid)
407         *sid = -1;
408
409     do{
410         idx = ogg->curidx;
411
412         while (idx < 0){
413             ret = ogg_read_page(s, &idx);
414             if (ret < 0)
415                 return ret;
416         }
417
418         os = ogg->streams + idx;
419
420         av_dlog(s, "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
421                 idx, os->pstart, os->psize, os->segp, os->nsegs);
422
423         if (!os->codec){
424             if (os->header < 0){
425                 os->codec = ogg_find_codec (os->buf, os->bufpos);
426                 if (!os->codec){
427                     av_log(s, AV_LOG_WARNING, "Codec not found\n");
428                     os->header = 0;
429                     return 0;
430                 }
431             }else{
432                 return 0;
433             }
434         }
435
436         segp = os->segp;
437         psize = os->psize;
438
439         while (os->segp < os->nsegs){
440             int ss = os->segments[os->segp++];
441             os->psize += ss;
442             if (ss < 255){
443                 complete = 1;
444                 break;
445             }
446         }
447
448         if (!complete && os->segp == os->nsegs){
449             ogg->curidx = -1;
450             // Do not set incomplete for empty packets.
451             // Together with the code in ogg_read_page
452             // that discards all continuation of empty packets
453             // we would get an infinite loop.
454             os->incomplete = !!os->psize;
455         }
456     }while (!complete);
457
458
459     if (os->granule == -1)
460         av_log(s, AV_LOG_WARNING, "Page at %"PRId64" is missing granule\n", os->page_pos);
461
462     ogg->curidx = idx;
463     os->incomplete = 0;
464
465     if (os->header) {
466         os->header = os->codec->header (s, idx);
467         if (!os->header){
468             os->segp = segp;
469             os->psize = psize;
470
471             // We have reached the first non-header packet in this stream.
472             // Unfortunately more header packets may still follow for others,
473             // but if we continue with header parsing we may lose data packets.
474             ogg->headers = 1;
475
476             // Update the header state for all streams and
477             // compute the data_offset.
478             if (!s->data_offset)
479                 s->data_offset = os->sync_pos;
480             for (i = 0; i < ogg->nstreams; i++) {
481                 struct ogg_stream *cur_os = ogg->streams + i;
482
483                 // if we have a partial non-header packet, its start is
484                 // obviously at or after the data start
485                 if (cur_os->incomplete)
486                     s->data_offset = FFMIN(s->data_offset, cur_os->sync_pos);
487             }
488         }else{
489             os->nb_header++;
490             os->pstart += os->psize;
491             os->psize = 0;
492         }
493     } else {
494         os->pflags = 0;
495         os->pduration = 0;
496         if (os->codec && os->codec->packet)
497             os->codec->packet (s, idx);
498         if (sid)
499             *sid = idx;
500         if (dstart)
501             *dstart = os->pstart;
502         if (dsize)
503             *dsize = os->psize;
504         if (fpos)
505             *fpos = os->sync_pos;
506         os->pstart += os->psize;
507         os->psize = 0;
508         if(os->pstart == os->bufpos)
509             os->bufpos = os->pstart = 0;
510         os->sync_pos = os->page_pos;
511     }
512
513     // determine whether there are more complete packets in this page
514     // if not, the page's granule will apply to this packet
515     os->page_end = 1;
516     for (i = os->segp; i < os->nsegs; i++)
517         if (os->segments[i] < 255) {
518             os->page_end = 0;
519             break;
520         }
521
522     if (os->segp == os->nsegs)
523         ogg->curidx = -1;
524
525     return 0;
526 }
527
528 static int ogg_get_length(AVFormatContext *s)
529 {
530     struct ogg *ogg = s->priv_data;
531     int i;
532     int64_t size, end;
533     int streams_left=0;
534
535     if(!s->pb->seekable)
536         return 0;
537
538 // already set
539     if (s->duration != AV_NOPTS_VALUE)
540         return 0;
541
542     size = avio_size(s->pb);
543     if(size < 0)
544         return 0;
545     end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0;
546
547     ogg_save (s);
548     avio_seek (s->pb, end, SEEK_SET);
549
550     while (!ogg_read_page (s, &i)){
551         if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
552             ogg->streams[i].codec) {
553             s->streams[i]->duration =
554                 ogg_gptopts (s, i, ogg->streams[i].granule, NULL);
555             if (s->streams[i]->start_time != AV_NOPTS_VALUE){
556                 s->streams[i]->duration -= s->streams[i]->start_time;
557                 streams_left-= (ogg->streams[i].got_start==-1);
558                 ogg->streams[i].got_start= 1;
559             }else if(!ogg->streams[i].got_start){
560                 ogg->streams[i].got_start= -1;
561                 streams_left++;
562             }
563         }
564     }
565
566     ogg_restore (s, 0);
567
568     ogg_save (s);
569     avio_seek (s->pb, s->data_offset, SEEK_SET);
570     ogg_reset(s);
571     while (streams_left > 0 && !ogg_packet(s, &i, NULL, NULL, NULL)) {
572         int64_t pts;
573         if (i < 0) continue;
574         pts = ogg_calc_pts(s, i, NULL);
575         if (pts != AV_NOPTS_VALUE && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start){
576             s->streams[i]->duration -= pts;
577             ogg->streams[i].got_start= 1;
578             streams_left--;
579         }else if(s->streams[i]->start_time != AV_NOPTS_VALUE && !ogg->streams[i].got_start){
580             ogg->streams[i].got_start= 1;
581             streams_left--;
582         }
583     }
584     ogg_restore (s, 0);
585
586     return 0;
587 }
588
589 static int ogg_read_header(AVFormatContext *s)
590 {
591     struct ogg *ogg = s->priv_data;
592     int ret, i;
593
594     ogg->curidx = -1;
595
596     //linear headers seek from start
597     do {
598         ret = ogg_packet(s, NULL, NULL, NULL, NULL);
599         if (ret < 0)
600             return ret;
601     } while (!ogg->headers);
602     av_dlog(s, "found headers\n");
603
604     for (i = 0; i < ogg->nstreams; i++) {
605         struct ogg_stream *os = ogg->streams + i;
606
607         if (ogg->streams[i].header < 0) {
608             av_log(s, AV_LOG_ERROR, "Header parsing failed for stream %d\n", i);
609             ogg->streams[i].codec = NULL;
610         } else if (os->codec && os->nb_header < os->codec->nb_header) {
611             av_log(s, AV_LOG_WARNING, "Number of headers (%d) mismatch for stream %d\n", os->nb_header, i);
612         }
613         if (os->start_granule != OGG_NOGRANULE_VALUE)
614             os->lastpts = s->streams[i]->start_time =
615                 ogg_gptopts(s, i, os->start_granule, NULL);
616     }
617
618     //linear granulepos seek from end
619     ogg_get_length (s);
620
621     return 0;
622 }
623
624 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts)
625 {
626     struct ogg *ogg = s->priv_data;
627     struct ogg_stream *os = ogg->streams + idx;
628     int64_t pts = AV_NOPTS_VALUE;
629
630     if (dts)
631         *dts = AV_NOPTS_VALUE;
632
633     if (os->lastpts != AV_NOPTS_VALUE) {
634         pts = os->lastpts;
635         os->lastpts = AV_NOPTS_VALUE;
636     }
637     if (os->lastdts != AV_NOPTS_VALUE) {
638         if (dts)
639             *dts = os->lastdts;
640         os->lastdts = AV_NOPTS_VALUE;
641     }
642     if (os->page_end) {
643         if (os->granule != -1LL) {
644             if (os->codec && os->codec->granule_is_start)
645                 pts = ogg_gptopts(s, idx, os->granule, dts);
646             else
647                 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
648             os->granule = -1LL;
649         }
650     }
651     return pts;
652 }
653
654 static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize)
655 {
656     struct ogg *ogg = s->priv_data;
657     struct ogg_stream *os = ogg->streams + idx;
658     if (psize && s->streams[idx]->codec->codec_id == AV_CODEC_ID_THEORA) {
659         if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) {
660             os->pflags ^= AV_PKT_FLAG_KEY;
661             av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n",
662                    (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-");
663         }
664     }
665 }
666
667 static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt)
668 {
669     struct ogg *ogg;
670     struct ogg_stream *os;
671     int idx, ret;
672     int pstart, psize;
673     int64_t fpos, pts, dts;
674
675     //Get an ogg packet
676 retry:
677     do{
678         ret = ogg_packet(s, &idx, &pstart, &psize, &fpos);
679         if (ret < 0)
680             return ret;
681     }while (idx < 0 || !s->streams[idx]);
682
683     ogg = s->priv_data;
684     os = ogg->streams + idx;
685
686     // pflags might not be set until after this
687     pts = ogg_calc_pts(s, idx, &dts);
688     ogg_validate_keyframe(s, idx, pstart, psize);
689
690     if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
691         goto retry;
692     os->keyframe_seek = 0;
693
694     //Alloc a pkt
695     ret = av_new_packet(pkt, psize);
696     if (ret < 0)
697         return ret;
698     pkt->stream_index = idx;
699     memcpy (pkt->data, os->buf + pstart, psize);
700
701     pkt->pts = pts;
702     pkt->dts = dts;
703     pkt->flags = os->pflags;
704     pkt->duration = os->pduration;
705     pkt->pos = fpos;
706
707     return psize;
708 }
709
710 static int ogg_read_close(AVFormatContext *s)
711 {
712     struct ogg *ogg = s->priv_data;
713     int i;
714
715     for (i = 0; i < ogg->nstreams; i++){
716         av_free (ogg->streams[i].buf);
717         av_free (ogg->streams[i].private);
718     }
719     av_free (ogg->streams);
720     return 0;
721 }
722
723 static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
724                                   int64_t *pos_arg, int64_t pos_limit)
725 {
726     struct ogg *ogg = s->priv_data;
727     AVIOContext *bc = s->pb;
728     int64_t pts = AV_NOPTS_VALUE;
729     int64_t keypos = -1;
730     int i;
731     int pstart, psize;
732     avio_seek(bc, *pos_arg, SEEK_SET);
733     ogg_reset(s);
734
735     while (avio_tell(bc) <= pos_limit && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) {
736         if (i == stream_index) {
737             struct ogg_stream *os = ogg->streams + stream_index;
738             pts = ogg_calc_pts(s, i, NULL);
739             ogg_validate_keyframe(s, i, pstart, psize);
740             if (os->pflags & AV_PKT_FLAG_KEY) {
741                 keypos = *pos_arg;
742             } else if (os->keyframe_seek) {
743                 // if we had a previous keyframe but no pts for it,
744                 // return that keyframe with this pts value.
745                 if (keypos >= 0)
746                     *pos_arg = keypos;
747                 else
748                     pts = AV_NOPTS_VALUE;
749             }
750         }
751         if (pts != AV_NOPTS_VALUE)
752             break;
753     }
754     ogg_reset(s);
755     return pts;
756 }
757
758 static int ogg_read_seek(AVFormatContext *s, int stream_index,
759                          int64_t timestamp, int flags)
760 {
761     struct ogg *ogg = s->priv_data;
762     struct ogg_stream *os = ogg->streams + stream_index;
763     int ret;
764
765     av_assert0(stream_index < ogg->nstreams);
766     // Ensure everything is reset even when seeking via
767     // the generated index.
768     ogg_reset(s);
769
770     // Try seeking to a keyframe first. If this fails (very possible),
771     // av_seek_frame will fall back to ignoring keyframes
772     if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO
773         && !(flags & AVSEEK_FLAG_ANY))
774         os->keyframe_seek = 1;
775
776     ret = ff_seek_frame_binary(s, stream_index, timestamp, flags);
777     os = ogg->streams + stream_index;
778     if (ret < 0)
779         os->keyframe_seek = 0;
780     return ret;
781 }
782
783 static int ogg_probe(AVProbeData *p)
784 {
785     if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7)
786         return AVPROBE_SCORE_MAX;
787     return 0;
788 }
789
790 AVInputFormat ff_ogg_demuxer = {
791     .name           = "ogg",
792     .long_name      = NULL_IF_CONFIG_SMALL("Ogg"),
793     .priv_data_size = sizeof(struct ogg),
794     .read_probe     = ogg_probe,
795     .read_header    = ogg_read_header,
796     .read_packet    = ogg_read_packet,
797     .read_close     = ogg_read_close,
798     .read_seek      = ogg_read_seek,
799     .read_timestamp = ogg_read_timestamp,
800     .extensions     = "ogg",
801     .flags          = AVFMT_GENERIC_INDEX,
802 };