diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java deleted file mode 100644 index c39bccda3d..0000000000 --- a/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2016 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.source; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.FormatHolder; -import com.google.android.exoplayer2.decoder.DecoderInputBuffer; -import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.util.Assertions; -import java.io.IOException; - -/** - * Wraps a {@link MediaPeriod} and clips its {@link SampleStream}s to provide a subsequence of their - * samples. - */ -/* package */ final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callback { - - public final MediaPeriod mediaPeriod; - private final ClippingMediaSource mediaSource; - - private MediaPeriod.Callback callback; - private long startUs; - private long endUs; - private ClippingSampleStream[] sampleStreams; - private boolean pendingInitialDiscontinuity; - - /** - * Creates a new clipping media period that provides a clipped view of the specified - * {@link MediaPeriod}'s sample streams. - * - * @param mediaPeriod The media period to clip. - * @param mediaSource The {@link ClippingMediaSource} to which this period belongs. - */ - public ClippingMediaPeriod(MediaPeriod mediaPeriod, ClippingMediaSource mediaSource) { - this.mediaPeriod = mediaPeriod; - this.mediaSource = mediaSource; - startUs = C.TIME_UNSET; - endUs = C.TIME_UNSET; - sampleStreams = new ClippingSampleStream[0]; - } - - @Override - public void prepare(MediaPeriod.Callback callback) { - this.callback = callback; - mediaPeriod.prepare(this); - } - - @Override - public void maybeThrowPrepareError() throws IOException { - mediaPeriod.maybeThrowPrepareError(); - } - - @Override - public TrackGroupArray getTrackGroups() { - return mediaPeriod.getTrackGroups(); - } - - @Override - public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, - SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { - sampleStreams = new ClippingSampleStream[streams.length]; - SampleStream[] internalStreams = new SampleStream[streams.length]; - for (int i = 0; i < streams.length; i++) { - sampleStreams[i] = (ClippingSampleStream) streams[i]; - internalStreams[i] = sampleStreams[i] != null ? sampleStreams[i].stream : null; - } - long enablePositionUs = mediaPeriod.selectTracks(selections, mayRetainStreamFlags, - internalStreams, streamResetFlags, positionUs + startUs); - Assertions.checkState(enablePositionUs == positionUs + startUs - || (enablePositionUs >= startUs && enablePositionUs <= endUs)); - for (int i = 0; i < streams.length; i++) { - if (internalStreams[i] == null) { - sampleStreams[i] = null; - } else if (streams[i] == null || sampleStreams[i].stream != internalStreams[i]) { - sampleStreams[i] = new ClippingSampleStream(this, internalStreams[i], startUs, endUs, - pendingInitialDiscontinuity); - } - streams[i] = sampleStreams[i]; - } - return enablePositionUs - startUs; - } - - @Override - public long readDiscontinuity() { - if (pendingInitialDiscontinuity) { - for (ClippingSampleStream sampleStream : sampleStreams) { - if (sampleStream != null) { - sampleStream.clearPendingDiscontinuity(); - } - } - pendingInitialDiscontinuity = false; - // Always read an initial discontinuity, using mediaPeriod's discontinuity if set. - long discontinuityUs = readDiscontinuity(); - return discontinuityUs != C.TIME_UNSET ? discontinuityUs : 0; - } - long discontinuityUs = mediaPeriod.readDiscontinuity(); - if (discontinuityUs == C.TIME_UNSET) { - return C.TIME_UNSET; - } - Assertions.checkState(discontinuityUs >= startUs && discontinuityUs <= endUs); - return discontinuityUs - startUs; - } - - @Override - public long getBufferedPositionUs() { - long bufferedPositionUs = mediaPeriod.getBufferedPositionUs(); - if (bufferedPositionUs == C.TIME_END_OF_SOURCE || bufferedPositionUs >= endUs) { - return C.TIME_END_OF_SOURCE; - } - return Math.max(0, bufferedPositionUs - startUs); - } - - @Override - public long seekToUs(long positionUs) { - for (ClippingSampleStream sampleStream : sampleStreams) { - if (sampleStream != null) { - sampleStream.clearSentEos(); - } - } - long seekUs = mediaPeriod.seekToUs(positionUs + startUs); - Assertions.checkState(seekUs == positionUs + startUs || (seekUs >= startUs && seekUs <= endUs)); - return seekUs - startUs; - } - - @Override - public long getNextLoadPositionUs() { - long nextLoadPositionUs = mediaPeriod.getNextLoadPositionUs(); - if (nextLoadPositionUs == C.TIME_END_OF_SOURCE || nextLoadPositionUs >= endUs) { - return C.TIME_END_OF_SOURCE; - } - return nextLoadPositionUs - startUs; - } - - @Override - public boolean continueLoading(long positionUs) { - return mediaPeriod.continueLoading(positionUs + startUs); - } - - // MediaPeriod.Callback implementation. - - @Override - public void onPrepared(MediaPeriod mediaPeriod) { - startUs = mediaSource.getStartUs(); - endUs = mediaSource.getEndUs(); - Assertions.checkState(startUs != C.TIME_UNSET && endUs != C.TIME_UNSET); - // If the clipping start position is non-zero, the clipping sample streams will adjust - // timestamps on buffers they read from the unclipped sample streams. These adjusted buffer - // timestamps can be negative, because sample streams provide buffers starting at a key-frame, - // which may be before the clipping start point. When the renderer reads a buffer with a - // negative timestamp, its offset timestamp can jump backwards compared to the last timestamp - // read in the previous period. Renderer implementations may not allow this, so we signal a - // discontinuity which resets the renderers before they read the clipping sample stream. - pendingInitialDiscontinuity = startUs != 0; - callback.onPrepared(this); - } - - @Override - public void onContinueLoadingRequested(MediaPeriod source) { - callback.onContinueLoadingRequested(this); - } - - /** - * Wraps a {@link SampleStream} and clips its samples. - */ - private static final class ClippingSampleStream implements SampleStream { - - private final MediaPeriod mediaPeriod; - private final SampleStream stream; - private final long startUs; - private final long endUs; - - private boolean pendingDiscontinuity; - private boolean sentEos; - - public ClippingSampleStream(MediaPeriod mediaPeriod, SampleStream stream, long startUs, - long endUs, boolean pendingDiscontinuity) { - this.mediaPeriod = mediaPeriod; - this.stream = stream; - this.startUs = startUs; - this.endUs = endUs; - this.pendingDiscontinuity = pendingDiscontinuity; - } - - public void clearPendingDiscontinuity() { - pendingDiscontinuity = false; - } - - public void clearSentEos() { - sentEos = false; - } - - @Override - public boolean isReady() { - return stream.isReady(); - } - - @Override - public void maybeThrowError() throws IOException { - stream.maybeThrowError(); - } - - @Override - public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) { - if (pendingDiscontinuity) { - return C.RESULT_NOTHING_READ; - } - if (sentEos) { - buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); - return C.RESULT_BUFFER_READ; - } - int result = stream.readData(formatHolder, buffer); - // TODO: Clear gapless playback metadata if a format was read (if applicable). - if ((result == C.RESULT_BUFFER_READ && buffer.timeUs >= endUs) - || (result == C.RESULT_NOTHING_READ - && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE)) { - buffer.clear(); - buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); - sentEos = true; - return C.RESULT_BUFFER_READ; - } - if (result == C.RESULT_BUFFER_READ) { - buffer.timeUs -= startUs; - } - return result; - } - - @Override - public void skipToKeyframeBefore(long timeUs) { - stream.skipToKeyframeBefore(startUs + timeUs); - } - - } - -} diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java deleted file mode 100644 index e92dce8231..0000000000 --- a/library/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2016 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.source; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.upstream.Allocator; -import com.google.android.exoplayer2.util.Assertions; -import java.io.IOException; - -/** - * {@link MediaSource} that wraps a source and clips its timeline based on specified start/end - * positions. The wrapped source may only have a single period/window and it must not be dynamic - * (live). The specified start position must correspond to a synchronization sample in the period. - */ -public final class ClippingMediaSource implements MediaSource, MediaSource.Listener { - - private final MediaSource mediaSource; - private final long startUs; - private final long endUs; - - private MediaSource.Listener sourceListener; - private ClippingTimeline clippingTimeline; - - /** - * Creates a new clipping source that wraps the specified source. - * - * @param mediaSource The single-period, non-dynamic source to wrap. - * @param startPositionUs The start position within {@code mediaSource}'s timeline at which to - * start providing samples, in microseconds. - * @param endPositionUs The end position within {@code mediaSource}'s timeline at which to stop - * providing samples, in microseconds. Specify {@link C#TIME_END_OF_SOURCE} to provide samples - * from the specified start point up to the end of the source. - */ - public ClippingMediaSource(MediaSource mediaSource, long startPositionUs, long endPositionUs) { - Assertions.checkArgument(startPositionUs >= 0); - this.mediaSource = Assertions.checkNotNull(mediaSource); - startUs = startPositionUs; - endUs = endPositionUs; - } - - /** - * Returns the start position of the clipping source's timeline in microseconds. - */ - /* package */ long getStartUs() { - return clippingTimeline.startUs; - } - - /** - * Returns the end position of the clipping source's timeline in microseconds. - */ - /* package */ long getEndUs() { - return clippingTimeline.endUs; - } - - @Override - public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { - this.sourceListener = listener; - mediaSource.prepareSource(player, false, this); - } - - @Override - public void maybeThrowSourceInfoRefreshError() throws IOException { - mediaSource.maybeThrowSourceInfoRefreshError(); - } - - @Override - public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { - return new ClippingMediaPeriod( - mediaSource.createPeriod(index, allocator, startUs + positionUs), this); - } - - @Override - public void releasePeriod(MediaPeriod mediaPeriod) { - mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod); - } - - @Override - public void releaseSource() { - mediaSource.releaseSource(); - } - - // MediaSource.Listener implementation. - - @Override - public void onSourceInfoRefreshed(Timeline timeline, Object manifest) { - clippingTimeline = new ClippingTimeline(timeline, startUs, endUs); - sourceListener.onSourceInfoRefreshed(clippingTimeline, manifest); - } - - /** - * Provides a clipped view of a specified timeline. - */ - private static final class ClippingTimeline extends Timeline { - - private final Timeline timeline; - private final long startUs; - private final long endUs; - - /** - * Creates a new timeline that wraps the specified timeline. - * - * @param timeline The timeline to clip. - * @param startUs The number of microseconds to clip from the start of {@code timeline}. - * @param endUs The end position in microseconds for the clipped timeline relative to the start - * of {@code timeline}, or {@link C#TIME_END_OF_SOURCE} to clip no samples from the end. - */ - public ClippingTimeline(Timeline timeline, long startUs, long endUs) { - Assertions.checkArgument(timeline.getWindowCount() == 1); - Assertions.checkArgument(timeline.getPeriodCount() == 1); - Window window = timeline.getWindow(0, new Window(), false); - Assertions.checkArgument(!window.isDynamic); - long resolvedEndUs = endUs == C.TIME_END_OF_SOURCE ? window.durationUs : endUs; - if (window.durationUs != C.TIME_UNSET) { - Assertions.checkArgument(startUs == 0 || window.isSeekable); - Assertions.checkArgument(resolvedEndUs <= window.durationUs); - Assertions.checkArgument(startUs <= resolvedEndUs); - } - Period period = timeline.getPeriod(0, new Period()); - Assertions.checkArgument(period.getPositionInWindowUs() == 0); - this.timeline = timeline; - this.startUs = startUs; - this.endUs = resolvedEndUs; - } - - @Override - public int getWindowCount() { - return 1; - } - - @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { - window = timeline.getWindow(0, window, setIds, defaultPositionProjectionUs); - window.durationUs = endUs != C.TIME_UNSET ? endUs - startUs : C.TIME_UNSET; - if (window.defaultPositionUs != C.TIME_UNSET) { - window.defaultPositionUs = Math.max(window.defaultPositionUs, startUs); - window.defaultPositionUs = endUs == C.TIME_UNSET ? window.defaultPositionUs - : Math.min(window.defaultPositionUs, endUs); - window.defaultPositionUs -= startUs; - } - long startMs = C.usToMs(startUs); - if (window.presentationStartTimeMs != C.TIME_UNSET) { - window.presentationStartTimeMs += startMs; - } - if (window.windowStartTimeMs != C.TIME_UNSET) { - window.windowStartTimeMs += startMs; - } - return window; - } - - @Override - public int getPeriodCount() { - return 1; - } - - @Override - public Period getPeriod(int periodIndex, Period period, boolean setIds) { - period = timeline.getPeriod(0, period, setIds); - period.durationUs = endUs != C.TIME_UNSET ? endUs - startUs : C.TIME_UNSET; - return period; - } - - @Override - public int getIndexOfPeriod(Object uid) { - return timeline.getIndexOfPeriod(uid); - } - - } - -}