From c1729b640cca5cb40806986be2f0dabbf281052b Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 1 Aug 2016 07:43:05 -0700 Subject: [PATCH] Allow seeking to a default position in a period. When seeking to the default position in a period, the containing source may actually return a position in another period. Multi-period live sources can do this to seek the player to the live edge. ExoPlayerImplInternal uses the same functionality when the playback position reaches the end of a period to determine what period/position to play next. This means that when playback transitions to a multi-period live source from some other source (playing a concatenation of those two sources), the player will play the live edge rather than the beginning of the earliest period. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=128984355 --- .../google/android/exoplayer2/ExoPlayer.java | 11 +++ .../android/exoplayer2/ExoPlayerImpl.java | 35 +++++-- .../exoplayer2/ExoPlayerImplInternal.java | 93 +++++++++++++++---- .../android/exoplayer2/SimpleExoPlayer.java | 5 + .../source/ConcatenatingMediaSource.java | 26 ++++-- .../source/ExtractorMediaSource.java | 5 + .../exoplayer2/source/MediaSource.java | 46 +++++++++ .../exoplayer2/source/MergingMediaSource.java | 5 + .../source/SingleSampleMediaSource.java | 5 + .../source/dash/DashMediaSource.java | 10 ++ .../exoplayer2/source/hls/HlsMediaSource.java | 6 ++ .../source/smoothstreaming/SsMediaSource.java | 6 ++ 12 files changed, 216 insertions(+), 37 deletions(-) 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 b8038eacf6..da0fb721fb 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -300,6 +300,17 @@ public interface ExoPlayer { */ void seekTo(int periodIndex, long positionMs); + /** + * Seeks to the default position associated with the specified period. The position can depend on + * the type of source passed to {@link #setMediaSource(MediaSource)}. For live streams it will + * typically be the live edge. For other types of streams it will typically be the start of the + * stream. + * + * @param periodIndex The index of the period whose associated default position should be seeked + * to. + */ + void seekToDefaultPosition(int periodIndex); + /** * Stops playback. Use {@code setPlayWhenReady(false)} rather than this method if the intention * is to pause playback. 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 2753580dc8..0f8ac55335 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -136,17 +136,25 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public void seekTo(int periodIndex, long positionMs) { boolean periodChanging = periodIndex != getCurrentPeriodIndex(); + boolean seekToDefaultPosition = positionMs == ExoPlayer.UNKNOWN_TIME; maskingPeriodIndex = periodIndex; - maskingPositionMs = positionMs; + maskingPositionMs = seekToDefaultPosition ? 0 : positionMs; maskingDurationMs = periodChanging ? ExoPlayer.UNKNOWN_TIME : getDuration(); pendingSeekAcks++; - internalPlayer.seekTo(periodIndex, positionMs * 1000); - for (EventListener listener : listeners) { - listener.onPositionDiscontinuity(periodIndex, positionMs); + internalPlayer.seekTo(periodIndex, seekToDefaultPosition ? C.UNSET_TIME_US : positionMs * 1000); + if (!seekToDefaultPosition) { + for (EventListener listener : listeners) { + listener.onPositionDiscontinuity(periodIndex, positionMs); + } } } + @Override + public void seekToDefaultPosition(int periodIndex) { + seekTo(periodIndex, ExoPlayer.UNKNOWN_TIME); + } + @Override public void stop() { internalPlayer.stop(); @@ -180,8 +188,8 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public long getCurrentPosition() { - return pendingSeekAcks == 0 ? playbackInfo.positionUs / 1000 - : maskingPositionMs; + return pendingSeekAcks > 0 ? maskingPositionMs + : playbackInfo.positionUs == C.UNSET_TIME_US ? 0 : (playbackInfo.positionUs / 1000); } @Override @@ -239,14 +247,23 @@ import java.util.concurrent.CopyOnWriteArraySet; break; } case ExoPlayerImplInternal.MSG_SEEK_ACK: { - pendingSeekAcks--; + if (--pendingSeekAcks == 0) { + long positionMs = playbackInfo.startPositionUs == C.UNSET_TIME_US ? 0 + : playbackInfo.startPositionUs / 1000; + if (playbackInfo.periodIndex != maskingPeriodIndex || positionMs != maskingPositionMs) { + for (EventListener listener : listeners) { + listener.onPositionDiscontinuity(playbackInfo.periodIndex, positionMs); + } + } + } break; } - case ExoPlayerImplInternal.MSG_PERIOD_CHANGED: { + case ExoPlayerImplInternal.MSG_POSITION_DISCONTINUITY: { playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; if (pendingSeekAcks == 0) { for (EventListener listener : listeners) { - listener.onPositionDiscontinuity(playbackInfo.periodIndex, 0); + listener.onPositionDiscontinuity(playbackInfo.periodIndex, + playbackInfo.startPositionUs / 1000); } } break; 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 c6ae5007f6..29d5428565 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -56,6 +56,7 @@ import java.util.ArrayList; public volatile long positionUs; public volatile long bufferedPositionUs; public volatile long durationUs; + public volatile long startPositionUs; public PlaybackInfo(int periodIndex) { this.periodIndex = periodIndex; @@ -71,7 +72,7 @@ import java.util.ArrayList; public static final int MSG_LOADING_CHANGED = 2; public static final int MSG_SET_PLAY_WHEN_READY_ACK = 3; public static final int MSG_SEEK_ACK = 4; - public static final int MSG_PERIOD_CHANGED = 5; + public static final int MSG_POSITION_DISCONTINUITY = 5; public static final int MSG_TIMELINE_CHANGED = 6; public static final int MSG_ERROR = 7; @@ -485,9 +486,20 @@ import java.util.ArrayList; private void seekToInternal(int periodIndex, long positionUs) throws ExoPlaybackException { try { + if (positionUs == C.UNSET_TIME_US && mediaSource != null) { + MediaSource.Position defaultStartPosition = + mediaSource.getDefaultStartPosition(periodIndex); + if (defaultStartPosition != null) { + // We know the default position so seek to it now. + periodIndex = defaultStartPosition.periodIndex; + positionUs = defaultStartPosition.positionUs; + } + } + if (periodIndex == playbackInfo.periodIndex - && (positionUs / 1000) == (playbackInfo.positionUs / 1000)) { - // Seek position equals the current position to the nearest millisecond. Do nothing. + && ((positionUs == C.UNSET_TIME_US && playbackInfo.positionUs == C.UNSET_TIME_US) + || ((positionUs / 1000) == (playbackInfo.positionUs / 1000)))) { + // Seek position equals the current position. Do nothing. return; } seekToPeriodPosition(periodIndex, positionUs); @@ -503,13 +515,15 @@ import java.util.ArrayList; positionUs = internalTimeline.seekTo(periodIndex, positionUs); if (periodIndex != playbackInfo.periodIndex) { playbackInfo = new PlaybackInfo(periodIndex); + playbackInfo.startPositionUs = positionUs; playbackInfo.positionUs = positionUs; - eventHandler.obtainMessage(MSG_PERIOD_CHANGED, playbackInfo).sendToTarget(); + eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); } else { + playbackInfo.startPositionUs = positionUs; playbackInfo.positionUs = positionUs; } - updatePlaybackPositions(); + if (mediaSource != null) { setState(ExoPlayer.STATE_BUFFERING); handler.sendEmptyMessage(MSG_DO_SOME_WORK); @@ -681,7 +695,14 @@ import java.util.ArrayList; // Release all loaded periods and seek to the new playing period index. releasePeriodsFrom(playingPeriod); playingPeriod = null; - seekToPeriodPosition(newPlayingPeriodIndex, 0); + + MediaSource.Position defaultStartPosition = + mediaSource.getDefaultStartPosition(newPlayingPeriodIndex); + if (defaultStartPosition != null) { + seekToPeriodPosition(defaultStartPosition.periodIndex, defaultStartPosition.positionUs); + } else { + seekToPeriodPosition(newPlayingPeriodIndex, C.UNSET_TIME_US); + } return; } @@ -746,9 +767,11 @@ import java.util.ArrayList; : mediaSource.getNewPlayingPeriodIndex(playbackInfo.periodIndex, oldTimeline); if (newPlayingIndex != Timeline.NO_PERIOD_INDEX && newPlayingIndex != playbackInfo.periodIndex) { + long oldPositionUs = playbackInfo.positionUs; playbackInfo = new PlaybackInfo(newPlayingIndex); + playbackInfo.startPositionUs = oldPositionUs; updatePlaybackPositions(); - eventHandler.obtainMessage(MSG_PERIOD_CHANGED, playbackInfo).sendToTarget(); + eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); } } } @@ -762,21 +785,33 @@ import java.util.ArrayList; // Update the loading period. if (loadingPeriod == null || (loadingPeriod.isFullyBuffered() && !loadingPeriod.isLast && bufferAheadPeriodCount < MAXIMUM_BUFFER_AHEAD_PERIODS)) { - // Try to obtain the next period to start loading. - int periodIndex = loadingPeriod == null ? playbackInfo.periodIndex - : loadingPeriod.index + 1; - // Attempt to create the next period. - MediaPeriod mediaPeriod = mediaSource.createPeriod(periodIndex); - if (mediaPeriod != null) { + int periodIndex = + loadingPeriod == null ? playbackInfo.periodIndex : loadingPeriod.index + 1; + long startPositionUs = playbackInfo.positionUs; + if (loadingPeriod != null || startPositionUs == C.UNSET_TIME_US) { + // We are starting to load the next period or seeking to the default position, so request + // a period and position from the source. + MediaSource.Position defaultStartPosition = + mediaSource.getDefaultStartPosition(periodIndex); + if (defaultStartPosition != null) { + periodIndex = defaultStartPosition.periodIndex; + startPositionUs = defaultStartPosition.positionUs; + } else { + startPositionUs = C.UNSET_TIME_US; + } + } + + MediaPeriod mediaPeriod; + if (startPositionUs != C.UNSET_TIME_US + && (mediaPeriod = mediaSource.createPeriod(periodIndex)) != null) { Period newPeriod = new Period(renderers, rendererCapabilities, trackSelector, mediaPeriod, - timeline.getPeriodId(periodIndex), periodIndex); + timeline.getPeriodId(periodIndex), periodIndex, startPositionUs); newPeriod.isLast = timeline.isFinal() && periodIndex == timeline.getPeriodCount() - 1; if (loadingPeriod != null) { loadingPeriod.setNextPeriod(newPeriod); } bufferAheadPeriodCount++; loadingPeriod = newPeriod; - long startPositionUs = playingPeriod == null ? playbackInfo.positionUs : 0; setIsLoading(true); loadingPeriod.mediaPeriod.preparePeriod(ExoPlayerImplInternal.this, loadControl.getAllocator(), startPositionUs); @@ -807,8 +842,9 @@ import java.util.ArrayList; setPlayingPeriod(playingPeriod.nextPeriod); bufferAheadPeriodCount--; playbackInfo = new PlaybackInfo(playingPeriod.index); + playbackInfo.startPositionUs = playingPeriod.startPositionUs; updatePlaybackPositions(); - eventHandler.obtainMessage(MSG_PERIOD_CHANGED, playbackInfo).sendToTarget(); + eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); } updateTimelineState(); if (readingPeriod == null) { @@ -858,12 +894,19 @@ import java.util.ArrayList; // Stale event. return; } - long startPositionUs = playingPeriod == null ? playbackInfo.positionUs : 0; - loadingPeriod.handlePrepared(startPositionUs, loadControl); + loadingPeriod.handlePrepared(loadingPeriod.startPositionUs, loadControl); if (playingPeriod == null) { // This is the first prepared period, so start playing it. readingPeriod = loadingPeriod; setPlayingPeriod(readingPeriod); + if (playbackInfo.startPositionUs == C.UNSET_TIME_US) { + // Update the playback info when seeking to a default position. + playbackInfo = new PlaybackInfo(playingPeriod.index); + playbackInfo.startPositionUs = playingPeriod.startPositionUs; + resetInternalPosition(playbackInfo.startPositionUs); + updatePlaybackPositions(); + eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); + } updateTimelineState(); } maybeContinueLoading(); @@ -895,6 +938,11 @@ import java.util.ArrayList; } public long seekTo(int periodIndex, long seekPositionUs) throws ExoPlaybackException { + if (seekPositionUs == C.UNSET_TIME_US) { + // We don't know where to seek to yet, so clear the whole timeline. + periodIndex = Timeline.NO_PERIOD_INDEX; + } + // Clear the timeline, but keep the requested period if it is already prepared. Period period = playingPeriod; Period newPlayingPeriod = null; @@ -929,7 +977,9 @@ import java.util.ArrayList; playingPeriod = null; readingPeriod = null; loadingPeriod = null; - resetInternalPosition(seekPositionUs); + if (seekPositionUs != C.UNSET_TIME_US) { + resetInternalPosition(seekPositionUs); + } } return seekPositionUs; } @@ -1120,6 +1170,7 @@ import java.util.ArrayList; public final MediaPeriod mediaPeriod; public final Object id; public final SampleStream[] sampleStreams; + public final long startPositionUs; public int index; public boolean isLast; @@ -1138,13 +1189,15 @@ import java.util.ArrayList; private TrackSelectionArray periodTrackSelections; public Period(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, - TrackSelector trackSelector, MediaPeriod mediaPeriod, Object id, int index) { + TrackSelector trackSelector, MediaPeriod mediaPeriod, Object id, int index, + long positionUs) { this.renderers = renderers; this.rendererCapabilities = rendererCapabilities; this.trackSelector = trackSelector; this.mediaPeriod = mediaPeriod; this.id = Assertions.checkNotNull(id); sampleStreams = new SampleStream[renderers.length]; + startPositionUs = positionUs; this.index = index; } 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 4c497efe36..8a5addd8c0 100644 --- a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -359,6 +359,11 @@ public final class SimpleExoPlayer implements ExoPlayer { player.seekTo(periodIndex, positionMs); } + @Override + public void seekToDefaultPosition(int periodIndex) { + player.seekToDefaultPosition(periodIndex); + } + @Override public void stop() { player.stop(); 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 86531fe29d..74540bc03d 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 @@ -53,15 +53,25 @@ public final class ConcatenatingMediaSource implements MediaSource { } @Override - public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) + public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldConcatenatedTimeline) throws IOException { - ConcatenatedTimeline oldConcatenatedTimeline = (ConcatenatedTimeline) oldTimeline; - int sourceIndex = oldConcatenatedTimeline.getSourceIndexForPeriod(oldPlayingPeriodIndex); - int sourceFirstPeriodIndex = oldConcatenatedTimeline.getFirstPeriodIndexInSource(sourceIndex); - return sourceFirstPeriodIndex == Timeline.NO_PERIOD_INDEX ? Timeline.NO_PERIOD_INDEX - : sourceFirstPeriodIndex + mediaSources[sourceIndex].getNewPlayingPeriodIndex( - oldPlayingPeriodIndex - sourceFirstPeriodIndex, - oldConcatenatedTimeline.timelines[sourceIndex]); + ConcatenatedTimeline oldTimeline = (ConcatenatedTimeline) oldConcatenatedTimeline; + int sourceIndex = oldTimeline.getSourceIndexForPeriod(oldPlayingPeriodIndex); + int oldFirstPeriodIndex = oldTimeline.getFirstPeriodIndexInSource(sourceIndex); + int firstPeriodIndex = timeline.getFirstPeriodIndexInSource(sourceIndex); + return firstPeriodIndex == Timeline.NO_PERIOD_INDEX ? Timeline.NO_PERIOD_INDEX + : firstPeriodIndex + mediaSources[sourceIndex].getNewPlayingPeriodIndex( + oldPlayingPeriodIndex - oldFirstPeriodIndex, oldTimeline.timelines[sourceIndex]); + } + + @Override + public Position getDefaultStartPosition(int index) { + int sourceIndex = timeline.getSourceIndexForPeriod(index); + int sourceFirstPeriodIndex = timeline.getFirstPeriodIndexInSource(sourceIndex); + Position defaultStartPosition = + mediaSources[sourceIndex].getDefaultStartPosition(index - sourceFirstPeriodIndex); + return new Position(defaultStartPosition.periodIndex + sourceFirstPeriodIndex, + defaultStartPosition.positionUs); } @Override 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 01d4e29dc9..152b5dcd14 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 @@ -187,6 +187,11 @@ public final class ExtractorMediaSource implements MediaPeriod, MediaSource, return oldPlayingPeriodIndex; } + @Override + public Position getDefaultStartPosition(int index) { + return Position.DEFAULT; + } + @Override public MediaPeriod createPeriod(int index) { Assertions.checkArgument(index == 0); 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 c545e7efcd..8e8682d11c 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 @@ -38,6 +38,38 @@ public interface MediaSource { } + /** + * A position in the timeline. + */ + final class Position { + + /** + * A start position at the earliest time in the first period. + */ + public static final Position DEFAULT = new Position(0, 0); + + /** + * The index of the period containing the timeline position. + */ + public final int periodIndex; + + /** + * The position in microseconds within the period. + */ + public final long positionUs; + + /** + * Creates a new timeline position. + * + * @param periodIndex The index of the period containing the timeline position. + * @param positionUs The position in microseconds within the period. + */ + public Position(int periodIndex, long positionUs) { + this.periodIndex = periodIndex; + this.positionUs = positionUs; + } + } + /** * Starts preparation of the source. * @@ -55,6 +87,20 @@ public interface MediaSource { */ int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) throws IOException; + /** + * Returns the default {@link Position} that the player should play when it reaches the period at + * {@code index}, or {@code null} if the default start period and position are not yet known. + *

+ * For example, sources can return a {@link Position} with the passed period {@code index} to play + * the period at {@code index} immediately after the period at {@code index - 1}. Or, sources + * providing multi-period live streams may return the index and position of the live edge when + * passed {@code index == 0} so that the playback position jumps to the live edge. + * + * @param index The index of the period the player has just reached. + * @return The default start position. + */ + Position getDefaultStartPosition(int index); + /** * Returns a {@link MediaPeriod} corresponding to the period at the specified index, or * {@code null} if the period at the specified index is not yet available. 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 0a8f02d5d7..cfffafbd14 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 @@ -64,6 +64,11 @@ public final class MergingMediaSource implements MediaSource { return mediaSources[0].getNewPlayingPeriodIndex(oldPlayingPeriodIndex, oldTimeline); } + @Override + public Position getDefaultStartPosition(int index) { + return mediaSources[0].getDefaultStartPosition(index); + } + @Override public MediaPeriod createPeriod(int index) throws IOException { MediaPeriod[] periods = new MediaPeriod[mediaSources.length]; 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 d9845e3f21..54d1ef5dc1 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 @@ -122,6 +122,11 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource, return oldPlayingPeriodIndex; } + @Override + public Position getDefaultStartPosition(int index) { + return Position.DEFAULT; + } + @Override public MediaPeriod createPeriod(int index) { Assertions.checkArgument(index == 0); 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 5b2fc2d5fe..b8e896879c 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 @@ -122,6 +122,16 @@ public final class DashMediaSource implements MediaSource { return Timeline.NO_PERIOD_INDEX; } + @Override + public Position getDefaultStartPosition(int index) { + if (index == 0 && manifest.dynamic) { + // The stream is live, so jump to the live edge. + // TODO[playlists]: Actually jump to the live edge, rather than the start of the last period. + return new Position(periods.size() - 1, 0); + } + return new Position(index, 0); + } + @Override public MediaPeriod createPeriod(int index) throws IOException { if (periods == null || periods.size() <= index) { 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 53dcba6052..d065aae01c 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 @@ -118,6 +118,12 @@ public final class HlsMediaSource implements MediaPeriod, MediaSource, return oldPlayingPeriodIndex; } + @Override + public Position getDefaultStartPosition(int index) { + // TODO: Return the position of the live edge, if applicable. + return Position.DEFAULT; + } + @Override public MediaPeriod createPeriod(int index) { Assertions.checkArgument(index == 0); 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 19e88a35ae..ed032a4ff4 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 @@ -100,6 +100,12 @@ public final class SsMediaSource implements MediaSource, return oldPlayingPeriodIndex; } + @Override + public Position getDefaultStartPosition(int index) { + // TODO: Return the position of the live edge, if applicable. + return Position.DEFAULT; + } + @Override public MediaPeriod createPeriod(int index) { Assertions.checkArgument(index == 0);