From 1a909fd1632c963ad855fbc198ddc659c88581a2 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 15 Aug 2016 07:20:50 -0700 Subject: [PATCH] Change SeekWindow -> Window, and add window duration. - This avoids the need to have to use the timeline to calculate a window duration, which can be awkward. - Window now represents a window of availability with an isSeekable flag, rather than a window of seekability. - Promoted Timeline and Window to top package; they're pretty important :). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=130278509 --- .../android/exoplayer2/demo/EventLogger.java | 10 +-- .../exoplayer2/demo/PlayerActivity.java | 2 +- .../exoplayer2/ext/flac/FlacPlaybackTest.java | 2 +- .../exoplayer2/ext/opus/OpusPlaybackTest.java | 2 +- .../exoplayer2/ext/vp9/VpxPlaybackTest.java | 2 +- .../google/android/exoplayer2/ExoPlayer.java | 1 - .../android/exoplayer2/ExoPlayerImpl.java | 1 - .../exoplayer2/ExoPlayerImplInternal.java | 1 - .../android/exoplayer2/SimpleExoPlayer.java | 1 - .../exoplayer2/{source => }/Timeline.java | 17 ++-- .../{source/SeekWindow.java => Window.java} | 61 +++++++++----- .../source/ConcatenatingMediaSource.java | 26 +++--- .../source/ExtractorMediaPeriod.java | 10 +-- .../source/ExtractorMediaSource.java | 4 +- .../exoplayer2/source/MediaSource.java | 1 + .../exoplayer2/source/MergingMediaSource.java | 1 + .../source/SinglePeriodTimeline.java | 83 +++++++------------ .../source/SingleSampleMediaSource.java | 3 +- .../source/dash/DashMediaSource.java | 43 +++++----- .../exoplayer2/source/hls/HlsMediaPeriod.java | 8 +- .../exoplayer2/source/hls/HlsMediaSource.java | 8 +- .../source/smoothstreaming/SsMediaSource.java | 30 +++---- .../exoplayer2/ui/DebugTextViewHelper.java | 2 +- .../playbacktests/util/ExoHostedTest.java | 2 +- 24 files changed, 160 insertions(+), 161 deletions(-) rename library/src/main/java/com/google/android/exoplayer2/{source => }/Timeline.java (84%) rename library/src/main/java/com/google/android/exoplayer2/{source/SeekWindow.java => Window.java} (55%) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java index eac68b2f32..4cc07fd925 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; import com.google.android.exoplayer2.metadata.MetadataRenderer; @@ -33,7 +34,6 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.metadata.id3.TxxxFrame; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; @@ -94,11 +94,11 @@ import java.util.Locale; public void onSourceInfoRefreshed(Timeline timeline, Object manifest) { boolean isFinal = timeline.isFinal(); int periodCount = timeline.getPeriodCount(); - int seekWindowCount = timeline.getSeekWindowCount(); + int windowCount = timeline.getWindowCount(); Log.d(TAG, "sourceInfo[isFinal=" + isFinal + ", startTime=" + timeline.getAbsoluteStartTime() - + ", periodCount=" + periodCount + ", seekWindows: " + seekWindowCount); - for (int seekWindowIndex = 0; seekWindowIndex < seekWindowCount; seekWindowIndex++) { - Log.d(TAG, " " + timeline.getSeekWindow(seekWindowIndex)); + + ", periodCount=" + periodCount + ", windows: " + windowCount); + for (int windowIndex = 0; windowIndex < windowCount; windowIndex++) { + Log.d(TAG, " " + timeline.getWindow(windowIndex)); } Log.d(TAG, "]"); } diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 659962d759..599e4286c6 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -41,6 +41,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; @@ -51,7 +52,6 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java index 328f60e116..93fc0290a9 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java @@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java index dd210b9268..c4358f915f 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java @@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java index 08f43b43b9..574e766ce8 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java @@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 9739e4048d..3e1e8bbcf4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.Timeline; /** * An extensible media player exposing traditional high-level media player functionality, such as diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index e28ef71a3f..573c4e6f40 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -23,7 +23,6 @@ import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Assertions; import java.util.concurrent.CopyOnWriteArraySet; diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 2ccb669762..2041c29979 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -26,7 +26,6 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SampleStream; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; diff --git a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 2eb7e2042e..16b30c6636 100644 --- a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -35,7 +35,6 @@ import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.metadata.id3.Id3Decoder; import com.google.android.exoplayer2.metadata.id3.Id3Frame; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.trackselection.TrackSelector; diff --git a/library/src/main/java/com/google/android/exoplayer2/source/Timeline.java b/library/src/main/java/com/google/android/exoplayer2/Timeline.java similarity index 84% rename from library/src/main/java/com/google/android/exoplayer2/source/Timeline.java rename to library/src/main/java/com/google/android/exoplayer2/Timeline.java index 940e671e51..b46c3a6149 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/Timeline.java +++ b/library/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.source; +package com.google.android.exoplayer2; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.source.MediaSource.Listener; /** * The player's timeline consisting of one or more periods. Instances are immutable. @@ -87,15 +86,15 @@ public interface Timeline { int getIndexOfPeriod(Object id); /** - * Returns the number of seek windows that can be accessed via {@link #getSeekWindow(int)}. + * Returns the number of windows that can be accessed via {@link #getWindow(int)}. */ - int getSeekWindowCount(); + int getWindowCount(); /** - * Returns the {@link SeekWindow} at {@code index}, which represents positions that can be seeked - * to in the timeline. The seek windows may change when - * {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} is called. + * Returns the {@link Window} at {@code index}, which represents positions that can be seeked + * to in the timeline. The windows may change when + * {@link Listener#onSourceInfoRefreshed(Timeline, Object)} is called. */ - SeekWindow getSeekWindow(int index); + Window getWindow(int index); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SeekWindow.java b/library/src/main/java/com/google/android/exoplayer2/Window.java similarity index 55% rename from library/src/main/java/com/google/android/exoplayer2/source/SeekWindow.java rename to library/src/main/java/com/google/android/exoplayer2/Window.java index 87e0baec5d..795a77bb64 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SeekWindow.java +++ b/library/src/main/java/com/google/android/exoplayer2/Window.java @@ -13,27 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.source; +package com.google.android.exoplayer2; /** - * A window of times the player can seek to. + * A window of available media. Instances are immutable. */ -public final class SeekWindow { - - public static final SeekWindow UNSEEKABLE = createWindowFromZero(0); +public final class Window { /** - * Creates a new {@link SeekWindow} containing times from zero up to {@code durationUs} in the - * first period. + * Creates a new {@link Window} containing times from zero up to {@code durationUs} in the first + * period. * * @param durationUs The duration of the window, in microseconds. + * @param isSeekable Whether seeking is supported within the window. */ - public static SeekWindow createWindowFromZero(long durationUs) { - return createWindow(0, 0, 0, durationUs); + public static Window createWindowFromZero(long durationUs, boolean isSeekable) { + return createWindow(0, 0, 0, durationUs, durationUs, isSeekable); } /** - * Creates a new {@link SeekWindow} representing the specified time range. + * Creates a new {@link Window} representing the specified time range. * * @param startPeriodIndex The index of the period containing the start of the window. * @param startTimeUs The start time of the window in microseconds, relative to the start of the @@ -41,10 +40,15 @@ public final class SeekWindow { * @param endPeriodIndex The index of the period containing the end of the window. * @param endTimeUs The end time of the window in microseconds, relative to the start of the * specified end period. + * @param durationUs The duration of the window in microseconds. + * @param isSeekable Whether seeking is supported within the window. */ - public static SeekWindow createWindow(int startPeriodIndex, long startTimeUs, - int endPeriodIndex, long endTimeUs) { - return new SeekWindow(startPeriodIndex, startTimeUs / 1000, endPeriodIndex, endTimeUs / 1000); + public static Window createWindow(int startPeriodIndex, long startTimeUs, + int endPeriodIndex, long endTimeUs, long durationUs, boolean isSeekable) { + return new Window(startPeriodIndex, startTimeUs / 1000, endPeriodIndex, + endTimeUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (endTimeUs / 1000), + durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000), + isSeekable); } /** @@ -65,24 +69,35 @@ public final class SeekWindow { * {@link #endPeriodIndex}, in milliseconds. */ public final long endTimeMs; + /** + * The duration of the window in milliseconds, or {@link C#UNSET_TIME_US} if unknown. + */ + public final long durationMs; + /** + * Whether it's possible to seek within the window. + */ + public final boolean isSeekable; - private SeekWindow(int startPeriodIndex, long startTimeMs, int endPeriodIndex, long endTimeMs) { + private Window(int startPeriodIndex, long startTimeMs, int endPeriodIndex, long endTimeMs, + long durationMs, boolean isSeekable) { this.startPeriodIndex = startPeriodIndex; this.startTimeMs = startTimeMs; this.endPeriodIndex = endPeriodIndex; this.endTimeMs = endTimeMs; + this.durationMs = durationMs; + this.isSeekable = isSeekable; } /** - * Returns a new seek window that is offset by the specified number of periods. + * Returns a new window that is offset by the specified number of periods. * * @param periodCount The number of periods to add to {@link #startPeriodIndex} and * {@link #endPeriodIndex} when constructing the copy. - * @return A new seek window that is offset by the specified number of periods. + * @return A new window that is offset by the specified number of periods. */ - public SeekWindow copyOffsetByPeriodCount(int periodCount) { - return new SeekWindow(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount, - endTimeMs); + public Window copyOffsetByPeriodCount(int periodCount) { + return new Window(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount, + endTimeMs, durationMs, isSeekable); } @Override @@ -103,16 +118,18 @@ public final class SeekWindow { if (obj == null || getClass() != obj.getClass()) { return false; } - SeekWindow other = (SeekWindow) obj; + Window other = (Window) obj; return other.startPeriodIndex == startPeriodIndex && other.startTimeMs == startTimeMs && other.endPeriodIndex == endPeriodIndex - && other.endTimeMs == endTimeMs; + && other.endTimeMs == endTimeMs + && other.durationMs == durationMs + && other.isSeekable == isSeekable; } @Override public String toString() { - return "SeekWindow[" + startPeriodIndex + ", " + startTimeMs + ", " + endPeriodIndex + ", " + return "Window[" + startPeriodIndex + ", " + startTimeMs + ", " + endPeriodIndex + ", " + endTimeMs + "]"; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 6b748bcdff..a54475e011 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -16,6 +16,8 @@ package com.google.android.exoplayer2.source; import android.util.Pair; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.Window; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Util; @@ -130,21 +132,21 @@ public final class ConcatenatingMediaSource implements MediaSource { private final Timeline[] timelines; private final boolean isFinal; private final int[] sourceOffsets; - private final SeekWindow[] seekWindows; + private final Window[] windows; public ConcatenatedTimeline(Timeline[] timelines) { boolean isFinal = true; int[] sourceOffsets = new int[timelines.length]; int sourceOffset = 0; - ArrayList concatenatedSeekWindows = new ArrayList<>(); + ArrayList concatenatedWindows = new ArrayList<>(); for (int i = 0; i < timelines.length; i++) { Timeline timeline = timelines[i]; isFinal &= timeline.isFinal(); - // Offset the seek windows so they are relative to the source. - int seekWindowCount = timeline.getSeekWindowCount(); - for (int j = 0; j < seekWindowCount; j++) { - SeekWindow sourceSeekWindow = timeline.getSeekWindow(j); - concatenatedSeekWindows.add(sourceSeekWindow.copyOffsetByPeriodCount(sourceOffset)); + // Offset the windows so they are relative to the source. + int windowCount = timeline.getWindowCount(); + for (int j = 0; j < windowCount; j++) { + Window sourceWindow = timeline.getWindow(j); + concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(sourceOffset)); } sourceOffset += timeline.getPeriodCount(); sourceOffsets[i] = sourceOffset; @@ -152,7 +154,7 @@ public final class ConcatenatingMediaSource implements MediaSource { this.timelines = timelines; this.isFinal = isFinal; this.sourceOffsets = sourceOffsets; - seekWindows = concatenatedSeekWindows.toArray(new SeekWindow[concatenatedSeekWindows.size()]); + windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]); } @Override @@ -208,13 +210,13 @@ public final class ConcatenatingMediaSource implements MediaSource { } @Override - public int getSeekWindowCount() { - return seekWindows.length; + public int getWindowCount() { + return windows.length; } @Override - public SeekWindow getSeekWindow(int index) { - return seekWindows[index]; + public Window getWindow(int index) { + return windows[index]; } private int getSourceIndexForPeriod(int periodIndex) { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index 080386da44..1113afa018 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -297,9 +297,8 @@ import java.util.Arrays; long largestQueuedTimestampUs = getLargestQueuedTimestampUs(); durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0 : largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US; - sourceListener.onSourceInfoRefreshed(seekMap.isSeekable() - ? SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs) - : SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs), null); + sourceListener.onSourceInfoRefreshed( + new SinglePeriodTimeline(0, durationUs, seekMap.isSeekable()), null); } } @@ -382,9 +381,8 @@ import java.util.Arrays; tracks = new TrackGroupArray(trackArray); prepared = true; callback.onPrepared(this); - sourceListener.onSourceInfoRefreshed(seekMap.isSeekable() - ? SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs) - : SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs), null); + sourceListener.onSourceInfoRefreshed( + new SinglePeriodTimeline(0, durationUs, seekMap.isSeekable()), null); } private void copyLengthFromLoader(ExtractingLoadable loadable) { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index f4038116fb..acda232262 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -19,9 +19,9 @@ import android.net.Uri; import android.os.Handler; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.Extractor; -import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; @@ -135,7 +135,7 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List @Override public void prepareSource(MediaSource.Listener listener) { sourceListener = listener; - timeline = SinglePeriodTimeline.createNonFinalTimeline(0); + timeline = new SinglePeriodTimeline(this, C.UNSET_TIME_US, false); listener.onSourceInfoRefreshed(timeline, null); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java index fa7bb582ff..f06809bbb9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index 63cfe79045..46c183857e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Assertions; diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java b/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java index 3e08c6ffa9..46da9ebfbf 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java @@ -17,68 +17,45 @@ 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.Window; import com.google.android.exoplayer2.util.Assertions; /** - * A {@link Timeline} consisting of a single period and seek window. + * A {@link Timeline} consisting of a single period and window. */ public final class SinglePeriodTimeline implements Timeline { - /** - * Returns a new timeline with one period of unknown duration and an empty seek window. - * - * @param id The identifier for the period. - * @return A new timeline with one period of unknown duration. - */ - public static Timeline createNonFinalTimeline(Object id) { - return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, SeekWindow.UNSEEKABLE); - } - - /** - * Returns a new timeline with one period of unknown duration and the specified seek window. - * - * @param id The identifier for the period. - * @param seekWindow The seek window. - * @return A new timeline with one period of unknown duration. - */ - public static Timeline createNonFinalTimeline(Object id, SeekWindow seekWindow) { - return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, seekWindow); - } - - /** - * Creates a final timeline with one period of known duration and an empty seek window. - * - * @param id The identifier for the period. - * @param durationUs The duration of the period, in microseconds. - * @return A new, unseekable, final timeline with one period. - */ - public static Timeline createUnseekableFinalTimeline(Object id, long durationUs) { - return new SinglePeriodTimeline(id, true, durationUs, SeekWindow.UNSEEKABLE); - } - - /** - * Creates a final timeline with one period of known duration and a seek window extending from - * zero to its duration. - * - * @param id The identifier for the period. - * @param durationUs The duration of the period, in microseconds. - * @return A new, seekable, final timeline with one period. - */ - public static Timeline createSeekableFinalTimeline(Object id, long durationUs) { - return new SinglePeriodTimeline(id, true, durationUs, - SeekWindow.createWindowFromZero(durationUs)); - } - private final Object id; private final boolean isFinal; private final long durationUs; - private final SeekWindow seekWindow; + private final Window window; - private SinglePeriodTimeline(Object id, boolean isFinal, long durationUs, SeekWindow seekWindow) { + /** + * Creates a final timeline with one period of known duration and a window extending from + * zero to its duration. + * + * @param id The identifier for the period. + * @param durationUs The duration of the period, in microseconds. + * @param isSeekable Whether seeking is supported within the period. + */ + public SinglePeriodTimeline(Object id, long durationUs, boolean isSeekable) { + this(id, durationUs, Window.createWindowFromZero(durationUs, isSeekable)); + } + + /** + * Creates a final timeline with one period of known duration and a window extending from + * zero to its duration. + * + * @param id The identifier for the period. + * @param durationUs The duration of the period, in microseconds. + * @param window The available window within the period. + */ + public SinglePeriodTimeline(Object id, long durationUs, Window window) { this.id = Assertions.checkNotNull(id); - this.isFinal = isFinal; this.durationUs = durationUs; - this.seekWindow = seekWindow; + this.window = window; + this.isFinal = true; // TODO: Remove. } @Override @@ -126,13 +103,13 @@ public final class SinglePeriodTimeline implements Timeline { } @Override - public int getSeekWindowCount() { + public int getWindowCount() { return 1; } @Override - public SeekWindow getSeekWindow(int index) { - return seekWindow; + public Window getWindow(int index) { + return window; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java index ee3c6ed306..d924aaa0fd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source; import android.net.Uri; import android.os.Handler; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; @@ -78,7 +79,7 @@ public final class SingleSampleMediaSource implements MediaSource { this.eventHandler = eventHandler; this.eventListener = eventListener; this.eventSourceId = eventSourceId; - timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs); + timeline = new SinglePeriodTimeline(0, durationUs, true); } // MediaSource implementation. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index e8c81abd0b..0fc4ef89df 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -22,13 +22,13 @@ import android.util.Log; import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.Window; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.SeekWindow; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.Period; @@ -59,7 +59,7 @@ public final class DashMediaSource implements MediaSource { /** * The interval in milliseconds between invocations of * {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} when the source's - * {@link SeekWindow} is changing dynamically (for example, for incomplete live streams). + * {@link Window} is changing dynamically (for example, for incomplete live streams). */ private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000; /** @@ -89,7 +89,7 @@ public final class DashMediaSource implements MediaSource { private long manifestLoadEndTimestamp; private DashManifest manifest; private Handler handler; - private SeekWindow seekWindow; + private Window window; private long elapsedRealtimeOffsetMs; private int firstPeriodId; @@ -151,20 +151,20 @@ public final class DashMediaSource implements MediaSource { @Override public Position getDefaultStartPosition(int index) { - if (seekWindow == null) { + if (window == null) { return null; } if (index == 0 && manifest.dynamic) { // The stream is live, so return a position a position offset from the live edge. - int periodIndex = seekWindow.endPeriodIndex; - long positionMs = seekWindow.endTimeMs - LIVE_EDGE_OFFSET_MS; - while (positionMs < 0 && periodIndex > seekWindow.startPeriodIndex) { + int periodIndex = window.endPeriodIndex; + long positionMs = window.endTimeMs - LIVE_EDGE_OFFSET_MS; + while (positionMs < 0 && periodIndex > window.startPeriodIndex) { periodIndex--; positionMs += manifest.getPeriodDurationMs(periodIndex); } positionMs = Math.max(positionMs, - periodIndex == seekWindow.startPeriodIndex ? seekWindow.startTimeMs : 0); + periodIndex == window.startPeriodIndex ? window.startTimeMs : 0); return new Position(periodIndex, positionMs * 1000); } return new Position(index, 0); @@ -370,7 +370,7 @@ public final class DashMediaSource implements MediaSource { } private void refreshSourceInfo() { - // Update the seek window. + // Update the window. int lastPeriodIndex = manifest.getPeriodCount() - 1; PeriodSeekInfo firstPeriodSeekInfo = PeriodSeekInfo.createPeriodSeekInfo(manifest.getPeriod(0), manifest.getPeriodDurationUs(0)); @@ -379,7 +379,7 @@ public final class DashMediaSource implements MediaSource { long currentStartTimeUs; long currentEndTimeUs; if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) { - // The seek window is changing so post a Runnable to update it. + // The window is changing so post a Runnable to update it. handler.postDelayed(refreshSourceInfoRunnable, NOTIFY_MANIFEST_INTERVAL_MS); long minStartPositionUs = firstPeriodSeekInfo.availableStartTimeUs; @@ -395,8 +395,13 @@ public final class DashMediaSource implements MediaSource { currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs; currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs; } - seekWindow = SeekWindow.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs); - sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, seekWindow), + long windowDurationUs = currentEndTimeUs - currentStartTimeUs; + for (int i = 0; i < manifest.getPeriodCount() - 1; i++) { + windowDurationUs += manifest.getPeriodDurationUs(i); + } + window = Window.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs, + windowDurationUs, true); + sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, window), manifest); } @@ -481,12 +486,12 @@ public final class DashMediaSource implements MediaSource { private final int firstPeriodId; private final DashManifest manifest; - private final SeekWindow seekWindow; + private final Window window; - public DashTimeline(int firstPeriodId, DashManifest manifest, SeekWindow seekWindow) { + public DashTimeline(int firstPeriodId, DashManifest manifest, Window window) { this.firstPeriodId = firstPeriodId; this.manifest = manifest; - this.seekWindow = seekWindow; + this.window = window; } @Override @@ -531,13 +536,13 @@ public final class DashMediaSource implements MediaSource { } @Override - public int getSeekWindowCount() { + public int getWindowCount() { return 1; } @Override - public SeekWindow getSeekWindow(int index) { - return seekWindow; + public Window getWindow(int index) { + return window; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 70518e597e..bdb5c928ba 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -20,13 +20,13 @@ import android.text.TextUtils; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.CompositeSequenceableLoader; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SinglePeriodTimeline; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; @@ -280,10 +280,8 @@ import java.util.List; trackGroups = new TrackGroupArray(trackGroupArray); callback.onPrepared(this); - // TODO[playlists]: Calculate the seek window. - Timeline timeline = isLive - ? SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs) - : SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs); + // TODO[playlists]: Calculate the window. + Timeline timeline = new SinglePeriodTimeline(0, durationUs, !isLive); sourceListener.onSourceInfoRefreshed(timeline, playlist); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 6bb4a6b886..6673d47e1b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -17,13 +17,14 @@ package com.google.android.exoplayer2.source.hls; import android.net.Uri; import android.os.Handler; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SinglePeriodTimeline; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Assertions; @@ -62,9 +63,10 @@ public final class HlsMediaSource implements MediaSource { @Override public void prepareSource(MediaSource.Listener listener) { - // TODO: Defer until the playlist has been loaded. sourceListener = listener; - listener.onSourceInfoRefreshed(SinglePeriodTimeline.createNonFinalTimeline(this), null); + // TODO: Defer until the playlist has been loaded. + listener.onSourceInfoRefreshed( + new SinglePeriodTimeline(this, C.UNSET_TIME_US, false), null); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 3af45e654e..0293192fde 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -20,14 +20,14 @@ import android.os.Handler; import android.os.SystemClock; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.Window; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.SeekWindow; import com.google.android.exoplayer2.source.SinglePeriodTimeline; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; @@ -72,7 +72,7 @@ public final class SsMediaSource implements MediaSource, private long manifestLoadStartTimestamp; private SsManifest manifest; - private SeekWindow seekWindow; + private Window window; private Handler manifestRefreshHandler; @@ -114,12 +114,12 @@ public final class SsMediaSource implements MediaSource, @Override public Position getDefaultStartPosition(int index) { - if (seekWindow == null) { + if (window == null) { return null; } if (manifest.isLive) { - long startPositionUs = Math.max(seekWindow.startTimeMs, - seekWindow.endTimeMs - LIVE_EDGE_OFFSET_MS) * 1000; + long startPositionUs = Math.max(window.startTimeMs, + window.endTimeMs - LIVE_EDGE_OFFSET_MS) * 1000; return new Position(0, startPositionUs); } return Position.DEFAULT; @@ -183,18 +183,20 @@ public final class SsMediaSource implements MediaSource, startTimeUs = Math.min(startTimeUs, element.getStartTimeUs(0)); } } - if (startTimeUs == Long.MAX_VALUE) { - timeline = SinglePeriodTimeline.createNonFinalTimeline(this); + if (startTimeUs == Long.MAX_VALUE || manifest.dvrWindowLengthUs == C.UNSET_TIME_US + || manifest.dvrWindowLengthUs == 0) { + timeline = new SinglePeriodTimeline(0, C.UNSET_TIME_US, false); } else { - timeline = SinglePeriodTimeline.createNonFinalTimeline(this, - SeekWindow.createWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs)); + long periodDurationUs = startTimeUs + manifest.dvrWindowLengthUs; + Window window = Window.createWindow(0, startTimeUs, 0, periodDurationUs, + manifest.dvrWindowLengthUs, true); + timeline = new SinglePeriodTimeline(0, periodDurationUs, window); } - } else if (manifest.durationUs == C.UNSET_TIME_US) { - timeline = SinglePeriodTimeline.createUnseekableFinalTimeline(0, C.UNSET_TIME_US); } else { - timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, manifest.durationUs); + boolean isSeekable = manifest.durationUs != C.UNSET_TIME_US; + timeline = new SinglePeriodTimeline(0, manifest.durationUs, isSeekable); } - seekWindow = timeline.getSeekWindow(0); + window = timeline.getWindow(0); sourceListener.onSourceInfoRefreshed(timeline, manifest); scheduleManifestRefresh(); } diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java b/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java index a58f4bef42..b0b342f1a9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java @@ -20,8 +20,8 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.decoder.DecoderCounters; -import com.google.android.exoplayer2.source.Timeline; /** * A helper class for periodically updating a {@link TextView} with debug information obtained from diff --git a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/util/ExoHostedTest.java b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/util/ExoHostedTest.java index 02e95a6de8..809360b249 100644 --- a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/util/ExoHostedTest.java +++ b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/util/ExoHostedTest.java @@ -25,12 +25,12 @@ import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;