FFmpeg
 All Data Structures Namespaces 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
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  avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg");
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) {
675  "Headers mismatch for stream %d: "
676  "expected %d received %d.\n",
677  i, os->codec->nb_header, os->nb_header);
679  return AVERROR_INVALIDDATA;
680  }
682  os->lastpts = s->streams[i]->start_time =
683  ogg_gptopts(s, i, os->start_granule, NULL);
684  }
685 
686  //linear granulepos seek from end
687  ogg_get_length(s);
688 
689  return 0;
690 }
691 
692 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts)
693 {
694  struct ogg *ogg = s->priv_data;
695  struct ogg_stream *os = ogg->streams + idx;
696  int64_t pts = AV_NOPTS_VALUE;
697 
698  if (dts)
699  *dts = AV_NOPTS_VALUE;
700 
701  if (os->lastpts != AV_NOPTS_VALUE) {
702  pts = os->lastpts;
703  os->lastpts = AV_NOPTS_VALUE;
704  }
705  if (os->lastdts != AV_NOPTS_VALUE) {
706  if (dts)
707  *dts = os->lastdts;
708  os->lastdts = AV_NOPTS_VALUE;
709  }
710  if (os->page_end) {
711  if (os->granule != -1LL) {
712  if (os->codec && os->codec->granule_is_start)
713  pts = ogg_gptopts(s, idx, os->granule, dts);
714  else
715  os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts);
716  os->granule = -1LL;
717  }
718  }
719  return pts;
720 }
721 
722 static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize)
723 {
724  struct ogg *ogg = s->priv_data;
725  struct ogg_stream *os = ogg->streams + idx;
726  if (psize && s->streams[idx]->codec->codec_id == AV_CODEC_ID_THEORA) {
727  if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) {
728  os->pflags ^= AV_PKT_FLAG_KEY;
729  av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n",
730  (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-");
731  }
732  }
733 }
734 
736 {
737  struct ogg *ogg;
738  struct ogg_stream *os;
739  int idx, ret;
740  int pstart, psize;
741  int64_t fpos, pts, dts;
742 
743  if (s->io_repositioned) {
744  ogg_reset(s);
745  s->io_repositioned = 0;
746  }
747 
748  //Get an ogg packet
749 retry:
750  do {
751  ret = ogg_packet(s, &idx, &pstart, &psize, &fpos);
752  if (ret < 0)
753  return ret;
754  } while (idx < 0 || !s->streams[idx]);
755 
756  ogg = s->priv_data;
757  os = ogg->streams + idx;
758 
759  // pflags might not be set until after this
760  pts = ogg_calc_pts(s, idx, &dts);
761  ogg_validate_keyframe(s, idx, pstart, psize);
762 
763  if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY))
764  goto retry;
765  os->keyframe_seek = 0;
766 
767  //Alloc a pkt
768  ret = av_new_packet(pkt, psize);
769  if (ret < 0)
770  return ret;
771  pkt->stream_index = idx;
772  memcpy(pkt->data, os->buf + pstart, psize);
773 
774  pkt->pts = pts;
775  pkt->dts = dts;
776  pkt->flags = os->pflags;
777  pkt->duration = os->pduration;
778  pkt->pos = fpos;
779 
780  return psize;
781 }
782 
783 static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index,
784  int64_t *pos_arg, int64_t pos_limit)
785 {
786  struct ogg *ogg = s->priv_data;
787  AVIOContext *bc = s->pb;
788  int64_t pts = AV_NOPTS_VALUE;
789  int64_t keypos = -1;
790  int i;
791  int pstart, psize;
792  avio_seek(bc, *pos_arg, SEEK_SET);
793  ogg_reset(s);
794 
795  while ( avio_tell(bc) <= pos_limit
796  && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) {
797  if (i == stream_index) {
798  struct ogg_stream *os = ogg->streams + stream_index;
799  pts = ogg_calc_pts(s, i, NULL);
800  ogg_validate_keyframe(s, i, pstart, psize);
801  if (os->pflags & AV_PKT_FLAG_KEY) {
802  keypos = *pos_arg;
803  } else if (os->keyframe_seek) {
804  // if we had a previous keyframe but no pts for it,
805  // return that keyframe with this pts value.
806  if (keypos >= 0)
807  *pos_arg = keypos;
808  else
809  pts = AV_NOPTS_VALUE;
810  }
811  }
812  if (pts != AV_NOPTS_VALUE)
813  break;
814  }
815  ogg_reset(s);
816  return pts;
817 }
818 
819 static int ogg_read_seek(AVFormatContext *s, int stream_index,
820  int64_t timestamp, int flags)
821 {
822  struct ogg *ogg = s->priv_data;
823  struct ogg_stream *os = ogg->streams + stream_index;
824  int ret;
825 
826  av_assert0(stream_index < ogg->nstreams);
827  // Ensure everything is reset even when seeking via
828  // the generated index.
829  ogg_reset(s);
830 
831  // Try seeking to a keyframe first. If this fails (very possible),
832  // av_seek_frame will fall back to ignoring keyframes
833  if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO
834  && !(flags & AVSEEK_FLAG_ANY))
835  os->keyframe_seek = 1;
836 
837  ret = ff_seek_frame_binary(s, stream_index, timestamp, flags);
838  os = ogg->streams + stream_index;
839  if (ret < 0)
840  os->keyframe_seek = 0;
841  return ret;
842 }
843 
844 static int ogg_probe(AVProbeData *p)
845 {
846  if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7)
847  return AVPROBE_SCORE_MAX;
848  return 0;
849 }
850 
852  .name = "ogg",
853  .long_name = NULL_IF_CONFIG_SMALL("Ogg"),
854  .priv_data_size = sizeof(struct ogg),
855  .read_probe = ogg_probe,
856  .read_header = ogg_read_header,
857  .read_packet = ogg_read_packet,
858  .read_close = ogg_read_close,
859  .read_seek = ogg_read_seek,
860  .read_timestamp = ogg_read_timestamp,
861  .extensions = "ogg",
863 };