diff --git a/tools/ismindex.c b/tools/ismindex.c index 734419b1c9..b9bcedebde 100644 --- a/tools/ismindex.c +++ b/tools/ismindex.c @@ -50,6 +50,7 @@ #include "cmdutils.h" #include "libavformat/avformat.h" +#include "libavformat/isom.h" #include "libavformat/os_support.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" @@ -226,6 +227,78 @@ fail: return ret; } +static int64_t read_trun_duration(AVIOContext *in, int64_t end) +{ + int64_t ret = 0; + int64_t pos; + int flags, i; + int entries; + avio_r8(in); /* version */ + flags = avio_rb24(in); + if (! (flags & MOV_TRUN_SAMPLE_DURATION)) { + fprintf(stderr, "no sample duration in trun flags"); + return -1; + } + entries = avio_rb32(in); + + if (flags & MOV_TRUN_DATA_OFFSET) avio_rb32(in); + if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) avio_rb32(in); + + pos = avio_tell(in); + for (i = 0; i < entries && pos < end; i++) { + int sample_duration = 0; + if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(in); + if (flags & MOV_TRUN_SAMPLE_SIZE) avio_rb32(in); + if (flags & MOV_TRUN_SAMPLE_FLAGS) avio_rb32(in); + if (flags & MOV_TRUN_SAMPLE_CTS) avio_rb32(in); + if (sample_duration < 0) { + fprintf(stderr, "negative sample duration %d\n", sample_duration); + return -1; + } + ret += sample_duration; + pos = avio_tell(in); + } + + return ret; +} + +static int64_t read_moof_duration(AVIOContext *in, int64_t offset) +{ + int64_t ret = -1; + int32_t moof_size, size, tag; + int64_t pos = 0; + + avio_seek(in, offset, SEEK_SET); + moof_size = avio_rb32(in); + tag = avio_rb32(in); + if (expect_tag(tag, MKBETAG('m', 'o', 'o', 'f')) != 0) goto fail; + while (pos < offset + moof_size) { + pos = avio_tell(in); + size = avio_rb32(in); + tag = avio_rb32(in); + if (tag == MKBETAG('t', 'r', 'a', 'f')) { + int64_t traf_pos = pos; + int64_t traf_size = size; + while (pos < traf_pos + traf_size) { + pos = avio_tell(in); + size = avio_rb32(in); + tag = avio_rb32(in); + if (tag == MKBETAG('t', 'r', 'u', 'n')) { + return read_trun_duration(in, pos + size); + } + avio_seek(in, pos + size, SEEK_SET); + } + fprintf(stderr, "couldn't find trun"); + goto fail; + } + avio_seek(in, pos + size, SEEK_SET); + } + fprintf(stderr, "couldn't find traf"); + +fail: + return ret; +} + static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f) { int ret = AVERROR_EOF, track_id; @@ -255,12 +328,7 @@ static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f) goto fail; } // The duration here is always the difference between consecutive - // start times and doesn't even try to read the actual duration of the - // media fragments. This is what other smooth streaming tools tend to - // do too, but cannot express missing fragments, and the start times - // may not match the stream metadata we get from libavformat. Correct - // calculation would require parsing the tfxd atom (if present, it's - // not mandatory) or parsing the full moof atoms separately. + // start times. for (i = 0; i < track->chunks; i++) { if (version == 1) { track->offsets[i].time = avio_rb64(f); @@ -283,6 +351,17 @@ static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f) track->offsets[track->chunks - 1].duration = track->offsets[0].time + track->duration - track->offsets[track->chunks - 1].time; + } + // Now try and read the actual durations from the trun sample data. + for (i = 0; i < track->chunks; i++) { + int64_t duration = read_moof_duration(f, track->offsets[i].offset); + if (duration > 0 && abs(duration - track->offsets[i].duration) > 3) { + // 3 allows for integer duration to drift a few units, + // e.g., for 1/3 durations + track->offsets[i].duration = duration; + } + } + if (track->chunks > 0) { if (track->offsets[track->chunks - 1].duration <= 0) { fprintf(stderr, "Calculated last chunk duration for track %d " "was non-positive (%"PRId64"), probably due to missing " @@ -577,10 +656,6 @@ static void print_track_chunks(FILE *out, struct Tracks *tracks, int main, fprintf(out, "\t\toffsets[i].duration); if (pos != track->offsets[i].time) { - // With the current logic for calculation of durations from - // chunk start times, this branch can only be hit on the first - // chunk - but that's still useful and this will keep working - // if the duration calculation is improved. fprintf(out, "t=\"%"PRId64"\" ", track->offsets[i].time); pos = track->offsets[i].time; }