diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index ca88140cc6..fda1eb2633 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -531,9 +531,10 @@ ogg_read_packet (AVFormatContext * s, AVPacket * pkt) struct ogg_stream *os; int idx = -1; int pstart, psize; - int64_t fpos; + int64_t fpos, pts, dts; //Get an ogg packet +retry: do{ if (ogg_packet (s, &idx, &pstart, &psize, &fpos) < 0) return AVERROR(EIO); @@ -542,13 +543,21 @@ ogg_read_packet (AVFormatContext * s, AVPacket * pkt) ogg = s->priv_data; os = ogg->streams + idx; + // pflags might not be set until after this + pts = ogg_calc_pts(s, idx, &dts); + + if (os->keyframe_seek && !(os->pflags & PKT_FLAG_KEY)) + goto retry; + os->keyframe_seek = 0; + //Alloc a pkt if (av_new_packet (pkt, psize) < 0) return AVERROR(EIO); pkt->stream_index = idx; memcpy (pkt->data, os->buf + pstart, psize); - pkt->pts = ogg_calc_pts(s, idx, &pkt->dts); + pkt->pts = pts; + pkt->dts = dts; pkt->flags = os->pflags; pkt->duration = os->pduration; pkt->pos = fpos; @@ -577,6 +586,7 @@ ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, int64_t pos_limit) { struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + stream_index; ByteIOContext *bc = s->pb; int64_t pts = AV_NOPTS_VALUE; int i; @@ -586,6 +596,8 @@ ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, while (url_ftell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) { if (i == stream_index) { pts = ogg_calc_pts(s, i, NULL); + if (os->keyframe_seek && !(os->pflags & PKT_FLAG_KEY)) + pts = AV_NOPTS_VALUE; } if (pts != AV_NOPTS_VALUE) break; @@ -594,6 +606,24 @@ ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, return pts; } +static int ogg_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + stream_index; + int ret; + + // Try seeking to a keyframe first. If this fails (very possible), + // av_seek_frame will fall back to ignoring keyframes + if (s->streams[stream_index]->codec->codec_type == CODEC_TYPE_VIDEO + && !(flags & AVSEEK_FLAG_ANY)) + os->keyframe_seek = 1; + + ret = av_seek_frame_binary(s, stream_index, timestamp, flags); + if (ret < 0) + os->keyframe_seek = 0; + return ret; +} + static int ogg_probe(AVProbeData *p) { if (p->buf[0] == 'O' && p->buf[1] == 'g' && @@ -612,7 +642,7 @@ AVInputFormat ogg_demuxer = { ogg_read_header, ogg_read_packet, ogg_read_close, - NULL, + ogg_read_seek, ogg_read_timestamp, .extensions = "ogg", .metadata_conv = ff_vorbiscomment_metadata_conv, diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index 6a0ec592da..8733d411b2 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -75,6 +75,7 @@ struct ogg_stream { uint8_t segments[255]; int incomplete; ///< whether we're expecting a continuation in the next page int page_end; ///< current packet is the last one completed in the page + int keyframe_seek; void *private; };