• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavformat/oggdec.c

Go to the documentation of this file.
00001 /*
00002  * Ogg bitstream support
00003  * Luca Barbato <lu_zero@gentoo.org>
00004  * Based on tcvp implementation
00005  */
00006 
00007 /*
00008     Copyright (C) 2005  Michael Ahlberg, Måns Rullgård
00009 
00010     Permission is hereby granted, free of charge, to any person
00011     obtaining a copy of this software and associated documentation
00012     files (the "Software"), to deal in the Software without
00013     restriction, including without limitation the rights to use, copy,
00014     modify, merge, publish, distribute, sublicense, and/or sell copies
00015     of the Software, and to permit persons to whom the Software is
00016     furnished to do so, subject to the following conditions:
00017 
00018     The above copyright notice and this permission notice shall be
00019     included in all copies or substantial portions of the Software.
00020 
00021     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00022     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00023     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00024     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00025     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00026     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00027     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00028     DEALINGS IN THE SOFTWARE.
00029  */
00030 
00031 #include <stdio.h>
00032 #include "oggdec.h"
00033 #include "avformat.h"
00034 #include "internal.h"
00035 #include "vorbiscomment.h"
00036 
00037 #define MAX_PAGE_SIZE 65307
00038 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
00039 
00040 static const struct ogg_codec * const ogg_codecs[] = {
00041     &ff_skeleton_codec,
00042     &ff_dirac_codec,
00043     &ff_speex_codec,
00044     &ff_vorbis_codec,
00045     &ff_theora_codec,
00046     &ff_flac_codec,
00047     &ff_celt_codec,
00048     &ff_old_dirac_codec,
00049     &ff_old_flac_codec,
00050     &ff_ogm_video_codec,
00051     &ff_ogm_audio_codec,
00052     &ff_ogm_text_codec,
00053     &ff_ogm_old_codec,
00054     NULL
00055 };
00056 
00057 //FIXME We could avoid some structure duplication
00058 static int ogg_save(AVFormatContext *s)
00059 {
00060     struct ogg *ogg = s->priv_data;
00061     struct ogg_state *ost =
00062         av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
00063     int i;
00064     ost->pos = avio_tell (s->pb);
00065     ost->curidx = ogg->curidx;
00066     ost->next = ogg->state;
00067     ost->nstreams = ogg->nstreams;
00068     memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
00069 
00070     for (i = 0; i < ogg->nstreams; i++){
00071         struct ogg_stream *os = ogg->streams + i;
00072         os->buf = av_malloc (os->bufsize);
00073         memset (os->buf, 0, os->bufsize);
00074         memcpy (os->buf, ost->streams[i].buf, os->bufpos);
00075     }
00076 
00077     ogg->state = ost;
00078 
00079     return 0;
00080 }
00081 
00082 static int ogg_restore(AVFormatContext *s, int discard)
00083 {
00084     struct ogg *ogg = s->priv_data;
00085     AVIOContext *bc = s->pb;
00086     struct ogg_state *ost = ogg->state;
00087     int i;
00088 
00089     if (!ost)
00090         return 0;
00091 
00092     ogg->state = ost->next;
00093 
00094     if (!discard){
00095         struct ogg_stream *old_streams = ogg->streams;
00096 
00097         for (i = 0; i < ogg->nstreams; i++)
00098             av_free (ogg->streams[i].buf);
00099 
00100         avio_seek (bc, ost->pos, SEEK_SET);
00101         ogg->curidx = ost->curidx;
00102         ogg->nstreams = ost->nstreams;
00103         ogg->streams = av_realloc (ogg->streams,
00104                                    ogg->nstreams * sizeof (*ogg->streams));
00105 
00106         if (ogg->streams) {
00107             memcpy(ogg->streams, ost->streams,
00108                    ost->nstreams * sizeof(*ogg->streams));
00109         } else {
00110             av_free(old_streams);
00111             ogg->nstreams = 0;
00112         }
00113     }
00114 
00115     av_free (ost);
00116 
00117     return 0;
00118 }
00119 
00120 static int ogg_reset(struct ogg *ogg)
00121 {
00122     int i;
00123 
00124     for (i = 0; i < ogg->nstreams; i++){
00125         struct ogg_stream *os = ogg->streams + i;
00126         os->bufpos = 0;
00127         os->pstart = 0;
00128         os->psize = 0;
00129         os->granule = -1;
00130         os->lastpts = AV_NOPTS_VALUE;
00131         os->lastdts = AV_NOPTS_VALUE;
00132         os->sync_pos = -1;
00133         os->page_pos = 0;
00134         os->nsegs = 0;
00135         os->segp = 0;
00136         os->incomplete = 0;
00137     }
00138 
00139     ogg->curidx = -1;
00140 
00141     return 0;
00142 }
00143 
00144 static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size)
00145 {
00146     int i;
00147 
00148     for (i = 0; ogg_codecs[i]; i++)
00149         if (size >= ogg_codecs[i]->magicsize &&
00150             !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
00151             return ogg_codecs[i];
00152 
00153     return NULL;
00154 }
00155 
00156 static int ogg_new_stream(AVFormatContext *s, uint32_t serial, int new_avstream)
00157 {
00158 
00159     struct ogg *ogg = s->priv_data;
00160     int idx = ogg->nstreams++;
00161     AVStream *st;
00162     struct ogg_stream *os;
00163 
00164     ogg->streams = av_realloc (ogg->streams,
00165                                ogg->nstreams * sizeof (*ogg->streams));
00166     memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
00167     os = ogg->streams + idx;
00168     os->serial = serial;
00169     os->bufsize = DECODER_BUFFER_SIZE;
00170     os->buf = av_malloc(os->bufsize);
00171     os->header = -1;
00172 
00173     if (new_avstream) {
00174         st = avformat_new_stream(s, NULL);
00175         if (!st)
00176             return AVERROR(ENOMEM);
00177 
00178         st->id = idx;
00179         avpriv_set_pts_info(st, 64, 1, 1000000);
00180     }
00181 
00182     return idx;
00183 }
00184 
00185 static int ogg_new_buf(struct ogg *ogg, int idx)
00186 {
00187     struct ogg_stream *os = ogg->streams + idx;
00188     uint8_t *nb = av_malloc(os->bufsize);
00189     int size = os->bufpos - os->pstart;
00190     if(os->buf){
00191         memcpy(nb, os->buf + os->pstart, size);
00192         av_free(os->buf);
00193     }
00194     os->buf = nb;
00195     os->bufpos = size;
00196     os->pstart = 0;
00197 
00198     return 0;
00199 }
00200 
00201 static int ogg_read_page(AVFormatContext *s, int *str)
00202 {
00203     AVIOContext *bc = s->pb;
00204     struct ogg *ogg = s->priv_data;
00205     struct ogg_stream *os;
00206     int ret, i = 0;
00207     int flags, nsegs;
00208     uint64_t gp;
00209     uint32_t serial;
00210     int size, idx;
00211     uint8_t sync[4];
00212     int sp = 0;
00213 
00214     ret = avio_read(bc, sync, 4);
00215     if (ret < 4)
00216         return ret < 0 ? ret : AVERROR_EOF;
00217 
00218     do{
00219         int c;
00220 
00221         if (sync[sp & 3] == 'O' &&
00222             sync[(sp + 1) & 3] == 'g' &&
00223             sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
00224             break;
00225 
00226         c = avio_r8(bc);
00227         if (url_feof(bc))
00228             return AVERROR_EOF;
00229         sync[sp++ & 3] = c;
00230     }while (i++ < MAX_PAGE_SIZE);
00231 
00232     if (i >= MAX_PAGE_SIZE){
00233         av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
00234         return AVERROR_INVALIDDATA;
00235     }
00236 
00237     if (avio_r8(bc) != 0)      /* version */
00238         return AVERROR_INVALIDDATA;
00239 
00240     flags = avio_r8(bc);
00241     gp = avio_rl64 (bc);
00242     serial = avio_rl32 (bc);
00243     avio_skip(bc, 8); /* seq, crc */
00244     nsegs = avio_r8(bc);
00245 
00246     idx = ogg_find_stream (ogg, serial);
00247     if (idx < 0){
00248         if (ogg->headers) {
00249             int n;
00250 
00251             for (n = 0; n < ogg->nstreams; n++) {
00252                 av_freep(&ogg->streams[n].buf);
00253                 if (!ogg->state || ogg->state->streams[n].private != ogg->streams[n].private)
00254                     av_freep(&ogg->streams[n].private);
00255             }
00256             ogg->curidx   = -1;
00257             ogg->nstreams = 0;
00258             idx = ogg_new_stream(s, serial, 0);
00259         } else {
00260             idx = ogg_new_stream(s, serial, 1);
00261         }
00262         if (idx < 0)
00263             return idx;
00264     }
00265 
00266     os = ogg->streams + idx;
00267     os->page_pos = avio_tell(bc) - 27;
00268 
00269     if(os->psize > 0)
00270         ogg_new_buf(ogg, idx);
00271 
00272     ret = avio_read(bc, os->segments, nsegs);
00273     if (ret < nsegs)
00274         return ret < 0 ? ret : AVERROR_EOF;
00275 
00276     os->nsegs = nsegs;
00277     os->segp = 0;
00278 
00279     size = 0;
00280     for (i = 0; i < nsegs; i++)
00281         size += os->segments[i];
00282 
00283     if (flags & OGG_FLAG_CONT || os->incomplete){
00284         if (!os->psize){
00285             while (os->segp < os->nsegs){
00286                 int seg = os->segments[os->segp++];
00287                 os->pstart += seg;
00288                 if (seg < 255)
00289                     break;
00290             }
00291             os->sync_pos = os->page_pos;
00292         }
00293     }else{
00294         os->psize = 0;
00295         os->sync_pos = os->page_pos;
00296     }
00297 
00298     if (os->bufsize - os->bufpos < size){
00299         uint8_t *nb = av_malloc (os->bufsize *= 2);
00300         memcpy (nb, os->buf, os->bufpos);
00301         av_free (os->buf);
00302         os->buf = nb;
00303     }
00304 
00305     ret = avio_read(bc, os->buf + os->bufpos, size);
00306     if (ret < size)
00307         return ret < 0 ? ret : AVERROR_EOF;
00308 
00309     os->bufpos += size;
00310     os->granule = gp;
00311     os->flags = flags;
00312 
00313     if (str)
00314         *str = idx;
00315 
00316     return 0;
00317 }
00318 
00319 static int ogg_packet(AVFormatContext *s, int *str, int *dstart, int *dsize,
00320                       int64_t *fpos)
00321 {
00322     struct ogg *ogg = s->priv_data;
00323     int idx, i, ret;
00324     struct ogg_stream *os;
00325     int complete = 0;
00326     int segp = 0, psize = 0;
00327 
00328     av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx);
00329 
00330     do{
00331         idx = ogg->curidx;
00332 
00333         while (idx < 0){
00334             ret = ogg_read_page(s, &idx);
00335             if (ret < 0)
00336                 return ret;
00337         }
00338 
00339         os = ogg->streams + idx;
00340 
00341         av_dlog(s, "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
00342                 idx, os->pstart, os->psize, os->segp, os->nsegs);
00343 
00344         if (!os->codec){
00345             if (os->header < 0){
00346                 os->codec = ogg_find_codec (os->buf, os->bufpos);
00347                 if (!os->codec){
00348                     av_log(s, AV_LOG_WARNING, "Codec not found\n");
00349                     os->header = 0;
00350                     return 0;
00351                 }
00352             }else{
00353                 return 0;
00354             }
00355         }
00356 
00357         segp = os->segp;
00358         psize = os->psize;
00359 
00360         while (os->segp < os->nsegs){
00361             int ss = os->segments[os->segp++];
00362             os->psize += ss;
00363             if (ss < 255){
00364                 complete = 1;
00365                 break;
00366             }
00367         }
00368 
00369         if (!complete && os->segp == os->nsegs){
00370             ogg->curidx = -1;
00371             os->incomplete = 1;
00372         }
00373     }while (!complete);
00374 
00375 
00376     if (os->granule == -1)
00377         av_log(s, AV_LOG_WARNING, "Page at %"PRId64" is missing granule\n", os->page_pos);
00378 
00379     ogg->curidx = idx;
00380     os->incomplete = 0;
00381 
00382     if (os->header) {
00383         os->header = os->codec->header (s, idx);
00384         if (!os->header){
00385             os->segp = segp;
00386             os->psize = psize;
00387 
00388             // We have reached the first non-header packet in this stream.
00389             // Unfortunately more header packets may still follow for others,
00390             // but if we continue with header parsing we may lose data packets.
00391             ogg->headers = 1;
00392 
00393             // Update the header state for all streams and
00394             // compute the data_offset.
00395             if (!s->data_offset)
00396                 s->data_offset = os->sync_pos;
00397             for (i = 0; i < ogg->nstreams; i++) {
00398                 struct ogg_stream *cur_os = ogg->streams + i;
00399 
00400                 // if we have a partial non-header packet, its start is
00401                 // obviously at or after the data start
00402                 if (cur_os->incomplete)
00403                     s->data_offset = FFMIN(s->data_offset, cur_os->sync_pos);
00404             }
00405         }else{
00406             os->pstart += os->psize;
00407             os->psize = 0;
00408         }
00409     } else {
00410         os->pflags = 0;
00411         os->pduration = 0;
00412         if (os->codec && os->codec->packet)
00413             os->codec->packet (s, idx);
00414         if (str)
00415             *str = idx;
00416         if (dstart)
00417             *dstart = os->pstart;
00418         if (dsize)
00419             *dsize = os->psize;
00420         if (fpos)
00421             *fpos = os->sync_pos;
00422         os->pstart += os->psize;
00423         os->psize = 0;
00424         if(os->pstart == os->bufpos)
00425             os->bufpos = os->pstart = 0;
00426         os->sync_pos = os->page_pos;
00427     }
00428 
00429     // determine whether there are more complete packets in this page
00430     // if not, the page's granule will apply to this packet
00431     os->page_end = 1;
00432     for (i = os->segp; i < os->nsegs; i++)
00433         if (os->segments[i] < 255) {
00434             os->page_end = 0;
00435             break;
00436         }
00437 
00438     if (os->segp == os->nsegs)
00439         ogg->curidx = -1;
00440 
00441     return 0;
00442 }
00443 
00444 static int ogg_get_headers(AVFormatContext *s)
00445 {
00446     struct ogg *ogg = s->priv_data;
00447     int ret;
00448 
00449     do{
00450         ret = ogg_packet(s, NULL, NULL, NULL, NULL);
00451         if (ret < 0)
00452             return ret;
00453     }while (!ogg->headers);
00454 
00455     av_dlog(s, "found headers\n");
00456 
00457     return 0;
00458 }
00459 
00460 static int ogg_get_length(AVFormatContext *s)
00461 {
00462     struct ogg *ogg = s->priv_data;
00463     int i;
00464     int64_t size, end;
00465     int streams_left=0;
00466 
00467     if(!s->pb->seekable)
00468         return 0;
00469 
00470 // already set
00471     if (s->duration != AV_NOPTS_VALUE)
00472         return 0;
00473 
00474     size = avio_size(s->pb);
00475     if(size < 0)
00476         return 0;
00477     end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0;
00478 
00479     ogg_save (s);
00480     avio_seek (s->pb, end, SEEK_SET);
00481 
00482     while (!ogg_read_page (s, &i)){
00483         if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
00484             ogg->streams[i].codec) {
00485             s->streams[i]->duration =
00486                 ogg_gptopts (s, i, ogg->streams[i].granule, NULL);
00487             if (s->streams[i]->start_time != AV_NOPTS_VALUE){
00488                 s->streams[i]->duration -= s->streams[i]->start_time;
00489                 streams_left-= (ogg->streams[i].got_start==-1);
00490                 ogg->streams[i].got_start= 1;
00491             }else if(!ogg->streams[i].got_start){
00492                 ogg->streams[i].got_start= -1;
00493                 streams_left++;
00494             }
00495         }
00496     }
00497 
00498     ogg_restore (s, 0);
00499 
00500     ogg_save (s);
00501     avio_seek (s->pb, 0, SEEK_SET);
00502     while (!ogg_read_page (s, &i)){
00503         if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
00504             ogg->streams[i].codec) {
00505             if(s->streams[i]->duration && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start){
00506                 int64_t start= ogg_gptopts (s, i, ogg->streams[i].granule, NULL);
00507                 if(av_rescale_q(start, s->streams[i]->time_base, AV_TIME_BASE_Q) > AV_TIME_BASE)
00508                     s->streams[i]->duration -= start;
00509                 ogg->streams[i].got_start= 1;
00510                 streams_left--;
00511             }
00512             if(streams_left<=0)
00513                 break;
00514         }
00515     }
00516     ogg_restore (s, 0);
00517 
00518     return 0;
00519 }
00520 
00521 static int ogg_read_header(AVFormatContext *s, AVFormatParameters *ap)
00522 {
00523     struct ogg *ogg = s->priv_data;
00524     int ret, i;
00525     ogg->curidx = -1;
00526     //linear headers seek from start
00527     ret = ogg_get_headers(s);
00528     if (ret < 0)
00529         return ret;
00530 
00531     for (i = 0; i < ogg->nstreams; i++)
00532         if (ogg->streams[i].header < 0)
00533             ogg->streams[i].codec = NULL;
00534 
00535     //linear granulepos seek from end
00536     ogg_get_length (s);
00537 
00538     //fill the extradata in the per codec callbacks
00539     return 0;
00540 }
00541 
00542 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts)
00543 {
00544     struct ogg *ogg = s->priv_data;
00545     struct ogg_stream *os = ogg->streams + idx;
00546     int64_t pts = AV_NOPTS_VALUE;
00547 
00548     if (dts)
00549         *dts = AV_NOPTS_VALUE;
00550 
00551     if (os->lastpts != AV_NOPTS_VALUE) {
00552         pts = os->lastpts;
00553         os->lastpts = AV_NOPTS_VALUE;
00554     }
00555     if (os->lastdts != AV_NOPTS_VALUE) {
00556         if (dts)
00557             *dts = os->lastdts;
00558         os->lastdts = AV_NOPTS_VALUE;
00559     }
00560     if (os->page_end) {
00561         if (os->granule != -1LL) {
00562             if (os->codec && os->codec->granule_is_start)
00563                 pts = ogg_gptopts(s, idx, os->granule, dts);
00564             else
00565                 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
00566             os->granule = -1LL;
00567         }
00568     }
00569     return pts;
00570 }
00571 
00572 static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt)
00573 {
00574     struct ogg *ogg;
00575     struct ogg_stream *os;
00576     int idx = -1, ret;
00577     int pstart, psize;
00578     int64_t fpos, pts, dts;
00579 
00580     //Get an ogg packet
00581 retry:
00582     do{
00583         ret = ogg_packet(s, &idx, &pstart, &psize, &fpos);
00584         if (ret < 0)
00585             return ret;
00586     }while (idx < 0 || !s->streams[idx]);
00587 
00588     ogg = s->priv_data;
00589     os = ogg->streams + idx;
00590 
00591     // pflags might not be set until after this
00592     pts = ogg_calc_pts(s, idx, &dts);
00593 
00594     if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
00595         goto retry;
00596     os->keyframe_seek = 0;
00597 
00598     //Alloc a pkt
00599     ret = av_new_packet(pkt, psize);
00600     if (ret < 0)
00601         return ret;
00602     pkt->stream_index = idx;
00603     memcpy (pkt->data, os->buf + pstart, psize);
00604 
00605     pkt->pts = pts;
00606     pkt->dts = dts;
00607     pkt->flags = os->pflags;
00608     pkt->duration = os->pduration;
00609     pkt->pos = fpos;
00610 
00611     return psize;
00612 }
00613 
00614 static int ogg_read_close(AVFormatContext *s)
00615 {
00616     struct ogg *ogg = s->priv_data;
00617     int i;
00618 
00619     for (i = 0; i < ogg->nstreams; i++){
00620         av_free (ogg->streams[i].buf);
00621         av_free (ogg->streams[i].private);
00622     }
00623     av_free (ogg->streams);
00624     return 0;
00625 }
00626 
00627 static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
00628                                   int64_t *pos_arg, int64_t pos_limit)
00629 {
00630     struct ogg *ogg = s->priv_data;
00631     AVIOContext *bc = s->pb;
00632     int64_t pts = AV_NOPTS_VALUE;
00633     int i = -1;
00634     avio_seek(bc, *pos_arg, SEEK_SET);
00635     ogg_reset(ogg);
00636 
00637     while (avio_tell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) {
00638         if (i == stream_index) {
00639             struct ogg_stream *os = ogg->streams + stream_index;
00640             pts = ogg_calc_pts(s, i, NULL);
00641             if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
00642                 pts = AV_NOPTS_VALUE;
00643         }
00644         if (pts != AV_NOPTS_VALUE)
00645             break;
00646     }
00647     ogg_reset(ogg);
00648     return pts;
00649 }
00650 
00651 static int ogg_read_seek(AVFormatContext *s, int stream_index,
00652                          int64_t timestamp, int flags)
00653 {
00654     struct ogg *ogg = s->priv_data;
00655     struct ogg_stream *os = ogg->streams + stream_index;
00656     int ret;
00657 
00658     // Try seeking to a keyframe first. If this fails (very possible),
00659     // av_seek_frame will fall back to ignoring keyframes
00660     if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO
00661         && !(flags & AVSEEK_FLAG_ANY))
00662         os->keyframe_seek = 1;
00663 
00664     ret = ff_seek_frame_binary(s, stream_index, timestamp, flags);
00665     os = ogg->streams + stream_index;
00666     if (ret < 0)
00667         os->keyframe_seek = 0;
00668     return ret;
00669 }
00670 
00671 static int ogg_probe(AVProbeData *p)
00672 {
00673     if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7)
00674         return AVPROBE_SCORE_MAX;
00675     return 0;
00676 }
00677 
00678 AVInputFormat ff_ogg_demuxer = {
00679     .name           = "ogg",
00680     .long_name      = NULL_IF_CONFIG_SMALL("Ogg"),
00681     .priv_data_size = sizeof(struct ogg),
00682     .read_probe     = ogg_probe,
00683     .read_header    = ogg_read_header,
00684     .read_packet    = ogg_read_packet,
00685     .read_close     = ogg_read_close,
00686     .read_seek      = ogg_read_seek,
00687     .read_timestamp = ogg_read_timestamp,
00688     .extensions     = "ogg",
00689     .flags          = AVFMT_GENERIC_INDEX,
00690 };
Generated on Fri Feb 1 2013 14:34:53 for FFmpeg by doxygen 1.7.1