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

Submitted by Charles Liu on Feb. 22, 2019, 10:20 a.m.

Details

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

Commit Message

Charles Liu Feb. 22, 2019, 10:20 a.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  | 374 +++++++++++++++++++++------------------------
 2 files changed, 181 insertions(+), 203 deletions(-)

Comments

Michael Niedermayer Feb. 23, 2019, 12:25 a.m.
On Fri, Feb 22, 2019 at 06:20:40PM +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  | 374 +++++++++++++++++++++------------------------
>  2 files changed, 181 insertions(+), 203 deletions(-)

still hitting out of memory and being killed by the kernel
the other issue seems to be fixed though

thx


[...]
Charles Liu Feb. 23, 2019, 7:22 a.m.
I can’t reproduce the OOM. Is it possible the problem relate to a specific
clip?

Here is my test steps:
1. To ensure that it’s not affected by caches, delete all except the .git
dir in my code dir. Then checkout again.
2. To ensure that the latest test clips are used. make fate-rsync
SAMPLES=fate-suite/
3. To ensure that OS environment is clean, run test in docker.
Below is one of my test logs:

> /ffmpeg_src# valgrind --leak-check=full ./ffmpeg -i issue/dash.mp4  -map 0
> -f null -
>
> *==8552== Memcheck, a memory error detector*
> *==8552== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.*
> *==8552== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright
> info*
> *==8552== Command: ./ffmpeg -i issue/dash.mp4 -map 0 -f null -*
> *==8552== *
> *ffmpeg version 4.1.git Copyright (c) 2000-2019 the FFmpeg developers*
> *  built with gcc 7 (Ubuntu 7.3.0-27ubuntu1~18.04)*
> *  configuration: --samples=fate-suite/ --disable-doc --fatal-warnings
> --valgrind=VALGRIND*
> *  libavutil      56. 26.100 / 56. 26.100*
> *  libavcodec     58. 47.102 / 58. 47.102*
> *  libavformat    58. 26.101 / 58. 26.101*
> *  libavdevice    58.  6.101 / 58.  6.101*
> *  libavfilter     7. 48.100 /  7. 48.100*
> *  libswscale      5.  4.100 /  5.  4.100*
> *  libswresample   3.  4.100 /  3.  4.100*
> *Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'issue/dash.mp4':*
> *  Metadata:*
> *    major_brand     : iso5*
> *    minor_version   : 512*
> *    compatible_brands: iso6mp41*
> *    encoder         : Lavf58.20.100*
> *  Duration: 00:00:57.76, start: 0.000000, bitrate: 5193 kb/s*
> *    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv,
> smpte170m/bt709/bt709), 1280x720, 5125 kb/s, 25 fps, 25 tbr, 250k tbn, 50
> tbc (default)*
> *    Metadata:*
> *      handler_name    : VideoHandler*
> *    Stream #0:1(und): Audio: aac (HE-AAC) (mp4a / 0x6134706D), 44100 Hz,
> stereo, fltp, 79 kb/s (default)*
> *    Metadata:*
> *      handler_name    : SoundHandler*
> *Stream mapping:*
> *  Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))*
> *  Stream #0:1 -> #0:1 (aac (native) -> pcm_s16le (native))*
> *Press [q] to stop, [?] for help*
> *Output #0, null, to 'pipe:':ze=N/A time=-577014:32:22.77 bitrate=N/A
> speed=N/A    *
> *  Metadata:*
> *    major_brand     : iso5*
> *    minor_version   : 512*
> *    compatible_brands: iso6mp41*
> *    encoder         : Lavf58.26.101*
> *    Stream #0:0(und): Video: wrapped_avframe, yuv420p(progressive),
> 1280x720, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc (default)*
> *    Metadata:*
> *      handler_name    : VideoHandler*
> *      encoder         : Lavc58.47.102 wrapped_avframe*
> *    Stream #0:1(und): Audio: pcm_s16le, 44100 Hz, stereo, s16, 1411 kb/s
> (default)*
> *    Metadata:*
> *      handler_name    : SoundHandler*
> *      encoder         : Lavc58.47.102 pcm_s16le*
> *frame= 1438 fps=5.0 q=-0.0 Lsize=N/A time=00:00:57.68 bitrate=N/A
> speed=0.202x    *
> *video:753kB audio:9904kB subtitle:0kB other streams:0kB global
> headers:0kB muxing overhead: unknown*
> *==8552== *
> *==8552== HEAP SUMMARY:*
> *==8552==     in use at exit: 0 bytes in 0 blocks*
> *==8552==   total heap usage: 207,112 allocs, 207,112 frees, 121,288,827
> bytes allocated*
> *==8552== *
> *==8552== All heap blocks were freed -- no leaks are possible*
> *==8552== *
> *==8552== For counts of detected and suppressed errors, rerun with: -v**==8552==
> ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)*



BTW, below is my dockefile. It show that the installed dependences. And how
to configure and test.
*FROM ubuntu:18.04*

*RUN apt-get update -qq && apt-get dist-upgrade -qy*
*RUN apt-get -qy install autoconf automake build-essential cmake libass-dev
libfreetype6-dev libtool libvorbis-dev pkg-config texinfo zlib1g-dev \*
*  libnuma-dev*
*RUN apt-get -qy install yasm libx264-dev libx265-dev libfdk-aac-dev*
*RUN apt-get -qy install valgrind*

*COPY . /ffmpeg_src/*
*WORKDIR /ffmpeg_src*

*RUN make distclean*
*RUN ./configure --samples=fate-suite/ --disable-doc --fatal-warnings
--valgrind=VALGRIND*
*RUN make -j3 && make fate*

*CMD ["/bin/bash"]*


Michael Niedermayer <michael@niedermayer.cc> 于2019年2月23日周六 上午8:25写道:

> On Fri, Feb 22, 2019 at 06:20:40PM +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  | 374 +++++++++++++++++++++------------------------
> >  2 files changed, 181 insertions(+), 203 deletions(-)
>
> still hitting out of memory and being killed by the kernel
> the other issue seems to be fixed though
>
> thx
>
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> The educated differ from the uneducated as much as the living from the
> dead. -- Aristotle
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Michael Niedermayer Feb. 23, 2019, 9:34 a.m.
On Sat, Feb 23, 2019 at 03:22:08PM +0800, C.H.Liu wrote:
> I can’t reproduce the OOM. Is it possible the problem relate to a specific
> clip?
> 
> Here is my test steps:
> 1. To ensure that it’s not affected by caches, delete all except the .git
> dir in my code dir. Then checkout again.
> 2. To ensure that the latest test clips are used. make fate-rsync
> SAMPLES=fate-suite/
> 3. To ensure that OS environment is clean, run test in docker.
> Below is one of my test logs:

heres some massif output that should show where the exessive allocation happens:
(note the box has 14gb free and the input file is less than 4kb size)

snapshot=70
#-----------
time=1856698079
mem_heap_B=8487679
mem_heap_extra_B=522580313
mem_stacks_B=0
heap_tree=peak
n2: 8487679 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
 n2: 8298639 0x12AAF2B: av_malloc (mem.c:87)
  n1: 8294364 0x12AAF59: av_malloc (mem.c:126)
   n1: 8294364 0x12AB24B: av_mallocz (mem.c:238)
    n1: 8294364 0x12AB122: av_mallocz_array (mem.c:195)
     n1: 8294364 0x743749: update_frag_index (mov.c:1257)
      n1: 8294364 0x756174: read_tfra (mov.c:7293)
       n1: 8294364 0x7563F0: mov_read_mfra (mov.c:7344)
        n1: 8294364 0x743B01: mov_read_moof (mov.c:1338)
         n1: 8294364 0x754A0C: mov_read_default (mov.c:6841)
          n1: 8294364 0x756596: mov_read_header (mov.c:7385)
           n1: 8294364 0x8254BB: avformat_open_input (utils.c:631)
            n1: 8294364 0x415CE6: open_input_file (ffmpeg_opt.c:1104)
             n1: 8294364 0x41F666: open_files (ffmpeg_opt.c:3273)
              n1: 8294364 0x41F7F8: ffmpeg_parse_options (ffmpeg_opt.c:3313)
               n0: 8294364 0x43DFC6: main (ffmpeg.c:4869)
  n0: 4275 in 7 places, all below massif's threshold (01.00%)
 n0: 189040 in 8 places, all below massif's threshold (01.00%)


[...]
Charles Liu Feb. 25, 2019, 11:07 a.m.
Would you mind share your input file? I still can’t reproduce the OOM.


I have tried several types of mp4.

I also tried to create a file like yours. According to the snapshot, seems
it has no ‘sidx’ box and enable ‘use_mfra_for’.

Massif didn’t report update_frag_index() function. I didn’t see an extra
heap as big as here, either.


The code around line 1257 creates an array of fragment indices for each
track. It should be very small. Interesting.

Sorry for disturbing.


Thanks and Regards,

Charles

Michael Niedermayer <michael@niedermayer.cc> 于2019年2月23日周六 下午5:35写道:

> On Sat, Feb 23, 2019 at 03:22:08PM +0800, C.H.Liu wrote:
> > I can’t reproduce the OOM. Is it possible the problem relate to a
> specific
> > clip?
> >
> > Here is my test steps:
> > 1. To ensure that it’s not affected by caches, delete all except the .git
> > dir in my code dir. Then checkout again.
> > 2. To ensure that the latest test clips are used. make fate-rsync
> > SAMPLES=fate-suite/
> > 3. To ensure that OS environment is clean, run test in docker.
> > Below is one of my test logs:
>
> heres some massif output that should show where the exessive allocation
> happens:
> (note the box has 14gb free and the input file is less than 4kb size)
>
> snapshot=70
> #-----------
> time=1856698079
> mem_heap_B=8487679
> mem_heap_extra_B=522580313
> mem_stacks_B=0
> heap_tree=peak
> n2: 8487679 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
>  n2: 8298639 0x12AAF2B: av_malloc (mem.c:87)
>   n1: 8294364 0x12AAF59: av_malloc (mem.c:126)
>    n1: 8294364 0x12AB24B: av_mallocz (mem.c:238)
>     n1: 8294364 0x12AB122: av_mallocz_array (mem.c:195)
>      n1: 8294364 0x743749: update_frag_index (mov.c:1257)
>       n1: 8294364 0x756174: read_tfra (mov.c:7293)
>        n1: 8294364 0x7563F0: mov_read_mfra (mov.c:7344)
>         n1: 8294364 0x743B01: mov_read_moof (mov.c:1338)
>          n1: 8294364 0x754A0C: mov_read_default (mov.c:6841)
>           n1: 8294364 0x756596: mov_read_header (mov.c:7385)
>            n1: 8294364 0x8254BB: avformat_open_input (utils.c:631)
>             n1: 8294364 0x415CE6: open_input_file (ffmpeg_opt.c:1104)
>              n1: 8294364 0x41F666: open_files (ffmpeg_opt.c:3273)
>               n1: 8294364 0x41F7F8: ffmpeg_parse_options
> (ffmpeg_opt.c:3313)
>                n0: 8294364 0x43DFC6: main (ffmpeg.c:4869)
>   n0: 4275 in 7 places, all below massif's threshold (01.00%)
>  n0: 189040 in 8 places, all below massif's threshold (01.00%)
>
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Its not that you shouldnt use gotos but rather that you should write
> readable code and code with gotos often but not always is less readable
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Charles Liu Feb. 26, 2019, 3:07 a.m.
Attach one of my massif outputs.

The max memory usage is approximately 22.05MB. And the *extra *heap is not
too big.

The peak snapshot is the 7th. The H.264 decoder is responsible for 86.11%
of the memory usage.

The update_frag_index is responsible for 78,264 bytes. And the relevant
code is at line 1276 instead of line 1257. In my opinion, this is usual. It
create fragment items for every ‘moof’.


Thanks and Regards,

Charles

On Mon, Feb 25, 2019 at 7:07 PM C.H.Liu <liuchh83@gmail.com> wrote:

>  Would you mind share your input file? I still can’t reproduce the OOM.
>
>
> I have tried several types of mp4.
>
> I also tried to create a file like yours. According to the snapshot, seems
> it has no ‘sidx’ box and enable ‘use_mfra_for’.
>
> Massif didn’t report update_frag_index() function. I didn’t see an extra
> heap as big as here, either.
>
>
> The code around line 1257 creates an array of fragment indices for each
> track. It should be very small. Interesting.
>
> Sorry for disturbing.
>
>
> Thanks and Regards,
>
> Charles
>
> Michael Niedermayer <michael@niedermayer.cc> 于2019年2月23日周六 下午5:35写道:
>
>> On Sat, Feb 23, 2019 at 03:22:08PM +0800, C.H.Liu wrote:
>> > I can’t reproduce the OOM. Is it possible the problem relate to a
>> specific
>> > clip?
>> >
>> > Here is my test steps:
>> > 1. To ensure that it’s not affected by caches, delete all except the
>> .git
>> > dir in my code dir. Then checkout again.
>> > 2. To ensure that the latest test clips are used. make fate-rsync
>> > SAMPLES=fate-suite/
>> > 3. To ensure that OS environment is clean, run test in docker.
>> > Below is one of my test logs:
>>
>> heres some massif output that should show where the exessive allocation
>> happens:
>> (note the box has 14gb free and the input file is less than 4kb size)
>>
>> snapshot=70
>> #-----------
>> time=1856698079
>> mem_heap_B=8487679
>> mem_heap_extra_B=522580313
>> mem_stacks_B=0
>> heap_tree=peak
>> n2: 8487679 (heap allocation functions) malloc/new/new[], --alloc-fns,
>> etc.
>>  n2: 8298639 0x12AAF2B: av_malloc (mem.c:87)
>>   n1: 8294364 0x12AAF59: av_malloc (mem.c:126)
>>    n1: 8294364 0x12AB24B: av_mallocz (mem.c:238)
>>     n1: 8294364 0x12AB122: av_mallocz_array (mem.c:195)
>>      n1: 8294364 0x743749: update_frag_index (mov.c:1257)
>>       n1: 8294364 0x756174: read_tfra (mov.c:7293)
>>        n1: 8294364 0x7563F0: mov_read_mfra (mov.c:7344)
>>         n1: 8294364 0x743B01: mov_read_moof (mov.c:1338)
>>          n1: 8294364 0x754A0C: mov_read_default (mov.c:6841)
>>           n1: 8294364 0x756596: mov_read_header (mov.c:7385)
>>            n1: 8294364 0x8254BB: avformat_open_input (utils.c:631)
>>             n1: 8294364 0x415CE6: open_input_file (ffmpeg_opt.c:1104)
>>              n1: 8294364 0x41F666: open_files (ffmpeg_opt.c:3273)
>>               n1: 8294364 0x41F7F8: ffmpeg_parse_options
>> (ffmpeg_opt.c:3313)
>>                n0: 8294364 0x43DFC6: main (ffmpeg.c:4869)
>>   n0: 4275 in 7 places, all below massif's threshold (01.00%)
>>  n0: 189040 in 8 places, all below massif's threshold (01.00%)
>>
>>
>> [...]
>> --
>> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>>
>> Its not that you shouldnt use gotos but rather that you should write
>> readable code and code with gotos often but not always is less readable
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>
Michael Niedermayer Feb. 27, 2019, 1:39 p.m.
On Mon, Feb 25, 2019 at 07:07:27PM +0800, C.H.Liu wrote:
>  Would you mind share your input file? I still can’t reproduce the OOM.

i belive i cannot share this sample, but the patch really needs to be
fully reviewed not just one bug that was found by chance fixed otherwise
we might be missing other bugs ...


> 
> 
> I have tried several types of mp4.
> 
> I also tried to create a file like yours. According to the snapshot, seems
> it has no ‘sidx’ box and enable ‘use_mfra_for’.
> 
> Massif didn’t report update_frag_index() function. I didn’t see an extra
> heap as big as here, either.

looking at the sample that triggers the OOM the do/while loop you add 
to update_frag_index() is entered and never exits while doing allocations
in it.

also the patch as is is not ok. You for example have a list of unrelated
changes in the commit message. Each should be in its own patch
also changes that are functional should be splited from non functional
changes. For example here:

     // 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;
+    }

you change frag_index to a local variable this is non functional and
should not be in a patch that does a functional change ideally.
Now sure if a functional change is trivial and clear such things are
sometimes done together but this patch is not clear, at least its not
clear to me ...

thanks

[...]
Charles Liu March 5, 2019, 4:03 p.m.
Thanks for your reply.
If a track id of the test clip does not appear in its ‘moov’, the do/while
loop cannot exit indeed.

The purpose of this patch is to solve AV_NOPTS_VALUE in the fragment info
structure. Because the original structures is oriented to ‘moof’ boxes in
file. I tried to organize fragment info according to media tracks.

Since the fragment structures has been changed, it fixes two other issues.
One is  #7572, which missed the last ‘sidx’ and ‘moof’.
The other is “mov->fragment.stsd_id == 1” in function cenc_filter().  Mov
muxer read all ‘moof’ while reading header. So the current fragment points
to the last ‘moof’. When we read packets/samples from the beginning, the
sample (belongs to the first ‘moof’) and the current fragment info do not
match.

I don’t mind waiting for other options. And we already have a quick fix
aa25198f1b925a464bdfa83a98476f. Though, I will still update the patch to
resolve the endless loop problem.

BTW, I have’t modified other commit messages. The frag index variable is
changed in isom.h.

Thanks again, and sorry for late response.

Regards,
Charles.

On Wed, Feb 27, 2019 at 9:39 PM Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Mon, Feb 25, 2019 at 07:07:27PM +0800, C.H.Liu wrote:
> >  Would you mind share your input file? I still can’t reproduce the OOM.
>
> i belive i cannot share this sample, but the patch really needs to be
> fully reviewed not just one bug that was found by chance fixed otherwise
> we might be missing other bugs ...
>
>
> >
> >
> > I have tried several types of mp4.
> >
> > I also tried to create a file like yours. According to the snapshot,
> seems
> > it has no ‘sidx’ box and enable ‘use_mfra_for’.
> >
> > Massif didn’t report update_frag_index() function. I didn’t see an extra
> > heap as big as here, either.
>
> looking at the sample that triggers the OOM the do/while loop you add
> to update_frag_index() is entered and never exits while doing allocations
> in it.
>
> also the patch as is is not ok. You for example have a list of unrelated
> changes in the commit message. Each should be in its own patch
> also changes that are functional should be splited from non functional
> changes. For example here:
>
>      // 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;
> +    }
>
> you change frag_index to a local variable this is non functional and
> should not be in a patch that does a functional change ideally.
> Now sure if a functional change is trivial and clear such things are
> sometimes done together but this patch is not clear, at least its not
> clear to me ...
>
> thanks
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> I do not agree with what you have to say, but I'll defend to the death your
> right to say it. -- 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..eea8fa4e8f 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..a16788fd21 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)
 {
-    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;
-
-    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;
-        }
+    MOVFragmentIndex *frag_index = NULL;
 
-    // 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,9 @@  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)
-{
-    av_assert0(frag_stream_info);
+static int64_t get_frag_time(MOVFragmentIndex *frag_index,
+                             int index, int track_id) {
+    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 +1212,9 @@  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;
+                                 AVStream *st, int64_t timestamp) {
+    int a, b, m;
     int64_t frag_time;
     int id = -1;
 
@@ -1282,89 +1230,98 @@  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++)
+                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 +1346,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 +4514,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 +4535,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 +4549,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 +4635,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 +4660,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 +4688,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 +4710,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 +4904,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 +4930,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 +4976,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 +4985,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 +5003,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 +5021,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 +5827,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)
@@ -6180,6 +6150,7 @@  static int mov_read_saio(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     int i, ret;
     unsigned int version, entry_count, aux_info_type, aux_info_param;
     unsigned int alloc_size = 0;
+    MOVFragmentIndex *frag_index;
 
     ret = get_current_encryption_info(c, &encryption_index, &sc);
     if (ret != 1)
@@ -6247,9 +6218,10 @@  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) {
+
+        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;
-        }
     }
 
     if (pb->eof_reached) {
@@ -6570,22 +6542,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 +6844,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 +7203,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 (j = 0; j < index->nb_items; j++)
+            mov_free_encryption_index(&index->item[j].stream_info.encryption_index);
+
+        av_freep(&index->item);
     }
-    av_freep(&mov->frag_index.item);
+    av_freep(&mov->frag_indices);
+    mov->nb_frag_indices = 0;
 
     av_freep(&mov->aes_decrypt);
     av_freep(&mov->chapter_tracks);
@@ -7285,6 +7259,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 +7274,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 +7290,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 +7559,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 +7595,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 +7666,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 +7790,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;
 }