return 0; /* now go for mdat */
}
+static MOVFragmentStreamInfo * get_frag_stream_info(
+ MOVFragmentIndex *frag_index,
+ int index,
+ int id)
+{
+ int i;
+ MOVFragmentIndexItem * item;
+
+ if (index < 0 || index >= frag_index->nb_items)
+ return NULL;
+ item = &frag_index->item[index];
+ for (i = 0; i < item->nb_stream_info; i++)
+ if (item->stream_info[i].id == id)
+ return &item->stream_info[i];
+
+ // This shouldn't happen
+ return NULL;
+}
+
+static void set_frag_stream(MOVFragmentIndex *frag_index, int id)
+{
+ int i;
+ MOVFragmentIndexItem * item;
+
+ if (frag_index->current < 0 ||
+ frag_index->current >= frag_index->nb_items)
+ return;
+
+ item = &frag_index->item[frag_index->current];
+ for (i = 0; i < item->nb_stream_info; i++)
+ if (item->stream_info[i].id == id) {
+ item->current = i;
+ return;
+ }
+
+ // id not found. This shouldn't happen.
+ item->current = -1;
+}
+
+static MOVFragmentStreamInfo * get_current_frag_stream_info(
+ MOVFragmentIndex *frag_index)
+{
+ MOVFragmentIndexItem * item = &frag_index->item[frag_index->current];
+ if (item->current >= 0 && item->current < item->nb_stream_info)
+ return &item->stream_info[item->current];
+
+ // This shouldn't happen
+ return NULL;
+}
+
+static int search_frag_moof_offset(MOVFragmentIndex *frag_index, int64_t offset)
+{
+ int a, b, m;
+ int64_t moof_offset;
+
+ // Optimize for appending new entries
+ if (!frag_index->nb_items ||
+ frag_index->item[frag_index->nb_items - 1].moof_offset < offset)
+ return frag_index->nb_items;
+
+ a = -1;
+ b = frag_index->nb_items;
+
+ while (b - a > 1) {
+ m = (a + b) >> 1;
+ moof_offset = frag_index->item[m].moof_offset;
+ if (moof_offset >= offset)
+ b = m;
+ if (moof_offset <= offset)
+ a = m;
+ }
+ return b;
+}
+
+static int64_t get_stream_info_time(MOVFragmentStreamInfo * frag_stream_info)
+{
+
+ if (frag_stream_info) {
+ if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE)
+ return frag_stream_info->sidx_pts;
+ if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE)
+ return frag_stream_info->first_tfra_pts;
+ if (frag_stream_info->tfdt_dts != AV_NOPTS_VALUE)
+ return frag_stream_info->tfdt_dts;
+ }
+ return AV_NOPTS_VALUE;
+}
+
+static int64_t get_frag_time(MOVFragmentIndex *frag_index,
+ int index, int track_id)
+{
+ MOVFragmentStreamInfo * frag_stream_info;
+ int64_t timestamp;
+ int i;
+
+ if (track_id >= 0) {
+ frag_stream_info = get_frag_stream_info(frag_index, index, track_id);
+ return frag_stream_info->sidx_pts;
+ }
+
+ for (i = 0; i < frag_index->item[index].nb_stream_info; i++) {
+ frag_stream_info = &frag_index->item[index].stream_info[i];
+ timestamp = get_stream_info_time(frag_stream_info);
+ if (timestamp != AV_NOPTS_VALUE)
+ return timestamp;
+ }
+ return AV_NOPTS_VALUE;
+}
+
+static int search_frag_timestamp(MOVFragmentIndex *frag_index,
+ AVStream *st, int64_t timestamp)
+{
+ int a, b, m;
+ int64_t frag_time;
+ int id = -1;
+
+ if (st) {
+ // If the stream is referenced by any sidx, limit the search
+ // to fragments that referenced this stream in the sidx
+ MOVStreamContext *sc = st->priv_data;
+ if (sc->has_sidx)
+ id = st->id;
+ }
+
+ a = -1;
+ b = frag_index->nb_items;
+
+ while (b - a > 1) {
+ m = (a + b) >> 1;
+ frag_time = get_frag_time(frag_index, m, id);
+ if (frag_time != AV_NOPTS_VALUE) {
+ if (frag_time >= timestamp)
+ b = m;
+ if (frag_time <= timestamp)
+ a = m;
+ }
+ }
+ return a;
+}
+
+static int update_frag_index(MOVContext *c, int64_t offset)
+{
+ int index, i;
+ MOVFragmentIndexItem * item;
+ MOVFragmentStreamInfo * frag_stream_info;
+
+ // If moof_offset already exists in frag_index, return index to it
+ index = search_frag_moof_offset(&c->frag_index, offset);
+ if (index < c->frag_index.nb_items &&
+ c->frag_index.item[index].moof_offset == offset)
+ return index;
+
+ // offset is not yet in frag index.
+ // Insert new item at index (sorted by moof offset)
+ item = av_fast_realloc(c->frag_index.item,
+ &c->frag_index.allocated_size,
+ (c->frag_index.nb_items + 1) *
+ sizeof(*c->frag_index.item));
+ if(!item)
+ return -1;
+ c->frag_index.item = item;
+
+ frag_stream_info = av_realloc_array(NULL, c->fc->nb_streams,
+ sizeof(*item->stream_info));
+ if (!frag_stream_info)
+ return -1;
+
+ for (i = 0; i < c->fc->nb_streams; i++) {
+ frag_stream_info[i].id = c->fc->streams[i]->id;
+ frag_stream_info[i].sidx_pts = AV_NOPTS_VALUE;
+ frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE;
+ frag_stream_info[i].first_tfra_pts = AV_NOPTS_VALUE;
+ frag_stream_info[i].index_entry = -1;
+ }
+
+ if (index < c->frag_index.nb_items)
+ memmove(c->frag_index.item + index + 1, c->frag_index.item + index,
+ (c->frag_index.nb_items - index) * sizeof(*c->frag_index.item));
+
+ item = &c->frag_index.item[index];
+ item->headers_read = 0;
+ item->current = 0;
+ item->nb_stream_info = c->fc->nb_streams;
+ item->moof_offset = offset;
+ item->stream_info = frag_stream_info;
+ c->frag_index.nb_items++;
+
+ return index;
+}
+
+static void fix_frag_index_entries(MOVFragmentIndex *frag_index, int index,
+ int id, int entries)
+{
+ int i;
+ MOVFragmentStreamInfo * frag_stream_info;
+
+ if (index < 0)
+ return;
+ for (i = index; i < frag_index->nb_items; i++) {
+ frag_stream_info = get_frag_stream_info(frag_index, i, id);
+ if (frag_stream_info && frag_stream_info->index_entry >= 0)
+ frag_stream_info->index_entry += entries;
+ }
+}
+
static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
if (!c->has_looked_for_mfra && c->use_mfra_for > 0) {
}
c->fragment.moof_offset = c->fragment.implicit_offset = avio_tell(pb) - 8;
av_log(c->fc, AV_LOG_TRACE, "moof offset %"PRIx64"\n", c->fragment.moof_offset);
+ c->frag_index.current = update_frag_index(c, c->fragment.moof_offset);
return mov_read_default(c, pb, atom);
}
{
MOVFragment *frag = &c->fragment;
MOVTrackExt *trex = NULL;
- MOVFragmentIndex* index = NULL;
- int flags, track_id, i, found = 0;
+ int flags, track_id, i;
avio_r8(pb); /* version */
flags = avio_rb24(pb);
if (!track_id)
return AVERROR_INVALIDDATA;
frag->track_id = track_id;
+ set_frag_stream(&c->frag_index, track_id);
for (i = 0; i < c->trex_count; i++)
if (c->trex_data[i].track_id == frag->track_id) {
trex = &c->trex_data[i];
avio_rb32(pb) : trex->size;
frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ?
avio_rb32(pb) : trex->flags;
- frag->time = AV_NOPTS_VALUE;
- for (i = 0; i < c->fragment_index_count; i++) {
- int j;
- 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;
- for (j = index->current_item; j < index->item_count; j++) {
- if (frag->implicit_offset == index->items[j].moof_offset) {
- av_log(c->fc, AV_LOG_DEBUG, "found fragment index entry "
- "for track %u and moof_offset %"PRId64"\n",
- frag->track_id, index->items[j].moof_offset);
- frag->time = index->items[j].time;
- index->current_item = j + 1;
- found = 1;
- break;
- }
- }
- if (found)
- break;
- }
- }
- if (index && !found) {
- av_log(c->fc, AV_LOG_DEBUG, "track %u has a fragment index but "
- "it doesn't have an (in-order) entry for moof_offset "
- "%"PRId64"\n", frag->track_id, frag->implicit_offset);
- }
av_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags);
+
return 0;
}
AVStream *st = NULL;
MOVStreamContext *sc;
int version, i;
+ MOVFragmentStreamInfo * frag_stream_info;
+ int64_t base_media_decode_time;
for (i = 0; i < c->fc->nb_streams; i++) {
if (c->fc->streams[i]->id == frag->track_id) {
version = avio_r8(pb);
avio_rb24(pb); /* flags */
if (version) {
- sc->track_end = avio_rb64(pb);
+ base_media_decode_time = avio_rb64(pb);
} else {
- sc->track_end = avio_rb32(pb);
+ base_media_decode_time = avio_rb32(pb);
}
+
+ frag_stream_info = get_current_frag_stream_info(&c->frag_index);
+ if (frag_stream_info)
+ frag_stream_info->tfdt_dts = base_media_decode_time;
+ sc->track_end = base_media_decode_time;
+
return 0;
}
MOVStreamContext *sc;
MOVStts *ctts_data;
uint64_t offset;
- int64_t dts;
+ int64_t dts, pts = AV_NOPTS_VALUE;
int data_offset = 0;
unsigned entries, first_sample_flags = frag->flags;
int flags, distance, i;
+ int64_t prev_dts = AV_NOPTS_VALUE;
+ int next_frag_index = -1, index_entry_pos;
+ size_t requested_size;
+ AVIndexEntry *new_entries;
+ MOVFragmentStreamInfo * frag_stream_info;
for (i = 0; i < c->fc->nb_streams; i++) {
if (c->fc->streams[i]->id == frag->track_id) {
sc = st->priv_data;
if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
+
+ // Find the next frag_index index that has a valid index_entry for
+ // the current track_id.
+ //
+ // A valid index_entry means the trun for the fragment was read
+ // and it's samples are in index_entries at the given position.
+ // New index entries will be inserted before the index_entry found.
+ index_entry_pos = st->nb_index_entries;
+ for (i = c->frag_index.current + 1; i < c->frag_index.nb_items; i++) {
+ frag_stream_info = get_frag_stream_info(&c->frag_index, i, frag->track_id);
+ if (frag_stream_info && frag_stream_info->index_entry >= 0) {
+ next_frag_index = i;
+ index_entry_pos = frag_stream_info->index_entry;
+ break;
+ }
+ }
+
avio_r8(pb); /* version */
flags = avio_rb24(pb);
entries = avio_rb32(pb);
return AVERROR_INVALIDDATA;
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;
- offset = frag->base_data_offset + data_offset;
+
+ frag_stream_info = get_current_frag_stream_info(&c->frag_index);
+ if (frag_stream_info)
+ {
+ if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE &&
+ c->use_mfra_for == FF_MOV_FLAG_MFRA_PTS) {
+ pts = frag_stream_info->first_tfra_pts;
+ av_log(c->fc, AV_LOG_DEBUG, "found mfra time %"PRId64
+ ", using it for pts\n", pts);
+ } else if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE) {
+ // FIXME: sidx earliest_presentation_time is *PTS*, s.b.
+ // pts = frag_stream_info->sidx_pts;
+ dts = frag_stream_info->sidx_pts - sc->time_offset;
+ av_log(c->fc, AV_LOG_DEBUG, "found sidx time %"PRId64
+ ", using it for pts\n", pts);
+ } else if (frag_stream_info->tfdt_dts != AV_NOPTS_VALUE) {
+ dts = frag_stream_info->tfdt_dts - sc->time_offset;
+ av_log(c->fc, AV_LOG_DEBUG, "found tfdt time %"PRId64
+ ", using it for dts\n", dts);
+ } else {
+ dts = sc->track_end - sc->time_offset;
+ av_log(c->fc, AV_LOG_DEBUG, "found track end time %"PRId64
+ ", using it for dts\n", dts);
+ }
+ } else {
+ dts = sc->track_end - sc->time_offset;
+ av_log(c->fc, AV_LOG_DEBUG, "found track end time %"PRId64
+ ", using it for dts\n", dts);
+ }
+ offset = frag->base_data_offset + data_offset;
distance = 0;
av_log(c->fc, AV_LOG_TRACE, "first sample flags 0x%x\n", first_sample_flags);
+
+ // realloc space for new index entries
+ if((unsigned)st->nb_index_entries + entries >= UINT_MAX / sizeof(AVIndexEntry)) {
+ entries = UINT_MAX / sizeof(AVIndexEntry) - st->nb_index_entries;
+ av_log(c->fc, AV_LOG_ERROR, "Failed to add index entry\n");
+ }
+ if (entries <= 0)
+ return -1;
+
+ requested_size = (st->nb_index_entries + entries) * sizeof(AVIndexEntry);
+ new_entries = av_fast_realloc(st->index_entries,
+ &st->index_entries_allocated_size,
+ requested_size);
+ if(!new_entries)
+ return AVERROR(ENOMEM);
+ st->index_entries= new_entries;
+
+ requested_size = (st->nb_index_entries + entries) * sizeof(*sc->ctts_data);
+ ctts_data = av_fast_realloc(sc->ctts_data, &sc->ctts_allocated_size,
+ requested_size);
+ if (!ctts_data)
+ return AVERROR(ENOMEM);
+ sc->ctts_data = ctts_data;
+
+ // In case there were samples without ctts entries, ensure they get
+ // zero valued entries. This ensures clips which mix boxes with and
+ // without ctts entries don't pickup uninitialized data.
+ memset(sc->ctts_data + sc->ctts_count, 0,
+ (st->nb_index_entries - sc->ctts_count) * sizeof(*sc->ctts_data));
+
+ if (index_entry_pos < st->nb_index_entries) {
+ // Make hole in index_entries and ctts_data for new samples
+ memmove(st->index_entries + index_entry_pos + entries,
+ st->index_entries + index_entry_pos,
+ sizeof(*st->index_entries) *
+ (st->nb_index_entries - index_entry_pos));
+ memmove(sc->ctts_data + index_entry_pos + entries,
+ sc->ctts_data + index_entry_pos,
+ sizeof(*sc->ctts_data) * (sc->ctts_count - index_entry_pos));
+ if (index_entry_pos < sc->current_sample) {
+ sc->current_sample += entries;
+ }
+ }
+
+ st->nb_index_entries += entries;
+ sc->ctts_count = st->nb_index_entries;
+
+ // Record the index_entry position in frag_index of this fragment
+ if (frag_stream_info)
+ frag_stream_info->index_entry = index_entry_pos;
+
+ if (index_entry_pos > 0)
+ prev_dts = st->index_entries[index_entry_pos-1].timestamp;
+
for (i = 0; i < entries && !pb->eof_reached; i++) {
unsigned sample_size = frag->size;
int sample_flags = i ? frag->flags : first_sample_flags;
unsigned sample_duration = frag->duration;
unsigned ctts_duration = 0;
int keyframe = 0;
- int ctts_index = 0;
- int old_nb_index_entries = st->nb_index_entries;
+ int index_entry_flags = 0;
if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(pb);
if (flags & MOV_TRUN_SAMPLE_SIZE) sample_size = avio_rb32(pb);
if (flags & MOV_TRUN_SAMPLE_CTS) ctts_duration = avio_rb32(pb);
mov_update_dts_shift(sc, ctts_duration);
- if (frag->time != AV_NOPTS_VALUE) {
- if (c->use_mfra_for == FF_MOV_FLAG_MFRA_PTS) {
- int64_t pts = frag->time;
- av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64
- " sc->dts_shift %d ctts.duration %d"
- " sc->time_offset %"PRId64" flags & MOV_TRUN_SAMPLE_CTS %d\n", pts,
- sc->dts_shift, ctts_duration,
- sc->time_offset, flags & MOV_TRUN_SAMPLE_CTS);
- dts = pts - sc->dts_shift;
- if (flags & MOV_TRUN_SAMPLE_CTS) {
- dts -= ctts_duration;
- } else {
- dts -= sc->time_offset;
- }
- av_log(c->fc, AV_LOG_DEBUG, "calculated into dts %"PRId64"\n", dts);
+ if (pts != AV_NOPTS_VALUE) {
+ dts = pts - sc->dts_shift;
+ if (flags & MOV_TRUN_SAMPLE_CTS) {
+ dts -= ctts_duration;
} else {
- dts = frag->time - sc->time_offset;
- av_log(c->fc, AV_LOG_DEBUG, "found frag time %"PRId64
- ", using it for dts\n", dts);
+ dts -= sc->time_offset;
}
- frag->time = AV_NOPTS_VALUE;
+ av_log(c->fc, AV_LOG_DEBUG,
+ "pts %"PRId64" calculated dts %"PRId64
+ " sc->dts_shift %d ctts.duration %d"
+ " sc->time_offset %"PRId64
+ " flags & MOV_TRUN_SAMPLE_CTS %d\n",
+ pts, dts,
+ sc->dts_shift, ctts_duration,
+ sc->time_offset, flags & MOV_TRUN_SAMPLE_CTS);
+ pts = AV_NOPTS_VALUE;
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
keyframe =
!(sample_flags & (MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC |
MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES));
- if (keyframe)
+ if (keyframe) {
distance = 0;
- ctts_index = av_add_index_entry(st, offset, dts, sample_size, distance,
- keyframe ? AVINDEX_KEYFRAME : 0);
- if (ctts_index >= 0 && old_nb_index_entries < st->nb_index_entries) {
- unsigned int size_needed = st->nb_index_entries * sizeof(*sc->ctts_data);
- unsigned int request_size = size_needed > sc->ctts_allocated_size ?
- FFMAX(size_needed, 2 * sc->ctts_allocated_size) : size_needed;
- unsigned int old_ctts_size = sc->ctts_allocated_size;
- ctts_data = av_fast_realloc(sc->ctts_data, &sc->ctts_allocated_size, request_size);
- if (!ctts_data) {
- av_freep(&sc->ctts_data);
- return AVERROR(ENOMEM);
- }
- sc->ctts_data = ctts_data;
-
- // In case there were samples without ctts entries, ensure they get
- // zero valued entries. This ensures clips which mix boxes with and
- // without ctts entries don't pickup uninitialized data.
- memset((uint8_t*)(sc->ctts_data) + old_ctts_size, 0, sc->ctts_allocated_size - old_ctts_size);
-
- if (ctts_index != old_nb_index_entries) {
- memmove(sc->ctts_data + ctts_index + 1, sc->ctts_data + ctts_index,
- sizeof(*sc->ctts_data) * (sc->ctts_count - ctts_index));
- if (ctts_index <= sc->current_sample) {
- // if we inserted a new item before the current sample, move the
- // counter ahead so it is still pointing to the same sample.
- sc->current_sample++;
- }
- }
-
- sc->ctts_data[ctts_index].count = 1;
- sc->ctts_data[ctts_index].duration = ctts_duration;
- sc->ctts_count++;
- } else {
- av_log(c->fc, AV_LOG_ERROR, "Failed to add index entry\n");
+ index_entry_flags |= AVINDEX_KEYFRAME;
}
+ // Fragments can overlap in time. Discard overlapping frames after
+ // decoding.
+ if (prev_dts >= dts)
+ index_entry_flags |= AVINDEX_DISCARD_FRAME;
+
+ st->index_entries[index_entry_pos].pos = offset;
+ st->index_entries[index_entry_pos].timestamp = dts;
+ st->index_entries[index_entry_pos].size= sample_size;
+ st->index_entries[index_entry_pos].min_distance= distance;
+ st->index_entries[index_entry_pos].flags = index_entry_flags;
+
+ sc->ctts_data[index_entry_pos].count = 1;
+ sc->ctts_data[index_entry_pos].duration = ctts_duration;
+ index_entry_pos++;
av_log(c->fc, AV_LOG_TRACE, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", "
- "size %u, distance %d, keyframe %d\n", st->index, ctts_index,
- offset, dts, sample_size, distance, keyframe);
+ "size %u, distance %d, keyframe %d\n", st->index,
+ index_entry_pos, offset, dts, sample_size, distance, keyframe);
distance++;
dts += sample_duration;
offset += sample_size;
sc->duration_for_fps += sample_duration;
sc->nb_frames_for_fps ++;
}
+ if (i < entries) {
+ // EOF found before reading all entries. Fix the hole this would
+ // leave in index_entries and ctts_data
+ int gap = entries - i;
+ memmove(st->index_entries + index_entry_pos,
+ st->index_entries + index_entry_pos + gap,
+ sizeof(*st->index_entries) *
+ (st->nb_index_entries - (index_entry_pos + gap)));
+ memmove(sc->ctts_data + index_entry_pos,
+ sc->ctts_data + index_entry_pos + gap,
+ sizeof(*sc->ctts_data) *
+ (sc->ctts_count - (index_entry_pos + gap)));
+
+ st->nb_index_entries -= gap;
+ sc->ctts_count -= gap;
+ if (index_entry_pos < sc->current_sample) {
+ sc->current_sample -= gap;
+ }
+ entries = i;
+ }
+
+ // The end of this new fragment may overlap in time with the start
+ // of the next fragment in index_entries. Mark the samples in the next
+ // fragment that overlap with AVINDEX_DISCARD_FRAME
+ prev_dts = AV_NOPTS_VALUE;
+ if (index_entry_pos > 0)
+ prev_dts = st->index_entries[index_entry_pos-1].timestamp;
+ for (i = index_entry_pos; i < st->nb_index_entries; i++) {
+ if (prev_dts < st->index_entries[i].timestamp)
+ break;
+ st->index_entries[i].flags |= AVINDEX_DISCARD_FRAME;
+ }
+
+ // If a hole was created to insert the new index_entries into,
+ // the index_entry recorded for all subsequent moof must
+ // be incremented by the number of entries inserted.
+ fix_frag_index_entries(&c->frag_index, next_frag_index,
+ frag->track_id, entries);
if (pb->eof_reached)
return AVERROR_EOF;
static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
- int64_t offset = avio_tell(pb) + atom.size, pts;
+ int64_t offset = avio_tell(pb) + atom.size, pts, timestamp;
uint8_t version;
- unsigned i, track_id;
+ unsigned i, j, track_id, item_count;
AVStream *st = NULL;
AVStream *ref_st = NULL;
MOVStreamContext *sc, *ref_sc = NULL;
- MOVFragmentIndex *index = NULL;
- MOVFragmentIndex **tmp;
AVRational timescale;
version = avio_r8(pb);
avio_rb16(pb); // reserved
- index = av_mallocz(sizeof(MOVFragmentIndex));
- if (!index)
- return AVERROR(ENOMEM);
-
- index->track_id = track_id;
-
- index->item_count = avio_rb16(pb);
- index->items = av_mallocz_array(index->item_count, sizeof(MOVFragmentIndexItem));
-
- if (!index->items) {
- av_freep(&index);
- return AVERROR(ENOMEM);
- }
+ item_count = avio_rb16(pb);
- for (i = 0; i < index->item_count; i++) {
+ for (i = 0; i < item_count; i++) {
+ int index;
+ MOVFragmentStreamInfo * frag_stream_info;
uint32_t size = avio_rb32(pb);
uint32_t duration = avio_rb32(pb);
if (size & 0x80000000) {
avpriv_request_sample(c->fc, "sidx reference_type 1");
- av_freep(&index->items);
- av_freep(&index);
return AVERROR_PATCHWELCOME;
}
avio_rb32(pb); // sap_flags
- index->items[i].moof_offset = offset;
- index->items[i].time = av_rescale_q(pts, st->time_base, timescale);
+ timestamp = av_rescale_q(pts, st->time_base, timescale);
+
+ index = update_frag_index(c, offset);
+ frag_stream_info = get_frag_stream_info(&c->frag_index, index, track_id);
+ if (frag_stream_info)
+ frag_stream_info->sidx_pts = timestamp;
+
offset += size;
pts += duration;
}
st->duration = sc->track_end = pts;
- tmp = av_realloc_array(c->fragment_index_data,
- c->fragment_index_count + 1,
- sizeof(MOVFragmentIndex*));
- if (!tmp) {
- av_freep(&index->items);
- av_freep(&index);
- return AVERROR(ENOMEM);
- }
-
- c->fragment_index_data = tmp;
- c->fragment_index_data[c->fragment_index_count++] = index;
sc->has_sidx = 1;
if (offset == avio_size(pb)) {
- for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == c->fragment_index_data[0]->track_id) {
- ref_st = c->fc->streams[i];
- ref_sc = ref_st->priv_data;
- break;
+ // Find first entry in fragment index that came from an sidx.
+ // This will pretty much always be the first entry.
+ for (i = 0; i < c->frag_index.nb_items; i++) {
+ MOVFragmentIndexItem * item = &c->frag_index.item[i];
+ for (j = 0; ref_st == NULL && j < item->nb_stream_info; j++) {
+ MOVFragmentStreamInfo * si;
+ si = &item->stream_info[j];
+ if (si->sidx_pts != AV_NOPTS_VALUE) {
+ ref_st = c->fc->streams[i];
+ ref_sc = ref_st->priv_data;
+ break;
+ }
}
}
for (i = 0; i < c->fc->nb_streams; i++) {
}
}
- c->fragment_index_complete = 1;
+ c->frag_index.complete = 1;
}
return 0;
return err;
}
if (c->found_moov && c->found_mdat &&
- ((!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_complete) ||
+ ((!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->frag_index.complete) ||
start_pos + a.size == avio_size(pb))) {
- if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_complete)
+ if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->frag_index.complete)
c->next_root_atom = start_pos + a.size;
c->atom_depth --;
return 0;
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]);
+ for (i = 0; i < mov->frag_index.nb_items; i++) {
+ av_freep(&mov->frag_index.item[i].stream_info);
}
- av_freep(&mov->fragment_index_data);
+ av_freep(&mov->frag_index.item);
av_freep(&mov->aes_decrypt);
av_freep(&mov->chapter_tracks);
static int read_tfra(MOVContext *mov, AVIOContext *f)
{
- MOVFragmentIndex* index = NULL;
int version, fieldlength, i, j;
int64_t pos = avio_tell(f);
uint32_t size = avio_rb32(f);
- void *tmp;
+ unsigned track_id, item_count;
if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a')) {
return 1;
}
av_log(mov->fc, AV_LOG_VERBOSE, "found tfra\n");
- index = av_mallocz(sizeof(MOVFragmentIndex));
- if (!index) {
- return AVERROR(ENOMEM);
- }
-
- tmp = av_realloc_array(mov->fragment_index_data,
- mov->fragment_index_count + 1,
- sizeof(MOVFragmentIndex*));
- if (!tmp) {
- av_freep(&index);
- return AVERROR(ENOMEM);
- }
- mov->fragment_index_data = tmp;
- mov->fragment_index_data[mov->fragment_index_count++] = index;
version = avio_r8(f);
avio_rb24(f);
- index->track_id = avio_rb32(f);
+ track_id = avio_rb32(f);
fieldlength = avio_rb32(f);
- index->item_count = avio_rb32(f);
- index->items = av_mallocz_array(
- index->item_count, sizeof(MOVFragmentIndexItem));
- if (!index->items) {
- index->item_count = 0;
- return AVERROR(ENOMEM);
- }
- for (i = 0; i < index->item_count; i++) {
+ item_count = avio_rb32(f);
+ for (i = 0; i < item_count; i++) {
int64_t time, offset;
+ int index;
+ MOVFragmentStreamInfo * frag_stream_info;
if (avio_feof(f)) {
- index->item_count = 0;
- av_freep(&index->items);
return AVERROR_INVALIDDATA;
}
time = avio_rb32(f);
offset = avio_rb32(f);
}
- index->items[i].time = time;
- index->items[i].moof_offset = offset;
+
+ // The first sample of each stream in a fragment is always a random
+ // access sample. So it's entry in the tfra can be used as the
+ // initial PTS of the fragment.
+ index = update_frag_index(mov, offset);
+ frag_stream_info = get_frag_stream_info(&mov->frag_index, index, track_id);
+ if (frag_stream_info &&
+ frag_stream_info->first_tfra_pts == AV_NOPTS_VALUE)
+ frag_stream_info->first_tfra_pts = time;
+
for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
avio_r8(f);
for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
}
ff_configure_buffers_for_index(s, AV_TIME_BASE);
- for (i = 0; i < mov->fragment_index_count; i++) {
- MOVFragmentIndex *idx = mov->fragment_index_data[i];
- for (j = 0; j < idx->item_count; j++)
- if (idx->items[j].moof_offset <= mov->fragment.moof_offset)
- idx->items[j].headers_read = 1;
- }
+ for (i = 0; i < mov->frag_index.nb_items; i++)
+ if (mov->frag_index.item[i].moof_offset <= mov->fragment.moof_offset)
+ mov->frag_index.item[i].headers_read = 1;
return 0;
}
return 1;
}
-static int mov_switch_root(AVFormatContext *s, int64_t target)
+static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
{
MOVContext *mov = s->priv_data;
- int i, j;
- int already_read = 0;
+ if (index >= 0 && index < mov->frag_index.nb_items)
+ target = mov->frag_index.item[index].moof_offset;
if (avio_seek(s->pb, target, SEEK_SET) != target) {
av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target);
return AVERROR_INVALIDDATA;
}
mov->next_root_atom = 0;
-
- for (i = 0; i < mov->fragment_index_count; i++) {
- MOVFragmentIndex *index = mov->fragment_index_data[i];
- int found = 0;
- for (j = 0; j < index->item_count; j++) {
- MOVFragmentIndexItem *item = &index->items[j];
- if (found) {
- mov->next_root_atom = item->moof_offset;
- break; // Advance to next index in outer loop
- } else if (item->moof_offset == target) {
- index->current_item = FFMIN(j, index->current_item);
- if (item->headers_read)
- already_read = 1;
- item->headers_read = 1;
- found = 1;
- }
- }
- if (!found)
- index->current_item = 0;
+ if (index < 0 || index >= mov->frag_index.nb_items)
+ index = search_frag_moof_offset(&mov->frag_index, target);
+ if (index < mov->frag_index.nb_items) {
+ if (index + 1 < mov->frag_index.nb_items)
+ mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset;
+ if (mov->frag_index.item[index].headers_read)
+ return 0;
+ mov->frag_index.item[index].headers_read = 1;
}
- if (already_read)
- return 0;
-
mov->found_mdat = 0;
if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
if (!mov->next_root_atom)
return AVERROR_EOF;
- if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0)
+ if ((ret = mov_switch_root(s, mov->next_root_atom, -1)) < 0)
return ret;
goto retry;
}
static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp)
{
MOVContext *mov = s->priv_data;
- MOVStreamContext *sc = st->priv_data;
- int i, j;
+ int index;
- if (!mov->fragment_index_complete)
+ if (!mov->frag_index.complete)
return 0;
- for (i = 0; i < mov->fragment_index_count; i++) {
- if (mov->fragment_index_data[i]->track_id == st->id || !sc->has_sidx) {
- MOVFragmentIndex *index = mov->fragment_index_data[i];
- for (j = index->item_count - 1; j >= 0; j--) {
- if (index->items[j].time <= timestamp) {
- if (index->items[j].headers_read)
- return 0;
-
- return mov_switch_root(s, index->items[j].moof_offset);
- }
- }
- }
- }
+ index = search_frag_timestamp(&mov->frag_index, st, timestamp);
+ if (index < 0)
+ index = 0;
+ if (!mov->frag_index.item[index].headers_read)
+ return mov_switch_root(s, -1, index);
+ if (index + 1 < mov->frag_index.nb_items)
+ mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset;
return 0;
}