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
This commit is contained in:
andrewlewis 2017-04-04 03:53:03 -07:00 committed by Oliver Woodman
parent 6a7db4b1ec
commit 8208a75f0a
2 changed files with 49 additions and 16 deletions

View file

@ -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

View file

@ -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 {