diff mbox

[FFmpeg-devel] mov: fix decode of fragments that overlap in time

Message ID 20171011011108.32025-1-jstebbins@jetheaddev.com
State Superseded
Headers show

Commit Message

John Stebbins Oct. 11, 2017, 1:11 a.m. UTC
When keyframe intervals of dash segments are not perfectly aligned,
fragments in the stream can overlap in time. The previous sorting by
timestamp causes packets to be read out of decode order and results
in decode errors.

Insert new "trun" index entries into index_entries in the order that
the trun are referenced by the sidx.
---
 libavformat/isom.h |  26 +-
 libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 485 insertions(+), 225 deletions(-)

Comments

Michael Niedermayer Oct. 12, 2017, 1:25 a.m. UTC | #1
On Tue, Oct 10, 2017 at 06:11:08PM -0700, John Stebbins wrote:
> When keyframe intervals of dash segments are not perfectly aligned,
> fragments in the stream can overlap in time. The previous sorting by
> timestamp causes packets to be read out of decode order and results
> in decode errors.
> 
> Insert new "trun" index entries into index_entries in the order that
> the trun are referenced by the sidx.
> ---
>  libavformat/isom.h |  26 +-
>  libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
>  2 files changed, 485 insertions(+), 225 deletions(-)

This still crashes: (not sure i can share the file, tell me if the
valgrind output is enough)

==16990== Conditional jump or move depends on uninitialised value(s)
==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
==16990==    by 0x6C543D: update_frag_index (mov.c:1318)
==16990==    by 0x6D4F77: read_tfra (mov.c:6384)
==16990==    by 0x6D51EF: mov_read_mfra (mov.c:6432)
==16990==    by 0x6C57F7: mov_read_moof (mov.c:1384)
==16990==    by 0x6D38E5: mov_read_default (mov.c:5949)
==16990==    by 0x6D5395: mov_read_header (mov.c:6473)
==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
==16990==    by 0x43D263: main (ffmpeg.c:4794)
==16990==
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] wav header size < 14 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] get_wav_header failed
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] error reading header
==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
==16990==    by 0x117DC28: av_vlog (log.c:383)
==16990==    by 0x117DBE8: av_log (log.c:375)
==16990==    by 0x6D53C2: mov_read_header (mov.c:6474)
==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
==16990==    by 0x43D263: main (ffmpeg.c:4794)
==16990== Conditional jump or move depends on uninitialised value(s)
==16990==    at 0x4C2B58C: free (vg_replace_malloc.c:446)
==16990==    by 0x11807BD: av_free (mem.c:209)
==16990==    by 0x11807F5: av_freep (mem.c:219)
==16990==    by 0x6D4CA9: mov_read_close (mov.c:6303)
==16990==    by 0x6D53D1: mov_read_header (mov.c:6475)
==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
==16990==    by 0x43D263: main (ffmpeg.c:4794)
==16990==
test.mp4: Invalid data found when processing input
==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
==16990==    by 0x117DC28: av_vlog (log.c:383)
==16990==    by 0x117DBE8: av_log (log.c:375)
==16990==    by 0x4274D1: print_error (cmdutils.c:1058)
==16990==    by 0x415430: open_input_file (ffmpeg_opt.c:1062)
==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
==16990==    by 0x43D263: main (ffmpeg.c:4794)
==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
==16990==    by 0x117DC28: av_vlog (log.c:383)
==16990==    by 0x117DBE8: av_log (log.c:375)
==16990==    by 0x42B964: term_exit (ffmpeg.c:317)
==16990==    by 0x42C64B: ffmpeg_cleanup (ffmpeg.c:618)
==16990==    by 0x42483F: exit_program (cmdutils.c:138)
==16990==    by 0x415469: open_input_file (ffmpeg_opt.c:1065)
==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
==16990==    by 0x43D263: main (ffmpeg.c:4794)



[...]
John Stebbins Oct. 12, 2017, 3:33 a.m. UTC | #2
On 10/11/2017 06:25 PM, Michael Niedermayer wrote:
> On Tue, Oct 10, 2017 at 06:11:08PM -0700, John Stebbins wrote:
>> When keyframe intervals of dash segments are not perfectly aligned,
>> fragments in the stream can overlap in time. The previous sorting by
>> timestamp causes packets to be read out of decode order and results
>> in decode errors.
>>
>> Insert new "trun" index entries into index_entries in the order that
>> the trun are referenced by the sidx.
>> ---
>>  libavformat/isom.h |  26 +-
>>  libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
>>  2 files changed, 485 insertions(+), 225 deletions(-)
> This still crashes: (not sure i can share the file, tell me if the
> valgrind output is enough)
>
> ==16990== Conditional jump or move depends on uninitialised value(s)
> ==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
> ==16990==    by 0x6C543D: update_frag_index (mov.c:1318)
> ==16990==    by 0x6D4F77: read_tfra (mov.c:6384)
> ==16990==    by 0x6D51EF: mov_read_mfra (mov.c:6432)
> ==16990==    by 0x6C57F7: mov_read_moof (mov.c:1384)
> ==16990==    by 0x6D38E5: mov_read_default (mov.c:5949)
> ==16990==    by 0x6D5395: mov_read_header (mov.c:6473)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] wav header size < 14 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] get_wav_header failed
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] error reading header
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x6D53C2: mov_read_header (mov.c:6474)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990== Conditional jump or move depends on uninitialised value(s)
> ==16990==    at 0x4C2B58C: free (vg_replace_malloc.c:446)
> ==16990==    by 0x11807BD: av_free (mem.c:209)
> ==16990==    by 0x11807F5: av_freep (mem.c:219)
> ==16990==    by 0x6D4CA9: mov_read_close (mov.c:6303)
> ==16990==    by 0x6D53D1: mov_read_header (mov.c:6475)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==
> test.mp4: Invalid data found when processing input
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x4274D1: print_error (cmdutils.c:1058)
> ==16990==    by 0x415430: open_input_file (ffmpeg_opt.c:1062)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x42B964: term_exit (ffmpeg.c:317)
> ==16990==    by 0x42C64B: ffmpeg_cleanup (ffmpeg.c:618)
> ==16990==    by 0x42483F: exit_program (cmdutils.c:138)
> ==16990==    by 0x415469: open_input_file (ffmpeg_opt.c:1065)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>
>
>

I'm travelling for a few days.  I may have time to take a look while I'm on the move.  But if not, I'll have a look when
I get back next Tue.
John Stebbins Oct. 12, 2017, 5:56 a.m. UTC | #3
On 10/11/2017 06:25 PM, Michael Niedermayer wrote:
> On Tue, Oct 10, 2017 at 06:11:08PM -0700, John Stebbins wrote:
>> When keyframe intervals of dash segments are not perfectly aligned,
>> fragments in the stream can overlap in time. The previous sorting by
>> timestamp causes packets to be read out of decode order and results
>> in decode errors.
>>
>> Insert new "trun" index entries into index_entries in the order that
>> the trun are referenced by the sidx.
>> ---
>>   libavformat/isom.h |  26 +-
>>   libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
>>   2 files changed, 485 insertions(+), 225 deletions(-)
> This still crashes: (not sure i can share the file, tell me if the
> valgrind output is enough)
>
> ==16990== Conditional jump or move depends on uninitialised value(s)
> ==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
> ==16990==    by 0x6C543D: update_frag_index (mov.c:1318)
> ==16990==    by 0x6D4F77: read_tfra (mov.c:6384)
> ==16990==    by 0x6D51EF: mov_read_mfra (mov.c:6432)
> ==16990==    by 0x6C57F7: mov_read_moof (mov.c:1384)
> ==16990==    by 0x6D38E5: mov_read_default (mov.c:5949)
> ==16990==    by 0x6D5395: mov_read_header (mov.c:6473)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] wav header size < 14 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] get_wav_header failed
> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] error reading header
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x6D53C2: mov_read_header (mov.c:6474)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990== Conditional jump or move depends on uninitialised value(s)
> ==16990==    at 0x4C2B58C: free (vg_replace_malloc.c:446)
> ==16990==    by 0x11807BD: av_free (mem.c:209)
> ==16990==    by 0x11807F5: av_freep (mem.c:219)
> ==16990==    by 0x6D4CA9: mov_read_close (mov.c:6303)
> ==16990==    by 0x6D53D1: mov_read_header (mov.c:6475)
> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==
> test.mp4: Invalid data found when processing input
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x4274D1: print_error (cmdutils.c:1058)
> ==16990==    by 0x415430: open_input_file (ffmpeg_opt.c:1062)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> ==16990==    by 0x117DC28: av_vlog (log.c:383)
> ==16990==    by 0x117DBE8: av_log (log.c:375)
> ==16990==    by 0x42B964: term_exit (ffmpeg.c:317)
> ==16990==    by 0x42C64B: ffmpeg_cleanup (ffmpeg.c:618)
> ==16990==    by 0x42483F: exit_program (cmdutils.c:138)
> ==16990==    by 0x415469: open_input_file (ffmpeg_opt.c:1065)
> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>
>
>
>

Something seems off with the valgrind output.  For example, the first 
warning:

==16990== Conditional jump or move depends on uninitialised value(s)
==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)

Line 1227 is:
     if (!frag_index->nb_items ||

frag_index here is &MOVContext.frag_index (a MOVFragmentIndex) and 
*should* be initialized to zero by the mallocz of priv_data_size in 
avformat_open_input (so initial value of frag_index->nb_items should be 
zero).  If for some reason this *isn't* being initialized to zero, I 
could see why the av_freep in mov_read_close would crash freeing 
unallocated data (line 6303).  But I just don't see how this could be 
uninitialized.

It's not really clear from the valgrind output where the crash is though.
Michael Niedermayer Oct. 12, 2017, 5:22 p.m. UTC | #4
On Wed, Oct 11, 2017 at 10:56:41PM -0700, John Stebbins wrote:
> On 10/11/2017 06:25 PM, Michael Niedermayer wrote:
> >On Tue, Oct 10, 2017 at 06:11:08PM -0700, John Stebbins wrote:
> >>When keyframe intervals of dash segments are not perfectly aligned,
> >>fragments in the stream can overlap in time. The previous sorting by
> >>timestamp causes packets to be read out of decode order and results
> >>in decode errors.
> >>
> >>Insert new "trun" index entries into index_entries in the order that
> >>the trun are referenced by the sidx.
> >>---
> >>  libavformat/isom.h |  26 +-
> >>  libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
> >>  2 files changed, 485 insertions(+), 225 deletions(-)
> >This still crashes: (not sure i can share the file, tell me if the
> >valgrind output is enough)
> >
> >==16990== Conditional jump or move depends on uninitialised value(s)
> >==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
> >==16990==    by 0x6C543D: update_frag_index (mov.c:1318)
> >==16990==    by 0x6D4F77: read_tfra (mov.c:6384)
> >==16990==    by 0x6D51EF: mov_read_mfra (mov.c:6432)
> >==16990==    by 0x6C57F7: mov_read_moof (mov.c:1384)
> >==16990==    by 0x6D38E5: mov_read_default (mov.c:5949)
> >==16990==    by 0x6D5395: mov_read_header (mov.c:6473)
> >==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> >==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> >==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> >==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> >==16990==    by 0x43D263: main (ffmpeg.c:4794)
> >==16990==
> >[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] wav header size < 14 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
> >[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)
> >[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] get_wav_header failed
> >[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] error reading header
> >==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> >==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> >==16990==    by 0x117DC28: av_vlog (log.c:383)
> >==16990==    by 0x117DBE8: av_log (log.c:375)
> >==16990==    by 0x6D53C2: mov_read_header (mov.c:6474)
> >==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> >==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> >==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> >==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> >==16990==    by 0x43D263: main (ffmpeg.c:4794)
> >==16990== Conditional jump or move depends on uninitialised value(s)
> >==16990==    at 0x4C2B58C: free (vg_replace_malloc.c:446)
> >==16990==    by 0x11807BD: av_free (mem.c:209)
> >==16990==    by 0x11807F5: av_freep (mem.c:219)
> >==16990==    by 0x6D4CA9: mov_read_close (mov.c:6303)
> >==16990==    by 0x6D53D1: mov_read_header (mov.c:6475)
> >==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
> >==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
> >==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> >==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> >==16990==    by 0x43D263: main (ffmpeg.c:4794)
> >==16990==
> >test.mp4: Invalid data found when processing input
> >==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> >==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> >==16990==    by 0x117DC28: av_vlog (log.c:383)
> >==16990==    by 0x117DBE8: av_log (log.c:375)
> >==16990==    by 0x4274D1: print_error (cmdutils.c:1058)
> >==16990==    by 0x415430: open_input_file (ffmpeg_opt.c:1062)
> >==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> >==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> >==16990==    by 0x43D263: main (ffmpeg.c:4794)
> >==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
> >==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
> >==16990==    by 0x117DC28: av_vlog (log.c:383)
> >==16990==    by 0x117DBE8: av_log (log.c:375)
> >==16990==    by 0x42B964: term_exit (ffmpeg.c:317)
> >==16990==    by 0x42C64B: ffmpeg_cleanup (ffmpeg.c:618)
> >==16990==    by 0x42483F: exit_program (cmdutils.c:138)
> >==16990==    by 0x415469: open_input_file (ffmpeg_opt.c:1065)
> >==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
> >==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
> >==16990==    by 0x43D263: main (ffmpeg.c:4794)
> >
> >
> >
> >
> 
> Something seems off with the valgrind output.  For example, the
> first warning:
> 
> ==16990== Conditional jump or move depends on uninitialised value(s)
> ==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
> 
> Line 1227 is:
>     if (!frag_index->nb_items ||
> 
> frag_index here is &MOVContext.frag_index (a MOVFragmentIndex) and
> *should* be initialized to zero by the mallocz of priv_data_size in
> avformat_open_input (so initial value of frag_index->nb_items should
> be zero).  If for some reason this *isn't* being initialized to
> zero, I could see why the av_freep in mov_read_close would crash
> freeing unallocated data (line 6303).  But I just don't see how this
> could be uninitialized.

The uninitialized value appears to be
frag_index->item[frag_index->nb_items - 1].moof_offset
nb_items is 3 here



> 
> It's not really clear from the valgrind output where the crash is though.

indeed

heres gdb output:

#0  0x00007ffff02ab13c in free () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x000000000117e60e in av_free (ptr=0x72656c646e6148) at libavutil/mem.c:209
#2  0x000000000117e646 in av_freep (arg=0x21711a8) at libavutil/mem.c:219
#3  0x00000000006d385a in mov_read_close (s=0x216ffc0) at libavformat/mov.c:6303
#4  0x00000000006d3f82 in mov_read_header (s=0x216ffc0) at libavformat/mov.c:6475
#5  0x00000000007a9542 in avformat_open_input (ps=0x7fffffffdcc8, filename=0x7fffffffe69a "test.mp4", fmt=0x0, options=0x216fdc8) at libavformat/utils.c:595
#6  0x0000000000414d5b in open_input_file (o=0x7fffffffddc0, filename=0x7fffffffe69a "/home/michael/videos/dale_secu3/read_tfra_heap_corruption_test.mp4") at fftools/ffmpeg_opt.c:1060
#7  0x000000000041ea4a in open_files (l=0x216fd78, inout=0x11b17b7 "input", open_file=0x414527 <open_input_file>) at fftools/ffmpeg_opt.c:3278
#8  0x000000000041ebdc in ffmpeg_parse_options (argc=3, argv=0x7fffffffe3d8) at fftools/ffmpeg_opt.c:3318
#9  0x000000000043cbb4 in main (argc=3, argv=0x7fffffffe3d8) at fftools/ffmpeg.c:4794


[...]
John Stebbins Oct. 12, 2017, 5:59 p.m. UTC | #5
On 10/12/2017 10:22 AM, Michael Niedermayer wrote:
> On Wed, Oct 11, 2017 at 10:56:41PM -0700, John Stebbins wrote:
>> On 10/11/2017 06:25 PM, Michael Niedermayer wrote:
>>> On Tue, Oct 10, 2017 at 06:11:08PM -0700, John Stebbins wrote:
>>>> When keyframe intervals of dash segments are not perfectly aligned,
>>>> fragments in the stream can overlap in time. The previous sorting by
>>>> timestamp causes packets to be read out of decode order and results
>>>> in decode errors.
>>>>
>>>> Insert new "trun" index entries into index_entries in the order that
>>>> the trun are referenced by the sidx.
>>>> ---
>>>>  libavformat/isom.h |  26 +-
>>>>  libavformat/mov.c  | 684 ++++++++++++++++++++++++++++++++++++-----------------
>>>>  2 files changed, 485 insertions(+), 225 deletions(-)
>>> This still crashes: (not sure i can share the file, tell me if the
>>> valgrind output is enough)
>>>
>>> ==16990== Conditional jump or move depends on uninitialised value(s)
>>> ==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
>>> ==16990==    by 0x6C543D: update_frag_index (mov.c:1318)
>>> ==16990==    by 0x6D4F77: read_tfra (mov.c:6384)
>>> ==16990==    by 0x6D51EF: mov_read_mfra (mov.c:6432)
>>> ==16990==    by 0x6C57F7: mov_read_moof (mov.c:1384)
>>> ==16990==    by 0x6D38E5: mov_read_default (mov.c:5949)
>>> ==16990==    by 0x6D5395: mov_read_header (mov.c:6473)
>>> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
>>> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
>>> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
>>> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
>>> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>>> ==16990==
>>> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] wav header size < 14 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
>>> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] If you want to help, upload a sample of this file to ftp://upload.ffmpeg.org/incoming/ and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)
>>> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] get_wav_header failed
>>> [mov,mp4,m4a,3gp,3g2,mj2 @ 0x1187f060] error reading header
>>> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
>>> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
>>> ==16990==    by 0x117DC28: av_vlog (log.c:383)
>>> ==16990==    by 0x117DBE8: av_log (log.c:375)
>>> ==16990==    by 0x6D53C2: mov_read_header (mov.c:6474)
>>> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
>>> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
>>> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
>>> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
>>> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>>> ==16990== Conditional jump or move depends on uninitialised value(s)
>>> ==16990==    at 0x4C2B58C: free (vg_replace_malloc.c:446)
>>> ==16990==    by 0x11807BD: av_free (mem.c:209)
>>> ==16990==    by 0x11807F5: av_freep (mem.c:219)
>>> ==16990==    by 0x6D4CA9: mov_read_close (mov.c:6303)
>>> ==16990==    by 0x6D53D1: mov_read_header (mov.c:6475)
>>> ==16990==    by 0x7AB6FA: avformat_open_input (utils.c:595)
>>> ==16990==    by 0x41540A: open_input_file (ffmpeg_opt.c:1060)
>>> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
>>> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
>>> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>>> ==16990==
>>> test.mp4: Invalid data found when processing input
>>> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
>>> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
>>> ==16990==    by 0x117DC28: av_vlog (log.c:383)
>>> ==16990==    by 0x117DBE8: av_log (log.c:375)
>>> ==16990==    by 0x4274D1: print_error (cmdutils.c:1058)
>>> ==16990==    by 0x415430: open_input_file (ffmpeg_opt.c:1062)
>>> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
>>> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
>>> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>>> ==16990==    at 0x117D019: VALGRIND_PRINTF_BACKTRACE (valgrind.h:4550)
>>> ==16990==    by 0x117DA89: av_log_default_callback (log.c:355)
>>> ==16990==    by 0x117DC28: av_vlog (log.c:383)
>>> ==16990==    by 0x117DBE8: av_log (log.c:375)
>>> ==16990==    by 0x42B964: term_exit (ffmpeg.c:317)
>>> ==16990==    by 0x42C64B: ffmpeg_cleanup (ffmpeg.c:618)
>>> ==16990==    by 0x42483F: exit_program (cmdutils.c:138)
>>> ==16990==    by 0x415469: open_input_file (ffmpeg_opt.c:1065)
>>> ==16990==    by 0x41F0F9: open_files (ffmpeg_opt.c:3278)
>>> ==16990==    by 0x41F28B: ffmpeg_parse_options (ffmpeg_opt.c:3318)
>>> ==16990==    by 0x43D263: main (ffmpeg.c:4794)
>>>
>>>
>>>
>>>
>> Something seems off with the valgrind output.  For example, the
>> first warning:
>>
>> ==16990== Conditional jump or move depends on uninitialised value(s)
>> ==16990==    at 0x6C518B: search_frag_moof_offset (mov.c:1227)
>>
>> Line 1227 is:
>>     if (!frag_index->nb_items ||
>>
>> frag_index here is &MOVContext.frag_index (a MOVFragmentIndex) and
>> *should* be initialized to zero by the mallocz of priv_data_size in
>> avformat_open_input (so initial value of frag_index->nb_items should
>> be zero).  If for some reason this *isn't* being initialized to
>> zero, I could see why the av_freep in mov_read_close would crash
>> freeing unallocated data (line 6303).  But I just don't see how this
>> could be uninitialized.
> The uninitialized value appears to be
> frag_index->item[frag_index->nb_items - 1].moof_offset
> nb_items is 3 here
>
>
>
>> It's not really clear from the valgrind output where the crash is though.
> indeed
>
> heres gdb output:
>
> #0  0x00007ffff02ab13c in free () from /lib/x86_64-linux-gnu/libc.so.6
> #1  0x000000000117e60e in av_free (ptr=0x72656c646e6148) at libavutil/mem.c:209
> #2  0x000000000117e646 in av_freep (arg=0x21711a8) at libavutil/mem.c:219
> #3  0x00000000006d385a in mov_read_close (s=0x216ffc0) at libavformat/mov.c:6303
> #4  0x00000000006d3f82 in mov_read_header (s=0x216ffc0) at libavformat/mov.c:6475
> #5  0x00000000007a9542 in avformat_open_input (ps=0x7fffffffdcc8, filename=0x7fffffffe69a "test.mp4", fmt=0x0, options=0x216fdc8) at libavformat/utils.c:595
> #6  0x0000000000414d5b in open_input_file (o=0x7fffffffddc0, filename=0x7fffffffe69a "/home/michael/videos/dale_secu3/read_tfra_heap_corruption_test.mp4") at fftools/ffmpeg_opt.c:1060
> #7  0x000000000041ea4a in open_files (l=0x216fd78, inout=0x11b17b7 "input", open_file=0x414527 <open_input_file>) at fftools/ffmpeg_opt.c:3278
> #8  0x000000000041ebdc in ffmpeg_parse_options (argc=3, argv=0x7fffffffe3d8) at fftools/ffmpeg_opt.c:3318
> #9  0x000000000043cbb4 in main (argc=3, argv=0x7fffffffe3d8) at fftools/ffmpeg.c:4794
>
>
>

Bah! I think I found it.  A memmove where I neglected to multiply the item count by item size.
diff mbox

Patch

diff --git a/libavformat/isom.h b/libavformat/isom.h
index b9380e9dcc..395d20d25c 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -93,7 +93,6 @@  typedef struct MOVFragment {
     unsigned duration;
     unsigned size;
     unsigned flags;
-    int64_t time;
 } MOVFragment;
 
 typedef struct MOVTrackExt {
@@ -109,17 +108,28 @@  typedef struct MOVSbgp {
     unsigned int index;
 } MOVSbgp;
 
+typedef struct MOVFragmentStreamInfo {
+    int id;
+    int64_t sidx_pts;
+    int64_t first_tfra_pts;
+    int64_t tfdt_dts;
+    int index_entry;
+} MOVFragmentStreamInfo;
+
 typedef struct MOVFragmentIndexItem {
     int64_t moof_offset;
-    int64_t time;
     int headers_read;
+    int current;
+    int nb_stream_info;
+    MOVFragmentStreamInfo * stream_info;
 } MOVFragmentIndexItem;
 
 typedef struct MOVFragmentIndex {
-    unsigned track_id;
-    unsigned item_count;
-    unsigned current_item;
-    MOVFragmentIndexItem *items;
+    int allocated_size;
+    int complete;
+    int current;
+    int nb_items;
+    MOVFragmentIndexItem * item;
 } MOVFragmentIndex;
 
 typedef struct MOVIndexRange {
@@ -250,9 +260,7 @@  typedef struct MOVContext {
     int moov_retry;
     int use_mfra_for;
     int has_looked_for_mfra;
-    MOVFragmentIndex** fragment_index_data;
-    unsigned fragment_index_count;
-    int fragment_index_complete;
+    MOVFragmentIndex frag_index;
     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 899690d920..4fe091f290 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -1168,6 +1168,211 @@  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)
+{
+    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);
+
+    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) {
@@ -1187,6 +1392,7 @@  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);
 }
 
@@ -4118,8 +4324,7 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
     MOVFragment *frag = &c->fragment;
     MOVTrackExt *trex = NULL;
-    MOVFragmentIndex* index = NULL;
-    int flags, track_id, i, found = 0;
+    int flags, track_id, i;
 
     avio_r8(pb); /* version */
     flags = avio_rb24(pb);
@@ -4128,6 +4333,7 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     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];
@@ -4149,35 +4355,8 @@  static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                      avio_rb32(pb) : trex->size;
     frag->flags    = flags & MOV_TFHD_DEFAULT_FLAGS ?
                      avio_rb32(pb) : trex->flags;
-    frag->time     = AV_NOPTS_VALUE;
-    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;
 }
 
@@ -4232,6 +4411,8 @@  static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     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) {
@@ -4249,10 +4430,16 @@  static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     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;
 }
 
@@ -4263,10 +4450,15 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     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) {
@@ -4281,6 +4473,23 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     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);
@@ -4290,18 +4499,99 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         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);
@@ -4309,27 +4599,22 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         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)
@@ -4338,47 +4623,28 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
             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;
@@ -4386,6 +4652,44 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         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;
@@ -4401,14 +4705,12 @@  static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 
 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);
@@ -4450,57 +4752,46 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 
     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++) {
@@ -4511,7 +4802,7 @@  static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
             }
         }
 
-        c->fragment_index_complete = 1;
+        c->frag_index.complete = 1;
     }
 
     return 0;
@@ -5661,9 +5952,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->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;
@@ -6008,12 +6299,10 @@  static int mov_read_close(AVFormatContext *s)
     av_freep(&mov->trex_data);
     av_freep(&mov->bitrates);
 
-    for (i = 0; i < mov->fragment_index_count; i++) {
-        MOVFragmentIndex* index = mov->fragment_index_data[i];
-        av_freep(&index->items);
-        av_freep(&mov->fragment_index_data[i]);
+    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);
@@ -6057,48 +6346,27 @@  static void export_orphan_timecode(AVFormatContext *s)
 
 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;
         }
 
@@ -6109,8 +6377,16 @@  static int read_tfra(MOVContext *mov, AVIOContext *f)
             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++)
@@ -6371,12 +6647,9 @@  static int mov_read_header(AVFormatContext *s)
     }
     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;
 }
@@ -6414,42 +6687,28 @@  static int should_retry(AVIOContext *pb, int error_code) {
     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 ||
@@ -6497,7 +6756,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)) < 0)
+        if ((ret = mov_switch_root(s, mov->next_root_atom, -1)) < 0)
             return ret;
         goto retry;
     }
@@ -6621,25 +6880,18 @@  static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
 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;
 }