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