diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java index c888afdd14..43036b154b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java @@ -81,12 +81,9 @@ import com.google.android.exoplayer2.util.Assertions; mayRetainStreamFlags = new boolean[rendererCapabilities.length]; MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, allocator); if (info.endPositionUs != C.TIME_END_OF_SOURCE) { - mediaPeriod = - new ClippingMediaPeriod( - mediaPeriod, - /* enableInitialDiscontinuity= */ true, - /* startUs= */ 0, - info.endPositionUs); + ClippingMediaPeriod clippingMediaPeriod = new ClippingMediaPeriod(mediaPeriod, true); + clippingMediaPeriod.setClipping(0, info.endPositionUs); + mediaPeriod = clippingMediaPeriod; } this.mediaPeriod = mediaPeriod; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java index 67616cb6c9..98bd7daaec 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java @@ -43,36 +43,35 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb /* package */ long endUs; /** - * Creates a new clipping media period that provides a clipped view of the specified {@link - * MediaPeriod}'s sample streams. - * - *

If the start point is guaranteed to be a key frame, pass {@code false} to {@code + * Creates a new clipping media period that provides a clipped view of the specified + * {@link MediaPeriod}'s sample streams. + *

+ * The clipping start/end positions must be specified by calling {@link #setClipping(long, long)} + * on the playback thread before preparation completes. + *

+ * If the start point is guaranteed to be a key frame, pass {@code false} to {@code * enableInitialPositionDiscontinuity} to suppress an initial discontinuity when the period is * first read from. * * @param mediaPeriod The media period to clip. * @param enableInitialDiscontinuity Whether the initial discontinuity should be enabled. - * @param startUs The clipping start time, in microseconds. - * @param endUs The clipping end time, in microseconds, or {@link C#TIME_END_OF_SOURCE} to - * indicate the end of the period. */ - public ClippingMediaPeriod( - MediaPeriod mediaPeriod, boolean enableInitialDiscontinuity, long startUs, long endUs) { + public ClippingMediaPeriod(MediaPeriod mediaPeriod, boolean enableInitialDiscontinuity) { this.mediaPeriod = mediaPeriod; sampleStreams = new ClippingSampleStream[0]; - pendingInitialDiscontinuityPositionUs = enableInitialDiscontinuity ? startUs : C.TIME_UNSET; - this.startUs = startUs; - this.endUs = endUs; + pendingInitialDiscontinuityPositionUs = enableInitialDiscontinuity ? 0 : C.TIME_UNSET; + startUs = C.TIME_UNSET; + endUs = C.TIME_UNSET; } /** - * Updates the clipping start/end times for this period, in microseconds. + * Sets the clipping start/end times for this period, in microseconds. * * @param startUs The clipping start time, in microseconds. * @param endUs The clipping end time, in microseconds, or {@link C#TIME_END_OF_SOURCE} to * indicate the end of the period. */ - public void updateClipping(long startUs, long endUs) { + public void setClipping(long startUs, long endUs) { this.startUs = startUs; this.endUs = endUs; } @@ -80,7 +79,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb @Override public void prepare(MediaPeriod.Callback callback, long positionUs) { this.callback = callback; - mediaPeriod.prepare(this, positionUs); + mediaPeriod.prepare(this, startUs + positionUs); } @Override @@ -102,19 +101,13 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb sampleStreams[i] = (ClippingSampleStream) streams[i]; childStreams[i] = sampleStreams[i] != null ? sampleStreams[i].childStream : null; } - long enablePositionUs = - mediaPeriod.selectTracks( - selections, mayRetainStreamFlags, childStreams, streamResetFlags, positionUs); - pendingInitialDiscontinuityPositionUs = - isPendingInitialDiscontinuity() - && positionUs == startUs - && shouldKeepInitialDiscontinuity(startUs, selections) - ? enablePositionUs - : C.TIME_UNSET; - Assertions.checkState( - enablePositionUs == positionUs - || (enablePositionUs >= startUs - && (endUs == C.TIME_END_OF_SOURCE || enablePositionUs <= endUs))); + long enablePositionUs = mediaPeriod.selectTracks(selections, mayRetainStreamFlags, + childStreams, streamResetFlags, positionUs + startUs) - startUs; + pendingInitialDiscontinuityPositionUs = isPendingInitialDiscontinuity() && positionUs == 0 + && shouldKeepInitialDiscontinuity(startUs, selections) ? enablePositionUs : C.TIME_UNSET; + Assertions.checkState(enablePositionUs == positionUs + || (enablePositionUs >= 0 + && (endUs == C.TIME_END_OF_SOURCE || startUs + enablePositionUs <= endUs))); for (int i = 0; i < streams.length; i++) { if (childStreams[i] == null) { sampleStreams[i] = null; @@ -128,12 +121,12 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb @Override public void discardBuffer(long positionUs, boolean toKeyframe) { - mediaPeriod.discardBuffer(positionUs, toKeyframe); + mediaPeriod.discardBuffer(positionUs + startUs, toKeyframe); } @Override public void reevaluateBuffer(long positionUs) { - mediaPeriod.reevaluateBuffer(positionUs); + mediaPeriod.reevaluateBuffer(positionUs + startUs); } @Override @@ -151,7 +144,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb } Assertions.checkState(discontinuityUs >= startUs); Assertions.checkState(endUs == C.TIME_END_OF_SOURCE || discontinuityUs <= endUs); - return discontinuityUs; + return discontinuityUs - startUs; } @Override @@ -161,7 +154,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb || (endUs != C.TIME_END_OF_SOURCE && bufferedPositionUs >= endUs)) { return C.TIME_END_OF_SOURCE; } - return bufferedPositionUs; + return Math.max(0, bufferedPositionUs - startUs); } @Override @@ -172,21 +165,23 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb sampleStream.clearSentEos(); } } - long seekUs = mediaPeriod.seekToUs(positionUs); + long offsetPositionUs = positionUs + startUs; + long seekUs = mediaPeriod.seekToUs(offsetPositionUs); Assertions.checkState( - seekUs == positionUs + seekUs == offsetPositionUs || (seekUs >= startUs && (endUs == C.TIME_END_OF_SOURCE || seekUs <= endUs))); - return seekUs; + return seekUs - startUs; } @Override public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { - if (positionUs == startUs) { + if (positionUs == 0) { // Never adjust seeks to the start of the clipped view. - return startUs; + return 0; } - SeekParameters clippedSeekParameters = clipSeekParameters(positionUs, seekParameters); - return mediaPeriod.getAdjustedSeekPositionUs(positionUs, clippedSeekParameters); + long offsetPositionUs = positionUs + startUs; + SeekParameters clippedSeekParameters = clipSeekParameters(offsetPositionUs, seekParameters); + return mediaPeriod.getAdjustedSeekPositionUs(offsetPositionUs, clippedSeekParameters) - startUs; } @Override @@ -196,18 +191,19 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb || (endUs != C.TIME_END_OF_SOURCE && nextLoadPositionUs >= endUs)) { return C.TIME_END_OF_SOURCE; } - return nextLoadPositionUs; + return nextLoadPositionUs - startUs; } @Override public boolean continueLoading(long positionUs) { - return mediaPeriod.continueLoading(positionUs); + return mediaPeriod.continueLoading(positionUs + startUs); } // MediaPeriod.Callback implementation. @Override public void onPrepared(MediaPeriod mediaPeriod) { + Assertions.checkState(startUs != C.TIME_UNSET && endUs != C.TIME_UNSET); callback.onPrepared(this); } @@ -220,17 +216,17 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb return pendingInitialDiscontinuityPositionUs != C.TIME_UNSET; } - private SeekParameters clipSeekParameters(long positionUs, SeekParameters seekParameters) { - long toleranceBeforeUs = Math.min(positionUs - startUs, seekParameters.toleranceBeforeUs); - long toleranceAfterUs = + private SeekParameters clipSeekParameters(long offsetPositionUs, SeekParameters seekParameters) { + long toleranceBeforeMs = Math.min(offsetPositionUs - startUs, seekParameters.toleranceBeforeUs); + long toleranceAfterMs = endUs == C.TIME_END_OF_SOURCE ? seekParameters.toleranceAfterUs - : Math.min(endUs - positionUs, seekParameters.toleranceAfterUs); - if (toleranceBeforeUs == seekParameters.toleranceBeforeUs - && toleranceAfterUs == seekParameters.toleranceAfterUs) { + : Math.min(endUs - offsetPositionUs, seekParameters.toleranceAfterUs); + if (toleranceBeforeMs == seekParameters.toleranceBeforeUs + && toleranceAfterMs == seekParameters.toleranceAfterUs) { return seekParameters; } else { - return new SeekParameters(toleranceBeforeUs, toleranceAfterUs); + return new SeekParameters(toleranceBeforeMs, toleranceAfterMs); } } @@ -314,6 +310,9 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb sentEos = true; return C.RESULT_BUFFER_READ; } + if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream()) { + buffer.timeUs -= startUs; + } return result; } @@ -322,7 +321,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb if (isPendingInitialDiscontinuity()) { return C.RESULT_NOTHING_READ; } - return childStream.skipData(positionUs); + return childStream.skipData(startUs + positionUs); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java index 7c5e0e52ef..08719098c5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java @@ -141,13 +141,10 @@ public final class ClippingMediaSource extends CompositeMediaSource { @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { - ClippingMediaPeriod mediaPeriod = - new ClippingMediaPeriod( - mediaSource.createPeriod(id, allocator), - enableInitialDiscontinuity, - periodStartUs, - periodEndUs); + ClippingMediaPeriod mediaPeriod = new ClippingMediaPeriod( + mediaSource.createPeriod(id, allocator), enableInitialDiscontinuity); mediaPeriods.add(mediaPeriod); + mediaPeriod.setClipping(periodStartUs, periodEndUs); return mediaPeriod; } @@ -186,7 +183,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { endUs == C.TIME_END_OF_SOURCE ? C.TIME_END_OF_SOURCE : windowPositionInPeriodUs + endUs; int count = mediaPeriods.size(); for (int i = 0; i < count; i++) { - mediaPeriods.get(i).updateClipping(periodStartUs, periodEndUs); + mediaPeriods.get(i).setClipping(periodStartUs, periodEndUs); } } @@ -249,7 +246,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { public Window getWindow(int windowIndex, Window window, boolean setIds, long defaultPositionProjectionUs) { timeline.getWindow(/* windowIndex= */ 0, window, setIds, defaultPositionProjectionUs); - window.positionInFirstPeriodUs += startUs; + window.positionInFirstPeriodUs = 0; window.durationUs = durationUs; if (window.defaultPositionUs != C.TIME_UNSET) { window.defaultPositionUs = Math.max(window.defaultPositionUs, startUs); @@ -270,11 +267,10 @@ public final class ClippingMediaSource extends CompositeMediaSource { @Override public Period getPeriod(int periodIndex, Period period, boolean setIds) { timeline.getPeriod(/* periodIndex= */ 0, period, setIds); - long positionInClippedWindowUs = period.getPositionInWindowUs() - startUs; - long periodDurationUs = - durationUs == C.TIME_UNSET ? C.TIME_UNSET : durationUs - positionInClippedWindowUs; return period.set( - period.id, period.uid, /* windowIndex= */ 0, periodDurationUs, positionInClippedWindowUs); + period.id, period.uid, /* windowIndex= */ 0, durationUs, /* positionInWindowUs= */ 0); } + } + } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index 60282d582c..1e135403ad 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -99,7 +99,7 @@ public final class ClippingMediaSourceTest { assertThat(clippedTimeline.getWindow(0, window).getDurationUs()) .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US); assertThat(clippedTimeline.getPeriod(0, period).getDurationUs()) - .isEqualTo(TEST_PERIOD_DURATION_US); + .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US); } @Test @@ -128,7 +128,7 @@ public final class ClippingMediaSourceTest { assertThat(clippedTimeline.getWindow(0, window).getDurationUs()) .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3); assertThat(clippedTimeline.getPeriod(0, period).getDurationUs()) - .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2); + .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3); } @Test @@ -171,7 +171,7 @@ public final class ClippingMediaSourceTest { assertThat(clippedTimeline.getWindow(0, window).getDurationUs()) .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3); assertThat(clippedTimeline.getPeriod(0, period).getDurationUs()) - .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 2); + .isEqualTo(TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US * 3); } @Test