From 8208a75f0a840f2b666ed2c8f1d4029d582d0d4f Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 4 Apr 2017 03:53:03 -0700 Subject: [PATCH] Improve multi-window playback controls behavior. Only enable multi-window mode when the duration of every period in the timeline is known. Also, remove the warning logged when there are too many windows as it doesn't add much. The player's current period index was not masked while there were unacknowledged seeks. This led to the displayed position jumping, between when seekTo was called (after which the position would be masked but not the period index) and the seek being acknowledged (at which point the time bar's position would jump back to the seek position, due to the period index being resolved). Mask the period index, like the window index, to fix this behavior. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=152116040 --- .../android/exoplayer2/ExoPlayerImpl.java | 23 +++++++++- .../exoplayer2/ui/PlaybackControlView.java | 42 ++++++++++++------- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 7a3da68d02..b9ab94a543 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -65,6 +65,7 @@ import java.util.concurrent.CopyOnWriteArraySet; // Playback information when there is a pending seek/set source operation. private int maskingWindowIndex; + private int maskingPeriodIndex; private long maskingWindowPositionMs; /** @@ -187,6 +188,22 @@ import java.util.concurrent.CopyOnWriteArraySet; } pendingSeekAcks++; maskingWindowIndex = windowIndex; + if (timeline.isEmpty()) { + maskingPeriodIndex = 0; + } else { + timeline.getWindow(windowIndex, window); + long resolvedPositionMs = + positionMs == C.TIME_UNSET ? window.getDefaultPositionUs() : positionMs; + int periodIndex = window.firstPeriodIndex; + long periodPositionUs = window.getPositionInFirstPeriodUs() + C.msToUs(resolvedPositionMs); + long periodDurationUs = timeline.getPeriod(periodIndex, period).getDurationUs(); + while (periodDurationUs != C.TIME_UNSET && periodPositionUs >= periodDurationUs + && periodIndex < window.lastPeriodIndex) { + periodPositionUs -= periodDurationUs; + periodDurationUs = timeline.getPeriod(++periodIndex, period).getDurationUs(); + } + maskingPeriodIndex = periodIndex; + } if (positionMs == C.TIME_UNSET) { maskingWindowPositionMs = 0; internalPlayer.seekTo(timeline, windowIndex, C.TIME_UNSET); @@ -235,7 +252,11 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public int getCurrentPeriodIndex() { - return playbackInfo.periodIndex; + if (timeline.isEmpty() || pendingSeekAcks > 0) { + return maskingPeriodIndex; + } else { + return playbackInfo.periodIndex; + } } @Override diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index dfa5e9abe0..405595a5ed 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; -import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -34,6 +33,7 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.Arrays; import java.util.Formatter; @@ -345,8 +345,9 @@ public class PlaybackControlView extends FrameLayout { /** * Sets whether the time bar should show all windows, as opposed to just the current one. If the - * timeline has more than {@link #MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR} windows the time bar will - * fall back to showing a single window. + * timeline has a period with unknown duration or more than + * {@link #MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR} windows the time bar will fall back to showing a + * single window. * * @param showMultiWindowTimeBar Whether the time bar should show all windows. */ @@ -523,14 +524,8 @@ public class PlaybackControlView extends FrameLayout { if (player == null) { return; } - if (showMultiWindowTimeBar) { - if (player.getCurrentTimeline().getWindowCount() <= MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR) { - multiWindowTimeBar = true; - return; - } - Log.w(TAG, "Too many windows for multi-window time bar. Falling back to showing one window."); - } - multiWindowTimeBar = false; + multiWindowTimeBar = showMultiWindowTimeBar + && canShowMultiWindowTimeBar(player.getCurrentTimeline(), period); } private void updateProgress() { @@ -568,10 +563,7 @@ public class PlaybackControlView extends FrameLayout { } else { isInAdBreak = false; long periodDurationUs = period.getDurationUs(); - if (periodDurationUs == C.TIME_UNSET) { - durationUs = C.TIME_UNSET; - break; - } + Assertions.checkState(periodDurationUs != C.TIME_UNSET); long periodDurationInWindowUs = periodDurationUs; if (j == window.firstPeriodIndex) { periodDurationInWindowUs -= window.positionInFirstPeriodUs; @@ -797,6 +789,26 @@ public class PlaybackControlView extends FrameLayout { || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS; } + /** + * Returns whether the specified {@code timeline} can be shown on a multi-window time bar. + * + * @param timeline The {@link Timeline} to check. + * @param period A scratch {@link Timeline.Period} instance. + * @return Whether the specified timeline can be shown on a multi-window time bar. + */ + private static boolean canShowMultiWindowTimeBar(Timeline timeline, Timeline.Period period) { + if (timeline.getWindowCount() > MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR) { + return false; + } + int periodCount = timeline.getPeriodCount(); + for (int i = 0; i < periodCount; i++) { + if (timeline.getPeriod(i, period).durationUs == C.TIME_UNSET) { + return false; + } + } + return true; + } + private final class ComponentListener implements ExoPlayer.EventListener, TimeBar.OnScrubListener, OnClickListener {