Commit 2cbf0ef0 by tonihei Committed by Oliver Woodman

Move playback state, isLoading, and track selector result to PlaybackInfo.

This is a no-op change replacing the local variables in ExoPlayerImplInternal
with the new ones in PlaybackInfo.

***
Use playbackState, isLoading and trackSelectorResult from playbackInfo in ExoPlayerImpl.

***
Move duplicated listener notification in ExoPlayerImpl to new method.

Also split reset method in one parts which creates the new playback info
and one part which notifies the listeners. The increment of the pending
operation counter needs to happen in between.

***
Use only one pending operation counter in ExoPlayerImpl.

This also allows to move onSeekProcessed into the notification chain.

***
Replace playback info changing messages to ExoPlayerImpl by single message type.

As they are all handled in the same way, they can be summarized to one message.

***
Only send playback info change notifications once per playback thread message.

This ensures that all concurrent changes actually reach ExoPlayerImpl concurrently.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178907165
parent a5cd0b87
...@@ -56,18 +56,15 @@ public final class ExoPlayerTest extends TestCase { ...@@ -56,18 +56,15 @@ public final class ExoPlayerTest extends TestCase {
* error. * error.
*/ */
public void testPlayEmptyTimeline() throws Exception { public void testPlayEmptyTimeline() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 0); Timeline timeline = Timeline.EMPTY;
FakeRenderer renderer = new FakeRenderer(); FakeRenderer renderer = new FakeRenderer();
// TODO(b/69665207): Without waiting for the timeline update, this test is flaky as the timeline ExoPlayerTestRunner testRunner =
// update happens after the transition to STATE_ENDED and the test runner may already have been new ExoPlayerTestRunner.Builder()
// stopped. Remove action schedule as soon as state changes are part of the masking and the .setTimeline(timeline)
// correct order of events is restored. .setRenderers(renderer)
ActionSchedule actionSchedule = new ActionSchedule.Builder("testPlayEmptyTimeline") .build()
.waitForTimelineChanged(timeline) .start()
.build(); .blockUntilEnded(TIMEOUT_MS);
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setRenderers(renderer).setActionSchedule(actionSchedule)
.build().start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
testRunner.assertNoPositionDiscontinuities(); testRunner.assertNoPositionDiscontinuities();
testRunner.assertTimelinesEqual(timeline); testRunner.assertTimelinesEqual(timeline);
assertEquals(0, renderer.formatReadCount); assertEquals(0, renderer.formatReadCount);
...@@ -307,21 +304,28 @@ public final class ExoPlayerTest extends TestCase { ...@@ -307,21 +304,28 @@ public final class ExoPlayerTest extends TestCase {
public void testSeekProcessedCallback() throws Exception { public void testSeekProcessedCallback() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 2); Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback") ActionSchedule actionSchedule =
// Initial seek before timeline preparation started. Expect immediate seek processed while new ActionSchedule.Builder("testSeekProcessedCallback")
// the player is still in STATE_IDLE. // Initial seek. Expect immediate seek processed.
.pause().seek(5) .pause()
// Wait until the media source starts preparing and issue more initial seeks. Expect only .seek(5)
// one seek processed after the source has been prepared. .waitForSeekProcessed()
.waitForPlaybackState(Player.STATE_BUFFERING).seek(2).seek(10) // Multiple overlapping seeks while the player is still preparing. Expect only one seek
// Wait until media source prepared and re-seek to same position. Expect a seek processed // processed.
// while still being in STATE_READY. .seek(2)
.waitForPlaybackState(Player.STATE_READY).seek(10) .seek(10)
// Start playback and wait until playback reaches second window. // Wait until media source prepared and re-seek to same position. Expect a seek
.play().waitForPositionDiscontinuity() // processed while still being in STATE_READY.
// Seek twice in concession, expecting the first seek to be replaced (and thus except only .waitForPlaybackState(Player.STATE_READY)
// on seek processed callback). .seek(10)
.seek(5).seek(60).build(); // Start playback and wait until playback reaches second window.
.play()
.waitForPositionDiscontinuity()
// Seek twice in concession, expecting the first seek to be replaced (and thus except
// only on seek processed callback).
.seek(5)
.seek(60)
.build();
final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>(); final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>();
Player.EventListener eventListener = new Player.DefaultEventListener() { Player.EventListener eventListener = new Player.DefaultEventListener() {
private int currentPlaybackState = Player.STATE_IDLE; private int currentPlaybackState = Player.STATE_IDLE;
...@@ -340,7 +344,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -340,7 +344,7 @@ public final class ExoPlayerTest extends TestCase {
.setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule) .setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
assertEquals(4, playbackStatesWhenSeekProcessed.size()); assertEquals(4, playbackStatesWhenSeekProcessed.size());
assertEquals(Player.STATE_IDLE, (int) playbackStatesWhenSeekProcessed.get(0)); assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(0));
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(1)); assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(1));
assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(2)); assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(2));
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(3)); assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(3));
...@@ -804,19 +808,24 @@ public final class ExoPlayerTest extends TestCase { ...@@ -804,19 +808,24 @@ public final class ExoPlayerTest extends TestCase {
public void testStopDuringPreparationOverwritesPreparation() throws Exception { public void testStopDuringPreparationOverwritesPreparation() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 1); Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testStopOverwritesPrepare") ActionSchedule actionSchedule =
.waitForPlaybackState(Player.STATE_BUFFERING) new ActionSchedule.Builder("testStopOverwritesPrepare")
.stop(true) .waitForPlaybackState(Player.STATE_BUFFERING)
.build(); .seek(0)
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() .stop(true)
.setTimeline(timeline) .waitForSeekProcessed()
.setActionSchedule(actionSchedule) .build();
.build() ExoPlayerTestRunner testRunner =
.start() new ExoPlayerTestRunner.Builder()
.blockUntilEnded(TIMEOUT_MS); .setTimeline(timeline)
.setActionSchedule(actionSchedule)
.build()
.start()
.blockUntilActionScheduleFinished(TIMEOUT_MS)
.blockUntilEnded(TIMEOUT_MS);
testRunner.assertTimelinesEqual(Timeline.EMPTY); testRunner.assertTimelinesEqual(Timeline.EMPTY);
testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED); testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED);
testRunner.assertNoPositionDiscontinuities(); testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK);
} }
public void testStopAndSeekAfterStopDoesNotResetTimeline() throws Exception { public void testStopAndSeekAfterStopDoesNotResetTimeline() throws Exception {
...@@ -855,8 +864,9 @@ public final class ExoPlayerTest extends TestCase { ...@@ -855,8 +864,9 @@ public final class ExoPlayerTest extends TestCase {
.waitForPlaybackState(Player.STATE_IDLE) .waitForPlaybackState(Player.STATE_IDLE)
.prepareSource( .prepareSource(
new FakeMediaSource(timeline, /* manifest= */ null), new FakeMediaSource(timeline, /* manifest= */ null),
/* resetPosition= */ false, /* resetPosition= */ true,
/* resetState= */ false) /* resetState= */ false)
.waitForPlaybackState(Player.STATE_READY)
.build(); .build();
ExoPlayerTestRunner testRunner = ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder() new ExoPlayerTestRunner.Builder()
......
...@@ -15,35 +15,59 @@ ...@@ -15,35 +15,59 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
/** /**
* Information about an ongoing playback. * Information about an ongoing playback.
*/ */
/* package */ final class PlaybackInfo { /* package */ final class PlaybackInfo {
public final Timeline timeline; public final @Nullable Timeline timeline;
public final Object manifest; public final @Nullable Object manifest;
public final MediaPeriodId periodId; public final MediaPeriodId periodId;
public final long startPositionUs; public final long startPositionUs;
public final long contentPositionUs; public final long contentPositionUs;
public final int playbackState;
public final boolean isLoading;
public final TrackSelectorResult trackSelectorResult;
public volatile long positionUs; public volatile long positionUs;
public volatile long bufferedPositionUs; public volatile long bufferedPositionUs;
public PlaybackInfo(Timeline timeline, Object manifest, int periodIndex, long startPositionUs) { public PlaybackInfo(
this(timeline, manifest, new MediaPeriodId(periodIndex), startPositionUs, C.TIME_UNSET); @Nullable Timeline timeline, long startPositionUs, TrackSelectorResult trackSelectorResult) {
this(
timeline,
/* manifest= */ null,
new MediaPeriodId(0),
startPositionUs,
/* contentPositionUs =*/ C.TIME_UNSET,
Player.STATE_IDLE,
/* isLoading= */ false,
trackSelectorResult);
} }
public PlaybackInfo(Timeline timeline, Object manifest, MediaPeriodId periodId, public PlaybackInfo(
long startPositionUs, long contentPositionUs) { @Nullable Timeline timeline,
@Nullable Object manifest,
MediaPeriodId periodId,
long startPositionUs,
long contentPositionUs,
int playbackState,
boolean isLoading,
TrackSelectorResult trackSelectorResult) {
this.timeline = timeline; this.timeline = timeline;
this.manifest = manifest; this.manifest = manifest;
this.periodId = periodId; this.periodId = periodId;
this.startPositionUs = startPositionUs; this.startPositionUs = startPositionUs;
this.contentPositionUs = contentPositionUs; this.contentPositionUs = contentPositionUs;
positionUs = startPositionUs; this.positionUs = startPositionUs;
bufferedPositionUs = startPositionUs; this.bufferedPositionUs = startPositionUs;
this.playbackState = playbackState;
this.isLoading = isLoading;
this.trackSelectorResult = trackSelectorResult;
} }
public PlaybackInfo fromNewPosition(int periodIndex, long startPositionUs, public PlaybackInfo fromNewPosition(int periodIndex, long startPositionUs,
...@@ -53,19 +77,88 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; ...@@ -53,19 +77,88 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
public PlaybackInfo fromNewPosition(MediaPeriodId periodId, long startPositionUs, public PlaybackInfo fromNewPosition(MediaPeriodId periodId, long startPositionUs,
long contentPositionUs) { long contentPositionUs) {
return new PlaybackInfo(timeline, manifest, periodId, startPositionUs, contentPositionUs); return new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
} }
public PlaybackInfo copyWithPeriodIndex(int periodIndex) { public PlaybackInfo copyWithPeriodIndex(int periodIndex) {
PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest, PlaybackInfo playbackInfo =
periodId.copyWithPeriodIndex(periodIndex), startPositionUs, contentPositionUs); new PlaybackInfo(
timeline,
manifest,
periodId.copyWithPeriodIndex(periodIndex),
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo); copyMutablePositions(this, playbackInfo);
return playbackInfo; return playbackInfo;
} }
public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) { public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) {
PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest, periodId, startPositionUs, PlaybackInfo playbackInfo =
contentPositionUs); new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo);
return playbackInfo;
}
public PlaybackInfo copyWithPlaybackState(int playbackState) {
PlaybackInfo playbackInfo =
new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo);
return playbackInfo;
}
public PlaybackInfo copyWithIsLoading(boolean isLoading) {
PlaybackInfo playbackInfo =
new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo);
return playbackInfo;
}
public PlaybackInfo copyWithTrackSelectorResult(TrackSelectorResult trackSelectorResult) {
PlaybackInfo playbackInfo =
new PlaybackInfo(
timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo); copyMutablePositions(this, playbackInfo);
return playbackInfo; return playbackInfo;
} }
......
...@@ -74,7 +74,7 @@ public final class TrackSelectorResult { ...@@ -74,7 +74,7 @@ public final class TrackSelectorResult {
* @return Whether this result is equivalent to {@code other} for all renderers. * @return Whether this result is equivalent to {@code other} for all renderers.
*/ */
public boolean isEquivalent(TrackSelectorResult other) { public boolean isEquivalent(TrackSelectorResult other) {
if (other == null) { if (other == null || other.selections.length != selections.length) {
return false; return false;
} }
for (int i = 0; i < selections.length; i++) { for (int i = 0; i < selections.length; i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment