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