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) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel