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