diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java index 07cc4b6df4..70d65b80a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java @@ -653,8 +653,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public void seekMap(SeekMap seekMap) { - this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs */ C.TIME_UNSET); - handler.post(maybeFinishPrepareRunnable); + handler.post(() -> setSeekMap(seekMap)); } // Icy metadata. Called by the loading thread. @@ -691,6 +690,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return trackOutput; } + private void setSeekMap(SeekMap seekMap) { + this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs */ C.TIME_UNSET); + if (preparedState == null) { + maybeFinishPrepare(); + } else { + preparedState = + new PreparedState(seekMap, preparedState.tracks, preparedState.trackIsAudioVideoFlags); + } + durationUs = seekMap.getDurationUs(); + isLive = length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET; + dataType = isLive ? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE : C.DATA_TYPE_MEDIA; + listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable(), isLive); + } + private void maybeFinishPrepare() { SeekMap seekMap = this.seekMap; if (released || preparedState != null || !sampleQueuesBuilt || seekMap == null) { @@ -705,7 +718,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; int trackCount = sampleQueues.length; TrackGroup[] trackArray = new TrackGroup[trackCount]; boolean[] trackIsAudioVideoFlags = new boolean[trackCount]; - durationUs = seekMap.getDurationUs(); for (int i = 0; i < trackCount; i++) { Format trackFormat = sampleQueues[i].getUpstreamFormat(); String mimeType = trackFormat.sampleMimeType; @@ -731,11 +743,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } trackArray[i] = new TrackGroup(trackFormat); } - isLive = length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET; - dataType = isLive ? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE : C.DATA_TYPE_MEDIA; preparedState = new PreparedState(seekMap, new TrackGroupArray(trackArray), trackIsAudioVideoFlags); - listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable(), isLive); Assertions.checkNotNull(callback).onPrepared(this); } diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java index 355de37f27..f8c63ff8e2 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/IndexSeeker.java @@ -27,11 +27,12 @@ import com.google.android.exoplayer2.util.Util; @VisibleForTesting /* package */ static final long MIN_TIME_BETWEEN_POINTS_US = C.MICROS_PER_SECOND / 10; - private final long durationUs; private final long dataEndPosition; private final LongArray timesUs; private final LongArray positions; + private long durationUs; + public IndexSeeker(long durationUs, long dataStartPosition, long dataEndPosition) { this.durationUs = durationUs; this.dataEndPosition = dataEndPosition; @@ -104,4 +105,8 @@ import com.google.android.exoplayer2.util.Util; long lastIndexedTimeUs = timesUs.get(timesUs.size() - 1); return timeUs - lastIndexedTimeUs < MIN_TIME_BETWEEN_POINTS_US; } + + /* package */ void setDurationUs(long durationUs) { + this.durationUs = durationUs; + } } diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index e518819d8d..2e8286cf86 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -216,6 +216,31 @@ public final class Mp3Extractor implements Extractor { public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { assertInitialized(); + int readResult = readInternal(input); + if (readResult == RESULT_END_OF_INPUT && seeker instanceof IndexSeeker) { + // Duration is exact when index seeker is used. + long durationUs = computeTimeUs(samplesRead); + if (seeker.getDurationUs() != durationUs) { + ((IndexSeeker) seeker).setDurationUs(durationUs); + extractorOutput.seekMap(seeker); + } + } + return readResult; + } + + /** + * Disables the extractor from being able to seek through the media. + * + *

Please note that this needs to be called before {@link #read}. + */ + public void disableSeeking() { + disableSeeking = true; + } + + // Internal methods. + + @RequiresNonNull({"extractorOutput", "currentTrackOutput", "realTrackOutput"}) + private int readInternal(ExtractorInput input) throws IOException, InterruptedException { if (synchronizedHeaderData == 0) { try { synchronize(input, false); @@ -254,17 +279,6 @@ public final class Mp3Extractor implements Extractor { return readSample(input); } - /** - * Disables the extractor from being able to seek through the media. - * - *

Please note that this needs to be called before {@link #read}. - */ - public void disableSeeking() { - disableSeeking = true; - } - - // Internal methods. - @RequiresNonNull({"currentTrackOutput", "realTrackOutput", "seeker"}) private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException { if (sampleBytesRemaining == 0) { diff --git a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.0.dump b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.0.dump index 5225210979..9ddb5b599f 100644 --- a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.0.dump +++ b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.0.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = UNSET TIME + duration = 2808000 getPosition(0) = [[timeUs=0, position=224]] numberOfTracks = 1 track 0: diff --git a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.1.dump b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.1.dump index ebdbb9cdb0..2c956b7619 100644 --- a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.1.dump +++ b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.1.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = UNSET TIME + duration = 2808000 getPosition(0) = [[timeUs=0, position=224]] numberOfTracks = 1 track 0: diff --git a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.2.dump b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.2.dump index d505251f61..2ba3879001 100644 --- a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.2.dump +++ b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.2.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = UNSET TIME + duration = 2808000 getPosition(0) = [[timeUs=0, position=224]] numberOfTracks = 1 track 0: diff --git a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.3.dump b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.3.dump index 1341231b6a..cb6ece77e9 100644 --- a/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.3.dump +++ b/library/extractor/src/test/assets/mp3/bear-vbr-no-seek-table.mp3.3.dump @@ -1,6 +1,6 @@ seekMap: isSeekable = true - duration = UNSET TIME + duration = 2808000 getPosition(0) = [[timeUs=0, position=224]] numberOfTracks = 1 track 0: