@ -19,6 +19,7 @@
# include <ctype.h>
# include <inttypes.h>
# include <algorithm>
# include <memory>
# include <stdint.h>
# include <stdlib.h>
@ -149,9 +150,13 @@ private:
bool mIsAudio ;
sp < ItemTable > mItemTable ;
// Start offset from composition time to presentation time.
// Support shift only for video tracks through mElstShiftStartTicks for now.
/* Shift start offset (move to earlier time) when media_time > 0,
* in media time scale .
*/
uint64_t mElstShiftStartTicks ;
/* Initial start offset (move to later time), empty edit list entry
* in media time scale .
*/
uint64_t mElstInitialEmptyEditTicks ;
size_t parseNALSize ( const uint8_t * data ) const ;
@ -1215,7 +1220,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
off64_t entriesoffset = data_offset + 8 ;
uint64_t segment_duration ;
int64_t media_time ;
uint64_t empty_edit_ticks = 0 ;
bool empty_edit_present = false ;
for ( int i = 0 ; i < entry_count ; + + i ) {
switch ( version ) {
@ -1247,45 +1251,37 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
}
// Empty edit entry would have to be first entry.
if ( media_time = = - 1 & & i = = 0 ) {
int64_t durationUs ;
if ( AMediaFormat_getInt64 ( mFileMetaData , AMEDIAFORMAT_KEY_DURATION ,
& durationUs ) ) {
empty_edit_ticks = segment_duration ;
ALOGV ( " initial empty edit ticks: % " PRIu64 , empty_edit_ticks ) ;
empty_edit_present = true ;
}
}
// Process second entry only when the first entry was an empty edit entry.
if ( empty_edit_present & & i = = 1 ) {
int64_t durationUs ;
if ( AMediaFormat_getInt64 ( mLastTrack - > meta , AMEDIAFORMAT_KEY_DURATION ,
& durationUs ) & &
mHeaderTimescale ! = 0 ) {
// Support only segment_duration<=track_duration and media_time==0 case.
uint64_t segmentDurationUs =
segment_duration * 1000000 / mHeaderTimescale ;
if ( segmentDurationUs = = 0 | | segmentDurationUs > durationUs | |
media_time ! = 0 ) {
ALOGW ( " for now, unsupported second entry in empty edit list " ) ;
}
}
empty_edit_present = true ;
ALOGV ( " initial empty edit ticks: % " PRIu64 , segment_duration ) ;
/* In movie header timescale, and needs to be converted to media timescale
* after we get that from a track ' s ' mdhd ' atom ,
* which at times come after ' elst ' .
*/
mLastTrack - > elst_initial_empty_edit_ticks = segment_duration ;
} else if ( media_time > = 0 & & i = = 0 ) {
ALOGV ( " first edit list entry " ) ;
mLastTrack - > elst_media_time = media_time ;
mLastTrack - > elst_segment_duration = segment_duration ;
ALOGV ( " segment_duration: % " PRIu64 " media_time: % " PRId64 ,
segment_duration , media_time ) ;
// media_time is in media timescale as are STTS/CTTS entries.
mLastTrack - > elst_shift_start_ticks = media_time ;
} else if ( empty_edit_present & & i = = 1 ) {
// Process second entry only when the first entry was an empty edit entry.
ALOGV ( " second edit list entry " ) ;
mLastTrack - > elst_media_time = media_time ;
mLastTrack - > elst_segment_duration = segment_duration ;
ALOGV ( " segment_duration: % " PRIu64 " media_time: % " PRId64 ,
segment_duration , media_time ) ;
mLastTrack - > elst_shift_start_ticks = media_time ;
} else {
ALOGW ( " for now, unsupported entry in edit list % " PRIu32 , entry_count ) ;
}
}
// save these for later, because the elst atom might precede
// the atoms that actually gives us the duration and sample rate
// needed to calculate the padding and delay values
mLastTrack - > elst_needs_processing = true ;
if ( empty_edit_present ) {
/* In movie header timescale, and needs to be converted to media timescale once
* we get that from a track ' s ' mdhd ' atom , which at times come after ' elst ' .
*/
mLastTrack - > elst_initial_empty_edit_ticks = empty_edit_ticks ;
} else {
mLastTrack - > elst_media_time = media_time ;
mLastTrack - > elst_segment_duration = segment_duration ;
ALOGV ( " segment_duration: % " PRIu64 " media_time: % " PRId64 , segment_duration ,
media_time ) ;
}
}
break ;
}
@ -4324,9 +4320,9 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
}
}
// media_time is in media timescale as are STTS/CTTS entries.
track - > elst_shift_start_ticks = track - > elst_media_time ;
ALOGV ( " track->elst_shift_start_ticks :% " PRIu64 , track - > elst_shift_start_ticks ) ;
uint64_t elst_initial_empty_edit_ticks = 0 ;
if ( mHeaderTimescale ! = 0 ) {
// Convert empty_edit_ticks from movie timescale to media timescale.
uint64_t elst_initial_empty_edit_ticks_mul = 0 , elst_initial_empty_edit_ticks_add = 0 ;
@ -4337,15 +4333,15 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
ALOGE ( " track->elst_initial_empty_edit_ticks overflow " ) ;
return nullptr ;
}
track - > elst_initial_empty_edit_ticks = elst_initial_empty_edit_ticks_add / mHeaderTimescale ;
ALOGV ( " track->elst_initial_empty_edit_ticks :% " PRIu64 ,
track - > elst_initial_empty_edit_ticks ) ;
elst_initial_empty_edit_ticks = elst_initial_empty_edit_ticks_add / mHeaderTimescale ;
}
ALOGV ( " elst_initial_empty_edit_ticks in MediaTimeScale :% " PRIu64 ,
elst_initial_empty_edit_ticks ) ;
MPEG4Source * source =
new MPEG4Source ( track - > meta , mDataSource , track - > timescale , track - > sampleTable ,
mSidxEntries , trex , mMoofOffset , itemTable ,
track - > elst_shift_start_ticks , track- > elst_initial_empty_edit_ticks) ;
track - > elst_shift_start_ticks , elst_initial_empty_edit_ticks) ;
if ( source - > init ( ) ! = OK ) {
delete source ;
return NULL ;
@ -5885,9 +5881,22 @@ media_status_t MPEG4Source::read(
break ;
}
if ( mode ! = ReadOptions : : SEEK_FRAME_INDEX ) {
seekTimeUs + = ( ( long double ) mElstShiftStartTicks * 1000000 ) / mTimescale ;
ALOGV ( " shifted seekTimeUs :% " PRId64 " , mElstShiftStartTicks:% " PRIu64 , seekTimeUs ,
mElstShiftStartTicks ) ;
int64_t elstInitialEmptyEditUs = 0 , elstShiftStartUs = 0 ;
if ( mElstInitialEmptyEditTicks > 0 ) {
elstInitialEmptyEditUs = ( ( long double ) mElstInitialEmptyEditTicks * 1000000 ) /
mTimescale ;
/* Sample's composition time from ctts/stts entries are non-negative(>=0).
* Hence , lower bound on seekTimeUs is 0.
*/
seekTimeUs = std : : max ( seekTimeUs - elstInitialEmptyEditUs , ( int64_t ) 0 ) ;
}
if ( mElstShiftStartTicks > 0 ) {
elstShiftStartUs = ( ( long double ) mElstShiftStartTicks * 1000000 ) / mTimescale ;
seekTimeUs + = elstShiftStartUs ;
}
ALOGV ( " shifted seekTimeUs:% " PRId64 " , elstInitialEmptyEditUs:% " PRIu64
" , elstShiftStartUs:% " PRIu64 , seekTimeUs , elstInitialEmptyEditUs ,
elstShiftStartUs ) ;
}
uint32_t sampleIndex ;
@ -5933,7 +5942,12 @@ media_status_t MPEG4Source::read(
if ( mode = = ReadOptions : : SEEK_CLOSEST
| | mode = = ReadOptions : : SEEK_FRAME_INDEX ) {
sampleTime - = mElstShiftStartTicks ;
if ( mElstInitialEmptyEditTicks > 0 ) {
sampleTime + = mElstInitialEmptyEditTicks ;
}
if ( mElstShiftStartTicks > 0 ) {
sampleTime - = mElstShiftStartTicks ;
}
targetSampleTimeUs = ( sampleTime * 1000000ll ) / mTimescale ;
}
@ -5976,12 +5990,12 @@ media_status_t MPEG4Source::read(
if ( err = = OK ) {
if ( mElstInitialEmptyEditTicks > 0 ) {
cts + = mElstInitialEmptyEditTicks ;
} else {
}
if ( mElstShiftStartTicks > 0 ) {
// cts can be negative. for example, initial audio samples for gapless playback.
cts - = ( int64_t ) mElstShiftStartTicks ;
}
}
} else {
err = mItemTable - > getImageOffsetAndSize (
options & & options - > getSeekTo ( & seekTimeUs , & mode ) ?
@ -6261,10 +6275,22 @@ media_status_t MPEG4Source::fragmentedRead(
int64_t seekTimeUs ;
ReadOptions : : SeekMode mode ;
if ( options & & options - > getSeekTo ( & seekTimeUs , & mode ) ) {
seekTimeUs + = ( ( long double ) mElstShiftStartTicks * 1000000 ) / mTimescale ;
ALOGV ( " shifted seekTimeUs :% " PRId64 " , mElstShiftStartTicks:% " PRIu64 , seekTimeUs ,
mElstShiftStartTicks ) ;
int64_t elstInitialEmptyEditUs = 0 , elstShiftStartUs = 0 ;
if ( mElstInitialEmptyEditTicks > 0 ) {
elstInitialEmptyEditUs = ( ( long double ) mElstInitialEmptyEditTicks * 1000000 ) /
mTimescale ;
/* Sample's composition time from ctts/stts entries are non-negative(>=0).
* Hence , lower bound on seekTimeUs is 0.
*/
seekTimeUs = std : : max ( seekTimeUs - elstInitialEmptyEditUs , ( int64_t ) 0 ) ;
}
if ( mElstShiftStartTicks > 0 ) {
elstShiftStartUs = ( ( long double ) mElstShiftStartTicks * 1000000 ) / mTimescale ;
seekTimeUs + = elstShiftStartUs ;
}
ALOGV ( " shifted seekTimeUs:% " PRId64 " , elstInitialEmptyEditUs:% " PRIu64
" , elstShiftStartUs:% " PRIu64 , seekTimeUs , elstInitialEmptyEditUs ,
elstShiftStartUs ) ;
int numSidxEntries = mSegments . size ( ) ;
if ( numSidxEntries ! = 0 ) {
@ -6355,7 +6381,8 @@ media_status_t MPEG4Source::fragmentedRead(
if ( mElstInitialEmptyEditTicks > 0 ) {
cts + = mElstInitialEmptyEditTicks ;
} else {
}
if ( mElstShiftStartTicks > 0 ) {
// cts can be negative. for example, initial audio samples for gapless playback.
cts - = ( int64_t ) mElstShiftStartTicks ;
}