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