[FFmpeg-devel] [PATCH] mov.c: read fragment start dts from fragmented mp4
Mika Raento
mikie at iki.fi
Wed Oct 8 14:05:07 CEST 2014
If present, an MFRA box and its TFRAs are read for fragment start times.
Without this change, timestamps for discontinuous fragmented mp4 are
wrong, and cause audio/video desync and are not usable for generating
HLS.
---
libavformat/isom.h | 14 +++++++
libavformat/mov.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 130 insertions(+)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 979e967..2b49b55 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -78,6 +78,7 @@ typedef struct MOVFragment {
unsigned duration;
unsigned size;
unsigned flags;
+ int64_t time;
} MOVFragment;
typedef struct MOVTrackExt {
@@ -93,6 +94,17 @@ typedef struct MOVSbgp {
unsigned int index;
} MOVSbgp;
+typedef struct MOVFragmentIndexItem {
+ int64_t time;
+} MOVFragmentIndexItem;
+
+typedef struct MOVFragmentIndex {
+ unsigned track_id;
+ unsigned current_item_index;
+ unsigned item_count;
+ MOVFragmentIndexItem *items;
+} MOVFragmentIndex;
+
typedef struct MOVStreamContext {
AVIOContext *pb;
int pb_is_copied;
@@ -171,6 +183,8 @@ typedef struct MOVContext {
int *bitrates; ///< bitrates read before streams creation
int bitrates_count;
int moov_retry;
+ MOVFragmentIndex** fragment_index_data;
+ unsigned fragment_index_count;
} MOVContext;
int ff_mp4_read_descr_len(AVIOContext *pb);
diff --git a/libavformat/mov.c b/libavformat/mov.c
index fdd0671..bf92e60 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2756,6 +2756,16 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n");
return AVERROR_INVALIDDATA;
}
+ MOVFragmentIndex* index = NULL;
+ for (i = 0; i < c->fragment_index_count; i++) {
+ MOVFragmentIndex* candidate = c->fragment_index_data[i];
+ if (candidate->track_id == frag->track_id) {
+ av_log(c->fc, AV_LOG_VERBOSE,
+ "found fragment index for track %u\n", frag->track_id);
+ index = candidate;
+ break;
+ }
+ }
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
@@ -2768,6 +2778,20 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb32(pb) : trex->size;
frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ?
avio_rb32(pb) : trex->flags;
+ frag->time = AV_NOPTS_VALUE;
+ if (index) {
+ // TODO: should check moof index from mfhd, rather than just
+ // relying on this code seeing the moofs in the same order as they
+ // are in the mfra, and only once each.
+ if (index->current_item_index == index->item_count) {
+ av_log(c->fc, AV_LOG_WARNING, "track %u has a fragment index "
+ "but it doesn't have entries for all moofs, at moof "
+ "%u\n", frag->track_id, index->current_item_index);
+ } else if (index->current_item_index < index->item_count) {
+ frag->time = index->items[index->current_item_index].time;
+ }
+ index->current_item_index++;
+ }
av_dlog(c->fc, "frag flags 0x%x\n", frag->flags);
return 0;
}
@@ -2860,6 +2884,10 @@ 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;
+ if (frag->time != AV_NOPTS_VALUE) {
+ av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64"\n", frag->time);
+ dts = frag->time;
+ }
offset = frag->base_data_offset + data_offset;
distance = 0;
av_dlog(c->fc, "first sample flags 0x%x\n", first_sample_flags);
@@ -3513,6 +3541,13 @@ static int mov_read_close(AVFormatContext *s)
av_freep(&mov->trex_data);
av_freep(&mov->bitrates);
+ for (i = 0; i < mov->fragment_index_count; i++) {
+ MOVFragmentIndex* index = mov->fragment_index_data[i];
+ av_freep(&index->items);
+ av_freep(&mov->fragment_index_data[i]);
+ }
+ av_freep(&mov->fragment_index_data);
+
return 0;
}
@@ -3550,6 +3585,84 @@ static void export_orphan_timecode(AVFormatContext *s)
}
}
+static int read_tfra(AVFormatContext *s)
+{
+ MOVContext *mov = s->priv_data;
+ AVIOContext *f = s->pb;
+ MOVFragmentIndex* index = NULL;
+ int version, fieldlength, i, j, err;
+ int64_t pos = avio_tell(f);
+ uint32_t size = avio_rb32(f);
+ if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a')) {
+ return -1;
+ }
+ av_log(s, AV_LOG_VERBOSE, "found tfra\n");
+ index = av_mallocz(sizeof(MOVFragmentIndex));
+ if (!index)
+ return AVERROR(ENOMEM);
+ mov->fragment_index_count++;
+ if ((err = av_reallocp(&mov->fragment_index_data,
+ mov->fragment_index_count *
+ sizeof(MOVFragmentIndex*))) < 0) {
+ av_freep(&index);
+ return err;
+ }
+ mov->fragment_index_data[mov->fragment_index_count - 1] =
+ index;
+
+ version = avio_r8(f);
+ avio_rb24(f);
+ index->track_id = avio_rb32(f);
+ fieldlength = avio_rb32(f);
+ index->item_count = avio_rb32(f);
+ index->items = av_mallocz(
+ index->item_count * sizeof(MOVFragmentIndexItem));
+ if (!index->items)
+ return AVERROR(ENOMEM);
+ for (i = 0; i < index->item_count; i++) {
+ int64_t time, offset;
+ if (version == 1) {
+ time = avio_rb64(f);
+ offset = avio_rb64(f);
+ } else {
+ time = avio_rb32(f);
+ offset = avio_rb32(f);
+ }
+ index->items[i].time = time;
+ for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
+ avio_r8(f);
+ for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
+ avio_r8(f);
+ for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
+ avio_r8(f);
+ }
+
+ avio_seek(f, pos + size, SEEK_SET);
+ return 0;
+}
+
+static int mov_read_mfra(AVFormatContext *s)
+{
+ AVIOContext *f = s->pb;
+ int32_t mfra_size;
+ avio_seek(f, avio_size(f) - 4, SEEK_SET);
+ mfra_size = avio_rb32(f);
+ avio_seek(f, -mfra_size, SEEK_CUR);
+ if (avio_rb32(f) != mfra_size) {
+ av_log(s, AV_LOG_DEBUG, "doesn't look like mfra (size)\n");
+ return -1;
+ }
+ if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
+ av_log(s, AV_LOG_DEBUG, "doesn't look like mfra (tag)\n");
+ return -1;
+ }
+ av_log(s, AV_LOG_VERBOSE, "stream has mfra\n");
+ while (!read_tfra(s)) {
+ /* Empty */
+ }
+ return 0;
+}
+
static int mov_read_header(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
@@ -3565,6 +3678,9 @@ static int mov_read_header(AVFormatContext *s)
else
atom.size = INT64_MAX;
+ if (pb->seekable) {
+ mov_read_mfra(s);
+ }
/* check MOV header */
do {
if (mov->moov_retry)
--
1.9.3 (Apple Git-50)
More information about the ffmpeg-devel
mailing list