diff --git a/libavformat/Makefile b/libavformat/Makefile index f7e47563da..f67d261402 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -19,6 +19,7 @@ OBJS = allformats.o \ protocols.o \ riff.o \ sdp.o \ + seek.o \ url.o \ utils.o \ diff --git a/libavformat/internal.h b/libavformat/internal.h index 13e0a3df30..d0b9d6200a 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -450,6 +450,24 @@ do {\ } while(0) #endif +#define RELATIVE_TS_BASE (INT64_MAX - (1LL << 48)) + +static av_always_inline int is_relative(int64_t ts) +{ + return ts > (RELATIVE_TS_BASE - (1LL << 48)); +} + +/** + * Wrap a given time stamp, if there is an indication for an overflow + * + * @param st stream + * @param timestamp the time stamp to wrap + * @return resulting time stamp + */ +int64_t ff_wrap_timestamp(const AVStream *st, int64_t timestamp); + +void ff_flush_packet_queue(AVFormatContext *s); + /** * Automatically create sub-directories * diff --git a/libavformat/seek.c b/libavformat/seek.c new file mode 100644 index 0000000000..40169736df --- /dev/null +++ b/libavformat/seek.c @@ -0,0 +1,753 @@ +/* + * Seeking and index-related functions + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/avassert.h" +#include "libavutil/mathematics.h" +#include "libavutil/timestamp.h" + +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" + +void avpriv_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) +{ + for (unsigned i = 0; i < s->nb_streams; i++) { + AVStream *const st = s->streams[i]; + FFStream *const sti = ffstream(st); + + sti->cur_dts = + av_rescale(timestamp, + st->time_base.den * (int64_t) ref_st->time_base.num, + st->time_base.num * (int64_t) ref_st->time_base.den); + } +} + +void ff_reduce_index(AVFormatContext *s, int stream_index) +{ + AVStream *const st = s->streams[stream_index]; + FFStream *const sti = ffstream(st); + unsigned int max_entries = s->max_index_size / sizeof(AVIndexEntry); + + if ((unsigned) sti->nb_index_entries >= max_entries) { + int i; + for (i = 0; 2 * i < sti->nb_index_entries; i++) + sti->index_entries[i] = sti->index_entries[2 * i]; + sti->nb_index_entries = i; + } +} + +int ff_add_index_entry(AVIndexEntry **index_entries, + int *nb_index_entries, + unsigned int *index_entries_allocated_size, + int64_t pos, int64_t timestamp, + int size, int distance, int flags) +{ + AVIndexEntry *entries, *ie; + int index; + + if ((unsigned) *nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry)) + return -1; + + if (timestamp == AV_NOPTS_VALUE) + return AVERROR(EINVAL); + + if (size < 0 || size > 0x3FFFFFFF) + return AVERROR(EINVAL); + + if (is_relative(timestamp)) //FIXME this maintains previous behavior but we should shift by the correct offset once known + timestamp -= RELATIVE_TS_BASE; + + entries = av_fast_realloc(*index_entries, + index_entries_allocated_size, + (*nb_index_entries + 1) * + sizeof(AVIndexEntry)); + if (!entries) + return -1; + + *index_entries = entries; + + index = ff_index_search_timestamp(*index_entries, *nb_index_entries, + timestamp, AVSEEK_FLAG_ANY); + if (index < 0) { + index = (*nb_index_entries)++; + ie = &entries[index]; + av_assert0(index == 0 || ie[-1].timestamp < timestamp); + } else { + ie = &entries[index]; + if (ie->timestamp != timestamp) { + if (ie->timestamp <= timestamp) + return -1; + memmove(entries + index + 1, entries + index, + sizeof(AVIndexEntry) * (*nb_index_entries - index)); + (*nb_index_entries)++; + } else if (ie->pos == pos && distance < ie->min_distance) + // do not reduce the distance + distance = ie->min_distance; + } + + ie->pos = pos; + ie->timestamp = timestamp; + ie->min_distance = distance; + ie->size = size; + ie->flags = flags; + + return index; +} + +int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, + int size, int distance, int flags) +{ + FFStream *const sti = ffstream(st); + timestamp = ff_wrap_timestamp(st, timestamp); + return ff_add_index_entry(&sti->index_entries, &sti->nb_index_entries, + &sti->index_entries_allocated_size, pos, + timestamp, size, distance, flags); +} + +int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries, + int64_t wanted_timestamp, int flags) +{ + int a, b, m; + int64_t timestamp; + + a = -1; + b = nb_entries; + + // Optimize appending index entries at the end. + if (b && entries[b - 1].timestamp < wanted_timestamp) + a = b - 1; + + while (b - a > 1) { + m = (a + b) >> 1; + + // Search for the next non-discarded packet. + while ((entries[m].flags & AVINDEX_DISCARD_FRAME) && m < b && m < nb_entries - 1) { + m++; + if (m == b && entries[m].timestamp >= wanted_timestamp) { + m = b - 1; + break; + } + } + + timestamp = entries[m].timestamp; + if (timestamp >= wanted_timestamp) + b = m; + if (timestamp <= wanted_timestamp) + a = m; + } + m = (flags & AVSEEK_FLAG_BACKWARD) ? a : b; + + if (!(flags & AVSEEK_FLAG_ANY)) + while (m >= 0 && m < nb_entries && + !(entries[m].flags & AVINDEX_KEYFRAME)) + m += (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1; + + if (m == nb_entries) + return -1; + return m; +} + +void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance) +{ + int64_t pos_delta = 0; + int64_t skip = 0; + //We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable + const char *proto = avio_find_protocol_name(s->url); + FFIOContext *ctx; + + av_assert0(time_tolerance >= 0); + + if (!proto) { + av_log(s, AV_LOG_INFO, + "Protocol name not provided, cannot determine if input is local or " + "a network protocol, buffers and access patterns cannot be configured " + "optimally without knowing the protocol\n"); + } + + if (proto && !(strcmp(proto, "file") && strcmp(proto, "pipe") && strcmp(proto, "cache"))) + return; + + for (unsigned ist1 = 0; ist1 < s->nb_streams; ist1++) { + AVStream *const st1 = s->streams[ist1]; + FFStream *const sti1 = ffstream(st1); + for (unsigned ist2 = 0; ist2 < s->nb_streams; ist2++) { + AVStream *const st2 = s->streams[ist2]; + FFStream *const sti2 = ffstream(st2); + + if (ist1 == ist2) + continue; + + for (int i1 = 0, i2 = 0; i1 < sti1->nb_index_entries; i1++) { + const AVIndexEntry *const e1 = &sti1->index_entries[i1]; + int64_t e1_pts = av_rescale_q(e1->timestamp, st1->time_base, AV_TIME_BASE_Q); + + skip = FFMAX(skip, e1->size); + for (; i2 < sti2->nb_index_entries; i2++) { + const AVIndexEntry *const e2 = &sti2->index_entries[i2]; + int64_t e2_pts = av_rescale_q(e2->timestamp, st2->time_base, AV_TIME_BASE_Q); + if (e2_pts < e1_pts || e2_pts - (uint64_t)e1_pts < time_tolerance) + continue; + pos_delta = FFMAX(pos_delta, e1->pos - e2->pos); + break; + } + } + } + } + + pos_delta *= 2; + ctx = ffiocontext(s->pb); + /* XXX This could be adjusted depending on protocol*/ + if (s->pb->buffer_size < pos_delta && pos_delta < (1<<24)) { + av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta); + + /* realloc the buffer and the original data will be retained */ + if (ffio_realloc_buf(s->pb, pos_delta)) { + av_log(s, AV_LOG_ERROR, "Realloc buffer fail.\n"); + return; + } + + ctx->short_seek_threshold = FFMAX(ctx->short_seek_threshold, pos_delta/2); + } + + if (skip < (1<<23)) { + ctx->short_seek_threshold = FFMAX(ctx->short_seek_threshold, skip); + } +} + +int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags) +{ + const FFStream *const sti = ffstream(st); + return ff_index_search_timestamp(sti->index_entries, sti->nb_index_entries, + wanted_timestamp, flags); +} + +int avformat_index_get_entries_count(const AVStream *st) +{ + return cffstream(st)->nb_index_entries; +} + +const AVIndexEntry *avformat_index_get_entry(AVStream *st, int idx) +{ + const FFStream *const sti = ffstream(st); + if (idx < 0 || idx >= sti->nb_index_entries) + return NULL; + + return &sti->index_entries[idx]; +} + +const AVIndexEntry *avformat_index_get_entry_from_timestamp(AVStream *st, + int64_t wanted_timestamp, + int flags) +{ + const FFStream *const sti = ffstream(st); + int idx = ff_index_search_timestamp(sti->index_entries, + sti->nb_index_entries, + wanted_timestamp, flags); + + if (idx < 0) + return NULL; + + return &sti->index_entries[idx]; +} + +static int64_t read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit, + int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) +{ + int64_t ts = read_timestamp(s, stream_index, ppos, pos_limit); + if (stream_index >= 0) + ts = ff_wrap_timestamp(s->streams[stream_index], ts); + return ts; +} + +int ff_seek_frame_binary(AVFormatContext *s, int stream_index, + int64_t target_ts, int flags) +{ + const AVInputFormat *const avif = s->iformat; + int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit; + int64_t ts_min, ts_max, ts; + int index; + int64_t ret; + AVStream *st; + FFStream *sti; + + if (stream_index < 0) + return -1; + + av_log(s, AV_LOG_TRACE, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts)); + + ts_max = + ts_min = AV_NOPTS_VALUE; + pos_limit = -1; // GCC falsely says it may be uninitialized. + + st = s->streams[stream_index]; + sti = ffstream(st); + if (sti->index_entries) { + const AVIndexEntry *e; + + /* FIXME: Whole function must be checked for non-keyframe entries in + * index case, especially read_timestamp(). */ + index = av_index_search_timestamp(st, target_ts, + flags | AVSEEK_FLAG_BACKWARD); + index = FFMAX(index, 0); + e = &sti->index_entries[index]; + + if (e->timestamp <= target_ts || e->pos == e->min_distance) { + pos_min = e->pos; + ts_min = e->timestamp; + av_log(s, AV_LOG_TRACE, "using cached pos_min=0x%"PRIx64" dts_min=%s\n", + pos_min, av_ts2str(ts_min)); + } else { + av_assert1(index == 0); + } + + index = av_index_search_timestamp(st, target_ts, + flags & ~AVSEEK_FLAG_BACKWARD); + av_assert0(index < sti->nb_index_entries); + if (index >= 0) { + e = &sti->index_entries[index]; + av_assert1(e->timestamp >= target_ts); + pos_max = e->pos; + ts_max = e->timestamp; + pos_limit = pos_max - e->min_distance; + av_log(s, AV_LOG_TRACE, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64 + " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max)); + } + } + + pos = ff_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit, + ts_min, ts_max, flags, &ts, avif->read_timestamp); + if (pos < 0) + return -1; + + /* do the seek */ + if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0) + return ret; + + ff_read_frame_flush(s); + avpriv_update_cur_dts(s, st, ts); + + return 0; +} + +int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos, + int64_t (*read_timestamp_func)(struct AVFormatContext *, int , int64_t *, int64_t )) +{ + int64_t step = 1024; + int64_t limit, ts_max; + int64_t filesize = avio_size(s->pb); + int64_t pos_max = filesize - 1; + do { + limit = pos_max; + pos_max = FFMAX(0, (pos_max) - step); + ts_max = read_timestamp(s, stream_index, + &pos_max, limit, read_timestamp_func); + step += step; + } while (ts_max == AV_NOPTS_VALUE && 2*limit > step); + if (ts_max == AV_NOPTS_VALUE) + return -1; + + for (;;) { + int64_t tmp_pos = pos_max + 1; + int64_t tmp_ts = read_timestamp(s, stream_index, + &tmp_pos, INT64_MAX, read_timestamp_func); + if (tmp_ts == AV_NOPTS_VALUE) + break; + av_assert0(tmp_pos > pos_max); + ts_max = tmp_ts; + pos_max = tmp_pos; + if (tmp_pos >= filesize) + break; + } + + if (ts) + *ts = ts_max; + if (pos) + *pos = pos_max; + + return 0; +} + +int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + int64_t pos_min, int64_t pos_max, int64_t pos_limit, + int64_t ts_min, int64_t ts_max, + int flags, int64_t *ts_ret, + int64_t (*read_timestamp_func)(struct AVFormatContext *, + int, int64_t *, int64_t)) +{ + FFFormatContext *const si = ffformatcontext(s); + int64_t pos, ts; + int64_t start_pos; + int no_change; + int ret; + + av_log(s, AV_LOG_TRACE, "gen_seek: %d %s\n", stream_index, av_ts2str(target_ts)); + + if (ts_min == AV_NOPTS_VALUE) { + pos_min = si->data_offset; + ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp_func); + if (ts_min == AV_NOPTS_VALUE) + return -1; + } + + if (ts_min >= target_ts) { + *ts_ret = ts_min; + return pos_min; + } + + if (ts_max == AV_NOPTS_VALUE) { + if ((ret = ff_find_last_ts(s, stream_index, &ts_max, &pos_max, read_timestamp_func)) < 0) + return ret; + pos_limit = pos_max; + } + + if (ts_max <= target_ts) { + *ts_ret = ts_max; + return pos_max; + } + + av_assert0(ts_min < ts_max); + + no_change = 0; + while (pos_min < pos_limit) { + av_log(s, AV_LOG_TRACE, + "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%s dts_max=%s\n", + pos_min, pos_max, av_ts2str(ts_min), av_ts2str(ts_max)); + av_assert0(pos_limit <= pos_max); + + if (no_change == 0) { + int64_t approximate_keyframe_distance = pos_max - pos_limit; + // interpolate position (better than dichotomy) + pos = av_rescale(target_ts - ts_min, pos_max - pos_min, + ts_max - ts_min) + + pos_min - approximate_keyframe_distance; + } else if (no_change == 1) { + // bisection if interpolation did not change min / max pos last time + pos = (pos_min + pos_limit) >> 1; + } else { + /* linear search if bisection failed, can only happen if there + * are very few or no keyframes between min/max */ + pos = pos_min; + } + if (pos <= pos_min) + pos = pos_min + 1; + else if (pos > pos_limit) + pos = pos_limit; + start_pos = pos; + + // May pass pos_limit instead of -1. + ts = read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp_func); + if (pos == pos_max) + no_change++; + else + no_change = 0; + av_log(s, AV_LOG_TRACE, "%"PRId64" %"PRId64" %"PRId64" / %s %s %s" + " target:%s limit:%"PRId64" start:%"PRId64" noc:%d\n", + pos_min, pos, pos_max, + av_ts2str(ts_min), av_ts2str(ts), av_ts2str(ts_max), av_ts2str(target_ts), + pos_limit, start_pos, no_change); + if (ts == AV_NOPTS_VALUE) { + av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n"); + return -1; + } + if (target_ts <= ts) { + pos_limit = start_pos - 1; + pos_max = pos; + ts_max = ts; + } + if (target_ts >= ts) { + pos_min = pos; + ts_min = ts; + } + } + + pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; + ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max; +#if 0 + pos_min = pos; + ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp_func); + pos_min++; + ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp_func); + av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" %s<=%s<=%s\n", + pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max)); +#endif + *ts_ret = ts; + return pos; +} + +static int seek_frame_byte(AVFormatContext *s, int stream_index, + int64_t pos, int flags) +{ + FFFormatContext *const si = ffformatcontext(s); + int64_t pos_min, pos_max; + + pos_min = si->data_offset; + pos_max = avio_size(s->pb) - 1; + + if (pos < pos_min) + pos = pos_min; + else if (pos > pos_max) + pos = pos_max; + + avio_seek(s->pb, pos, SEEK_SET); + + s->io_repositioned = 1; + + return 0; +} + +static int seek_frame_generic(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + FFFormatContext *const si = ffformatcontext(s); + AVStream *const st = s->streams[stream_index]; + FFStream *const sti = ffstream(st); + const AVIndexEntry *ie; + int index; + int64_t ret; + + index = av_index_search_timestamp(st, timestamp, flags); + + if (index < 0 && sti->nb_index_entries && + timestamp < sti->index_entries[0].timestamp) + return -1; + + if (index < 0 || index == sti->nb_index_entries - 1) { + AVPacket *const pkt = si->pkt; + int nonkey = 0; + + if (sti->nb_index_entries) { + av_assert0(sti->index_entries); + ie = &sti->index_entries[sti->nb_index_entries - 1]; + if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0) + return ret; + s->io_repositioned = 1; + avpriv_update_cur_dts(s, st, ie->timestamp); + } else { + if ((ret = avio_seek(s->pb, si->data_offset, SEEK_SET)) < 0) + return ret; + s->io_repositioned = 1; + } + av_packet_unref(pkt); + for (;;) { + int read_status; + do { + read_status = av_read_frame(s, pkt); + } while (read_status == AVERROR(EAGAIN)); + if (read_status < 0) + break; + if (stream_index == pkt->stream_index && pkt->dts > timestamp) { + if (pkt->flags & AV_PKT_FLAG_KEY) { + av_packet_unref(pkt); + break; + } + if (nonkey++ > 1000 && st->codecpar->codec_id != AV_CODEC_ID_CDGRAPHICS) { + av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey); + av_packet_unref(pkt); + break; + } + } + av_packet_unref(pkt); + } + index = av_index_search_timestamp(st, timestamp, flags); + } + if (index < 0) + return -1; + + ff_read_frame_flush(s); + if (s->iformat->read_seek) + if (s->iformat->read_seek(s, stream_index, timestamp, flags) >= 0) + return 0; + ie = &sti->index_entries[index]; + if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0) + return ret; + s->io_repositioned = 1; + avpriv_update_cur_dts(s, st, ie->timestamp); + + return 0; +} + +static int seek_frame_internal(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + AVStream *st; + int ret; + + if (flags & AVSEEK_FLAG_BYTE) { + if (s->iformat->flags & AVFMT_NO_BYTE_SEEK) + return -1; + ff_read_frame_flush(s); + return seek_frame_byte(s, stream_index, timestamp, flags); + } + + if (stream_index < 0) { + stream_index = av_find_default_stream_index(s); + if (stream_index < 0) + return -1; + + st = s->streams[stream_index]; + /* timestamp for default must be expressed in AV_TIME_BASE units */ + timestamp = av_rescale(timestamp, st->time_base.den, + AV_TIME_BASE * (int64_t) st->time_base.num); + } + + /* first, we try the format specific seek */ + if (s->iformat->read_seek) { + ff_read_frame_flush(s); + ret = s->iformat->read_seek(s, stream_index, timestamp, flags); + } else + ret = -1; + if (ret >= 0) + return 0; + + if (s->iformat->read_timestamp && + !(s->iformat->flags & AVFMT_NOBINSEARCH)) { + ff_read_frame_flush(s); + return ff_seek_frame_binary(s, stream_index, timestamp, flags); + } else if (!(s->iformat->flags & AVFMT_NOGENSEARCH)) { + ff_read_frame_flush(s); + return seek_frame_generic(s, stream_index, timestamp, flags); + } else + return -1; +} + +int av_seek_frame(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + int ret; + + if (s->iformat->read_seek2 && !s->iformat->read_seek) { + int64_t min_ts = INT64_MIN, max_ts = INT64_MAX; + if ((flags & AVSEEK_FLAG_BACKWARD)) + max_ts = timestamp; + else + min_ts = timestamp; + return avformat_seek_file(s, stream_index, min_ts, timestamp, max_ts, + flags & ~AVSEEK_FLAG_BACKWARD); + } + + ret = seek_frame_internal(s, stream_index, timestamp, flags); + + if (ret >= 0) + ret = avformat_queue_attached_pictures(s); + + return ret; +} + +int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, + int64_t ts, int64_t max_ts, int flags) +{ + if (min_ts > ts || max_ts < ts) + return -1; + if (stream_index < -1 || stream_index >= (int)s->nb_streams) + return AVERROR(EINVAL); + + if (s->seek2any > 0) + flags |= AVSEEK_FLAG_ANY; + flags &= ~AVSEEK_FLAG_BACKWARD; + + if (s->iformat->read_seek2) { + int ret; + ff_read_frame_flush(s); + + if (stream_index == -1 && s->nb_streams == 1) { + AVRational time_base = s->streams[0]->time_base; + ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); + min_ts = av_rescale_rnd(min_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + max_ts = av_rescale_rnd(max_ts, time_base.den, + time_base.num * (int64_t)AV_TIME_BASE, + AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); + stream_index = 0; + } + + ret = s->iformat->read_seek2(s, stream_index, min_ts, + ts, max_ts, flags); + + if (ret >= 0) + ret = avformat_queue_attached_pictures(s); + return ret; + } + + if (s->iformat->read_timestamp) { + // try to seek via read_timestamp() + } + + // Fall back on old API if new is not implemented but old is. + // Note the old API has somewhat different semantics. + if (s->iformat->read_seek || 1) { + int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0); + int ret = av_seek_frame(s, stream_index, ts, flags | dir); + if (ret < 0 && ts != min_ts && max_ts != ts) { + ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir); + if (ret >= 0) + ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD)); + } + return ret; + } + + // try some generic seek like seek_frame_generic() but with new ts semantics + return -1; //unreachable +} + +/** Flush the frame reader. */ +void ff_read_frame_flush(AVFormatContext *s) +{ + FFFormatContext *const si = ffformatcontext(s); + + ff_flush_packet_queue(s); + + /* Reset read state for each stream. */ + for (unsigned i = 0; i < s->nb_streams; i++) { + AVStream *const st = s->streams[i]; + FFStream *const sti = ffstream(st); + + if (sti->parser) { + av_parser_close(sti->parser); + sti->parser = NULL; + } + sti->last_IP_pts = AV_NOPTS_VALUE; + sti->last_dts_for_order_check = AV_NOPTS_VALUE; + if (sti->first_dts == AV_NOPTS_VALUE) + sti->cur_dts = RELATIVE_TS_BASE; + else + /* We set the current DTS to an unspecified origin. */ + sti->cur_dts = AV_NOPTS_VALUE; + + sti->probe_packets = s->max_probe_packets; + + for (int j = 0; j < MAX_REORDER_DELAY + 1; j++) + sti->pts_buffer[j] = AV_NOPTS_VALUE; + + if (si->inject_global_side_data) + sti->inject_global_side_data = 1; + + sti->skip_samples = 0; + } +} + +int avformat_flush(AVFormatContext *s) +{ + ff_read_frame_flush(s); + return 0; +} diff --git a/libavformat/utils.c b/libavformat/utils.c index e228047107..c11ab1d252 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -88,19 +88,6 @@ int ff_unlock_avformat(void) return ff_mutex_unlock(&avformat_mutex) ? -1 : 0; } -#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48)) - -static int is_relative(int64_t ts) { - return ts > (RELATIVE_TS_BASE - (1LL<<48)); -} - -/** - * Wrap a given time stamp, if there is an indication for an overflow - * - * @param st stream - * @param timestamp the time stamp to wrap - * @return resulting time stamp - */ static int64_t wrap_timestamp(const AVStream *st, int64_t timestamp) { const FFStream *const sti = cffstream(st); @@ -116,6 +103,11 @@ static int64_t wrap_timestamp(const AVStream *st, int64_t timestamp) return timestamp; } +int64_t ff_wrap_timestamp(const AVStream *st, int64_t timestamp) +{ + return wrap_timestamp(st, timestamp); +} + int64_t av_stream_get_end_pts(const AVStream *st) { if (cffstream(st)->priv_pts) { @@ -1771,7 +1763,7 @@ return_packet: } /* XXX: suppress the packet queue */ -static void flush_packet_queue(AVFormatContext *s) +void ff_flush_packet_queue(AVFormatContext *s) { FFFormatContext *const si = ffformatcontext(s); avpriv_packet_list_free(&si->parse_queue, &si->parse_queue_end); @@ -1781,9 +1773,6 @@ static void flush_packet_queue(AVFormatContext *s) si->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; } -/*******************************************************/ -/* seek support */ - int av_find_default_stream_index(AVFormatContext *s) { int best_stream = 0; @@ -1820,731 +1809,6 @@ int av_find_default_stream_index(AVFormatContext *s) return best_stream; } -/** Flush the frame reader. */ -void ff_read_frame_flush(AVFormatContext *s) -{ - FFFormatContext *const si = ffformatcontext(s); - - flush_packet_queue(s); - - /* Reset read state for each stream. */ - for (unsigned i = 0; i < s->nb_streams; i++) { - AVStream *const st = s->streams[i]; - FFStream *const sti = ffstream(st); - - if (sti->parser) { - av_parser_close(sti->parser); - sti->parser = NULL; - } - sti->last_IP_pts = AV_NOPTS_VALUE; - sti->last_dts_for_order_check = AV_NOPTS_VALUE; - if (sti->first_dts == AV_NOPTS_VALUE) - sti->cur_dts = RELATIVE_TS_BASE; - else - /* We set the current DTS to an unspecified origin. */ - sti->cur_dts = AV_NOPTS_VALUE; - - sti->probe_packets = s->max_probe_packets; - - for (int j = 0; j < MAX_REORDER_DELAY + 1; j++) - sti->pts_buffer[j] = AV_NOPTS_VALUE; - - if (si->inject_global_side_data) - sti->inject_global_side_data = 1; - - sti->skip_samples = 0; - } -} - -void avpriv_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) -{ - for (unsigned i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - FFStream *const sti = ffstream(st); - - sti->cur_dts = - av_rescale(timestamp, - st->time_base.den * (int64_t) ref_st->time_base.num, - st->time_base.num * (int64_t) ref_st->time_base.den); - } -} - -void ff_reduce_index(AVFormatContext *s, int stream_index) -{ - AVStream *st = s->streams[stream_index]; - FFStream *const sti = ffstream(st); - unsigned int max_entries = s->max_index_size / sizeof(AVIndexEntry); - - if ((unsigned) sti->nb_index_entries >= max_entries) { - int i; - for (i = 0; 2 * i < sti->nb_index_entries; i++) - sti->index_entries[i] = sti->index_entries[2 * i]; - sti->nb_index_entries = i; - } -} - -int ff_add_index_entry(AVIndexEntry **index_entries, - int *nb_index_entries, - unsigned int *index_entries_allocated_size, - int64_t pos, int64_t timestamp, - int size, int distance, int flags) -{ - AVIndexEntry *entries, *ie; - int index; - - if ((unsigned) *nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry)) - return -1; - - if (timestamp == AV_NOPTS_VALUE) - return AVERROR(EINVAL); - - if (size < 0 || size > 0x3FFFFFFF) - return AVERROR(EINVAL); - - if (is_relative(timestamp)) //FIXME this maintains previous behavior but we should shift by the correct offset once known - timestamp -= RELATIVE_TS_BASE; - - entries = av_fast_realloc(*index_entries, - index_entries_allocated_size, - (*nb_index_entries + 1) * - sizeof(AVIndexEntry)); - if (!entries) - return -1; - - *index_entries = entries; - - index = ff_index_search_timestamp(*index_entries, *nb_index_entries, - timestamp, AVSEEK_FLAG_ANY); - - if (index < 0) { - index = (*nb_index_entries)++; - ie = &entries[index]; - av_assert0(index == 0 || ie[-1].timestamp < timestamp); - } else { - ie = &entries[index]; - if (ie->timestamp != timestamp) { - if (ie->timestamp <= timestamp) - return -1; - memmove(entries + index + 1, entries + index, - sizeof(AVIndexEntry) * (*nb_index_entries - index)); - (*nb_index_entries)++; - } else if (ie->pos == pos && distance < ie->min_distance) - // do not reduce the distance - distance = ie->min_distance; - } - - ie->pos = pos; - ie->timestamp = timestamp; - ie->min_distance = distance; - ie->size = size; - ie->flags = flags; - - return index; -} - -int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, - int size, int distance, int flags) -{ - FFStream *const sti = ffstream(st); - timestamp = wrap_timestamp(st, timestamp); - return ff_add_index_entry(&sti->index_entries, &sti->nb_index_entries, - &sti->index_entries_allocated_size, pos, - timestamp, size, distance, flags); -} - -int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries, - int64_t wanted_timestamp, int flags) -{ - int a, b, m; - int64_t timestamp; - - a = -1; - b = nb_entries; - - // Optimize appending index entries at the end. - if (b && entries[b - 1].timestamp < wanted_timestamp) - a = b - 1; - - while (b - a > 1) { - m = (a + b) >> 1; - - // Search for the next non-discarded packet. - while ((entries[m].flags & AVINDEX_DISCARD_FRAME) && m < b && m < nb_entries - 1) { - m++; - if (m == b && entries[m].timestamp >= wanted_timestamp) { - m = b - 1; - break; - } - } - - timestamp = entries[m].timestamp; - if (timestamp >= wanted_timestamp) - b = m; - if (timestamp <= wanted_timestamp) - a = m; - } - m = (flags & AVSEEK_FLAG_BACKWARD) ? a : b; - - if (!(flags & AVSEEK_FLAG_ANY)) - while (m >= 0 && m < nb_entries && - !(entries[m].flags & AVINDEX_KEYFRAME)) - m += (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1; - - if (m == nb_entries) - return -1; - return m; -} - -void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance) -{ - int64_t pos_delta = 0; - int64_t skip = 0; - //We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable - const char *proto = avio_find_protocol_name(s->url); - FFIOContext *ctx; - - av_assert0(time_tolerance >= 0); - - if (!proto) { - av_log(s, AV_LOG_INFO, - "Protocol name not provided, cannot determine if input is local or " - "a network protocol, buffers and access patterns cannot be configured " - "optimally without knowing the protocol\n"); - } - - if (proto && !(strcmp(proto, "file") && strcmp(proto, "pipe") && strcmp(proto, "cache"))) - return; - - for (unsigned ist1 = 0; ist1 < s->nb_streams; ist1++) { - AVStream *st1 = s->streams[ist1]; - FFStream *const sti1 = ffstream(st1); - for (unsigned ist2 = 0; ist2 < s->nb_streams; ist2++) { - AVStream *st2 = s->streams[ist2]; - FFStream *const sti2 = ffstream(st2); - - if (ist1 == ist2) - continue; - - for (int i1 = 0, i2 = 0; i1 < sti1->nb_index_entries; i1++) { - const AVIndexEntry *const e1 = &sti1->index_entries[i1]; - int64_t e1_pts = av_rescale_q(e1->timestamp, st1->time_base, AV_TIME_BASE_Q); - - skip = FFMAX(skip, e1->size); - for (; i2 < sti2->nb_index_entries; i2++) { - const AVIndexEntry *const e2 = &sti2->index_entries[i2]; - int64_t e2_pts = av_rescale_q(e2->timestamp, st2->time_base, AV_TIME_BASE_Q); - if (e2_pts < e1_pts || e2_pts - (uint64_t)e1_pts < time_tolerance) - continue; - pos_delta = FFMAX(pos_delta, e1->pos - e2->pos); - break; - } - } - } - } - - pos_delta *= 2; - ctx = ffiocontext(s->pb); - /* XXX This could be adjusted depending on protocol*/ - if (s->pb->buffer_size < pos_delta && pos_delta < (1<<24)) { - av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta); - - /* realloc the buffer and the original data will be retained */ - if (ffio_realloc_buf(s->pb, pos_delta)) { - av_log(s, AV_LOG_ERROR, "Realloc buffer fail.\n"); - return; - } - - ctx->short_seek_threshold = FFMAX(ctx->short_seek_threshold, pos_delta/2); - } - - if (skip < (1<<23)) { - ctx->short_seek_threshold = FFMAX(ctx->short_seek_threshold, skip); - } -} - -int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags) -{ - FFStream *const sti = ffstream(st); - return ff_index_search_timestamp(sti->index_entries, sti->nb_index_entries, - wanted_timestamp, flags); -} - -int avformat_index_get_entries_count(const AVStream *st) -{ - return cffstream(st)->nb_index_entries; -} - -const AVIndexEntry *avformat_index_get_entry(AVStream *st, int idx) -{ - const FFStream *const sti = ffstream(st); - if (idx < 0 || idx >= sti->nb_index_entries) - return NULL; - - return &sti->index_entries[idx]; -} - -const AVIndexEntry *avformat_index_get_entry_from_timestamp(AVStream *st, - int64_t wanted_timestamp, - int flags) -{ - const FFStream *const sti = ffstream(st); - int idx = ff_index_search_timestamp(sti->index_entries, - sti->nb_index_entries, - wanted_timestamp, flags); - - if (idx < 0) - return NULL; - - return &sti->index_entries[idx]; -} - -static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit, - int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) -{ - int64_t ts = read_timestamp(s, stream_index, ppos, pos_limit); - if (stream_index >= 0) - ts = wrap_timestamp(s->streams[stream_index], ts); - return ts; -} - -int ff_seek_frame_binary(AVFormatContext *s, int stream_index, - int64_t target_ts, int flags) -{ - const AVInputFormat *avif = s->iformat; - int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit; - int64_t ts_min, ts_max, ts; - int index; - int64_t ret; - AVStream *st; - FFStream *sti; - - if (stream_index < 0) - return -1; - - av_log(s, AV_LOG_TRACE, "read_seek: %d %s\n", stream_index, av_ts2str(target_ts)); - - ts_max = - ts_min = AV_NOPTS_VALUE; - pos_limit = -1; // GCC falsely says it may be uninitialized. - - st = s->streams[stream_index]; - sti = ffstream(st); - if (sti->index_entries) { - AVIndexEntry *e; - - /* FIXME: Whole function must be checked for non-keyframe entries in - * index case, especially read_timestamp(). */ - index = av_index_search_timestamp(st, target_ts, - flags | AVSEEK_FLAG_BACKWARD); - index = FFMAX(index, 0); - e = &sti->index_entries[index]; - - if (e->timestamp <= target_ts || e->pos == e->min_distance) { - pos_min = e->pos; - ts_min = e->timestamp; - av_log(s, AV_LOG_TRACE, "using cached pos_min=0x%"PRIx64" dts_min=%s\n", - pos_min, av_ts2str(ts_min)); - } else { - av_assert1(index == 0); - } - - index = av_index_search_timestamp(st, target_ts, - flags & ~AVSEEK_FLAG_BACKWARD); - av_assert0(index < sti->nb_index_entries); - if (index >= 0) { - e = &sti->index_entries[index]; - av_assert1(e->timestamp >= target_ts); - pos_max = e->pos; - ts_max = e->timestamp; - pos_limit = pos_max - e->min_distance; - av_log(s, AV_LOG_TRACE, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64 - " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max)); - } - } - - pos = ff_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit, - ts_min, ts_max, flags, &ts, avif->read_timestamp); - if (pos < 0) - return -1; - - /* do the seek */ - if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0) - return ret; - - ff_read_frame_flush(s); - avpriv_update_cur_dts(s, st, ts); - - return 0; -} - -int ff_find_last_ts(AVFormatContext *s, int stream_index, int64_t *ts, int64_t *pos, - int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) -{ - int64_t step = 1024; - int64_t limit, ts_max; - int64_t filesize = avio_size(s->pb); - int64_t pos_max = filesize - 1; - do { - limit = pos_max; - pos_max = FFMAX(0, (pos_max) - step); - ts_max = ff_read_timestamp(s, stream_index, - &pos_max, limit, read_timestamp); - step += step; - } while (ts_max == AV_NOPTS_VALUE && 2*limit > step); - if (ts_max == AV_NOPTS_VALUE) - return -1; - - for (;;) { - int64_t tmp_pos = pos_max + 1; - int64_t tmp_ts = ff_read_timestamp(s, stream_index, - &tmp_pos, INT64_MAX, read_timestamp); - if (tmp_ts == AV_NOPTS_VALUE) - break; - av_assert0(tmp_pos > pos_max); - ts_max = tmp_ts; - pos_max = tmp_pos; - if (tmp_pos >= filesize) - break; - } - - if (ts) - *ts = ts_max; - if (pos) - *pos = pos_max; - - return 0; -} - -int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, - int64_t pos_min, int64_t pos_max, int64_t pos_limit, - int64_t ts_min, int64_t ts_max, - int flags, int64_t *ts_ret, - int64_t (*read_timestamp)(struct AVFormatContext *, int, - int64_t *, int64_t)) -{ - FFFormatContext *const si = ffformatcontext(s); - int64_t pos, ts; - int64_t start_pos; - int no_change; - int ret; - - av_log(s, AV_LOG_TRACE, "gen_seek: %d %s\n", stream_index, av_ts2str(target_ts)); - - if (ts_min == AV_NOPTS_VALUE) { - pos_min = si->data_offset; - ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); - if (ts_min == AV_NOPTS_VALUE) - return -1; - } - - if (ts_min >= target_ts) { - *ts_ret = ts_min; - return pos_min; - } - - if (ts_max == AV_NOPTS_VALUE) { - if ((ret = ff_find_last_ts(s, stream_index, &ts_max, &pos_max, read_timestamp)) < 0) - return ret; - pos_limit = pos_max; - } - - if (ts_max <= target_ts) { - *ts_ret = ts_max; - return pos_max; - } - - av_assert0(ts_min < ts_max); - - no_change = 0; - while (pos_min < pos_limit) { - av_log(s, AV_LOG_TRACE, - "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%s dts_max=%s\n", - pos_min, pos_max, av_ts2str(ts_min), av_ts2str(ts_max)); - av_assert0(pos_limit <= pos_max); - - if (no_change == 0) { - int64_t approximate_keyframe_distance = pos_max - pos_limit; - // interpolate position (better than dichotomy) - pos = av_rescale(target_ts - ts_min, pos_max - pos_min, - ts_max - ts_min) + - pos_min - approximate_keyframe_distance; - } else if (no_change == 1) { - // bisection if interpolation did not change min / max pos last time - pos = (pos_min + pos_limit) >> 1; - } else { - /* linear search if bisection failed, can only happen if there - * are very few or no keyframes between min/max */ - pos = pos_min; - } - if (pos <= pos_min) - pos = pos_min + 1; - else if (pos > pos_limit) - pos = pos_limit; - start_pos = pos; - - // May pass pos_limit instead of -1. - ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp); - if (pos == pos_max) - no_change++; - else - no_change = 0; - av_log(s, AV_LOG_TRACE, "%"PRId64" %"PRId64" %"PRId64" / %s %s %s" - " target:%s limit:%"PRId64" start:%"PRId64" noc:%d\n", - pos_min, pos, pos_max, - av_ts2str(ts_min), av_ts2str(ts), av_ts2str(ts_max), av_ts2str(target_ts), - pos_limit, start_pos, no_change); - if (ts == AV_NOPTS_VALUE) { - av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n"); - return -1; - } - if (target_ts <= ts) { - pos_limit = start_pos - 1; - pos_max = pos; - ts_max = ts; - } - if (target_ts >= ts) { - pos_min = pos; - ts_min = ts; - } - } - - pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; - ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max; -#if 0 - pos_min = pos; - ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); - pos_min++; - ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); - av_log(s, AV_LOG_TRACE, "pos=0x%"PRIx64" %s<=%s<=%s\n", - pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max)); -#endif - *ts_ret = ts; - return pos; -} - -static int seek_frame_byte(AVFormatContext *s, int stream_index, - int64_t pos, int flags) -{ - FFFormatContext *const si = ffformatcontext(s); - int64_t pos_min, pos_max; - - pos_min = si->data_offset; - pos_max = avio_size(s->pb) - 1; - - if (pos < pos_min) - pos = pos_min; - else if (pos > pos_max) - pos = pos_max; - - avio_seek(s->pb, pos, SEEK_SET); - - s->io_repositioned = 1; - - return 0; -} - -static int seek_frame_generic(AVFormatContext *s, int stream_index, - int64_t timestamp, int flags) -{ - FFFormatContext *const si = ffformatcontext(s); - AVStream *const st = s->streams[stream_index]; - FFStream *const sti = ffstream(st); - int index; - int64_t ret; - AVIndexEntry *ie; - - - index = av_index_search_timestamp(st, timestamp, flags); - - if (index < 0 && sti->nb_index_entries && - timestamp < sti->index_entries[0].timestamp) - return -1; - - if (index < 0 || index == sti->nb_index_entries - 1) { - AVPacket *pkt = ffformatcontext(s)->pkt; - int nonkey = 0; - - if (sti->nb_index_entries) { - av_assert0(sti->index_entries); - ie = &sti->index_entries[sti->nb_index_entries - 1]; - if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0) - return ret; - s->io_repositioned = 1; - avpriv_update_cur_dts(s, st, ie->timestamp); - } else { - if ((ret = avio_seek(s->pb, si->data_offset, SEEK_SET)) < 0) - return ret; - s->io_repositioned = 1; - } - av_packet_unref(pkt); - for (;;) { - int read_status; - do { - read_status = av_read_frame(s, pkt); - } while (read_status == AVERROR(EAGAIN)); - if (read_status < 0) - break; - if (stream_index == pkt->stream_index && pkt->dts > timestamp) { - if (pkt->flags & AV_PKT_FLAG_KEY) { - av_packet_unref(pkt); - break; - } - if (nonkey++ > 1000 && st->codecpar->codec_id != AV_CODEC_ID_CDGRAPHICS) { - av_log(s, AV_LOG_ERROR,"seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found\n", nonkey); - av_packet_unref(pkt); - break; - } - } - av_packet_unref(pkt); - } - index = av_index_search_timestamp(st, timestamp, flags); - } - if (index < 0) - return -1; - - ff_read_frame_flush(s); - if (s->iformat->read_seek) - if (s->iformat->read_seek(s, stream_index, timestamp, flags) >= 0) - return 0; - ie = &sti->index_entries[index]; - if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0) - return ret; - s->io_repositioned = 1; - avpriv_update_cur_dts(s, st, ie->timestamp); - - return 0; -} - -static int seek_frame_internal(AVFormatContext *s, int stream_index, - int64_t timestamp, int flags) -{ - int ret; - AVStream *st; - - if (flags & AVSEEK_FLAG_BYTE) { - if (s->iformat->flags & AVFMT_NO_BYTE_SEEK) - return -1; - ff_read_frame_flush(s); - return seek_frame_byte(s, stream_index, timestamp, flags); - } - - if (stream_index < 0) { - stream_index = av_find_default_stream_index(s); - if (stream_index < 0) - return -1; - - st = s->streams[stream_index]; - /* timestamp for default must be expressed in AV_TIME_BASE units */ - timestamp = av_rescale(timestamp, st->time_base.den, - AV_TIME_BASE * (int64_t) st->time_base.num); - } - - /* first, we try the format specific seek */ - if (s->iformat->read_seek) { - ff_read_frame_flush(s); - ret = s->iformat->read_seek(s, stream_index, timestamp, flags); - } else - ret = -1; - if (ret >= 0) - return 0; - - if (s->iformat->read_timestamp && - !(s->iformat->flags & AVFMT_NOBINSEARCH)) { - ff_read_frame_flush(s); - return ff_seek_frame_binary(s, stream_index, timestamp, flags); - } else if (!(s->iformat->flags & AVFMT_NOGENSEARCH)) { - ff_read_frame_flush(s); - return seek_frame_generic(s, stream_index, timestamp, flags); - } else - return -1; -} - -int av_seek_frame(AVFormatContext *s, int stream_index, - int64_t timestamp, int flags) -{ - int ret; - - if (s->iformat->read_seek2 && !s->iformat->read_seek) { - int64_t min_ts = INT64_MIN, max_ts = INT64_MAX; - if ((flags & AVSEEK_FLAG_BACKWARD)) - max_ts = timestamp; - else - min_ts = timestamp; - return avformat_seek_file(s, stream_index, min_ts, timestamp, max_ts, - flags & ~AVSEEK_FLAG_BACKWARD); - } - - ret = seek_frame_internal(s, stream_index, timestamp, flags); - - if (ret >= 0) - ret = avformat_queue_attached_pictures(s); - - return ret; -} - -int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, - int64_t ts, int64_t max_ts, int flags) -{ - if (min_ts > ts || max_ts < ts) - return -1; - if (stream_index < -1 || stream_index >= (int)s->nb_streams) - return AVERROR(EINVAL); - - if (s->seek2any>0) - flags |= AVSEEK_FLAG_ANY; - flags &= ~AVSEEK_FLAG_BACKWARD; - - if (s->iformat->read_seek2) { - int ret; - ff_read_frame_flush(s); - - if (stream_index == -1 && s->nb_streams == 1) { - AVRational time_base = s->streams[0]->time_base; - ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base); - min_ts = av_rescale_rnd(min_ts, time_base.den, - time_base.num * (int64_t)AV_TIME_BASE, - AV_ROUND_UP | AV_ROUND_PASS_MINMAX); - max_ts = av_rescale_rnd(max_ts, time_base.den, - time_base.num * (int64_t)AV_TIME_BASE, - AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); - stream_index = 0; - } - - ret = s->iformat->read_seek2(s, stream_index, min_ts, - ts, max_ts, flags); - - if (ret >= 0) - ret = avformat_queue_attached_pictures(s); - return ret; - } - - if (s->iformat->read_timestamp) { - // try to seek via read_timestamp() - } - - // Fall back on old API if new is not implemented but old is. - // Note the old API has somewhat different semantics. - if (s->iformat->read_seek || 1) { - int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0); - int ret = av_seek_frame(s, stream_index, ts, flags | dir); - if (ret<0 && ts != min_ts && max_ts != ts) { - ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir); - if (ret >= 0) - ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD)); - } - return ret; - } - - // try some generic seek like seek_frame_generic() but with new ts semantics - return -1; //unreachable -} - -int avformat_flush(AVFormatContext *s) -{ - ff_read_frame_flush(s); - return 0; -} - /*******************************************************/ /** @@ -2748,7 +2012,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) int retry = 0; /* flush packet queue */ - flush_packet_queue(ic); + ff_flush_packet_queue(ic); for (unsigned i = 0; i < ic->nb_streams; i++) { AVStream *const st = ic->streams[i]; @@ -4358,7 +3622,7 @@ void avformat_free_context(AVFormatContext *s) av_packet_free(&si->pkt); av_packet_free(&si->parse_pkt); av_freep(&s->streams); - flush_packet_queue(s); + ff_flush_packet_queue(s); av_freep(&s->url); av_free(s); }