From eeb38d760d0748b14c4faed1bfd04efad9ed0806 Mon Sep 17 00:00:00 2001 From: John Stebbins Date: Sun, 19 Feb 2017 14:45:44 -0700 Subject: [PATCH] mov: fix edit list issue that can cause A/V desync Only the first entry in the edit list was factored into the time_offset of the first sample in a track. But when there is a delay (empty edit entry) the mediatime from the second entry must also be factored into the time_offset. --- libavformat/isom.h | 2 ++ libavformat/mov.c | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libavformat/isom.h b/libavformat/isom.h index 8cc5ab7..7c345e9 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -125,6 +125,8 @@ typedef struct MOVStreamContext { int *keyframes; int time_scale; int64_t time_offset; ///< time offset of the first edit list entry + int64_t time_offset_delay; + int64_t time_offset_skip; int current_sample; unsigned int bytes_per_frame; unsigned int samples_per_frame; diff --git a/libavformat/mov.c b/libavformat/mov.c index 5c9f85c..1657647 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -2323,10 +2323,10 @@ static void mov_build_index(MOVContext *mov, AVStream *st) uint64_t stream_size = 0; /* adjust first dts according to edit list */ - if (sc->time_offset && mov->time_scale > 0) { - if (sc->time_offset < 0) - sc->time_offset = av_rescale(sc->time_offset, sc->time_scale, mov->time_scale); - current_dts = -sc->time_offset; + if (mov->time_scale > 0) { + sc->time_offset = av_rescale(sc->time_offset_delay, sc->time_scale, + mov->time_scale) - sc->time_offset_skip; + current_dts = sc->time_offset; } /* only use old uncompressed audio chunk demuxing when stts specifies it */ @@ -2999,6 +2999,10 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc = st->priv_data; if (sc->pseudo_stream_id+1 != frag->stsd_id) return 0; + if (c->time_scale > 0) { + sc->time_offset = av_rescale(sc->time_offset_delay, sc->time_scale, + c->time_scale) - sc->time_offset_skip; + } avio_r8(pb); /* version */ flags = avio_rb24(pb); entries = avio_rb32(pb); @@ -3029,7 +3033,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) } if (flags & MOV_TRUN_DATA_OFFSET) data_offset = avio_rb32(pb); if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) first_sample_flags = avio_rb32(pb); - dts = sc->track_end - sc->time_offset; + dts = sc->track_end + sc->time_offset; offset = frag->base_data_offset + data_offset; distance = 0; av_log(c->fc, AV_LOG_TRACE, "first sample flags 0x%x\n", first_sample_flags); @@ -3069,7 +3073,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_EOF; frag->implicit_offset = offset; - st->duration = sc->track_end = dts + sc->time_offset; + st->duration = sc->track_end = dts - sc->time_offset; return 0; } @@ -3152,6 +3156,7 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) { MOVStreamContext *sc; int i, edit_count, version; + int time_offset_done = 0; if (c->fc->nb_streams < 1) return 0; @@ -3175,8 +3180,17 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) time = (int32_t)avio_rb32(pb); /* media time */ } avio_rb32(pb); /* Media rate */ - if (i == 0 && time >= -1) { - sc->time_offset = time != -1 ? time : -duration; + if (!time_offset_done) { + if (time == -1) { + /* delay is in movie timescale */ + sc->time_offset_delay += duration; + } else if (time >= 0) { + /* samples to skip is in track timescale */ + sc->time_offset_skip = time; + time_offset_done = 1; + } + /* timescales may not be known yet, so we can not compute + * a single combined time_offset yet */ } } -- 2.9.3