The current behavior may produce packets in a different order after seeking, compared to demuxing linearly from the beginning. This is because the MOV demuxer seeks in each stream based on timestamp, which may not necessarily match the original packet order.
This makes implementing certain operations, such as segmenting, quite hard, and slower than need be. Therefore, add an option which retains the same packet order after seeking, as when a file is demuxed linearly. This is accomplished by seeking in the other streams, based on file position, rather than timestamp. The positional search in the index entries is implemented as a linear search since, in MOV, the index entries may be out of order in terms of file position, in particularily insane files. Signed-off-by: Derek Buitenhuis <derek.buitenh...@gmail.com> --- libavformat/isom.h | 1 + libavformat/mov.c | 65 +++++++++++++++++++++++++++++++++++++++++---------- libavformat/version.h | 2 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/libavformat/isom.h b/libavformat/isom.h index c24b962..88405c5 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -155,6 +155,7 @@ typedef struct MOVContext { unsigned trex_count; int itunes_metadata; ///< metadata are itunes style int chapter_track; + int seek_keep_order; int64_t next_root_atom; ///< offset of the next root atom int export_all; int export_xmp; diff --git a/libavformat/mov.c b/libavformat/mov.c index df7357f..063f78c 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -3436,13 +3436,35 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags) +static int mov_search_sample_pos(AVStream *st, int64_t pos) +{ + int i; + + /* This is linear search because entries may be out of order pos-wise. */ + for (i = 0; i < st->nb_index_entries; i++) + if (st->index_entries[i].pos >= pos) + return i; + + return -1; +} + +static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t val, int flags, int pos) { MOVStreamContext *sc = st->priv_data; + int64_t timestamp; int sample, time_sample; int i; - sample = av_index_search_timestamp(st, timestamp, flags); + if (!pos) { + sample = av_index_search_timestamp(st, val, flags); + timestamp = val; + } else { + sample = mov_search_sample_pos(st, val); + if (sample < 0) + return AVERROR_INVALIDDATA; + + timestamp = st->index_entries[sample].timestamp; + } av_dlog(s, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample); if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp) sample = 0; @@ -3468,10 +3490,10 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags) { + MOVContext *mc = s->priv_data; AVStream *st; - int64_t seek_timestamp, timestamp; + int64_t timestamp; int sample; - int i; if (stream_index >= s->nb_streams) return AVERROR_INVALIDDATA; @@ -3479,20 +3501,35 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti sample_time = 0; st = s->streams[stream_index]; - sample = mov_seek_stream(s, st, sample_time, flags); + sample = mov_seek_stream(s, st, sample_time, flags, 0); if (sample < 0) return sample; - /* adjust seek timestamp to found sample timestamp */ - seek_timestamp = st->index_entries[sample].timestamp; + if (!mc->seek_keep_order) { + /* adjust seek timestamp to found sample timestamp */ + int64_t seek_timestamp = st->index_entries[sample].timestamp; + int i; - for (i = 0; i < s->nb_streams; i++) { - st = s->streams[i]; - if (stream_index == i) + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (stream_index == i) + continue; + + timestamp = av_rescale_q(seek_timestamp, s->streams[stream_index]->time_base, st->time_base); + mov_seek_stream(s, st, timestamp, flags, 0); + } + } else { + int64_t seek_pos = st->index_entries[sample].pos; + int i; + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + + if (stream_index == i) continue; - timestamp = av_rescale_q(seek_timestamp, s->streams[stream_index]->time_base, st->time_base); - mov_seek_stream(s, st, timestamp, flags); + mov_seek_stream(s, st, seek_pos, flags, 1); + } } return 0; } @@ -3500,6 +3537,10 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti #define OFFSET(x) offsetof(MOVContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM static const AVOption mov_options[] = { + {"seek_keep_order", + "If seeking, keep the same packet demuxing order as if demuxing linearly.", + OFFSET(seek_keep_order), AV_OPT_TYPE_INT, { .i64 = 0 }, + 0, 1, FLAGS}, { "export_all", "Export unrecognized metadata entries", OFFSET(export_all), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS }, { "export_xmp", "Export full XMP metadata", OFFSET(export_xmp), diff --git a/libavformat/version.h b/libavformat/version.h index 9ceb6ee..495302a 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFORMAT_VERSION_MAJOR 56 -#define LIBAVFORMAT_VERSION_MINOR 17 +#define LIBAVFORMAT_VERSION_MINOR 18 #define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- 1.8.3.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel