libavformat/oggdec.c
9146ca37
 /*
  * Ogg bitstream support
  * Luca Barbato <lu_zero@gentoo.org>
  * Based on tcvp implementation
  */
 
e873c03a
 /*
9146ca37
     Copyright (C) 2005  Michael Ahlberg, Måns Rullgård
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation
     files (the "Software"), to deal in the Software without
     restriction, including without limitation the rights to use, copy,
     modify, merge, publish, distribute, sublicense, and/or sell copies
     of the Software, and to permit persons to whom the Software is
     furnished to do so, subject to the following conditions:
 
     The above copyright notice and this permission notice shall be
     included in all copies or substantial portions of the Software.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     DEALINGS IN THE SOFTWARE.
e873c03a
  */
9146ca37
 
 #include <stdio.h>
a0ddef24
 #include "oggdec.h"
9146ca37
 #include "avformat.h"
a2faa951
 #include "internal.h"
66061a12
 #include "vorbiscomment.h"
9146ca37
 
 #define MAX_PAGE_SIZE 65307
 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
 
77be08ee
 static const struct ogg_codec * const ogg_codecs[] = {
32ad8692
     &ff_skeleton_codec,
24ca518b
     &ff_dirac_codec,
547ea47d
     &ff_speex_codec,
     &ff_vorbis_codec,
     &ff_theora_codec,
     &ff_flac_codec,
e61b83d2
     &ff_celt_codec,
24ca518b
     &ff_old_dirac_codec,
547ea47d
     &ff_old_flac_codec,
     &ff_ogm_video_codec,
     &ff_ogm_audio_codec,
     &ff_ogm_text_codec,
     &ff_ogm_old_codec,
9146ca37
     NULL
 };
 
 //FIXME We could avoid some structure duplication
e575685f
 static int ogg_save(AVFormatContext *s)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
     struct ogg_state *ost =
ad3aa874
         av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
9146ca37
     int i;
384c9c2f
     ost->pos = avio_tell (s->pb);
9146ca37
     ost->curidx = ogg->curidx;
     ost->next = ogg->state;
20be72c8
     ost->nstreams = ogg->nstreams;
9146ca37
     memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
 
     for (i = 0; i < ogg->nstreams; i++){
77be08ee
         struct ogg_stream *os = ogg->streams + i;
9ded14fc
         os->buf = av_mallocz (os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
9146ca37
         memcpy (os->buf, ost->streams[i].buf, os->bufpos);
     }
 
     ogg->state = ost;
 
     return 0;
 }
 
e575685f
 static int ogg_restore(AVFormatContext *s, int discard)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
471fe57e
     AVIOContext *bc = s->pb;
77be08ee
     struct ogg_state *ost = ogg->state;
9146ca37
     int i;
 
     if (!ost)
         return 0;
 
     ogg->state = ost->next;
 
     if (!discard){
bc851a29
         struct ogg_stream *old_streams = ogg->streams;
 
9146ca37
         for (i = 0; i < ogg->nstreams; i++)
             av_free (ogg->streams[i].buf);
 
f59d8ff8
         avio_seek (bc, ost->pos, SEEK_SET);
9146ca37
         ogg->curidx = ost->curidx;
20be72c8
         ogg->nstreams = ost->nstreams;
bc851a29
         ogg->streams = av_realloc (ogg->streams,
                                    ogg->nstreams * sizeof (*ogg->streams));
 
         if (ogg->streams) {
             memcpy(ogg->streams, ost->streams,
                    ost->nstreams * sizeof(*ogg->streams));
         } else {
             av_free(old_streams);
             ogg->nstreams = 0;
         }
9146ca37
     }
 
     av_free (ost);
 
     return 0;
 }
 
e575685f
 static int ogg_reset(struct ogg *ogg)
9146ca37
 {
     int i;
 
     for (i = 0; i < ogg->nstreams; i++){
77be08ee
         struct ogg_stream *os = ogg->streams + i;
9146ca37
         os->bufpos = 0;
         os->pstart = 0;
         os->psize = 0;
         os->granule = -1;
5e15c7d9
         os->lastpts = AV_NOPTS_VALUE;
2d4970d8
         os->lastdts = AV_NOPTS_VALUE;
73823cb9
         os->sync_pos = -1;
         os->page_pos = 0;
9146ca37
         os->nsegs = 0;
         os->segp = 0;
ecc0027b
         os->incomplete = 0;
9146ca37
     }
 
     ogg->curidx = -1;
 
     return 0;
 }
 
e575685f
 static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size)
9146ca37
 {
     int i;
 
     for (i = 0; ogg_codecs[i]; i++)
         if (size >= ogg_codecs[i]->magicsize &&
             !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
             return ogg_codecs[i];
 
     return NULL;
 }
 
e575685f
 static int ogg_new_stream(AVFormatContext *s, uint32_t serial, int new_avstream)
9146ca37
 {
 
77be08ee
     struct ogg *ogg = s->priv_data;
9146ca37
     int idx = ogg->nstreams++;
     AVStream *st;
77be08ee
     struct ogg_stream *os;
9146ca37
 
6eebba08
     os = av_realloc (ogg->streams, ogg->nstreams * sizeof (*ogg->streams));
 
     if (!os)
         return AVERROR(ENOMEM);
 
     ogg->streams = os;
 
9146ca37
     memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
     os = ogg->streams + idx;
     os->serial = serial;
     os->bufsize = DECODER_BUFFER_SIZE;
9ded14fc
     os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
9146ca37
     os->header = -1;
 
5780f41a
     if (new_avstream) {
84ad31ff
         st = avformat_new_stream(s, NULL);
5780f41a
         if (!st)
             return AVERROR(ENOMEM);
9146ca37
 
84ad31ff
         st->id = idx;
c3f9ebf7
         avpriv_set_pts_info(st, 64, 1, 1000000);
5780f41a
     }
9146ca37
 
     return idx;
 }
 
e575685f
 static int ogg_new_buf(struct ogg *ogg, int idx)
12a195e3
 {
77be08ee
     struct ogg_stream *os = ogg->streams + idx;
9ded14fc
     uint8_t *nb = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE);
12a195e3
     int size = os->bufpos - os->pstart;
     if(os->buf){
         memcpy(nb, os->buf + os->pstart, size);
         av_free(os->buf);
     }
     os->buf = nb;
     os->bufpos = size;
     os->pstart = 0;
 
     return 0;
 }
 
e575685f
 static int ogg_read_page(AVFormatContext *s, int *str)
9146ca37
 {
471fe57e
     AVIOContext *bc = s->pb;
77be08ee
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os;
bec994df
     int ret, i = 0;
9146ca37
     int flags, nsegs;
     uint64_t gp;
     uint32_t serial;
     int size, idx;
191e8ca7
     uint8_t sync[4];
9146ca37
     int sp = 0;
 
9cec1bbd
     ret = avio_read(bc, sync, 4);
bec994df
     if (ret < 4)
         return ret < 0 ? ret : AVERROR_EOF;
9146ca37
 
     do{
         int c;
 
         if (sync[sp & 3] == 'O' &&
             sync[(sp + 1) & 3] == 'g' &&
             sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
             break;
 
1447dc59
         c = avio_r8(bc);
         if (url_feof(bc))
bec994df
             return AVERROR_EOF;
9146ca37
         sync[sp++ & 3] = c;
     }while (i++ < MAX_PAGE_SIZE);
 
     if (i >= MAX_PAGE_SIZE){
         av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
bec994df
         return AVERROR_INVALIDDATA;
9146ca37
     }
 
1447dc59
     if (avio_r8(bc) != 0)      /* version */
bec994df
         return AVERROR_INVALIDDATA;
9146ca37
 
1447dc59
     flags = avio_r8(bc);
e63a3628
     gp = avio_rl64 (bc);
     serial = avio_rl32 (bc);
e65ab9d9
     avio_skip(bc, 8); /* seq, crc */
1447dc59
     nsegs = avio_r8(bc);
9146ca37
 
     idx = ogg_find_stream (ogg, serial);
     if (idx < 0){
8f3eebd6
         if (ogg->headers) {
dc713546
             int n;
 
             for (n = 0; n < ogg->nstreams; n++) {
                 av_freep(&ogg->streams[n].buf);
9ed6cbc3
                 if (!ogg->state || ogg->state->streams[n].private != ogg->streams[n].private)
                     av_freep(&ogg->streams[n].private);
dc713546
             }
             ogg->curidx   = -1;
             ogg->nstreams = 0;
5780f41a
             idx = ogg_new_stream(s, serial, 0);
         } else {
             idx = ogg_new_stream(s, serial, 1);
47dec30e
         }
9146ca37
         if (idx < 0)
bec994df
             return idx;
9146ca37
     }
 
     os = ogg->streams + idx;
384c9c2f
     os->page_pos = avio_tell(bc) - 27;
9146ca37
 
40c5e1fa
     if(os->psize > 0)
12a195e3
         ogg_new_buf(ogg, idx);
 
9cec1bbd
     ret = avio_read(bc, os->segments, nsegs);
bec994df
     if (ret < nsegs)
         return ret < 0 ? ret : AVERROR_EOF;
9146ca37
 
     os->nsegs = nsegs;
     os->segp = 0;
 
     size = 0;
     for (i = 0; i < nsegs; i++)
         size += os->segments[i];
 
ecc0027b
     if (flags & OGG_FLAG_CONT || os->incomplete){
9146ca37
         if (!os->psize){
             while (os->segp < os->nsegs){
                 int seg = os->segments[os->segp++];
                 os->pstart += seg;
                 if (seg < 255)
bad4a6bb
                     break;
9146ca37
             }
73823cb9
             os->sync_pos = os->page_pos;
9146ca37
         }
     }else{
bad4a6bb
         os->psize = 0;
73823cb9
         os->sync_pos = os->page_pos;
9146ca37
     }
 
     if (os->bufsize - os->bufpos < size){
9ded14fc
         uint8_t *nb = av_malloc ((os->bufsize *= 2) + FF_INPUT_BUFFER_PADDING_SIZE);
6eebba08
         if (!nb)
             return AVERROR(ENOMEM);
9146ca37
         memcpy (nb, os->buf, os->bufpos);
         av_free (os->buf);
         os->buf = nb;
     }
 
9cec1bbd
     ret = avio_read(bc, os->buf + os->bufpos, size);
bec994df
     if (ret < size)
         return ret < 0 ? ret : AVERROR_EOF;
9146ca37
 
     os->bufpos += size;
     os->granule = gp;
     os->flags = flags;
 
9ded14fc
     memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE);
9146ca37
     if (str)
         *str = idx;
 
     return 0;
 }
 
e575685f
 static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
                       int64_t *fpos)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
bec994df
     int idx, i, ret;
77be08ee
     struct ogg_stream *os;
9146ca37
     int complete = 0;
     int segp = 0, psize = 0;
 
2ef241c0
     av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx);
9146ca37
 
     do{
         idx = ogg->curidx;
 
         while (idx < 0){
9cec1bbd
             ret = ogg_read_page(s, &idx);
bec994df
             if (ret < 0)
                 return ret;
9146ca37
         }
 
         os = ogg->streams + idx;
 
2ef241c0
         av_dlog(s, "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
9146ca37
                 idx, os->pstart, os->psize, os->segp, os->nsegs);
 
         if (!os->codec){
             if (os->header < 0){
                 os->codec = ogg_find_codec (os->buf, os->bufpos);
                 if (!os->codec){
bec994df
                     av_log(s, AV_LOG_WARNING, "Codec not found\n");
9146ca37
                     os->header = 0;
                     return 0;
                 }
             }else{
                 return 0;
             }
         }
 
         segp = os->segp;
         psize = os->psize;
 
         while (os->segp < os->nsegs){
             int ss = os->segments[os->segp++];
             os->psize += ss;
             if (ss < 255){
                 complete = 1;
                 break;
             }
         }
 
         if (!complete && os->segp == os->nsegs){
             ogg->curidx = -1;
ecc0027b
             os->incomplete = 1;
9146ca37
         }
     }while (!complete);
 
 
adc725b5
     if (os->granule == -1)
461628c6
         av_log(s, AV_LOG_WARNING, "Page at %"PRId64" is missing granule\n", os->page_pos);
adc725b5
 
9146ca37
     ogg->curidx = idx;
ecc0027b
     os->incomplete = 0;
9146ca37
 
81b743eb
     if (os->header) {
         os->header = os->codec->header (s, idx);
         if (!os->header){
bad4a6bb
             os->segp = segp;
             os->psize = psize;
365d8e47
 
94dfea71
             // We have reached the first non-header packet in this stream.
             // Unfortunately more header packets may still follow for others,
d459d8e2
             // but if we continue with header parsing we may lose data packets.
bad4a6bb
             ogg->headers = 1;
365d8e47
 
             // Update the header state for all streams and
             // compute the data_offset.
94dfea71
             if (!s->data_offset)
                 s->data_offset = os->sync_pos;
365d8e47
             for (i = 0; i < ogg->nstreams; i++) {
                 struct ogg_stream *cur_os = ogg->streams + i;
 
                 // if we have a partial non-header packet, its start is
                 // obviously at or after the data start
                 if (cur_os->incomplete)
                     s->data_offset = FFMIN(s->data_offset, cur_os->sync_pos);
             }
9146ca37
         }else{
bad4a6bb
             os->pstart += os->psize;
             os->psize = 0;
9146ca37
         }
81b743eb
     } else {
e1a794b2
         os->pflags = 0;
15299b38
         os->pduration = 0;
9146ca37
         if (os->codec && os->codec->packet)
             os->codec->packet (s, idx);
         if (str)
             *str = idx;
12a195e3
         if (dstart)
             *dstart = os->pstart;
         if (dsize)
             *dsize = os->psize;
73823cb9
         if (fpos)
             *fpos = os->sync_pos;
12a195e3
         os->pstart += os->psize;
         os->psize = 0;
49125aed
         if(os->pstart == os->bufpos)
             os->bufpos = os->pstart = 0;
73823cb9
         os->sync_pos = os->page_pos;
9146ca37
     }
 
5e15c7d9
     // determine whether there are more complete packets in this page
     // if not, the page's granule will apply to this packet
     os->page_end = 1;
     for (i = os->segp; i < os->nsegs; i++)
         if (os->segments[i] < 255) {
             os->page_end = 0;
             break;
         }
 
9146ca37
     if (os->segp == os->nsegs)
         ogg->curidx = -1;
 
     return 0;
 }
 
e575685f
 static int ogg_get_headers(AVFormatContext *s)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
bec994df
     int ret;
9146ca37
 
     do{
9cec1bbd
         ret = ogg_packet(s, NULL, NULL, NULL, NULL);
bec994df
         if (ret < 0)
             return ret;
9146ca37
     }while (!ogg->headers);
 
2ef241c0
     av_dlog(s, "found headers\n");
9146ca37
 
     return 0;
 }
 
e575685f
 static int ogg_get_length(AVFormatContext *s)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
44a088ea
     int i;
bc5c918e
     int64_t size, end;
d2cab951
     int streams_left=0;
69599eea
 
8978feda
     if(!s->pb->seekable)
69599eea
         return 0;
9146ca37
 
 // already set
     if (s->duration != AV_NOPTS_VALUE)
         return 0;
 
db44ea96
     size = avio_size(s->pb);
56466d7b
     if(size < 0)
         return 0;
ddd94932
     end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0;
56466d7b
 
9146ca37
     ogg_save (s);
f59d8ff8
     avio_seek (s->pb, end, SEEK_SET);
9146ca37
 
     while (!ogg_read_page (s, &i)){
e22f2aaf
         if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
44a088ea
             ogg->streams[i].codec) {
             s->streams[i]->duration =
                 ogg_gptopts (s, i, ogg->streams[i].granule, NULL);
d2cab951
             if (s->streams[i]->start_time != AV_NOPTS_VALUE){
44a088ea
                 s->streams[i]->duration -= s->streams[i]->start_time;
d2cab951
                 streams_left-= (ogg->streams[i].got_start==-1);
                 ogg->streams[i].got_start= 1;
             }else if(!ogg->streams[i].got_start){
                 ogg->streams[i].got_start= -1;
                 streams_left++;
             }
44a088ea
         }
9146ca37
     }
 
     ogg_restore (s, 0);
 
5901cd62
     ogg_save (s);
48b9b7ed
     avio_seek (s->pb, 0, SEEK_SET);
5901cd62
     while (!ogg_read_page (s, &i)){
         if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
             ogg->streams[i].codec) {
d2cab951
             if(s->streams[i]->duration && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start){
7e7012d0
                 int64_t start= ogg_gptopts (s, i, ogg->streams[i].granule, NULL);
                 if(av_rescale_q(start, s->streams[i]->time_base, AV_TIME_BASE_Q) > AV_TIME_BASE)
                     s->streams[i]->duration -= start;
d2cab951
                 ogg->streams[i].got_start= 1;
                 streams_left--;
             }
             if(streams_left<=0)
                 break;
5901cd62
         }
     }
     ogg_restore (s, 0);
 
9146ca37
     return 0;
 }
 
03fec31c
 static int ogg_read_close(AVFormatContext *s)
 {
     struct ogg *ogg = s->priv_data;
     int i;
 
     for (i = 0; i < ogg->nstreams; i++) {
         av_free(ogg->streams[i].buf);
8829c790
         if (ogg->streams[i].codec &&
             ogg->streams[i].codec->cleanup) {
             ogg->streams[i].codec->cleanup(s, i);
         }
03fec31c
         av_free(ogg->streams[i].private);
     }
     av_free(ogg->streams);
     return 0;
 }
 
 static int ogg_read_header(AVFormatContext *s)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
bec994df
     int ret, i;
9146ca37
     ogg->curidx = -1;
     //linear headers seek from start
9cec1bbd
     ret = ogg_get_headers(s);
03fec31c
     if (ret < 0) {
         ogg_read_close(s);
bec994df
         return ret;
03fec31c
     }
9146ca37
 
c9da676d
     for (i = 0; i < ogg->nstreams; i++)
         if (ogg->streams[i].header < 0)
             ogg->streams[i].codec = NULL;
 
9146ca37
     //linear granulepos seek from end
     ogg_get_length (s);
 
     //fill the extradata in the per codec callbacks
     return 0;
 }
 
6abaa272
 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts)
 {
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + idx;
     int64_t pts = AV_NOPTS_VALUE;
 
     if (dts)
         *dts = AV_NOPTS_VALUE;
 
     if (os->lastpts != AV_NOPTS_VALUE) {
         pts = os->lastpts;
         os->lastpts = AV_NOPTS_VALUE;
     }
     if (os->lastdts != AV_NOPTS_VALUE) {
         if (dts)
             *dts = os->lastdts;
         os->lastdts = AV_NOPTS_VALUE;
     }
     if (os->page_end) {
         if (os->granule != -1LL) {
             if (os->codec && os->codec->granule_is_start)
                 pts = ogg_gptopts(s, idx, os->granule, dts);
             else
                 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
             os->granule = -1LL;
adc725b5
         }
6abaa272
     }
     return pts;
 }
9146ca37
 
e575685f
 static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt)
9146ca37
 {
77be08ee
     struct ogg *ogg;
     struct ogg_stream *os;
392aa6e4
     int idx = -1, ret;
12a195e3
     int pstart, psize;
d8b91fae
     int64_t fpos, pts, dts;
9146ca37
 
115329f1
     //Get an ogg packet
d8b91fae
 retry:
9146ca37
     do{
9cec1bbd
         ret = ogg_packet(s, &idx, &pstart, &psize, &fpos);
392aa6e4
         if (ret < 0)
             return ret;
9146ca37
     }while (idx < 0 || !s->streams[idx]);
 
     ogg = s->priv_data;
     os = ogg->streams + idx;
 
d8b91fae
     // pflags might not be set until after this
     pts = ogg_calc_pts(s, idx, &dts);
 
cc947f04
     if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
d8b91fae
         goto retry;
     os->keyframe_seek = 0;
 
9146ca37
     //Alloc a pkt
9cec1bbd
     ret = av_new_packet(pkt, psize);
392aa6e4
     if (ret < 0)
         return ret;
9146ca37
     pkt->stream_index = idx;
12a195e3
     memcpy (pkt->data, os->buf + pstart, psize);
5e15c7d9
 
d8b91fae
     pkt->pts = pts;
     pkt->dts = dts;
e1a794b2
     pkt->flags = os->pflags;
15299b38
     pkt->duration = os->pduration;
73823cb9
     pkt->pos = fpos;
e1a794b2
 
12a195e3
     return psize;
9146ca37
 }
 
e575685f
 static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
                                   int64_t *pos_arg, int64_t pos_limit)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
471fe57e
     AVIOContext *bc = s->pb;
a1f29b95
     int64_t pts = AV_NOPTS_VALUE;
4cc3467e
     int i = -1;
f59d8ff8
     avio_seek(bc, *pos_arg, SEEK_SET);
873d117e
     ogg_reset(ogg);
 
384c9c2f
     while (avio_tell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) {
873d117e
         if (i == stream_index) {
4cc3467e
             struct ogg_stream *os = ogg->streams + stream_index;
873d117e
             pts = ogg_calc_pts(s, i, NULL);
cc947f04
             if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
d8b91fae
                 pts = AV_NOPTS_VALUE;
a1f29b95
         }
873d117e
         if (pts != AV_NOPTS_VALUE)
             break;
a1f29b95
     }
     ogg_reset(ogg);
     return pts;
9146ca37
 }
 
e575685f
 static int ogg_read_seek(AVFormatContext *s, int stream_index,
                          int64_t timestamp, int flags)
d8b91fae
 {
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + stream_index;
     int ret;
 
     // Try seeking to a keyframe first. If this fails (very possible),
     // av_seek_frame will fall back to ignoring keyframes
72415b2a
     if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO
d8b91fae
         && !(flags & AVSEEK_FLAG_ANY))
         os->keyframe_seek = 1;
 
a2faa951
     ret = ff_seek_frame_binary(s, stream_index, timestamp, flags);
4cc3467e
     os = ogg->streams + stream_index;
d8b91fae
     if (ret < 0)
         os->keyframe_seek = 0;
     return ret;
 }
 
2e70e4aa
 static int ogg_probe(AVProbeData *p)
 {
f95257d2
     if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7)
2e70e4aa
         return AVPROBE_SCORE_MAX;
f95257d2
     return 0;
2e70e4aa
 }
 
66355be3
 AVInputFormat ff_ogg_demuxer = {
b3bbc6fd
     .name           = "ogg",
     .long_name      = NULL_IF_CONFIG_SMALL("Ogg"),
     .priv_data_size = sizeof(struct ogg),
     .read_probe     = ogg_probe,
     .read_header    = ogg_read_header,
     .read_packet    = ogg_read_packet,
     .read_close     = ogg_read_close,
     .read_seek      = ogg_read_seek,
     .read_timestamp = ogg_read_timestamp,
     .extensions     = "ogg",
     .flags          = AVFMT_GENERIC_INDEX,
9146ca37
 };