Skip to content

Commit

Permalink
ismindex: handle discontinuous streams better
Browse files Browse the repository at this point in the history
Read the fragment duration from the trun sample data, rather than
assuming that there are no gaps.

Signed-off-by: Martin Storsjö <[email protected]>
  • Loading branch information
mikaraento authored and mstorsjo committed Oct 9, 2014
1 parent 1a02e78 commit 2bc2209
Showing 1 changed file with 86 additions and 10 deletions.
96 changes: 86 additions & 10 deletions tools/ismindex.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <string.h>

#include "libavformat/avformat.h"
#include "libavformat/isom.h"
#include "libavformat/os_support.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
Expand Down Expand Up @@ -224,6 +225,79 @@ static int write_fragments(struct Tracks *tracks, int start_index,
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\n");
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\n");
goto fail;
}
avio_seek(in, pos + size, SEEK_SET);
}
fprintf(stderr, "Couldn't find traf\n");

fail:
return ret;
}

static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
{
int ret = AVERROR_EOF, track_id;
Expand Down Expand Up @@ -253,12 +327,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);
Expand All @@ -281,6 +350,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 "
Expand Down Expand Up @@ -575,10 +655,6 @@ static void print_track_chunks(FILE *out, struct Tracks *tracks, int main,
fprintf(out, "\t\t<c n=\"%d\" d=\"%"PRId64"\" ",
i, track->offsets[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;
}
Expand Down

0 comments on commit 2bc2209

Please sign in to comment.