fate passes with these changes, no new tests added. - mika
On 9 October 2014 08:53, Mika Raento <mi...@iki.fi> wrote: > 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 | 131 > +++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 145 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..2b14c48 100644 > --- a/libavformat/mov.c > +++ b/libavformat/mov.c > @@ -2738,6 +2738,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext > *pb, MOVAtom atom) > { > MOVFragment *frag = &c->fragment; > MOVTrackExt *trex = NULL; > + MOVFragmentIndex* index = NULL; > int flags, track_id, i; > > avio_r8(pb); /* version */ > @@ -2756,6 +2757,15 @@ 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; > } > + 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_DEBUG, > + "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,99 @@ 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; > + if (version == 1) { > + time = avio_rb64(f); > + avio_rb64(f); /* offset */ > + } else { > + time = avio_rb32(f); > + avio_rb32(f); /* offset */ > + } > + 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; > + int64_t stream_size = avio_size(f); > + int64_t original_pos = avio_tell(f); > + int32_t mfra_size; > + int ret = -1; > + if ((ret = avio_seek(f, stream_size - 4, SEEK_SET)) < 0) goto fail; > + mfra_size = avio_rb32(f); > + if (mfra_size < 0 || mfra_size > stream_size) { > + av_log(s, AV_LOG_DEBUG, "doesn't look like mfra (unreasonable > size)\n"); > + ret = -1; > + goto fail; > + } > + if ((ret = avio_seek(f, -mfra_size, SEEK_CUR)) < 0) goto fail; > + if (avio_rb32(f) != mfra_size) { > + av_log(s, AV_LOG_DEBUG, "doesn't look like mfra (size mismatch)\n"); > + ret = -1; > + goto fail; > + } > + if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) { > + av_log(s, AV_LOG_DEBUG, "doesn't look like mfra (tag mismatch)\n"); > + goto fail; > + } > + av_log(s, AV_LOG_VERBOSE, "stream has mfra\n"); > + while (!read_tfra(s)) { > + /* Empty */ > + } > +fail: > + ret = avio_seek(f, original_pos, SEEK_SET); > + if (ret < 0) > + av_log(s, AV_LOG_ERROR, "failed to seek back after looking for > mfra"); > + else > + ret = 0; > + return ret; > +} > + > static int mov_read_header(AVFormatContext *s) > { > MOVContext *mov = s->priv_data; > @@ -3565,6 +3693,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