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 {
* error.
*/
public void testPlayEmptyTimeline() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 0);
Timeline timeline = Timeline.EMPTY;
FakeRenderer renderer = new FakeRenderer();
// TODO(b/69665207): Without waiting for the timeline update, this test is flaky as the timeline
// update happens after the transition to STATE_ENDED and the test runner may already have been
// stopped. Remove action schedule as soon as state changes are part of the masking and the
// correct order of events is restored.
ActionSchedule actionSchedule = new ActionSchedule.Builder("testPlayEmptyTimeline")
.waitForTimelineChanged(timeline)
.build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setRenderers(renderer).setActionSchedule(actionSchedule)
.build().start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder()
.setTimeline(timeline)
.setRenderers(renderer)
.build()
.start()
.blockUntilEnded(TIMEOUT_MS);
testRunner.assertNoPositionDiscontinuities();
testRunner.assertTimelinesEqual(timeline);
assertEquals(0, renderer.formatReadCount);
......@@ -307,21 +304,28 @@ public final class ExoPlayerTest extends TestCase {
public void testSeekProcessedCallback() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback")
// Initial seek before timeline preparation started. Expect immediate seek processed while
// the player is still in STATE_IDLE.
.pause().seek(5)
// Wait until the media source starts preparing and issue more initial seeks. Expect only
// one seek processed after the source has been prepared.
.waitForPlaybackState(Player.STATE_BUFFERING).seek(2).seek(10)
// Wait until media source prepared and re-seek to same position. Expect a seek processed
// while still being in STATE_READY.
.waitForPlaybackState(Player.STATE_READY).seek(10)
// 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();
ActionSchedule actionSchedule =
new ActionSchedule.Builder("testSeekProcessedCallback")
// Initial seek. Expect immediate seek processed.
.pause()
.seek(5)
.waitForSeekProcessed()
// Multiple overlapping seeks while the player is still preparing. Expect only one seek
// processed.
.seek(2)
.seek(10)
// Wait until media source prepared and re-seek to same position. Expect a seek
// processed while still being in STATE_READY.
.waitForPlaybackState(Player.STATE_READY)
.seek(10)
// 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<>();
Player.EventListener eventListener = new Player.DefaultEventListener() {
private int currentPlaybackState = Player.STATE_IDLE;
......@@ -340,7 +344,7 @@ public final class ExoPlayerTest extends TestCase {
.setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS);
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_READY, (int) playbackStatesWhenSeekProcessed.get(2));
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(3));
......@@ -804,19 +808,24 @@ public final class ExoPlayerTest extends TestCase {
public void testStopDuringPreparationOverwritesPreparation() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testStopOverwritesPrepare")
.waitForPlaybackState(Player.STATE_BUFFERING)
.stop(true)
.build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline)
.setActionSchedule(actionSchedule)
.build()
.start()
.blockUntilEnded(TIMEOUT_MS);
ActionSchedule actionSchedule =
new ActionSchedule.Builder("testStopOverwritesPrepare")
.waitForPlaybackState(Player.STATE_BUFFERING)
.seek(0)
.stop(true)
.waitForSeekProcessed()
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder()
.setTimeline(timeline)
.setActionSchedule(actionSchedule)
.build()
.start()
.blockUntilActionScheduleFinished(TIMEOUT_MS)
.blockUntilEnded(TIMEOUT_MS);
testRunner.assertTimelinesEqual(Timeline.EMPTY);
testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED);
testRunner.assertNoPositionDiscontinuities();
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK);
}
public void testStopAndSeekAfterStopDoesNotResetTimeline() throws Exception {
......@@ -855,8 +864,9 @@ public final class ExoPlayerTest extends TestCase {
.waitForPlaybackState(Player.STATE_IDLE)
.prepareSource(
new FakeMediaSource(timeline, /* manifest= */ null),
/* resetPosition= */ false,
/* resetPosition= */ true,
/* resetState= */ false)
.waitForPlaybackState(Player.STATE_READY)
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder()
......
......@@ -15,35 +15,59 @@
*/
package com.google.android.exoplayer2;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
/**
* Information about an ongoing playback.
*/
/* package */ final class PlaybackInfo {
public final Timeline timeline;
public final Object manifest;
public final @Nullable Timeline timeline;
public final @Nullable Object manifest;
public final MediaPeriodId periodId;
public final long startPositionUs;
public final long contentPositionUs;
public final int playbackState;
public final boolean isLoading;
public final TrackSelectorResult trackSelectorResult;
public volatile long positionUs;
public volatile long bufferedPositionUs;
public PlaybackInfo(Timeline timeline, Object manifest, int periodIndex, long startPositionUs) {
this(timeline, manifest, new MediaPeriodId(periodIndex), startPositionUs, C.TIME_UNSET);
public PlaybackInfo(
@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,
long startPositionUs, long contentPositionUs) {
public PlaybackInfo(
@Nullable Timeline timeline,
@Nullable Object manifest,
MediaPeriodId periodId,
long startPositionUs,
long contentPositionUs,
int playbackState,
boolean isLoading,
TrackSelectorResult trackSelectorResult) {
this.timeline = timeline;
this.manifest = manifest;
this.periodId = periodId;
this.startPositionUs = startPositionUs;
this.contentPositionUs = contentPositionUs;
positionUs = startPositionUs;
bufferedPositionUs = startPositionUs;
this.positionUs = startPositionUs;
this.bufferedPositionUs = startPositionUs;
this.playbackState = playbackState;
this.isLoading = isLoading;
this.trackSelectorResult = trackSelectorResult;
}
public PlaybackInfo fromNewPosition(int periodIndex, long startPositionUs,
......@@ -53,19 +77,88 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
public PlaybackInfo fromNewPosition(MediaPeriodId periodId, long startPositionUs,
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) {
PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest,
periodId.copyWithPeriodIndex(periodIndex), startPositionUs, contentPositionUs);
PlaybackInfo playbackInfo =
new PlaybackInfo(
timeline,
manifest,
periodId.copyWithPeriodIndex(periodIndex),
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackSelectorResult);
copyMutablePositions(this, playbackInfo);
return playbackInfo;
}
public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) {
PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest, periodId, startPositionUs,
contentPositionUs);
PlaybackInfo playbackInfo =
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);
return playbackInfo;
}
......
......@@ -74,7 +74,7 @@ public final class TrackSelectorResult {
* @return Whether this result is equivalent to {@code other} for all renderers.
*/
public boolean isEquivalent(TrackSelectorResult other) {
if (other == null) {
if (other == null || other.selections.length != selections.length) {
return false;
}
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