@ -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 ) * sizeof ( * c - > frag_index . item ) ) ;
item = & c - > frag_index . item [ index ] ;
item - > headers_read = 0 ;
item - > current = 0 ;
item - > nb_stream_info = c - > fc - > nb_streams ;
item - > moof_offset = offset ;
item - > stream_info = frag_stream_info ;
c - > frag_index . nb_items + + ;
return index ;
}
static void fix_frag_index_entries ( MOVFragmentIndex * frag_index , int index ,
int id , int entries )
{
int i ;
MOVFragmentStreamInfo * frag_stream_info ;
if ( index < 0 )
return ;
for ( i = index ; i < frag_index - > nb_items ; i + + ) {
frag_stream_info = get_frag_stream_info ( frag_index , i , id ) ;
if ( frag_stream_info & & frag_stream_info - > index_entry > = 0 )
frag_stream_info - > index_entry + = entries ;
}
}
static int mov_read_moof ( MOVContext * c , AVIOContext * pb , MOVAtom atom )
{
if ( ! c - > has_looked_for_mfra & & c - > use_mfra_for > 0 ) {
@ -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 ;
@ -5680,9 +5971,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 ;
@ -6027,12 +6318,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 ) ;
@ -6076,48 +6365,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 ;
}
@ -6128,8 +6396,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 + + )
@ -6390,12 +6666,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 ;
}
@ -6433,42 +6706,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 | |
@ -6516,7 +6775,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 ;
}
@ -6640,25 +6899,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 ;
}