From fbfa43f5a3ef11f5da115c439092b521564fb4fa Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 4 Dec 2017 08:12:35 -0800 Subject: [PATCH] Enhance SeekMaps to return SeekPoints Issue: #2882 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177814974 --- .../src/androidTest/assets/bear.flac.0.dump | 2 +- .../src/androidTest/assets/bear.flac.1.dump | 2 +- .../src/androidTest/assets/bear.flac.2.dump | 2 +- .../src/androidTest/assets/bear.flac.3.dump | 2 +- .../exoplayer2/ext/flac/FlacExtractor.java | 52 ++++--- .../androidTest/assets/flv/sample.flv.0.dump | 2 +- .../androidTest/assets/mkv/sample.mkv.0.dump | 2 +- .../androidTest/assets/mkv/sample.mkv.1.dump | 2 +- .../androidTest/assets/mkv/sample.mkv.2.dump | 2 +- .../androidTest/assets/mkv/sample.mkv.3.dump | 2 +- .../subsample_encrypted_altref.webm.0.dump | 2 +- .../subsample_encrypted_noaltref.webm.0.dump | 2 +- .../androidTest/assets/mp3/bear.mp3.0.dump | 2 +- .../androidTest/assets/mp3/bear.mp3.1.dump | 2 +- .../androidTest/assets/mp3/bear.mp3.2.dump | 2 +- .../androidTest/assets/mp3/bear.mp3.3.dump | 2 +- .../assets/mp3/play-trimmed.mp3.0.dump | 2 +- .../assets/mp3/play-trimmed.mp3.1.dump | 2 +- .../assets/mp3/play-trimmed.mp3.2.dump | 2 +- .../assets/mp3/play-trimmed.mp3.3.dump | 2 +- .../assets/mp3/play-trimmed.mp3.unklen.dump | 2 +- .../androidTest/assets/mp4/sample.mp4.0.dump | 2 +- .../androidTest/assets/mp4/sample.mp4.1.dump | 2 +- .../androidTest/assets/mp4/sample.mp4.2.dump | 2 +- .../androidTest/assets/mp4/sample.mp4.3.dump | 2 +- .../assets/mp4/sample_fragmented.mp4.0.dump | 2 +- .../mp4/sample_fragmented_sei.mp4.0.dump | 2 +- .../androidTest/assets/ogg/bear.opus.0.dump | 2 +- .../androidTest/assets/ogg/bear.opus.1.dump | 2 +- .../androidTest/assets/ogg/bear.opus.2.dump | 2 +- .../androidTest/assets/ogg/bear.opus.3.dump | 2 +- .../assets/ogg/bear.opus.unklen.dump | 2 +- .../assets/ogg/bear_flac.ogg.0.dump | 2 +- .../assets/ogg/bear_flac.ogg.1.dump | 2 +- .../assets/ogg/bear_flac.ogg.2.dump | 2 +- .../assets/ogg/bear_flac.ogg.3.dump | 2 +- .../assets/ogg/bear_flac.ogg.unklen.dump | 2 +- .../ogg/bear_flac_noseektable.ogg.0.dump | 2 +- .../ogg/bear_flac_noseektable.ogg.1.dump | 2 +- .../ogg/bear_flac_noseektable.ogg.2.dump | 2 +- .../ogg/bear_flac_noseektable.ogg.3.dump | 2 +- .../ogg/bear_flac_noseektable.ogg.unklen.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.0.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.1.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.2.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.3.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.unklen.dump | 2 +- .../assets/rawcc/sample.rawcc.0.dump | 2 +- .../androidTest/assets/ts/sample.ac3.0.dump | 2 +- .../androidTest/assets/ts/sample.adts.0.dump | 2 +- .../androidTest/assets/ts/sample.ps.0.dump | 2 +- .../androidTest/assets/ts/sample.ts.0.dump | 2 +- .../androidTest/assets/wav/sample.wav.0.dump | 8 +- .../androidTest/assets/wav/sample.wav.1.dump | 6 +- .../androidTest/assets/wav/sample.wav.2.dump | 4 +- .../androidTest/assets/wav/sample.wav.3.dump | 4 +- .../exoplayer2/extractor/ChunkIndex.java | 11 +- .../android/exoplayer2/extractor/SeekMap.java | 91 ++++++++--- .../exoplayer2/extractor/SeekPoint.java | 62 ++++++++ .../extractor/mp3/ConstantBitrateSeeker.java | 18 ++- .../exoplayer2/extractor/mp3/VbriSeeker.java | 12 +- .../exoplayer2/extractor/mp3/XingSeeker.java | 8 +- .../extractor/mp4/Mp4Extractor.java | 146 ++++++++++++++---- .../extractor/ogg/DefaultOggSeeker.java | 8 +- .../exoplayer2/extractor/ogg/FlacReader.java | 15 +- .../exoplayer2/extractor/wav/WavHeader.java | 19 ++- .../source/ExtractorMediaPeriod.java | 3 +- .../extractor/mp3/XingSeekerTest.java | 34 ++-- .../exoplayer2/testutil/ExtractorAsserts.java | 2 +- .../testutil/FakeExtractorOutput.java | 7 +- 70 files changed, 439 insertions(+), 173 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/extractor/SeekPoint.java 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 tracks = new ArrayList<>(); long earliestSampleOffset = Long.MAX_VALUE; @@ -402,6 +419,9 @@ public final class Mp4Extractor implements Extractor, SeekMap { mp4Track.trackOutput.format(format); durationUs = Math.max(durationUs, track.durationUs); + if (track.type == C.TRACK_TYPE_VIDEO && firstVideoTrackIndex == C.INDEX_UNSET) { + firstVideoTrackIndex = tracks.size(); + } tracks.add(mp4Track); long firstSampleOffset = trackSampleTable.offsets[0]; @@ -409,8 +429,10 @@ public final class Mp4Extractor implements Extractor, SeekMap { earliestSampleOffset = firstSampleOffset; } } + this.firstVideoTrackIndex = firstVideoTrackIndex; this.durationUs = durationUs; this.tracks = tracks.toArray(new Mp4Track[tracks.size()]); + extractorOutput.endTracks(); extractorOutput.seekMap(this); } @@ -538,6 +560,66 @@ public final class Mp4Extractor implements Extractor, SeekMap { } } + /** + * Adjusts a seek point offset to take into account the track with the given {@code sampleTable}, + * for a given {@code seekTimeUs}. + * + * @param sampleTable The sample table to use. + * @param seekTimeUs The seek time in microseconds. + * @param offset The current offset. + * @return The adjusted offset. + */ + private static long maybeAdjustSeekOffset( + TrackSampleTable sampleTable, long seekTimeUs, long offset) { + int sampleIndex = getSynchronizationSampleIndex(sampleTable, seekTimeUs); + if (sampleIndex == C.INDEX_UNSET) { + return offset; + } + long sampleOffset = sampleTable.offsets[sampleIndex]; + return Math.min(sampleOffset, offset); + } + + /** + * Returns the index of the synchronization sample before or at {@code timeUs}, or the index of + * the first synchronization sample if located after {@code timeUs}, or {@link C#INDEX_UNSET} if + * there are no synchronization samples in the table. + * + * @param sampleTable The sample table in which to locate a synchronization sample. + * @param timeUs A time in microseconds. + * @return The index of the synchronization sample before or at {@code timeUs}, or the index of + * the first synchronization sample if located after {@code timeUs}, or {@link C#INDEX_UNSET} + * if there are no synchronization samples in the table. + */ + private static int getSynchronizationSampleIndex(TrackSampleTable sampleTable, long timeUs) { + int sampleIndex = sampleTable.getIndexOfEarlierOrEqualSynchronizationSample(timeUs); + if (sampleIndex == C.INDEX_UNSET) { + // Handle the case where the requested time is before the first synchronization sample. + sampleIndex = sampleTable.getIndexOfLaterOrEqualSynchronizationSample(timeUs); + } + return sampleIndex; + } + + /** + * 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; + } + /** * Returns whether the extractor should decode a leaf atom with type {@code atom}. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeeker.java index 77def57275..042ab681f9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeeker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/DefaultOggSeeker.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ogg; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.util.Assertions; import java.io.EOFException; import java.io.IOException; @@ -219,12 +220,13 @@ import java.io.IOException; } @Override - public long getPosition(long timeUs) { + public SeekPoints getSeekPoints(long timeUs) { if (timeUs == 0) { - return startPosition; + return new SeekPoints(new SeekPoint(0, startPosition)); } long granule = streamReader.convertTimeToGranule(timeUs); - return getEstimatedPosition(startPosition, granule, DEFAULT_OFFSET); + long estimatedPosition = getEstimatedPosition(startPosition, granule, DEFAULT_OFFSET); + return new SeekPoints(new SeekPoint(timeUs, estimatedPosition)); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java index 304fb3dd96..5eb0727908 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/FlacReader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ogg; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.util.FlacStreamInfo; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -192,10 +193,20 @@ import java.util.List; } @Override - public long getPosition(long timeUs) { + public SeekPoints getSeekPoints(long timeUs) { long granule = convertTimeToGranule(timeUs); int index = Util.binarySearchFloor(seekPointGranules, granule, true, true); - return firstFrameOffset + seekPointOffsets[index]; + long seekTimeUs = convertGranuleToTime(seekPointGranules[index]); + long seekPosition = firstFrameOffset + seekPointOffsets[index]; + SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition); + if (seekTimeUs >= timeUs || index == seekPointGranules.length - 1) { + return new SeekPoints(seekPoint); + } else { + long secondSeekTimeUs = convertGranuleToTime(seekPointGranules[index + 1]); + long secondSeekPosition = firstFrameOffset + seekPointOffsets[index + 1]; + 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/wav/WavHeader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java index 2cdd31cb6f..33db6c1e6c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.wav; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.util.Util; /** Header for a WAV file. */ @@ -83,13 +84,22 @@ import com.google.android.exoplayer2.util.Util; } @Override - public long getPosition(long timeUs) { + public SeekPoints getSeekPoints(long timeUs) { long positionOffset = (timeUs * averageBytesPerSecond) / C.MICROS_PER_SECOND; // Constrain to nearest preceding frame offset. positionOffset = (positionOffset / blockAlignment) * blockAlignment; positionOffset = Util.constrainValue(positionOffset, 0, dataSize - blockAlignment); - // Add data start position. - return dataStartPosition + positionOffset; + long seekPosition = dataStartPosition + positionOffset; + long seekTimeUs = getTimeUs(seekPosition); + SeekPoint seekPoint = new SeekPoint(seekTimeUs, seekPosition); + if (seekTimeUs >= timeUs || positionOffset == dataSize - blockAlignment) { + return new SeekPoints(seekPoint); + } else { + long secondSeekPosition = seekPosition + blockAlignment; + long secondSeekTimeUs = getTimeUs(secondSeekPosition); + SeekPoint secondSeekPoint = new SeekPoint(secondSeekTimeUs, secondSeekPosition); + return new SeekPoints(seekPoint, secondSeekPoint); + } } // Misc getters. @@ -100,7 +110,8 @@ import com.google.android.exoplayer2.util.Util; * @param position The position in bytes. */ public long getTimeUs(long position) { - return position * C.MICROS_PER_SECOND / averageBytesPerSecond; + long positionOffset = Math.max(0, position - dataStartPosition); + return (positionOffset * C.MICROS_PER_SECOND) / averageBytesPerSecond; } /** Returns the bytes per frame of this WAV. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index c0586b3a28..344286ed3d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -549,7 +549,8 @@ import java.util.Arrays; pendingResetPositionUs = C.TIME_UNSET; return; } - loadable.setLoadPosition(seekMap.getPosition(pendingResetPositionUs), pendingResetPositionUs); + loadable.setLoadPosition( + seekMap.getSeekPoints(pendingResetPositionUs).first.position, pendingResetPositionUs); pendingResetPositionUs = C.TIME_UNSET; } extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp3/XingSeekerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp3/XingSeekerTest.java index e644abc7ef..46cd7a2451 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp3/XingSeekerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mp3/XingSeekerTest.java @@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.MpegAudioHeader; +import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; +import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import org.junit.Before; @@ -92,27 +94,39 @@ public final class XingSeekerTest { } @Test - public void testGetPositionAtStartOfStream() { - assertThat(seeker.getPosition(0)).isEqualTo(XING_FRAME_POSITION + xingFrameSize); - assertThat(seekerWithInputLength.getPosition(0)).isEqualTo(XING_FRAME_POSITION + xingFrameSize); + public void testGetSeekPointsAtStartOfStream() { + SeekPoints seekPoints = seeker.getSeekPoints(0); + SeekPoint seekPoint = seekPoints.first; + assertThat(seekPoint).isEqualTo(seekPoints.second); + assertThat(seekPoint.timeUs).isEqualTo(0); + assertThat(seekPoint.position).isEqualTo(XING_FRAME_POSITION + xingFrameSize); } @Test - public void testGetPositionAtEndOfStream() { - assertThat(seeker.getPosition(STREAM_DURATION_US)) - .isEqualTo(STREAM_LENGTH - 1); - assertThat(seekerWithInputLength.getPosition(STREAM_DURATION_US)) - .isEqualTo(STREAM_LENGTH - 1); + public void testGetSeekPointsAtEndOfStream() { + SeekPoints seekPoints = seeker.getSeekPoints(STREAM_DURATION_US); + SeekPoint seekPoint = seekPoints.first; + assertThat(seekPoint).isEqualTo(seekPoints.second); + assertThat(seekPoint.timeUs).isEqualTo(STREAM_DURATION_US); + assertThat(seekPoint.position).isEqualTo(STREAM_LENGTH - 1); } @Test public void testGetTimeForAllPositions() { for (int offset = xingFrameSize; offset < DATA_SIZE_BYTES; offset++) { int position = XING_FRAME_POSITION + offset; + // Test seeker. long timeUs = seeker.getTimeUs(position); - assertThat(seeker.getPosition(timeUs)).isEqualTo(position); + SeekPoints seekPoints = seeker.getSeekPoints(timeUs); + SeekPoint seekPoint = seekPoints.first; + assertThat(seekPoint).isEqualTo(seekPoints.second); + assertThat(seekPoint.position).isEqualTo(position); + // Test seekerWithInputLength. timeUs = seekerWithInputLength.getTimeUs(position); - assertThat(seekerWithInputLength.getPosition(timeUs)).isEqualTo(position); + seekPoints = seekerWithInputLength.getSeekPoints(timeUs); + seekPoint = seekPoints.first; + assertThat(seekPoint).isEqualTo(seekPoints.second); + assertThat(seekPoint.position).isEqualTo(position); } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java index db63662c45..8c419ce1a0 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java @@ -143,7 +143,7 @@ public final class ExtractorAsserts { long durationUs = seekMap.getDurationUs(); for (int j = 0; j < 4; j++) { long timeUs = (durationUs * j) / 3; - long position = seekMap.getPosition(timeUs); + long position = seekMap.getSeekPoints(timeUs).first.position; input.setPosition((int) position); for (int i = 0; i < extractorOutput.numberOfTracks; i++) { extractorOutput.trackOutputs.valueAt(i).clear(); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java index ee8927ea21..3f9a7c542f 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorOutput.java @@ -78,7 +78,7 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab Assert.assertNotNull(seekMap); Assert.assertEquals(expected.seekMap.getClass(), seekMap.getClass()); Assert.assertEquals(expected.seekMap.isSeekable(), seekMap.isSeekable()); - Assert.assertEquals(expected.seekMap.getPosition(0), seekMap.getPosition(0)); + Assert.assertEquals(expected.seekMap.getSeekPoints(0), seekMap.getSeekPoints(0)); } for (int i = 0; i < numberOfTracks; i++) { Assert.assertEquals(expected.trackOutputs.keyAt(i), trackOutputs.keyAt(i)); @@ -114,10 +114,11 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab @Override public void dump(Dumper dumper) { if (seekMap != null) { - dumper.startBlock("seekMap") + dumper + .startBlock("seekMap") .add("isSeekable", seekMap.isSeekable()) .addTime("duration", seekMap.getDurationUs()) - .add("getPosition(0)", seekMap.getPosition(0)) + .add("getPosition(0)", seekMap.getSeekPoints(0)) .endBlock(); } dumper.add("numberOfTracks", numberOfTracks);