[FFmpeg-devel] avformat/mov: fix hang while seek on a kind of fragmented mp4.

Submitted by Charles Liu on Feb. 20, 2019, 3:54 p.m.

Details

Message ID 20190220155456.53126-1-liuchh83@gmail.com
State New
Headers show

Commit Message

Charles Liu Feb. 20, 2019, 3:54 p.m.
1. organize fragmented information according to the tracks.
2. do NOT skip the last boxes of fragmented info.

ticket #7572

Signed-off-by: Charles Liu <liuchh83@gmail.com>
---
 libavformat/isom.h |  10 +-
 libavformat/mov.c  | 375 ++++++++++++++++++++++-----------------------
 2 files changed, 185 insertions(+), 200 deletions(-)

Comments

Carl Eugen Hoyos Feb. 20, 2019, 11:32 p.m.
2019-02-20 16:54 GMT+01:00, Charles Liu <liuchh83@gmail.com>:
> 1. organize fragmented information according to the tracks.
> 2. do NOT skip the last boxes of fragmented info.
>
> ticket #7572

How can I reproduce the hang with current FFmpeg git head?

Carl Eugen
Charles Liu Feb. 21, 2019, 2:46 a.m.
There was a quick solution at aa25198f1b925a464bdfa83a98476f08d26c9209,
which luckily works for #7572. To reproduce issue, you can switch to the
commit just before it.

As we discussed, #7572 has a deeper reason. We missed the last ‘sidx’ and
‘moof’ boxes.

This patch try to fix AV_NOPTS_VALUE in fragmented info structure. It may
be caused by missing boxes. Or the fragmented structures are not suitable
for separate audio and video ‘moof’.


Thanks and Regards,

Charles Liu



Carl Eugen Hoyos <ceffmpeg@gmail.com> 于2019年2月21日周四 上午7:33写道:

> 2019-02-20 16:54 GMT+01:00, Charles Liu <liuchh83@gmail.com>:
> > 1. organize fragmented information according to the tracks.
> > 2. do NOT skip the last boxes of fragmented info.
> >
> > ticket #7572
>
> How can I reproduce the hang with current FFmpeg git head?
>
> Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Michael Niedermayer Feb. 22, 2019, 1:40 a.m.
On Wed, Feb 20, 2019 at 11:54:56PM +0800, Charles Liu wrote:
> 1. organize fragmented information according to the tracks.
> 2. do NOT skip the last boxes of fragmented info.
> 
> ticket #7572
> 
> Signed-off-by: Charles Liu <liuchh83@gmail.com>
> ---
>  libavformat/isom.h |  10 +-
>  libavformat/mov.c  | 375 ++++++++++++++++++++++-----------------------
>  2 files changed, 185 insertions(+), 200 deletions(-)

this causes OOM and crashes

[mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] stream 0, timescale not set
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] stream 1, timescale not set
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] error reading header
*** Error in `./ffmpeg': double free or corruption (fasttop): 0x000000000379b100 ***
Aborted (core dumped)


[...]
Charles Liu Feb. 22, 2019, 10:33 a.m.
I have updated a new patch that has passed valgrind as follows:
*$ ./configure --samples=fate-suite/ --disable-doc --fatal-warnings
--valgrind=VALGRIND*
*$ make -j4 && make fate-mov*
But I never encountered a ‘stream 0, timescale not set’ log in here. Please
let me know if you have any questions.
Sorry for the previous trouble.

Thanks and Regards,
Charles Liu

Michael Niedermayer <michael@niedermayer.cc> 于2019年2月22日周五 上午9:40写道:

> On Wed, Feb 20, 2019 at 11:54:56PM +0800, Charles Liu wrote:
> > 1. organize fragmented information according to the tracks.
> > 2. do NOT skip the last boxes of fragmented info.
> >
> > ticket #7572
> >
> > Signed-off-by: Charles Liu <liuchh83@gmail.com>
> > ---
> >  libavformat/isom.h |  10 +-
> >  libavformat/mov.c  | 375 ++++++++++++++++++++++-----------------------
> >  2 files changed, 185 insertions(+), 200 deletions(-)
>
> this causes OOM and crashes
>
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] stream 0, timescale not set
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] stream 1, timescale not set
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x3796f00] error reading header
> *** Error in `./ffmpeg': double free or corruption (fasttop):
> 0x000000000379b100 ***
> Aborted (core dumped)
>
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> No snowflake in an avalanche ever feels responsible. -- Voltaire
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Patch hide | download patch | download mbox

diff --git a/libavformat/isom.h b/libavformat/isom.h
index 69452cae8e..2b06f5fa58 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -125,7 +125,7 @@  typedef struct MOVEncryptionIndex {
 } MOVEncryptionIndex;
 
 typedef struct MOVFragmentStreamInfo {
-    int id;
+    unsigned stsd_id;
     int64_t sidx_pts;
     int64_t first_tfra_pts;
     int64_t tfdt_dts;
@@ -136,14 +136,13 @@  typedef struct MOVFragmentStreamInfo {
 typedef struct MOVFragmentIndexItem {
     int64_t moof_offset;
     int headers_read;
-    int current;
-    int nb_stream_info;
-    MOVFragmentStreamInfo * stream_info;
+    MOVFragmentStreamInfo stream_info;
 } MOVFragmentIndexItem;
 
 typedef struct MOVFragmentIndex {
     int allocated_size;
     int complete;
+    int id;  // track id
     int current;
     int nb_items;
     MOVFragmentIndexItem * item;
@@ -274,7 +273,8 @@  typedef struct MOVContext {
     int moov_retry;
     int use_mfra_for;
     int has_looked_for_mfra;
-    MOVFragmentIndex frag_index;
+    unsigned nb_frag_indices;
+    MOVFragmentIndex **frag_indices;
     int atom_depth;
     unsigned int aax_mode;  ///< 'aax' file has been detected
     uint8_t file_key[20];
diff --git a/libavformat/mov.c b/libavformat/mov.c
index bbd588c705..cd37d08299 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -1153,59 +1153,29 @@  static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     return 0; /* now go for mdat */
 }
 
-static MOVFragmentStreamInfo * get_frag_stream_info(
-    MOVFragmentIndex *frag_index,
-    int index,
-    int id)
+static MOVFragmentIndex *mov_find_frag_index(MOVFragmentIndex **frag_indices, int nb_frag_indices, int track_id)   // name?
 {
-    int i;
-    MOVFragmentIndexItem * item;
+    unsigned i;
+    MOVFragmentIndex *frag_index = NULL;
 
-    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];
+    for (i = 0; i < nb_frag_indices; i++)
+        if (frag_indices[i]->id == track_id)
+            frag_index = frag_indices[i];
 
-    // This shouldn't happen
-    return NULL;
+    return frag_index;
 }
 
-static void set_frag_stream(MOVFragmentIndex *frag_index, int id)
+static MOVFragmentStreamInfo *get_current_frag_stream_info(MOVContext *c, int id)
 {
-    int i;
-    MOVFragmentIndexItem * item;
+    MOVFragmentIndex *frag_index = NULL;
 
-    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;
-    if (frag_index->current < 0 ||
+    frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, id);
+    if (!frag_index ||
+        frag_index->current < 0 ||
         frag_index->current >= frag_index->nb_items)
         return NULL;
 
-    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;
+    return &frag_index->item[frag_index->current].stream_info;
 }
 
 static int search_frag_moof_offset(MOVFragmentIndex *frag_index, int64_t offset)
@@ -1232,9 +1202,10 @@  static int search_frag_moof_offset(MOVFragmentIndex *frag_index, int64_t offset)
     return b;
 }
 
-static int64_t get_stream_info_time(MOVFragmentStreamInfo * frag_stream_info)
+static int64_t get_frag_time(MOVFragmentIndex *frag_index,
+                             int index, int track_id)
 {
-    av_assert0(frag_stream_info);
+    MOVFragmentStreamInfo *frag_stream_info = &frag_index->item[index].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)
@@ -1242,31 +1213,10 @@  static int64_t get_stream_info_time(MOVFragmentStreamInfo * frag_stream_info)
     return frag_stream_info->tfdt_dts;
 }
 
-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, m0;
+    int a, b, m;
     int64_t frag_time;
     int id = -1;
 
@@ -1282,89 +1232,103 @@  static int search_frag_timestamp(MOVFragmentIndex *frag_index,
     b = frag_index->nb_items;
 
     while (b - a > 1) {
-        m0 = m = (a + b) >> 1;
-
-        while (m < b &&
-               (frag_time = get_frag_time(frag_index, m, id)) == AV_NOPTS_VALUE)
-            m++;
+        m = (a + b) >> 1;
+        frag_time = get_frag_time(frag_index, m, id);
+        if (frag_time == AV_NOPTS_VALUE)
+            return -1;
 
-        if (m < b && frag_time <= timestamp)
+        if (frag_time >= timestamp)
+            b = m;
+        if (frag_time <= timestamp)
             a = m;
-        else
-            b = m0;
     }
 
     return a;
 }
 
-static int update_frag_index(MOVContext *c, int64_t offset)
+static int update_frag_index(MOVContext *c, int track_id, int64_t offset)
 {
     int index, i;
-    MOVFragmentIndexItem * item;
-    MOVFragmentStreamInfo * frag_stream_info;
+    MOVFragmentIndex *frag_index = NULL;
+    MOVFragmentIndexItem *item;
+
+    do {
+        frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, track_id);
+        if (frag_index == NULL) {
+            c->nb_frag_indices = c->fc->nb_streams;
+            c->frag_indices = av_mallocz_array(c->nb_frag_indices, sizeof(*c->frag_indices));
+            if (!c->frag_indices)
+                return AVERROR(ENOMEM);
+
+            for (i = 0; i < c->fc->nb_streams; i++) {
+                MOVFragmentIndex *tmp_frag_index = av_mallocz(sizeof(MOVFragmentIndex));
+                if (!tmp_frag_index)
+                    return AVERROR(ENOMEM);
+
+                c->frag_indices[i] = tmp_frag_index;
+                c->frag_indices[i]->id = c->fc->streams[i]->id;
+            }
+        }
+    } while (!frag_index);
 
     // 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)
+    index = search_frag_moof_offset(frag_index, offset);
+    if (index < frag_index->nb_items &&
+        frag_index->item[index].moof_offset == offset) {
+        frag_index->current = index;
         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;
+    item = av_fast_realloc(frag_index->item,
+                           &frag_index->allocated_size,
+                           (frag_index->nb_items + 1) *
+                               sizeof(*frag_index->item));
+    if (!item)
+        return AVERROR(ENOMEM);
+    frag_index->item = item;
 
     for (i = 0; i < c->fc->nb_streams; i++) {
         // Avoid building frag index if streams lack track id.
         if (c->fc->streams[i]->id < 0)
             return AVERROR_INVALIDDATA;
-
-        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;
-        frag_stream_info[i].encryption_index = NULL;
     }
 
-    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));
+    if (index < frag_index->nb_items)
+        memmove(frag_index->item + index + 1, frag_index->item + index,
+                (frag_index->nb_items - index) * sizeof(*frag_index->item));
 
-    item = &c->frag_index.item[index];
+    item = &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++;
+    item->stream_info.sidx_pts = AV_NOPTS_VALUE;
+    item->stream_info.tfdt_dts = AV_NOPTS_VALUE;
+    item->stream_info.first_tfra_pts = AV_NOPTS_VALUE;
+    item->stream_info.index_entry = -1;
+    item->stream_info.encryption_index = NULL;
+
+    frag_index->nb_items++;
+    frag_index->current = index;
 
     return index;
 }
 
-static void fix_frag_index_entries(MOVFragmentIndex *frag_index, int index,
-                                   int id, int entries)
-{
+static void fix_frag_index_entries(MOVFragmentIndex **frag_indices, int nb_frag_indices, int track_id,
+                                   int index, int entries) {
     int i;
-    MOVFragmentStreamInfo * frag_stream_info;
+    MOVFragmentIndex *frag_index;
 
     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;
-    }
+
+    frag_index = mov_find_frag_index(frag_indices, nb_frag_indices, track_id);
+    if (!frag_index)
+        return;
+
+    for (i = index; i < frag_index->nb_items; i++)
+        if (frag_index->item[i].stream_info.index_entry >= 0)
+            frag_index->item[i].stream_info.index_entry += entries;
 }
 
 static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
@@ -1389,7 +1353,6 @@  static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     }
     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);
 }
 
@@ -4558,7 +4521,8 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
     MOVFragment *frag = &c->fragment;
     MOVTrackExt *trex = NULL;
-    int flags, track_id, i;
+    int flags, track_id, i, index;
+    MOVFragmentIndex *frag_index = NULL;
 
     c->fragment.found_tfhd = 1;
 
@@ -4578,7 +4542,6 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         return 0;
     }
     frag->track_id = track_id;
-    set_frag_stream(&c->frag_index, track_id);
 
     frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
                              avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
@@ -4593,6 +4556,14 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                      avio_rb32(pb) : trex->flags;
     av_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags);
 
+    index = update_frag_index(c, frag->track_id, frag->moof_offset);
+    if (index < 0) return index;
+
+    frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, track_id);
+    if (index < frag_index->nb_items)
+        frag_index->item[index].stream_info.stsd_id = frag->stsd_id;
+    frag_index->item[index].headers_read = 1;
+
     return 0;
 }
 
@@ -4671,7 +4642,7 @@  static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         base_media_decode_time = avio_rb32(pb);
     }
 
-    frag_stream_info = get_current_frag_stream_info(&c->frag_index);
+    frag_stream_info = get_current_frag_stream_info(c, c->fragment.track_id);
     if (frag_stream_info)
         frag_stream_info->tfdt_dts = base_media_decode_time;
     sc->track_end = base_media_decode_time;
@@ -4696,6 +4667,7 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     size_t old_ctts_allocated_size;
     AVIndexEntry *new_entries;
     MOVFragmentStreamInfo * frag_stream_info;
+    MOVFragmentIndex *frag_index = NULL;
 
     if (!frag->found_tfhd) {
         av_log(c->fc, AV_LOG_ERROR, "trun track id unknown, no tfhd was found\n");
@@ -4723,12 +4695,14 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     // 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;
+    frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, st->id);
+    if (frag_index) {
+        for (i = frag_index->current + 1; i < frag_index->nb_items; i++) {
+            if (frag_index->item[i].stream_info.index_entry >= 0) {
+                next_frag_index = i;
+                index_entry_pos = frag_index->item[i].stream_info.index_entry;
+                break;
+            }
         }
     }
     av_assert0(index_entry_pos <= st->nb_index_entries);
@@ -4743,7 +4717,7 @@  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);
 
-    frag_stream_info = get_current_frag_stream_info(&c->frag_index);
+    frag_stream_info = get_current_frag_stream_info(c, c->fragment.track_id);
     if (frag_stream_info)
     {
         if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE &&
@@ -4937,8 +4911,8 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     // 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);
+    fix_frag_index_entries(c->frag_indices, c->nb_frag_indices, frag->track_id,
+                           next_frag_index, entries);
 
     if (pb->eof_reached) {
         av_log(c->fc, AV_LOG_WARNING, "reached eof, corrupted TRUN atom\n");
@@ -4963,6 +4937,8 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     AVStream *ref_st = NULL;
     MOVStreamContext *sc, *ref_sc = NULL;
     AVRational timescale;
+    MOVFragmentIndex *frag_index;
+    MOVFragmentStreamInfo *frag_stream_info = NULL;
 
     version = avio_r8(pb);
     if (version > 1) {
@@ -5007,7 +4983,6 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 
     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) {
@@ -5017,8 +4992,10 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         avio_rb32(pb); // sap_flags
         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);
+        index = update_frag_index(c, track_id, offset);
+        if (index < 0) return index;
+
+        frag_stream_info = get_current_frag_stream_info(c, track_id);
         if (frag_stream_info)
             frag_stream_info->sidx_pts = timestamp;
 
@@ -5033,13 +5010,11 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     if (offset == avio_size(pb)) {
         // 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[j];
+        for (i = 0; i < c->nb_frag_indices; i++) {
+            MOVFragmentIndex *frag_index = c->frag_indices[i];
+            for (j = 0; j < frag_index->nb_items; j++) {
+                if (frag_index->item[j].stream_info.sidx_pts != AV_NOPTS_VALUE) {
+                    ref_st = c->fc->streams[i];
                     ref_sc = ref_st->priv_data;
                     break;
                 }
@@ -5053,7 +5028,9 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
             }
         }
 
-        c->frag_index.complete = 1;
+        frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, st->id);
+        if (frag_index)
+            frag_index->complete = 1;
     }
 
     return 0;
@@ -5857,12 +5834,12 @@  static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
     AVStream *st;
     int i;
 
-    frag_stream_info = get_current_frag_stream_info(&c->frag_index);
+    frag_stream_info = get_current_frag_stream_info(c, c->fragment.track_id);
     if (frag_stream_info) {
         for (i = 0; i < c->fc->nb_streams; i++) {
-            if (c->fc->streams[i]->id == frag_stream_info->id) {
-              st = c->fc->streams[i];
-              break;
+            if (c->fc->streams[i]->id == c->fragment.track_id) {
+                st = c->fc->streams[i];
+                break;
             }
         }
         if (i == c->fc->nb_streams)
@@ -6247,7 +6224,9 @@  static int mov_read_saio(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         } else {
             encryption_index->auxiliary_offsets[i] = avio_rb64(pb);
         }
-        if (c->frag_index.current >= 0) {
+
+        MOVFragmentIndex *frag_index = mov_find_frag_index(c->frag_indices, c->nb_frag_indices, c->fragment.track_id);
+        if (frag_index && frag_index->current >= 0) {
             encryption_index->auxiliary_offsets[i] += c->fragment.base_data_offset;
         }
     }
@@ -6570,22 +6549,23 @@  static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
     return 0;
 }
 
-static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
+static int cenc_filter(MOVContext *mov, AVStream *st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
 {
-    MOVFragmentStreamInfo *frag_stream_info;
-    MOVEncryptionIndex *encryption_index;
+    MOVEncryptionIndex *encryption_index = NULL;
     AVEncryptionInfo *encrypted_sample;
-    int encrypted_index, ret;
+    int encrypted_index = current_index, ret, index = -1;
+    MOVFragmentIndex *frag_index;
 
-    frag_stream_info = get_frag_stream_info(&mov->frag_index, mov->frag_index.current, st->id);
-    encrypted_index = current_index;
-    encryption_index = NULL;
-    if (frag_stream_info) {
+    frag_index = mov_find_frag_index(mov->frag_indices, mov->nb_frag_indices, st->id);
+    if (frag_index)
+        index = search_frag_timestamp(frag_index, st, pkt->pts);
+
+    if (index != -1) {
         // Note this only supports encryption info in the first sample descriptor.
-        if (mov->fragment.stsd_id == 1) {
-            if (frag_stream_info->encryption_index) {
-                encrypted_index = current_index - frag_stream_info->index_entry;
-                encryption_index = frag_stream_info->encryption_index;
+        if (frag_index->item[index].stream_info.stsd_id == 1) {
+            if (frag_index->item[index].stream_info.encryption_index) {
+                encrypted_index = current_index - frag_index->item[index].stream_info.index_entry;
+                encryption_index = frag_index->item[index].stream_info.encryption_index;
             } else {
                 encryption_index = sc->cenc.encryption_index;
             }
@@ -6871,9 +6851,9 @@  static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                 return err;
             }
             if (c->found_moov && c->found_mdat &&
-                ((!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->frag_index.complete) ||
+                ((!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
                  start_pos + a.size == avio_size(pb))) {
-                if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX || c->frag_index.complete)
+                if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || c->fc->flags & AVFMT_FLAG_IGNIDX)
                     c->next_root_atom = start_pos + a.size;
                 c->atom_depth --;
                 return 0;
@@ -7230,14 +7210,15 @@  static int mov_read_close(AVFormatContext *s)
     av_freep(&mov->trex_data);
     av_freep(&mov->bitrates);
 
-    for (i = 0; i < mov->frag_index.nb_items; i++) {
-        MOVFragmentStreamInfo *frag = mov->frag_index.item[i].stream_info;
-        for (j = 0; j < mov->frag_index.item[i].nb_stream_info; j++) {
-            mov_free_encryption_index(&frag[j].encryption_index);
-        }
-        av_freep(&mov->frag_index.item[i].stream_info);
+    for (i = 0; i < mov->nb_frag_indices; i++) {
+        MOVFragmentIndex *index = mov->frag_indices[i];
+        for (i = 0; i < index->nb_items; i++)
+            mov_free_encryption_index(&index->item[i].stream_info.encryption_index);
+
+        av_freep(&index->item);
+        av_freep(&index);
     }
-    av_freep(&mov->frag_index.item);
+    av_freep(&mov->frag_indices);
 
     av_freep(&mov->aes_decrypt);
     av_freep(&mov->chapter_tracks);
@@ -7285,6 +7266,7 @@  static int read_tfra(MOVContext *mov, AVIOContext *f)
     int64_t pos = avio_tell(f);
     uint32_t size = avio_rb32(f);
     unsigned track_id, item_count;
+    MOVFragmentStreamInfo *frag_stream_info = NULL;
 
     if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a')) {
         return 1;
@@ -7299,7 +7281,6 @@  static int read_tfra(MOVContext *mov, AVIOContext *f)
     for (i = 0; i < item_count; i++) {
         int64_t time, offset;
         int index;
-        MOVFragmentStreamInfo * frag_stream_info;
 
         if (avio_feof(f)) {
             return AVERROR_INVALIDDATA;
@@ -7316,11 +7297,14 @@  static int read_tfra(MOVContext *mov, AVIOContext *f)
         // 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);
+        index = update_frag_index(mov, track_id, offset);
+        if (index < 0) return index;
+
+        frag_stream_info = get_current_frag_stream_info(mov, track_id);
         if (frag_stream_info &&
-            frag_stream_info->first_tfra_pts == AV_NOPTS_VALUE)
+            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);
@@ -7582,10 +7566,6 @@  static int mov_read_header(AVFormatContext *s)
     }
     ff_configure_buffers_for_index(s, AV_TIME_BASE);
 
-    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;
 }
 
@@ -7622,27 +7602,26 @@  static int should_retry(AVIOContext *pb, int error_code) {
     return 1;
 }
 
-static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
+static int mov_switch_root(AVFormatContext *s, int64_t target)
 {
-    int ret;
+    int ret, i, index;
+    MOVFragmentIndex *frag_index;
     MOVContext *mov = s->priv_data;
 
-    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;
-    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;
+    for (i = 0; i < mov->nb_frag_indices; i++) {
+        frag_index = mov_find_frag_index(mov->frag_indices, mov->nb_frag_indices, i + 1);
+        if (frag_index == NULL)
+            return -1;
+
+        index = search_frag_moof_offset(frag_index, target);
+        if (index < frag_index->nb_items)
+            if (frag_index->item[index].moof_offset == target && frag_index->item[index].headers_read)
+                return 0;
     }
 
     mov->found_mdat = 0;
@@ -7694,7 +7673,7 @@  static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     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, -1)) < 0)
+        if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0)
             return ret;
         goto retry;
     }
@@ -7818,17 +7797,23 @@  static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp
 {
     MOVContext *mov = s->priv_data;
     int index;
+    int64_t target;
+    MOVFragmentIndex *frag_index;
 
-    if (!mov->frag_index.complete)
+    frag_index = mov_find_frag_index(mov->frag_indices, mov->nb_frag_indices, st->id);
+    if (frag_index == NULL || !frag_index->complete)
         return 0;
 
-    index = search_frag_timestamp(&mov->frag_index, st, timestamp);
+    index = search_frag_timestamp(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;
+
+    if (index < frag_index->nb_items) {
+        target = frag_index->item[index].moof_offset;
+
+        if (!frag_index->item[index].headers_read)
+            return mov_switch_root(s, target);
+    }
 
     return 0;
 }