diff --git a/extensions/flac/src/androidTest/assets/bear.flac.0.dump b/extensions/flac/src/androidTest/assets/bear.flac.0.dump index b03636f2bb..6908f5cc93 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.0.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8880 + getPosition(0) = [[timeUs=0, position=8880]] numberOfTracks = 1 track 0: format: diff --git a/extensions/flac/src/androidTest/assets/bear.flac.1.dump b/extensions/flac/src/androidTest/assets/bear.flac.1.dump index 4e8388dba8..1414443187 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.1.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8880 + getPosition(0) = [[timeUs=0, position=8880]] numberOfTracks = 1 track 0: format: diff --git a/extensions/flac/src/androidTest/assets/bear.flac.2.dump b/extensions/flac/src/androidTest/assets/bear.flac.2.dump index 0860c36cef..e343241650 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.2.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8880 + getPosition(0) = [[timeUs=0, position=8880]] numberOfTracks = 1 track 0: format: diff --git a/extensions/flac/src/androidTest/assets/bear.flac.3.dump b/extensions/flac/src/androidTest/assets/bear.flac.3.dump index 6f7f72b806..95ab255bd0 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.3.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8880 + getPosition(0) = [[timeUs=0, position=8880]] numberOfTracks = 1 track 0: format: diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index a2f141a712..b630298c6e 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -25,6 +25,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.FlacStreamInfo; import com.google.android.exoplayer2.util.MimeTypes; @@ -104,26 +105,11 @@ public final class FlacExtractor implements Extractor { } metadataParsed = true; - extractorOutput.seekMap(new SeekMap() { - final boolean isSeekable = decoderJni.getSeekPosition(0) != -1; - final long durationUs = streamInfo.durationUs(); - - @Override - public boolean isSeekable() { - return isSeekable; - } - - @Override - public long getPosition(long timeUs) { - return isSeekable ? decoderJni.getSeekPosition(timeUs) : 0; - } - - @Override - public long getDurationUs() { - return durationUs; - } - - }); + boolean isSeekable = decoderJni.getSeekPosition(0) != -1; + extractorOutput.seekMap( + isSeekable + ? new FlacSeekMap(streamInfo.durationUs(), decoderJni) + : new SeekMap.Unseekable(streamInfo.durationUs(), 0)); Format mediaFormat = Format.createAudioSampleFormat( null, @@ -184,4 +170,30 @@ public final class FlacExtractor implements Extractor { } } + private static final class FlacSeekMap implements SeekMap { + + private final long durationUs; + private final FlacDecoderJni decoderJni; + + public FlacSeekMap(long durationUs, FlacDecoderJni decoderJni) { + this.durationUs = durationUs; + this.decoderJni = decoderJni; + } + + @Override + public boolean isSeekable() { + return true; + } + + @Override + public SeekPoints getSeekPoints(long timeUs) { + // TODO: Access the seek table via JNI to return two seek points when appropriate. + return new SeekPoints(new SeekPoint(timeUs, decoderJni.getSeekPosition(timeUs))); + } + + @Override + public long getDurationUs() { + return durationUs; + } + } } diff --git a/library/core/src/androidTest/assets/flv/sample.flv.0.dump b/library/core/src/androidTest/assets/flv/sample.flv.0.dump index b4129ecb88..7a4a74770c 100644 --- a/library/core/src/androidTest/assets/flv/sample.flv.0.dump +++ b/library/core/src/androidTest/assets/flv/sample.flv.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = 1136000 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 2 track 8: format: diff --git a/library/core/src/androidTest/assets/mkv/sample.mkv.0.dump b/library/core/src/androidTest/assets/mkv/sample.mkv.0.dump index 34bad9b82a..0f005ee5a9 100644 --- a/library/core/src/androidTest/assets/mkv/sample.mkv.0.dump +++ b/library/core/src/androidTest/assets/mkv/sample.mkv.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1072000 - getPosition(0) = 5576 + getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: format: diff --git a/library/core/src/androidTest/assets/mkv/sample.mkv.1.dump b/library/core/src/androidTest/assets/mkv/sample.mkv.1.dump index 546c934eff..378f5d7f2a 100644 --- a/library/core/src/androidTest/assets/mkv/sample.mkv.1.dump +++ b/library/core/src/androidTest/assets/mkv/sample.mkv.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1072000 - getPosition(0) = 5576 + getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: format: diff --git a/library/core/src/androidTest/assets/mkv/sample.mkv.2.dump b/library/core/src/androidTest/assets/mkv/sample.mkv.2.dump index ec84908172..80caf24a93 100644 --- a/library/core/src/androidTest/assets/mkv/sample.mkv.2.dump +++ b/library/core/src/androidTest/assets/mkv/sample.mkv.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1072000 - getPosition(0) = 5576 + getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: format: diff --git a/library/core/src/androidTest/assets/mkv/sample.mkv.3.dump b/library/core/src/androidTest/assets/mkv/sample.mkv.3.dump index ac8d9a2c1c..c9672ba9c4 100644 --- a/library/core/src/androidTest/assets/mkv/sample.mkv.3.dump +++ b/library/core/src/androidTest/assets/mkv/sample.mkv.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1072000 - getPosition(0) = 5576 + getPosition(0) = [[timeUs=67000, position=5576]] numberOfTracks = 2 track 1: format: diff --git a/library/core/src/androidTest/assets/mkv/subsample_encrypted_altref.webm.0.dump b/library/core/src/androidTest/assets/mkv/subsample_encrypted_altref.webm.0.dump index f533e14c3f..abc07dc503 100644 --- a/library/core/src/androidTest/assets/mkv/subsample_encrypted_altref.webm.0.dump +++ b/library/core/src/androidTest/assets/mkv/subsample_encrypted_altref.webm.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = 1000 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 1: format: diff --git a/library/core/src/androidTest/assets/mkv/subsample_encrypted_noaltref.webm.0.dump b/library/core/src/androidTest/assets/mkv/subsample_encrypted_noaltref.webm.0.dump index d84c549dea..c43a43b576 100644 --- a/library/core/src/androidTest/assets/mkv/subsample_encrypted_noaltref.webm.0.dump +++ b/library/core/src/androidTest/assets/mkv/subsample_encrypted_noaltref.webm.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = 1000 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 1: format: diff --git a/library/core/src/androidTest/assets/mp3/bear.mp3.0.dump b/library/core/src/androidTest/assets/mp3/bear.mp3.0.dump index b66d263c84..eca3a6687d 100644 --- a/library/core/src/androidTest/assets/mp3/bear.mp3.0.dump +++ b/library/core/src/androidTest/assets/mp3/bear.mp3.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2784000 - getPosition(0) = 201 + getPosition(0) = [[timeUs=0, position=201]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/bear.mp3.1.dump b/library/core/src/androidTest/assets/mp3/bear.mp3.1.dump index a57894e81e..12abf149c4 100644 --- a/library/core/src/androidTest/assets/mp3/bear.mp3.1.dump +++ b/library/core/src/androidTest/assets/mp3/bear.mp3.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2784000 - getPosition(0) = 201 + getPosition(0) = [[timeUs=0, position=201]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/bear.mp3.2.dump b/library/core/src/androidTest/assets/mp3/bear.mp3.2.dump index 3f393e768e..3568616e76 100644 --- a/library/core/src/androidTest/assets/mp3/bear.mp3.2.dump +++ b/library/core/src/androidTest/assets/mp3/bear.mp3.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2784000 - getPosition(0) = 201 + getPosition(0) = [[timeUs=0, position=201]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/bear.mp3.3.dump b/library/core/src/androidTest/assets/mp3/bear.mp3.3.dump index a2387eb887..8a31fe5e7d 100644 --- a/library/core/src/androidTest/assets/mp3/bear.mp3.3.dump +++ b/library/core/src/androidTest/assets/mp3/bear.mp3.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2784000 - getPosition(0) = 201 + getPosition(0) = [[timeUs=0, position=201]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump index 37a04215ee..88601665b0 100644 --- a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump +++ b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 26125 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump index 37a04215ee..88601665b0 100644 --- a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump +++ b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 26125 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump index 37a04215ee..88601665b0 100644 --- a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump +++ b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 26125 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump index 37a04215ee..88601665b0 100644 --- a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump +++ b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 26125 - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump index b75aefd91b..2c0ac67561 100644 --- a/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump +++ b/library/core/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample.mp4.0.dump b/library/core/src/androidTest/assets/mp4/sample.mp4.0.dump index be0a16681c..7cd3486505 100644 --- a/library/core/src/androidTest/assets/mp4/sample.mp4.0.dump +++ b/library/core/src/androidTest/assets/mp4/sample.mp4.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1024000 - getPosition(0) = 48 + getPosition(0) = [[timeUs=0, position=48]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample.mp4.1.dump b/library/core/src/androidTest/assets/mp4/sample.mp4.1.dump index a759e4250a..fcf9402cba 100644 --- a/library/core/src/androidTest/assets/mp4/sample.mp4.1.dump +++ b/library/core/src/androidTest/assets/mp4/sample.mp4.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1024000 - getPosition(0) = 48 + getPosition(0) = [[timeUs=0, position=48]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample.mp4.2.dump b/library/core/src/androidTest/assets/mp4/sample.mp4.2.dump index 59ee715255..5dbb6e1561 100644 --- a/library/core/src/androidTest/assets/mp4/sample.mp4.2.dump +++ b/library/core/src/androidTest/assets/mp4/sample.mp4.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1024000 - getPosition(0) = 48 + getPosition(0) = [[timeUs=0, position=48]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample.mp4.3.dump b/library/core/src/androidTest/assets/mp4/sample.mp4.3.dump index a81a4189d9..bac707446d 100644 --- a/library/core/src/androidTest/assets/mp4/sample.mp4.3.dump +++ b/library/core/src/androidTest/assets/mp4/sample.mp4.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1024000 - getPosition(0) = 48 + getPosition(0) = [[timeUs=0, position=48]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample_fragmented.mp4.0.dump b/library/core/src/androidTest/assets/mp4/sample_fragmented.mp4.0.dump index 95f6528fd6..736e57693c 100644 --- a/library/core/src/androidTest/assets/mp4/sample_fragmented.mp4.0.dump +++ b/library/core/src/androidTest/assets/mp4/sample_fragmented.mp4.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 1828 + getPosition(0) = [[timeUs=0, position=1828]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/mp4/sample_fragmented_sei.mp4.0.dump b/library/core/src/androidTest/assets/mp4/sample_fragmented_sei.mp4.0.dump index ebd33133e2..8186a2b9ce 100644 --- a/library/core/src/androidTest/assets/mp4/sample_fragmented_sei.mp4.0.dump +++ b/library/core/src/androidTest/assets/mp4/sample_fragmented_sei.mp4.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 1828 + getPosition(0) = [[timeUs=0, position=1828]] numberOfTracks = 3 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear.opus.0.dump b/library/core/src/androidTest/assets/ogg/bear.opus.0.dump index 8033ce8089..4d09067f3b 100644 --- a/library/core/src/androidTest/assets/ogg/bear.opus.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear.opus.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2747500 - getPosition(0) = 125 + getPosition(0) = [[timeUs=0, position=125]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear.opus.1.dump b/library/core/src/androidTest/assets/ogg/bear.opus.1.dump index f9aceae68a..821351e989 100644 --- a/library/core/src/androidTest/assets/ogg/bear.opus.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear.opus.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2747500 - getPosition(0) = 125 + getPosition(0) = [[timeUs=0, position=125]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear.opus.2.dump b/library/core/src/androidTest/assets/ogg/bear.opus.2.dump index f2f07f3e2f..3aea1e8d74 100644 --- a/library/core/src/androidTest/assets/ogg/bear.opus.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear.opus.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2747500 - getPosition(0) = 125 + getPosition(0) = [[timeUs=0, position=125]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear.opus.3.dump b/library/core/src/androidTest/assets/ogg/bear.opus.3.dump index 905055797c..b49af29f2c 100644 --- a/library/core/src/androidTest/assets/ogg/bear.opus.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear.opus.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2747500 - getPosition(0) = 125 + getPosition(0) = [[timeUs=0, position=125]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear.opus.unklen.dump b/library/core/src/androidTest/assets/ogg/bear.opus.unklen.dump index cd29da3e27..b2d5a9f3d2 100644 --- a/library/core/src/androidTest/assets/ogg/bear.opus.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear.opus.unklen.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump index 5ba8cc29ae..572d1da891 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8457 + getPosition(0) = [[timeUs=0, position=8457]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump index f698fd28cf..d53f257fd2 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8457 + getPosition(0) = [[timeUs=0, position=8457]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump index 8d803d0bac..cdfd6efab8 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8457 + getPosition(0) = [[timeUs=0, position=8457]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump index 09f6267270..9b029d3301 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8457 + getPosition(0) = [[timeUs=0, position=8457]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump index 5ba8cc29ae..572d1da891 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8457 + getPosition(0) = [[timeUs=0, position=8457]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump index 73e537f8c8..1c02c1bbef 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8407 + getPosition(0) = [[timeUs=0, position=8407]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump index 3b7dc3fd1e..81d79b8674 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8407 + getPosition(0) = [[timeUs=0, position=8407]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump index b6a6741fcc..f8b00bcb3a 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8407 + getPosition(0) = [[timeUs=0, position=8407]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump index 738002f7ef..b020618488 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 8407 + getPosition(0) = [[timeUs=0, position=8407]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump index a237fd0dfc..bf135434f4 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump index 8e2c5125a3..860e8a3b5b 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 3995 + getPosition(0) = [[timeUs=0, position=3995]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump index aa25303ac3..11afeb9665 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 3995 + getPosition(0) = [[timeUs=0, position=3995]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump index 58969058fa..f2f97ebcfa 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 3995 + getPosition(0) = [[timeUs=0, position=3995]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump index 4c789a8431..5d5f284cf2 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 2741000 - getPosition(0) = 3995 + getPosition(0) = [[timeUs=0, position=3995]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump index 2f163572bf..ee1176773e 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/rawcc/sample.rawcc.0.dump b/library/core/src/androidTest/assets/rawcc/sample.rawcc.0.dump index 3e84813162..d430d1d8d4 100644 --- a/library/core/src/androidTest/assets/rawcc/sample.rawcc.0.dump +++ b/library/core/src/androidTest/assets/rawcc/sample.rawcc.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ts/sample.ac3.0.dump b/library/core/src/androidTest/assets/ts/sample.ac3.0.dump index 1b6c77efb6..bedffcf198 100644 --- a/library/core/src/androidTest/assets/ts/sample.ac3.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ac3.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 1 track 0: format: diff --git a/library/core/src/androidTest/assets/ts/sample.adts.0.dump b/library/core/src/androidTest/assets/ts/sample.adts.0.dump index 0a7427d3f1..a97cf860d1 100644 --- a/library/core/src/androidTest/assets/ts/sample.adts.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.adts.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 2 track 0: format: diff --git a/library/core/src/androidTest/assets/ts/sample.ps.0.dump b/library/core/src/androidTest/assets/ts/sample.ps.0.dump index 98f3c6a85a..41db704d56 100644 --- a/library/core/src/androidTest/assets/ts/sample.ps.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ps.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 2 track 192: format: diff --git a/library/core/src/androidTest/assets/ts/sample.ts.0.dump b/library/core/src/androidTest/assets/ts/sample.ts.0.dump index 91e48b1722..e900b94673 100644 --- a/library/core/src/androidTest/assets/ts/sample.ts.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ts.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = 0 + getPosition(0) = [[timeUs=0, position=0]] numberOfTracks = 2 track 256: format: diff --git a/library/core/src/androidTest/assets/wav/sample.wav.0.dump b/library/core/src/androidTest/assets/wav/sample.wav.0.dump index 9ad01284b7..5d0f4d77f0 100644 --- a/library/core/src/androidTest/assets/wav/sample.wav.0.dump +++ b/library/core/src/androidTest/assets/wav/sample.wav.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1000000 - getPosition(0) = 78 + getPosition(0) = [[timeUs=0, position=78]] numberOfTracks = 1 track 0: format: @@ -27,15 +27,15 @@ track 0: initializationData: sample count = 3 sample 0: - time = 884 + time = 0 flags = 1 data = length 32768, hash 9A8CEEBA sample 1: - time = 372403 + time = 371519 flags = 1 data = length 32768, hash C1717317 sample 2: - time = 743922 + time = 743038 flags = 1 data = length 22664, hash 819F5F62 tracksEnded = true diff --git a/library/core/src/androidTest/assets/wav/sample.wav.1.dump b/library/core/src/androidTest/assets/wav/sample.wav.1.dump index ca98cc5cf5..e59239bff8 100644 --- a/library/core/src/androidTest/assets/wav/sample.wav.1.dump +++ b/library/core/src/androidTest/assets/wav/sample.wav.1.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1000000 - getPosition(0) = 78 + getPosition(0) = [[timeUs=0, position=78]] numberOfTracks = 1 track 0: format: @@ -27,11 +27,11 @@ track 0: initializationData: sample count = 2 sample 0: - time = 334195 + time = 333310 flags = 1 data = length 32768, hash 42D6E860 sample 1: - time = 705714 + time = 704829 flags = 1 data = length 26034, hash 62692C38 tracksEnded = true diff --git a/library/core/src/androidTest/assets/wav/sample.wav.2.dump b/library/core/src/androidTest/assets/wav/sample.wav.2.dump index da212b220a..c80a260385 100644 --- a/library/core/src/androidTest/assets/wav/sample.wav.2.dump +++ b/library/core/src/androidTest/assets/wav/sample.wav.2.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1000000 - getPosition(0) = 78 + getPosition(0) = [[timeUs=0, position=78]] numberOfTracks = 1 track 0: format: @@ -27,7 +27,7 @@ track 0: initializationData: sample count = 1 sample 0: - time = 667528 + time = 666643 flags = 1 data = length 29402, hash 4241604E tracksEnded = true diff --git a/library/core/src/androidTest/assets/wav/sample.wav.3.dump b/library/core/src/androidTest/assets/wav/sample.wav.3.dump index 3275ba6ef5..9f25028923 100644 --- a/library/core/src/androidTest/assets/wav/sample.wav.3.dump +++ b/library/core/src/androidTest/assets/wav/sample.wav.3.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = true duration = 1000000 - getPosition(0) = 78 + getPosition(0) = [[timeUs=0, position=78]] numberOfTracks = 1 track 0: format: @@ -27,7 +27,7 @@ track 0: initializationData: sample count = 1 sample 0: - time = 1000861 + time = 999977 flags = 1 data = length 2, hash 116 tracksEnded = true diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ChunkIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ChunkIndex.java index baa5589f4b..d0c66f930a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ChunkIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ChunkIndex.java @@ -91,8 +91,15 @@ public final class ChunkIndex implements SeekMap { } @Override - public long getPosition(long timeUs) { - return offsets[getChunkIndex(timeUs)]; + public SeekPoints getSeekPoints(long timeUs) { + int chunkIndex = getChunkIndex(timeUs); + SeekPoint seekPoint = new SeekPoint(timesUs[chunkIndex], offsets[chunkIndex]); + if (seekPoint.timeUs >= timeUs || chunkIndex == length - 1) { + return new SeekPoints(seekPoint); + } else { + SeekPoint nextSeekPoint = new SeekPoint(timesUs[chunkIndex + 1], offsets[chunkIndex + 1]); + return new SeekPoints(seekPoint, nextSeekPoint); + } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekMap.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekMap.java index 964c43a45a..aa718c23e5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekMap.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekMap.java @@ -16,36 +16,36 @@ package com.google.android.exoplayer2.extractor; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.Assertions; /** * Maps seek positions (in microseconds) to corresponding positions (byte offsets) in the stream. */ public interface SeekMap { - /** - * A {@link SeekMap} that does not support seeking. - */ + /** A {@link SeekMap} that does not support seeking. */ final class Unseekable implements SeekMap { private final long durationUs; - private final long startPosition; + private final SeekPoints startSeekPoints; /** - * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if - * the duration is unknown. + * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the + * duration is unknown. */ public Unseekable(long durationUs) { this(durationUs, 0); } /** - * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if - * the duration is unknown. + * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the + * duration is unknown. * @param startPosition The position (byte offset) of the start of the media. */ public Unseekable(long durationUs, long startPosition) { this.durationUs = durationUs; - this.startPosition = startPosition; + startSeekPoints = + new SeekPoints(startPosition == 0 ? SeekPoint.START : new SeekPoint(0, startPosition)); } @Override @@ -59,17 +59,58 @@ public interface SeekMap { } @Override - public long getPosition(long timeUs) { - return startPosition; + public SeekPoints getSeekPoints(long timeUs) { + return startSeekPoints; + } + } + + /** Contains one or two {@link SeekPoint}s. */ + final class SeekPoints { + + /** The first seek point. */ + public final SeekPoint first; + /** The second seek point, or {@link #first} if there's only one seek point. */ + public final SeekPoint second; + + /** @param point The single seek point. */ + public SeekPoints(SeekPoint point) { + this(point, point); } + /** + * @param first The first seek point. + * @param second The second seek point. + */ + public SeekPoints(SeekPoint first, SeekPoint second) { + this.first = Assertions.checkNotNull(first); + this.second = Assertions.checkNotNull(second); + } + + @Override + public String toString() { + return "[" + first + (first.equals(second) ? "" : (", " + second)) + "]"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SeekPoints other = (SeekPoints) obj; + return first.equals(other.first) && second.equals(other.second); + } + + @Override + public int hashCode() { + return (31 * first.hashCode()) + second.hashCode(); + } } /** * Returns whether seeking is supported. - *
- * If seeking is not supported then the only valid seek position is the start of the file, and so - * {@link #getPosition(long)} will return 0 for all input values. * * @return Whether seeking is supported. */ @@ -78,20 +119,22 @@ public interface SeekMap { /** * Returns the duration of the stream in microseconds. * - * @return The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the - * duration is unknown. + * @return The duration of the stream in microseconds, or {@link C#TIME_UNSET} if the duration is + * unknown. */ long getDurationUs(); /** - * Maps a seek position in microseconds to a corresponding position (byte offset) in the stream - * from which data can be provided to the extractor. + * Obtains seek points for the specified seek time in microseconds. The returned {@link + * SeekPoints} will contain one or two distinct seek points. * - * @param timeUs A seek position in microseconds. - * @return The corresponding position (byte offset) in the stream from which data can be provided - * to the extractor. If {@link #isSeekable()} returns false then the returned value will be - * independent of {@code timeUs}, and will indicate the start of the media in the stream. + *
Two seek points [A, B] are returned in the case that seeking can only be performed to
+ * discrete points in time, there does not exist a seek point at exactly the requested time, and
+ * there exist seek points on both sides of it. In this case A and B are the closest seek points
+ * before and after the requested time. A single seek point is returned in all other cases.
+ *
+ * @param timeUs A seek time in microseconds.
+ * @return The corresponding seek points.
*/
- long getPosition(long timeUs);
-
+ SeekPoints getSeekPoints(long timeUs);
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekPoint.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekPoint.java
new file mode 100644
index 0000000000..93cfbd9200
--- /dev/null
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekPoint.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2.extractor;
+
+/** Defines a seek point in a media stream. */
+public final class SeekPoint {
+
+ /** A {@link SeekPoint} whose time and byte offset are both set to 0. */
+ public static final SeekPoint START = new SeekPoint(0, 0);
+
+ /** The time of the seek point, in microseconds. */
+ public final long timeUs;
+
+ /** The byte offset of the seek point. */
+ public final long position;
+
+ /**
+ * @param timeUs The time of the seek point, in microseconds.
+ * @param position The byte offset of the seek point.
+ */
+ public SeekPoint(long timeUs, long position) {
+ this.timeUs = timeUs;
+ this.position = position;
+ }
+
+ @Override
+ public String toString() {
+ return "[timeUs=" + timeUs + ", position=" + position + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ SeekPoint other = (SeekPoint) obj;
+ return timeUs == other.timeUs && position == other.position;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) timeUs;
+ result = 31 * result + (int) position;
+ return result;
+ }
+}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java
index 442e62deca..d358c0cae1 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.mp3;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
+import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.util.Util;
/**
@@ -57,16 +58,25 @@ import com.google.android.exoplayer2.util.Util;
}
@Override
- public long getPosition(long timeUs) {
+ public SeekPoints getSeekPoints(long timeUs) {
if (dataSize == C.LENGTH_UNSET) {
- return firstFramePosition;
+ return new SeekPoints(new SeekPoint(0, firstFramePosition));
}
long positionOffset = (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE);
// Constrain to nearest preceding frame offset.
positionOffset = (positionOffset / frameSize) * frameSize;
positionOffset = Util.constrainValue(positionOffset, 0, dataSize - frameSize);
- // Add data start position.
- return firstFramePosition + positionOffset;
+ long seekPosition = firstFramePosition + positionOffset;
+ long seekTimeUs = getTimeUs(seekPosition);
+ SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition);
+ if (seekTimeUs >= timeUs || positionOffset == dataSize - frameSize) {
+ return new SeekPoints(seekPoint);
+ } else {
+ long secondSeekPosition = seekPosition + frameSize;
+ long secondSeekTimeUs = getTimeUs(secondSeekPosition);
+ SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition);
+ return new SeekPoints(seekPoint, secondSeekPoint);
+ }
}
@Override
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/VbriSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/VbriSeeker.java
index cc631d9f7e..f918b5c43d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/VbriSeeker.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/VbriSeeker.java
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.mp3;
import android.util.Log;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
+import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
@@ -106,8 +107,15 @@ import com.google.android.exoplayer2.util.Util;
}
@Override
- public long getPosition(long timeUs) {
- return positions[Util.binarySearchFloor(timesUs, timeUs, true, true)];
+ public SeekPoints getSeekPoints(long timeUs) {
+ int tableIndex = Util.binarySearchFloor(timesUs, timeUs, true, true);
+ SeekPoint seekPoint = new SeekPoint(timesUs[tableIndex], positions[tableIndex]);
+ if (seekPoint.timeUs >= timeUs || tableIndex == timesUs.length - 1) {
+ return new SeekPoints(seekPoint);
+ } else {
+ SeekPoint nextSeekPoint = new SeekPoint(timesUs[tableIndex + 1], positions[tableIndex + 1]);
+ return new SeekPoints(seekPoint, nextSeekPoint);
+ }
}
@Override
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/XingSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/XingSeeker.java
index e532249a64..a3bd5a2da2 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/XingSeeker.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/XingSeeker.java
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.mp3;
import android.util.Log;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.MpegAudioHeader;
+import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
@@ -107,10 +108,11 @@ import com.google.android.exoplayer2.util.Util;
}
@Override
- public long getPosition(long timeUs) {
+ public SeekPoints getSeekPoints(long timeUs) {
if (!isSeekable()) {
- return dataStartPosition + xingFrameSize;
+ return new SeekPoints(new SeekPoint(0, dataStartPosition + xingFrameSize));
}
+ timeUs = Util.constrainValue(timeUs, 0, durationUs);
double percent = (timeUs * 100d) / durationUs;
double scaledPosition;
if (percent <= 0) {
@@ -129,7 +131,7 @@ import com.google.android.exoplayer2.util.Util;
long positionOffset = Math.round((scaledPosition / 256) * dataSize);
// Ensure returned positions skip the frame containing the XING header.
positionOffset = Util.constrainValue(positionOffset, xingFrameSize, dataSize - 1);
- return dataStartPosition + positionOffset;
+ return new SeekPoints(new SeekPoint(timeUs, dataStartPosition + positionOffset));
}
@Override
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
index f2412bf4ba..50fc0aec80 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
@@ -26,6 +26,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
+import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom;
import com.google.android.exoplayer2.metadata.Metadata;
@@ -108,6 +109,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
// Extractor outputs.
private ExtractorOutput extractorOutput;
private Mp4Track[] tracks;
+ private int firstVideoTrackIndex;
private long durationUs;
private boolean isQuickTime;
@@ -196,21 +198,56 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
@Override
- public long getPosition(long timeUs) {
- long earliestSamplePosition = Long.MAX_VALUE;
- for (Mp4Track track : tracks) {
- TrackSampleTable sampleTable = track.sampleTable;
- int sampleIndex = sampleTable.getIndexOfEarlierOrEqualSynchronizationSample(timeUs);
+ public SeekPoints getSeekPoints(long timeUs) {
+ if (tracks.length == 0) {
+ return new SeekPoints(SeekPoint.START);
+ }
+
+ long firstTimeUs;
+ long firstOffset;
+ long secondTimeUs = C.TIME_UNSET;
+ long secondOffset = C.POSITION_UNSET;
+
+ // If we have a video track, use it to establish one or two seek points.
+ if (firstVideoTrackIndex != C.INDEX_UNSET) {
+ TrackSampleTable sampleTable = tracks[firstVideoTrackIndex].sampleTable;
+ int sampleIndex = getSynchronizationSampleIndex(sampleTable, timeUs);
if (sampleIndex == C.INDEX_UNSET) {
- // Handle the case where the requested time is before the first synchronization sample.
- sampleIndex = sampleTable.getIndexOfLaterOrEqualSynchronizationSample(timeUs);
+ return new SeekPoints(SeekPoint.START);
}
- long offset = sampleTable.offsets[sampleIndex];
- if (offset < earliestSamplePosition) {
- earliestSamplePosition = offset;
+ long sampleTimeUs = sampleTable.timestampsUs[sampleIndex];
+ firstTimeUs = sampleTimeUs;
+ firstOffset = sampleTable.offsets[sampleIndex];
+ if (sampleTimeUs < timeUs && sampleIndex < sampleTable.sampleCount - 1) {
+ int secondSampleIndex = sampleTable.getIndexOfLaterOrEqualSynchronizationSample(timeUs);
+ if (secondSampleIndex != C.INDEX_UNSET && secondSampleIndex != sampleIndex) {
+ secondTimeUs = sampleTable.timestampsUs[secondSampleIndex];
+ secondOffset = sampleTable.offsets[secondSampleIndex];
+ }
+ }
+ } else {
+ firstTimeUs = timeUs;
+ firstOffset = Long.MAX_VALUE;
+ }
+
+ // Take into account other tracks.
+ for (int i = 0; i < tracks.length; i++) {
+ if (i != firstVideoTrackIndex) {
+ TrackSampleTable sampleTable = tracks[i].sampleTable;
+ firstOffset = maybeAdjustSeekOffset(sampleTable, firstTimeUs, firstOffset);
+ if (secondTimeUs != C.TIME_UNSET) {
+ secondOffset = maybeAdjustSeekOffset(sampleTable, secondTimeUs, secondOffset);
+ }
}
}
- return earliestSamplePosition;
+
+ SeekPoint firstSeekPoint = new SeekPoint(firstTimeUs, firstOffset);
+ if (secondTimeUs == C.TIME_UNSET) {
+ return new SeekPoints(firstSeekPoint);
+ } else {
+ SeekPoint secondSeekPoint = new SeekPoint(secondTimeUs, secondOffset);
+ return new SeekPoints(firstSeekPoint, secondSeekPoint);
+ }
}
// Private methods.
@@ -326,31 +363,11 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
}
- /**
- * Process an ftyp atom to determine whether the media is QuickTime.
- *
- * @param atomData The ftyp atom data.
- * @return Whether the media is QuickTime.
- */
- private static boolean processFtypAtom(ParsableByteArray atomData) {
- atomData.setPosition(Atom.HEADER_SIZE);
- int majorBrand = atomData.readInt();
- if (majorBrand == BRAND_QUICKTIME) {
- return true;
- }
- atomData.skipBytes(4); // minor_version
- while (atomData.bytesLeft() > 0) {
- if (atomData.readInt() == BRAND_QUICKTIME) {
- return true;
- }
- }
- return false;
- }
-
/**
* Updates the stored track metadata to reflect the contents of the specified moov atom.
*/
private void processMoovAtom(ContainerAtom moov) throws ParserException {
+ int firstVideoTrackIndex = C.INDEX_UNSET;
long durationUs = C.TIME_UNSET;
List