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>
c7c976c6
 #include "libavutil/avassert.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
 };
 
8443082d
 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts);
 
9146ca37
 //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;
ef0d7797
         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;
 }
 
5f9f78dc
 static int ogg_reset(AVFormatContext *s)
9146ca37
 {
5f9f78dc
     struct ogg *ogg = s->priv_data;
9146ca37
     int i;
96fb233e
     int64_t start_pos = avio_tell(s->pb);
9146ca37
 
     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;
96fb233e
         if (start_pos <= s->data_offset) {
             os->lastpts = 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
 
     ogg->streams = av_realloc (ogg->streams,
                                ogg->nstreams * sizeof (*ogg->streams));
     memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
     os = ogg->streams + idx;
     os->serial = serial;
     os->bufsize = DECODER_BUFFER_SIZE;
ef0d7797
     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;
ef0d7797
     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
     }
 
6fd47806
     if (avio_r8(bc) != 0){      /* version */
         av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n");
bec994df
         return AVERROR_INVALIDDATA;
6fd47806
     }
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;
 
251ce231
             if (ogg->nstreams != 1) {
                 av_log_missing_feature(s, "Changing stream parameters in multistream ogg is", 0);
9bb3b7d0
                 return idx;
251ce231
             }
9bb3b7d0
 
dc713546
             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
         }
a6bb09fc
         if (idx < 0) {
             av_log (s, AV_LOG_ERROR, "failed to create stream (OOM?)\n");
bec994df
             return idx;
a6bb09fc
         }
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){
d7b542ae
             // If this is the very first segment we started
             // playback in the middle of a continuation packet.
             // Discard it since we missed the start of it.
9146ca37
             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){
ef0d7797
         uint8_t *nb = av_malloc ((os->bufsize *= 2) + FF_INPUT_BUFFER_PADDING_SIZE);
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;
 
ef0d7797
     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;
d7b542ae
             // Do not set incomplete for empty packets.
             // Together with the code in ogg_read_page
             // that discards all continuation of empty packets
             // we would get an infinite loop.
             os->incomplete = !!os->psize;
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);
5931c754
     avio_seek (s->pb, s->data_offset, SEEK_SET);
8443082d
     ogg_reset(s);
     while (!ogg_packet(s, &i, NULL, NULL, NULL)) {
         int64_t pts = ogg_calc_pts(s, i, NULL);
         if (pts != AV_NOPTS_VALUE && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start){
             s->streams[i]->duration -= pts;
             ogg->streams[i].got_start= 1;
             streams_left--;
         }
d2cab951
             if(streams_left<=0)
                 break;
5901cd62
     }
     ogg_restore (s, 0);
 
9146ca37
     return 0;
 }
 
6e9651d1
 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);
     if (ret < 0)
bec994df
         return ret;
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
 
787528b4
 static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize)
 {
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + idx;
     if (psize && s->streams[idx]->codec->codec_id == CODEC_ID_THEORA) {
         if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) {
             os->pflags ^= AV_PKT_FLAG_KEY;
030e96fe
             av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n",
                    (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-");
787528b4
         }
     }
 }
 
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);
787528b4
     ogg_validate_keyframe(s, idx, pstart, psize);
d8b91fae
 
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 int ogg_read_close(AVFormatContext *s)
9146ca37
 {
77be08ee
     struct ogg *ogg = s->priv_data;
9146ca37
     int i;
 
     for (i = 0; i < ogg->nstreams; i++){
         av_free (ogg->streams[i].buf);
1ed923ea
         av_free (ogg->streams[i].private);
9146ca37
     }
     av_free (ogg->streams);
     return 0;
 }
 
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;
1b0dc96f
     int64_t keypos = -1;
4cc3467e
     int i = -1;
787528b4
     int pstart, psize;
f59d8ff8
     avio_seek(bc, *pos_arg, SEEK_SET);
5f9f78dc
     ogg_reset(s);
873d117e
 
ef32fbaf
     while (avio_tell(bc) <= pos_limit && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) {
873d117e
         if (i == stream_index) {
4cc3467e
             struct ogg_stream *os = ogg->streams + stream_index;
873d117e
             pts = ogg_calc_pts(s, i, NULL);
787528b4
             ogg_validate_keyframe(s, i, pstart, psize);
1b0dc96f
             if (os->pflags & AV_PKT_FLAG_KEY) {
                 keypos = *pos_arg;
             } else if (os->keyframe_seek) {
                 // if we had a previous keyframe but no pts for it,
                 // return that keyframe with this pts value.
                 if (keypos >= 0)
                     *pos_arg = keypos;
                 else
                     pts = AV_NOPTS_VALUE;
             }
a1f29b95
         }
873d117e
         if (pts != AV_NOPTS_VALUE)
             break;
a1f29b95
     }
5f9f78dc
     ogg_reset(s);
a1f29b95
     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;
 
c7c976c6
     av_assert0(stream_index < ogg->nstreams);
8162c6f0
     // Ensure everything is reset even when seeking via
     // the generated index.
5f9f78dc
     ogg_reset(s);
8162c6f0
 
d8b91fae
     // 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
 };