Commit 4ad4e3e4 by bachinger Committed by Oliver Woodman

Rollback of https://github.com/google/ExoPlayer/commit/3b22db33ba944df6829b1eff328efb0cd25e1678

*** Original commit ***

add top-level playlist API to ExoPlayer

Public design doc:
https://docs.google.com/document/d/11h0S91KI5TB3NNZUtsCzg0S7r6nyTnF_tDZZAtmY93g

Issue: #6161

***

PiperOrigin-RevId: 270728267
parent cc8b774b
Showing with 228 additions and 949 deletions
......@@ -49,6 +49,7 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep
import com.google.android.exoplayer2.offline.DownloadHelper;
import com.google.android.exoplayer2.offline.DownloadRequest;
import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
......@@ -78,7 +79,6 @@ import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/** An activity that plays media using {@link SimpleExoPlayer}. */
......@@ -141,7 +141,7 @@ public class PlayerActivity extends AppCompatActivity
private DataSource.Factory dataSourceFactory;
private SimpleExoPlayer player;
private List<MediaSource> mediaSources;
private MediaSource mediaSource;
private DefaultTrackSelector trackSelector;
private DefaultTrackSelector.Parameters trackSelectorParameters;
private DebugTextViewHelper debugViewHelper;
......@@ -343,8 +343,8 @@ public class PlayerActivity extends AppCompatActivity
Intent intent = getIntent();
releaseMediaDrms();
mediaSources = createTopLevelMediaSources(intent);
if (mediaSources.isEmpty()) {
mediaSource = createTopLevelMediaSource(intent);
if (mediaSource == null) {
return;
}
......@@ -388,12 +388,12 @@ public class PlayerActivity extends AppCompatActivity
if (haveStartPosition) {
player.seekTo(startWindow, startPosition);
}
player.setMediaItems(mediaSources, /* resetPosition= */ !haveStartPosition);
player.prepare();
player.prepare(mediaSource, !haveStartPosition, false);
updateButtonVisibility();
}
private List<MediaSource> createTopLevelMediaSources(Intent intent) {
@Nullable
private MediaSource createTopLevelMediaSource(Intent intent) {
String action = intent.getAction();
boolean actionIsListView = ACTION_VIEW_LIST.equals(action);
if (!actionIsListView && !ACTION_VIEW.equals(action)) {
......@@ -421,30 +421,34 @@ public class PlayerActivity extends AppCompatActivity
}
}
List<MediaSource> mediaSources = new ArrayList<>();
for (UriSample sample : samples) {
mediaSources.add(createLeafMediaSource(sample));
MediaSource[] mediaSources = new MediaSource[samples.length];
for (int i = 0; i < samples.length; i++) {
mediaSources[i] = createLeafMediaSource(samples[i]);
}
if (seenAdsTagUri && mediaSources.size() == 1) {
MediaSource mediaSource =
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
if (seenAdsTagUri) {
Uri adTagUri = samples[0].adTagUri;
if (!adTagUri.equals(loadedAdTagUri)) {
releaseAdsLoader();
loadedAdTagUri = adTagUri;
}
MediaSource adsMediaSource = createAdsMediaSource(mediaSources.get(0), adTagUri);
if (adsMediaSource != null) {
mediaSources.set(0, adsMediaSource);
if (actionIsListView) {
showToast(R.string.unsupported_ads_in_concatenation);
} else {
showToast(R.string.ima_not_loaded);
if (!adTagUri.equals(loadedAdTagUri)) {
releaseAdsLoader();
loadedAdTagUri = adTagUri;
}
MediaSource adsMediaSource = createAdsMediaSource(mediaSource, adTagUri);
if (adsMediaSource != null) {
mediaSource = adsMediaSource;
} else {
showToast(R.string.ima_not_loaded);
}
}
} else if (seenAdsTagUri && mediaSources.size() > 1) {
showToast(R.string.unsupported_ads_in_concatenation);
releaseAdsLoader();
} else {
releaseAdsLoader();
}
return mediaSources;
return mediaSource;
}
private MediaSource createLeafMediaSource(UriSample parameters) {
......@@ -544,7 +548,7 @@ public class PlayerActivity extends AppCompatActivity
debugViewHelper = null;
player.release();
player = null;
mediaSources = null;
mediaSource = null;
trackSelector = null;
}
if (adsLoader != null) {
......
......@@ -106,6 +106,7 @@ public final class CastPlayer extends BasePlayer {
private int pendingSeekCount;
private int pendingSeekWindowIndex;
private long pendingSeekPositionMs;
private boolean waitingForInitialTimeline;
/**
* @param castContext The context from which the cast session is obtained.
......@@ -167,6 +168,7 @@ public final class CastPlayer extends BasePlayer {
MediaQueueItem[] items, int startIndex, long positionMs, @RepeatMode int repeatMode) {
if (remoteMediaClient != null) {
positionMs = positionMs != C.TIME_UNSET ? positionMs : 0;
waitingForInitialTimeline = true;
return remoteMediaClient.queueLoad(items, startIndex, getCastRepeatMode(repeatMode),
positionMs, null);
}
......@@ -592,13 +594,15 @@ public final class CastPlayer extends BasePlayer {
private void maybeUpdateTimelineAndNotify() {
if (updateTimeline()) {
// TODO: Differentiate TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED and
// TIMELINE_CHANGE_REASON_SOURCE_UPDATE [see internal: b/65152553].
@Player.TimelineChangeReason
int reason =
waitingForInitialTimeline
? Player.TIMELINE_CHANGE_REASON_PREPARED
: Player.TIMELINE_CHANGE_REASON_DYNAMIC;
waitingForInitialTimeline = false;
notificationsBatch.add(
new ListenerNotificationTask(
listener ->
listener.onTimelineChanged(
currentTimeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE)));
listener -> listener.onTimelineChanged(currentTimeline, reason)));
}
}
......
......@@ -30,6 +30,7 @@ import java.util.ArrayList;
private final Timeline.Period period;
private final Timeline timeline;
private boolean prepared;
@Player.State private int state;
private boolean playWhenReady;
private long position;
......@@ -46,17 +47,13 @@ import java.util.ArrayList;
timeline = Timeline.EMPTY;
}
/**
* Sets the timeline on this fake player, which notifies listeners with the changed timeline and
* the given timeline change reason.
*
* @param timeline The new timeline.
* @param timelineChangeReason The reason for the timeline change.
*/
public void updateTimeline(Timeline timeline, @TimelineChangeReason int timelineChangeReason) {
/** Sets the timeline on this fake player, which notifies listeners with the changed timeline. */
public void updateTimeline(Timeline timeline) {
for (Player.EventListener listener : listeners) {
listener.onTimelineChanged(timeline, timelineChangeReason);
listener.onTimelineChanged(
timeline, prepared ? TIMELINE_CHANGE_REASON_DYNAMIC : TIMELINE_CHANGE_REASON_PREPARED);
}
prepared = true;
}
/**
......
......@@ -285,9 +285,7 @@ public class ImaAdsLoaderTest {
public void onAdPlaybackState(AdPlaybackState adPlaybackState) {
adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs);
this.adPlaybackState = adPlaybackState;
fakeExoPlayer.updateTimeline(
new SinglePeriodAdTimeline(contentTimeline, adPlaybackState),
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
fakeExoPlayer.updateTimeline(new SinglePeriodAdTimeline(contentTimeline, adPlaybackState));
}
@Override
......
......@@ -28,7 +28,6 @@ import com.google.android.exoplayer2.source.LoopingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
......@@ -40,7 +39,6 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import java.util.List;
/**
* An extensible media player that plays {@link MediaSource}s. Instances can be obtained from {@link
......@@ -141,7 +139,7 @@ public interface ExoPlayer extends Player {
private LoadControl loadControl;
private BandwidthMeter bandwidthMeter;
private Looper looper;
@Nullable private AnalyticsCollector analyticsCollector;
private AnalyticsCollector analyticsCollector;
private boolean useLazyPreparation;
private boolean buildCalled;
......@@ -172,7 +170,7 @@ public interface ExoPlayer extends Player {
new DefaultLoadControl(),
DefaultBandwidthMeter.getSingletonInstance(context),
Util.getLooper(),
/* analyticsCollector= */ null,
new AnalyticsCollector(Clock.DEFAULT),
/* useLazyPreparation= */ true,
Clock.DEFAULT);
}
......@@ -199,7 +197,7 @@ public interface ExoPlayer extends Player {
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Looper looper,
@Nullable AnalyticsCollector analyticsCollector,
AnalyticsCollector analyticsCollector,
boolean useLazyPreparation,
Clock clock) {
Assertions.checkArgument(renderers.length > 0);
......@@ -320,156 +318,38 @@ public interface ExoPlayer extends Player {
Assertions.checkState(!buildCalled);
buildCalled = true;
return new ExoPlayerImpl(
renderers,
trackSelector,
loadControl,
bandwidthMeter,
analyticsCollector,
useLazyPreparation,
clock,
looper);
renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
}
}
/** Returns the {@link Looper} associated with the playback thread. */
Looper getPlaybackLooper();
/** @deprecated Use {@link #prepare()} instead. */
@Deprecated
/**
* Retries a failed or stopped playback. Does nothing if the player has been reset, or if playback
* has not failed or been stopped.
*/
void retry();
/** @deprecated Use {@link #setMediaItem(MediaSource)} and {@link #prepare()} instead. */
@Deprecated
void prepare(MediaSource mediaSource);
/** @deprecated Use {@link #setMediaItems(List, int, long)} and {@link #prepare()} instead. */
@Deprecated
void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);
/** Prepares the player. */
void prepare();
/**
* Clears the playlist and adds the specified {@link MediaSource MediaSources}.
*
* @param mediaItems The new {@link MediaSource MediaSources}.
* Prepares the player to play the provided {@link MediaSource}. Equivalent to {@code
* prepare(mediaSource, true, true)}.
*/
void setMediaItems(List<MediaSource> mediaItems);
void prepare(MediaSource mediaSource);
/**
* Clears the playlist and adds the specified {@link MediaSource MediaSources}.
* Prepares the player to play the provided {@link MediaSource}, optionally resetting the playback
* position the default position in the first {@link Timeline.Window}.
*
* @param mediaItems The new {@link MediaSource MediaSources}.
* @param mediaSource The {@link MediaSource} to play.
* @param resetPosition Whether the playback position should be reset to the default position in
* the first {@link Timeline.Window}. If false, playback will start from the position defined
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
* @param resetState Whether the timeline, manifest, tracks and track selections should be reset.
* Should be true unless the player is being prepared to play the same media as it was playing
* previously (e.g. if playback failed and is being retried).
*/
void setMediaItems(List<MediaSource> mediaItems, boolean resetPosition);
/**
* Clears the playlist and adds the specified {@link MediaSource MediaSources}.
*
* @param mediaItems The new {@link MediaSource MediaSources}.
* @param startWindowIndex The window index to start playback from. If {@link C#INDEX_UNSET} is
* passed, the current position is not reset.
* @param startPositionMs The position in milliseconds to start playback from. If {@link
* C#TIME_UNSET} is passed, the default position of the given window is used. In any case, if
* {@code startWindowIndex} is set to {@link C#INDEX_UNSET}, this parameter is ignored and the
* position is not reset at all.
*/
void setMediaItems(List<MediaSource> mediaItems, int startWindowIndex, long startPositionMs);
/**
* Clears the playlist and adds the specified {@link MediaSource}.
*
* @param mediaItem The new {@link MediaSource}.
*/
void setMediaItem(MediaSource mediaItem);
/**
* Clears the playlist and adds the specified {@link MediaSource}.
*
* @param mediaItem The new {@link MediaSource}.
* @param startPositionMs The position in milliseconds to start playback from.
*/
void setMediaItem(MediaSource mediaItem, long startPositionMs);
/**
* Adds a media item to the end of the playlist.
*
* @param mediaSource The {@link MediaSource} to add.
*/
void addMediaItem(MediaSource mediaSource);
/**
* Adds a media item at the given index of the playlist.
*
* @param index The index at which to add the item.
* @param mediaSource The {@link MediaSource} to add.
*/
void addMediaItem(int index, MediaSource mediaSource);
/**
* Adds a list of media items to the end of the playlist.
*
* @param mediaSources The {@link MediaSource MediaSources} to add.
*/
void addMediaItems(List<MediaSource> mediaSources);
/**
* Adds a list of media items at the given index of the playlist.
*
* @param index The index at which to add the media items.
* @param mediaSources The {@link MediaSource MediaSources} to add.
*/
void addMediaItems(int index, List<MediaSource> mediaSources);
/**
* Moves the media item at the current index to the new index.
*
* @param currentIndex The current index of the media item to move.
* @param newIndex The new index of the media item. If the new index is larger than the size of
* the playlist the item is moved to the end of the playlist.
*/
void moveMediaItem(int currentIndex, int newIndex);
/**
* Moves the media item range to the new index.
*
* @param fromIndex The start of the range to move.
* @param toIndex The first item not to be included in the range (exclusive).
* @param newIndex The new index of the first media item of the range. If the new index is larger
* than the size of the remaining playlist after removing the range, the range is moved to the
* end of the playlist.
*/
void moveMediaItems(int fromIndex, int toIndex, int newIndex);
/**
* Removes the media item at the given index of the playlist.
*
* @param index The index at which to remove the media item.
* @return The removed {@link MediaSource} or null if no item exists at the given index.
*/
@Nullable
MediaSource removeMediaItem(int index);
/**
* Removes a range of media items from the playlist.
*
* @param fromIndex The index at which to start removing media items.
* @param toIndex The index of the first item to be kept (exclusive).
*/
void removeMediaItems(int fromIndex, int toIndex);
/** Clears the playlist. */
void clearMediaItems();
/**
* Sets the shuffle order.
*
* @param shuffleOrder The shuffle order.
*/
void setShuffleOrder(ShuffleOrder shuffleOrder);
void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);
/**
* Creates a message that can be sent to a {@link PlayerMessage.Target}. By default, the message
......
......@@ -296,7 +296,6 @@ public final class ExoPlayerFactory {
drmSessionManager,
bandwidthMeter,
analyticsCollector,
/* useLazyPreparation= */ true,
Clock.DEFAULT,
looper);
}
......@@ -345,13 +344,6 @@ public final class ExoPlayerFactory {
BandwidthMeter bandwidthMeter,
Looper looper) {
return new ExoPlayerImpl(
renderers,
trackSelector,
loadControl,
bandwidthMeter,
/* analyticsCollector= */ null,
/* useLazyPreparation= */ true,
Clock.DEFAULT,
looper);
renderers, trackSelector, loadControl, bandwidthMeter, Clock.DEFAULT, looper);
}
}
......@@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.source.ClippingMediaPeriod;
import com.google.android.exoplayer2.source.EmptySampleStream;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroupArray;
......@@ -55,7 +56,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
private final boolean[] mayRetainStreamFlags;
private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector trackSelector;
private final Playlist playlist;
private final MediaSource mediaSource;
@Nullable private MediaPeriodHolder next;
private TrackGroupArray trackGroups;
......@@ -69,7 +70,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
* @param rendererPositionOffsetUs The renderer time of the start of the period, in microseconds.
* @param trackSelector The track selector.
* @param allocator The allocator.
* @param playlist The playlist.
* @param mediaSource The media source that produced the media period.
* @param info Information used to identify this media period in its timeline period.
* @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each
* renderer.
......@@ -79,13 +80,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
long rendererPositionOffsetUs,
TrackSelector trackSelector,
Allocator allocator,
Playlist playlist,
MediaSource mediaSource,
MediaPeriodInfo info,
TrackSelectorResult emptyTrackSelectorResult) {
this.rendererCapabilities = rendererCapabilities;
this.rendererPositionOffsetUs = rendererPositionOffsetUs;
this.trackSelector = trackSelector;
this.playlist = playlist;
this.mediaSource = mediaSource;
this.uid = info.id.periodUid;
this.info = info;
this.trackGroups = TrackGroupArray.EMPTY;
......@@ -93,7 +94,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
sampleStreams = new SampleStream[rendererCapabilities.length];
mayRetainStreamFlags = new boolean[rendererCapabilities.length];
mediaPeriod =
createMediaPeriod(info.id, playlist, allocator, info.startPositionUs, info.endPositionUs);
createMediaPeriod(
info.id, mediaSource, allocator, info.startPositionUs, info.endPositionUs);
}
/**
......@@ -303,7 +305,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Releases the media period. No other method should be called after the release. */
public void release() {
disableTrackSelectionsInResult();
releaseMediaPeriod(info.endPositionUs, playlist, mediaPeriod);
releaseMediaPeriod(info.endPositionUs, mediaSource, mediaPeriod);
}
/**
......@@ -400,11 +402,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Returns a media period corresponding to the given {@code id}. */
private static MediaPeriod createMediaPeriod(
MediaPeriodId id,
Playlist playlist,
MediaSource mediaSource,
Allocator allocator,
long startPositionUs,
long endPositionUs) {
MediaPeriod mediaPeriod = playlist.createPeriod(id, allocator, startPositionUs);
MediaPeriod mediaPeriod = mediaSource.createPeriod(id, allocator, startPositionUs);
if (endPositionUs != C.TIME_UNSET && endPositionUs != C.TIME_END_OF_SOURCE) {
mediaPeriod =
new ClippingMediaPeriod(
......@@ -415,12 +417,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Releases the given {@code mediaPeriod}, logging and suppressing any errors. */
private static void releaseMediaPeriod(
long endPositionUs, Playlist playlist, MediaPeriod mediaPeriod) {
long endPositionUs, MediaSource mediaSource, MediaPeriod mediaPeriod) {
try {
if (endPositionUs != C.TIME_UNSET && endPositionUs != C.TIME_END_OF_SOURCE) {
playlist.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
} else {
playlist.releasePeriod(mediaPeriod);
mediaSource.releasePeriod(mediaPeriod);
}
} catch (RuntimeException e) {
// There's nothing we can do.
......
......@@ -19,6 +19,7 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Player.RepeatMode;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
......@@ -133,7 +134,7 @@ import com.google.android.exoplayer2.util.Assertions;
* @param rendererCapabilities The renderer capabilities.
* @param trackSelector The track selector.
* @param allocator The allocator.
* @param playlist The playlist.
* @param mediaSource The media source that produced the media period.
* @param info Information used to identify this media period in its timeline period.
* @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each
* renderer.
......@@ -142,7 +143,7 @@ import com.google.android.exoplayer2.util.Assertions;
RendererCapabilities[] rendererCapabilities,
TrackSelector trackSelector,
Allocator allocator,
Playlist playlist,
MediaSource mediaSource,
MediaPeriodInfo info,
TrackSelectorResult emptyTrackSelectorResult) {
long rendererPositionOffsetUs =
......@@ -157,7 +158,7 @@ import com.google.android.exoplayer2.util.Assertions;
rendererPositionOffsetUs,
trackSelector,
allocator,
playlist,
mediaSource,
info,
emptyTrackSelectorResult);
if (loading != null) {
......
......@@ -356,8 +356,7 @@ public interface Player {
* {@link #onPositionDiscontinuity(int)}.
*
* @param timeline The latest timeline. Never null, but may be empty.
* @param manifest The latest manifest in case the timeline has a single window only. Always
* null if the timeline has more than a single window.
* @param manifest The latest manifest. May be null.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
* @deprecated Use {@link #onTimelineChanged(Timeline, int)} instead. The manifest can be
* accessed by using {@link #getCurrentManifest()} or {@code timeline.getWindow(windowIndex,
......@@ -585,17 +584,25 @@ public interface Player {
int DISCONTINUITY_REASON_INTERNAL = 4;
/**
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
* #TIMELINE_CHANGE_REASON_SOURCE_UPDATE}.
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PREPARED}, {@link
* #TIMELINE_CHANGE_REASON_RESET} or {@link #TIMELINE_CHANGE_REASON_DYNAMIC}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, TIMELINE_CHANGE_REASON_SOURCE_UPDATE})
@IntDef({
TIMELINE_CHANGE_REASON_PREPARED,
TIMELINE_CHANGE_REASON_RESET,
TIMELINE_CHANGE_REASON_DYNAMIC
})
@interface TimelineChangeReason {}
/** Timeline changed as a result of a change of the playlist items or the order of the items. */
int TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED = 0;
/** Timeline changed as a result of a dynamic update introduced by the played media. */
int TIMELINE_CHANGE_REASON_SOURCE_UPDATE = 1;
/** Timeline and manifest changed as a result of a player initialization with new media. */
int TIMELINE_CHANGE_REASON_PREPARED = 0;
/** Timeline and manifest changed as a result of a player reset. */
int TIMELINE_CHANGE_REASON_RESET = 1;
/**
* Timeline or manifest changed as a result of an dynamic update introduced by the played media.
*/
int TIMELINE_CHANGE_REASON_DYNAMIC = 2;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable
......
......@@ -19,7 +19,6 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/**
* A flexible representation of the structure of media. A timeline is able to represent the
......@@ -271,46 +270,6 @@ public abstract class Timeline {
return positionInFirstPeriodUs;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Window that = (Window) obj;
return Util.areEqual(uid, that.uid)
&& Util.areEqual(tag, that.tag)
&& Util.areEqual(manifest, that.manifest)
&& presentationStartTimeMs == that.presentationStartTimeMs
&& windowStartTimeMs == that.windowStartTimeMs
&& isSeekable == that.isSeekable
&& isDynamic == that.isDynamic
&& defaultPositionUs == that.defaultPositionUs
&& durationUs == that.durationUs
&& firstPeriodIndex == that.firstPeriodIndex
&& lastPeriodIndex == that.lastPeriodIndex
&& positionInFirstPeriodUs == that.positionInFirstPeriodUs;
}
@Override
public int hashCode() {
int result = 7;
result = 31 * result + uid.hashCode();
result = 31 * result + (tag == null ? 0 : tag.hashCode());
result = 31 * result + (manifest == null ? 0 : manifest.hashCode());
result = 31 * result + (int) (presentationStartTimeMs ^ (presentationStartTimeMs >>> 32));
result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32));
result = 31 * result + (isSeekable ? 1 : 0);
result = 31 * result + (isDynamic ? 1 : 0);
result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32));
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));
result = 31 * result + firstPeriodIndex;
result = 31 * result + lastPeriodIndex;
result = 31 * result + (int) (positionInFirstPeriodUs ^ (positionInFirstPeriodUs >>> 32));
return result;
}
}
/**
......@@ -567,34 +526,6 @@ public abstract class Timeline {
return adPlaybackState.adResumePositionUs;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Period that = (Period) obj;
return Util.areEqual(id, that.id)
&& Util.areEqual(uid, that.uid)
&& windowIndex == that.windowIndex
&& durationUs == that.durationUs
&& positionInWindowUs == that.positionInWindowUs
&& Util.areEqual(adPlaybackState, that.adPlaybackState);
}
@Override
public int hashCode() {
int result = 7;
result = 31 * result + (id == null ? 0 : id.hashCode());
result = 31 * result + (uid == null ? 0 : uid.hashCode());
result = 31 * result + windowIndex;
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));
result = 31 * result + (int) (positionInWindowUs ^ (positionInWindowUs >>> 32));
result = 31 * result + (adPlaybackState == null ? 0 : adPlaybackState.hashCode());
return result;
}
}
/** An empty timeline. */
......
......@@ -132,8 +132,11 @@ public class AnalyticsCollector
}
}
/** Resets the analytics collector for a new playlist. */
public final void resetForNewPlaylist() {
/**
* Resets the analytics collector for a new media source. Should be called before the player is
* prepared with a new media source.
*/
public final void resetForNewMediaSource() {
// Copying the list is needed because onMediaPeriodReleased will modify the list.
List<MediaPeriodInfo> mediaPeriodInfos =
new ArrayList<>(mediaPeriodQueueTracker.mediaPeriodInfoQueue);
......@@ -783,13 +786,9 @@ public class AnalyticsCollector
/** Updates the queue with a newly created media period. */
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
int periodIndex = timeline.getIndexOfPeriod(mediaPeriodId.periodUid);
boolean isInTimeline = periodIndex != C.INDEX_UNSET;
boolean isInTimeline = timeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET;
MediaPeriodInfo mediaPeriodInfo =
new MediaPeriodInfo(
mediaPeriodId,
isInTimeline ? timeline : Timeline.EMPTY,
isInTimeline ? timeline.getPeriod(periodIndex, period).windowIndex : windowIndex);
new MediaPeriodInfo(mediaPeriodId, isInTimeline ? timeline : Timeline.EMPTY, windowIndex);
mediaPeriodInfoQueue.add(mediaPeriodInfo);
mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo);
lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0);
......@@ -805,7 +804,7 @@ public class AnalyticsCollector
public boolean onMediaPeriodReleased(MediaPeriodId mediaPeriodId) {
MediaPeriodInfo mediaPeriodInfo = mediaPeriodIdToInfo.remove(mediaPeriodId);
if (mediaPeriodInfo == null) {
// The media period has already been removed from the queue in resetForNewPlaylist().
// The media period has already been removed from the queue in resetForNewMediaSource().
return false;
}
mediaPeriodInfoQueue.remove(mediaPeriodInfo);
......
......@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
package com.google.android.exoplayer2.source;
import android.util.Pair;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Assertions;
/** Abstract base class for the concatenation of one or more {@link Timeline}s. */
public abstract class AbstractConcatenatedTimeline extends Timeline {
/* package */ abstract class AbstractConcatenatedTimeline extends Timeline {
private final int childCount;
private final ShuffleOrder shuffleOrder;
......@@ -74,8 +76,8 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
}
@Override
public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
public int getNextWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (isAtomic) {
// Adapt repeat and shuffle mode to atomic concatenation.
repeatMode = repeatMode == Player.REPEAT_MODE_ONE ? Player.REPEAT_MODE_ALL : repeatMode;
......@@ -84,10 +86,12 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
// Find next window within current child.
int childIndex = getChildIndexByWindowIndex(windowIndex);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
int nextWindowIndexInChild = getTimelineByChildIndex(childIndex).getNextWindowIndex(
windowIndex - firstWindowIndexInChild,
repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode,
shuffleModeEnabled);
int nextWindowIndexInChild =
getTimelineByChildIndex(childIndex)
.getNextWindowIndex(
windowIndex - firstWindowIndexInChild,
repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode,
shuffleModeEnabled);
if (nextWindowIndexInChild != C.INDEX_UNSET) {
return firstWindowIndexInChild + nextWindowIndexInChild;
}
......@@ -108,8 +112,8 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
}
@Override
public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
public int getPreviousWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (isAtomic) {
// Adapt repeat and shuffle mode to atomic concatenation.
repeatMode = repeatMode == Player.REPEAT_MODE_ONE ? Player.REPEAT_MODE_ALL : repeatMode;
......@@ -118,10 +122,12 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
// Find previous window within current child.
int childIndex = getChildIndexByWindowIndex(windowIndex);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
int previousWindowIndexInChild = getTimelineByChildIndex(childIndex).getPreviousWindowIndex(
windowIndex - firstWindowIndexInChild,
repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode,
shuffleModeEnabled);
int previousWindowIndexInChild =
getTimelineByChildIndex(childIndex)
.getPreviousWindowIndex(
windowIndex - firstWindowIndexInChild,
repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode,
shuffleModeEnabled);
if (previousWindowIndexInChild != C.INDEX_UNSET) {
return firstWindowIndexInChild + previousWindowIndexInChild;
}
......@@ -219,8 +225,8 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
int childIndex = getChildIndexByPeriodIndex(periodIndex);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex);
getTimelineByChildIndex(childIndex).getPeriod(periodIndex - firstPeriodIndexInChild, period,
setIds);
getTimelineByChildIndex(childIndex)
.getPeriod(periodIndex - firstPeriodIndexInChild, period, setIds);
period.windowIndex += firstWindowIndexInChild;
if (setIds) {
period.uid =
......@@ -242,7 +248,8 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
return C.INDEX_UNSET;
}
int periodIndexInChild = getTimelineByChildIndex(childIndex).getIndexOfPeriod(periodUid);
return periodIndexInChild == C.INDEX_UNSET ? C.INDEX_UNSET
return periodIndexInChild == C.INDEX_UNSET
? C.INDEX_UNSET
: getFirstPeriodIndexByChildIndex(childIndex) + periodIndexInChild;
}
......@@ -307,13 +314,14 @@ public abstract class AbstractConcatenatedTimeline extends Timeline {
protected abstract Object getChildUidByChildIndex(int childIndex);
private int getNextChildIndex(int childIndex, boolean shuffleModeEnabled) {
return shuffleModeEnabled ? shuffleOrder.getNextIndex(childIndex)
return shuffleModeEnabled
? shuffleOrder.getNextIndex(childIndex)
: childIndex < childCount - 1 ? childIndex + 1 : C.INDEX_UNSET;
}
private int getPreviousChildIndex(int childIndex, boolean shuffleModeEnabled) {
return shuffleModeEnabled ? shuffleOrder.getPreviousIndex(childIndex)
return shuffleModeEnabled
? shuffleOrder.getPreviousIndex(childIndex)
: childIndex > 0 ? childIndex - 1 : C.INDEX_UNSET;
}
}
......@@ -19,7 +19,6 @@ import android.os.Handler;
import android.os.Message;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.AbstractConcatenatedTimeline;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource.MediaSourceHolder;
......
......@@ -16,7 +16,6 @@
package com.google.android.exoplayer2.source;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.AbstractConcatenatedTimeline;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
......
......@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Window;
......@@ -62,7 +61,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
}
/** Returns the {@link Timeline}. */
public synchronized Timeline getTimeline() {
public Timeline getTimeline() {
return timeline;
}
......@@ -130,7 +129,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
}
@Override
protected synchronized void onChildSourceInfoRefreshed(
protected void onChildSourceInfoRefreshed(
Void id, MediaSource mediaSource, Timeline newTimeline) {
if (isPrepared) {
timeline = timeline.cloneWithUpdatedTimeline(newTimeline);
......@@ -294,8 +293,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
}
/** Dummy placeholder timeline with one dynamic window with a period of indeterminate duration. */
@VisibleForTesting
public static final class DummyTimeline extends Timeline {
private static final class DummyTimeline extends Timeline {
@Nullable private final Object tag;
......@@ -334,8 +332,8 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
@Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
return period.set(
/* id= */ setIds ? 0 : null,
/* uid= */ setIds ? MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID : null,
/* id= */ 0,
/* uid= */ MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID,
/* windowIndex= */ 0,
/* durationUs = */ C.TIME_UNSET,
/* positionInWindowUs= */ 0);
......
......@@ -594,10 +594,12 @@ public class EventLogger implements AnalyticsListener {
private static String getTimelineChangeReasonString(@Player.TimelineChangeReason int reason) {
switch (reason) {
case Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE:
return "SOURCE_UPDATE";
case Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED:
return "PLAYLIST_CHANGED";
case Player.TIMELINE_CHANGE_REASON_PREPARED:
return "PREPARED";
case Player.TIMELINE_CHANGE_REASON_RESET:
return "RESET";
case Player.TIMELINE_CHANGE_REASON_DYNAMIC:
return "DYNAMIC";
default:
return "?";
}
......
......@@ -53,7 +53,6 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
......@@ -2025,42 +2024,6 @@ public final class Util {
}
}
/**
* Checks whether the timelines are the same.
*
* @param firstTimeline The first {@link Timeline}.
* @param secondTimeline The second {@link Timeline} to compare with.
* @return {@code true} if the both timelines are the same.
*/
public static boolean areTimelinesSame(Timeline firstTimeline, Timeline secondTimeline) {
if (firstTimeline == secondTimeline) {
return true;
}
if (secondTimeline.getWindowCount() != firstTimeline.getWindowCount()
|| secondTimeline.getPeriodCount() != firstTimeline.getPeriodCount()) {
return false;
}
Timeline.Window firstWindow = new Timeline.Window();
Timeline.Period firstPeriod = new Timeline.Period();
Timeline.Window secondWindow = new Timeline.Window();
Timeline.Period secondPeriod = new Timeline.Period();
for (int i = 0; i < firstTimeline.getWindowCount(); i++) {
if (!firstTimeline
.getWindow(i, firstWindow)
.equals(secondTimeline.getWindow(i, secondWindow))) {
return false;
}
}
for (int i = 0; i < firstTimeline.getPeriodCount(); i++) {
if (!firstTimeline
.getPeriod(i, firstPeriod, /* setIds= */ true)
.equals(secondTimeline.getPeriod(i, secondPeriod, /* setIds= */ true))) {
return false;
}
}
return true;
}
private static HashMap<String, String> createIso3ToIso2Map() {
String[] iso2Languages = Locale.getISOLanguages();
HashMap<String, String> iso3ToIso2 =
......
......@@ -15,8 +15,6 @@
*/
package com.google.android.exoplayer2;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
......@@ -60,142 +58,4 @@ public class TimelineTest {
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
}
@Test
public void testWindowEquals() {
Timeline.Window window = new Timeline.Window();
assertThat(window).isEqualTo(new Timeline.Window());
Timeline.Window otherWindow = new Timeline.Window();
otherWindow.tag = new Object();
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.manifest = new Object();
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.presentationStartTimeMs = C.TIME_UNSET;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.windowStartTimeMs = C.TIME_UNSET;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.isSeekable = true;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.isDynamic = true;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.defaultPositionUs = C.TIME_UNSET;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.durationUs = C.TIME_UNSET;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.firstPeriodIndex = 1;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.lastPeriodIndex = 1;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
otherWindow.positionInFirstPeriodUs = C.TIME_UNSET;
assertThat(window).isNotEqualTo(otherWindow);
window.uid = new Object();
window.tag = new Object();
window.manifest = new Object();
window.presentationStartTimeMs = C.TIME_UNSET;
window.windowStartTimeMs = C.TIME_UNSET;
window.isSeekable = true;
window.isDynamic = true;
window.defaultPositionUs = C.TIME_UNSET;
window.durationUs = C.TIME_UNSET;
window.firstPeriodIndex = 1;
window.lastPeriodIndex = 1;
window.positionInFirstPeriodUs = C.TIME_UNSET;
otherWindow =
otherWindow.set(
window.uid,
window.tag,
window.manifest,
window.presentationStartTimeMs,
window.windowStartTimeMs,
window.isSeekable,
window.isDynamic,
window.defaultPositionUs,
window.durationUs,
window.firstPeriodIndex,
window.lastPeriodIndex,
window.positionInFirstPeriodUs);
assertThat(window).isEqualTo(otherWindow);
}
@Test
public void testWindowHashCode() {
Timeline.Window window = new Timeline.Window();
Timeline.Window otherWindow = new Timeline.Window();
assertThat(window.hashCode()).isEqualTo(otherWindow.hashCode());
window.tag = new Object();
assertThat(window.hashCode()).isNotEqualTo(otherWindow.hashCode());
otherWindow.tag = window.tag;
assertThat(window.hashCode()).isEqualTo(otherWindow.hashCode());
}
@Test
public void testPeriodEquals() {
Timeline.Period period = new Timeline.Period();
assertThat(period).isEqualTo(new Timeline.Period());
Timeline.Period otherPeriod = new Timeline.Period();
otherPeriod.id = new Object();
assertThat(period).isNotEqualTo(otherPeriod);
otherPeriod = new Timeline.Period();
otherPeriod.uid = new Object();
assertThat(period).isNotEqualTo(otherPeriod);
otherPeriod = new Timeline.Period();
otherPeriod.windowIndex = 12;
assertThat(period).isNotEqualTo(otherPeriod);
otherPeriod = new Timeline.Period();
otherPeriod.durationUs = 11L;
assertThat(period).isNotEqualTo(otherPeriod);
otherPeriod = new Timeline.Period();
period.id = new Object();
period.uid = new Object();
period.windowIndex = 1;
period.durationUs = 123L;
otherPeriod =
otherPeriod.set(
period.id,
period.uid,
period.windowIndex,
period.durationUs,
/* positionInWindowUs= */ 0);
assertThat(period).isEqualTo(otherPeriod);
}
@Test
public void testPeriodHashCode() {
Timeline.Period period = new Timeline.Period();
Timeline.Period otherPeriod = new Timeline.Period();
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
period.windowIndex = 12;
assertThat(period.hashCode()).isNotEqualTo(otherPeriod.hashCode());
otherPeriod.windowIndex = period.windowIndex;
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
}
}
......@@ -27,10 +27,10 @@ import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.testutil.Action.ClearVideoSurface;
import com.google.android.exoplayer2.testutil.Action.ExecuteRunnable;
import com.google.android.exoplayer2.testutil.Action.PlayUntilPosition;
import com.google.android.exoplayer2.testutil.Action.PrepareSource;
import com.google.android.exoplayer2.testutil.Action.Seek;
import com.google.android.exoplayer2.testutil.Action.SendMessages;
import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady;
......@@ -38,7 +38,6 @@ import com.google.android.exoplayer2.testutil.Action.SetPlaybackParameters;
import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled;
import com.google.android.exoplayer2.testutil.Action.SetRepeatMode;
import com.google.android.exoplayer2.testutil.Action.SetShuffleModeEnabled;
import com.google.android.exoplayer2.testutil.Action.SetShuffleOrder;
import com.google.android.exoplayer2.testutil.Action.SetVideoSurface;
import com.google.android.exoplayer2.testutil.Action.Stop;
import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException;
......@@ -170,19 +169,7 @@ public final class ActionSchedule {
* @return The builder, for convenience.
*/
public Builder seek(int windowIndex, long positionMs) {
return apply(new Seek(tag, windowIndex, positionMs, /* catchIllegalSeekException= */ false));
}
/**
* Schedules a seek action to be executed.
*
* @param windowIndex The window to seek to.
* @param positionMs The seek position.
* @param catchIllegalSeekException Whether an illegal seek position should be caught or not.
* @return The builder, for convenience.
*/
public Builder seek(int windowIndex, long positionMs, boolean catchIllegalSeekException) {
return apply(new Seek(tag, windowIndex, positionMs, catchIllegalSeekException));
return apply(new Seek(tag, windowIndex, positionMs));
}
/**
......@@ -314,100 +301,23 @@ public final class ActionSchedule {
}
/**
* Schedules a set media items action to be executed.
*
* @param windowIndex The window index to start playback from or {@link C#INDEX_UNSET} if the
* playback position should not be reset.
* @param positionMs The position in milliseconds from where playback should start. If {@link
* C#TIME_UNSET} is passed the default position is used. In any case, if {@code windowIndex}
* is set to {@link C#INDEX_UNSET} the position is not reset at all and this parameter is
* ignored.
* @return The builder, for convenience.
*/
public Builder setMediaItems(int windowIndex, long positionMs, MediaSource... sources) {
return apply(new Action.SetMediaItems(tag, windowIndex, positionMs, sources));
}
/**
* Schedules a set media items action to be executed.
*
* @param resetPosition Whether the playback position should be reset.
* @return The builder, for convenience.
*/
public Builder setMediaItems(boolean resetPosition, MediaSource... sources) {
return apply(new Action.SetMediaItemsResetPosition(tag, resetPosition, sources));
}
/**
* Schedules a set media items action to be executed.
*
* @param mediaSources The media sources to add.
* @return The builder, for convenience.
*/
public Builder setMediaItems(MediaSource... mediaSources) {
return apply(
new Action.SetMediaItems(
tag, /* windowIndex= */ C.INDEX_UNSET, /* positionMs= */ C.TIME_UNSET, mediaSources));
}
/**
* Schedules a add media items action to be executed.
*
* @param mediaSources The media sources to add.
* @return The builder, for convenience.
*/
public Builder addMediaItems(MediaSource... mediaSources) {
return apply(new Action.AddMediaItems(tag, mediaSources));
}
/**
* Schedules a move media item action to be executed.
* Schedules a new source preparation action to be executed.
*
* @param currentIndex The current index of the item to move.
* @param newIndex The index after the item has been moved.
* @return The builder, for convenience.
*/
public Builder moveMediaItem(int currentIndex, int newIndex) {
return apply(new Action.MoveMediaItem(tag, currentIndex, newIndex));
public Builder prepareSource(MediaSource mediaSource) {
return apply(new PrepareSource(tag, mediaSource));
}
/**
* Schedules a remove media item action to be executed.
* Schedules a new source preparation action to be executed.
*
* @param index The index of the media item to be removed.
* @see com.google.android.exoplayer2.ExoPlayer#prepare(MediaSource, boolean, boolean)
* @return The builder, for convenience.
*/
public Builder removeMediaItem(int index) {
return apply(new Action.RemoveMediaItem(tag, index));
}
/**
* Schedules a remove media items action to be executed.
*
* @param fromIndex The start of the range of media items to be removed.
* @param toIndex The end of the range of media items to be removed (exclusive).
* @return The builder, for convenience.
*/
public Builder removeMediaItems(int fromIndex, int toIndex) {
return apply(new Action.RemoveMediaItems(tag, fromIndex, toIndex));
}
/**
* Schedules a prepare action to be executed.
*
* @return The builder, for convenience.
*/
public Builder prepare() {
return apply(new Action.Prepare(tag));
}
/**
* Schedules a clear media items action to be created.
*
* @return The builder. for convenience,
*/
public Builder clearMediaItems() {
return apply(new Action.ClearMediaItems(tag));
public Builder prepareSource(
MediaSource mediaSource, boolean resetPosition, boolean resetState) {
return apply(new PrepareSource(tag, mediaSource, resetPosition, resetState));
}
/**
......@@ -420,16 +330,6 @@ public final class ActionSchedule {
}
/**
* Schedules a set shuffle order action to be executed.
*
* @param shuffleOrder The shuffle order.
* @return The builder, for convenience.
*/
public Builder setShuffleOrder(ShuffleOrder shuffleOrder) {
return apply(new SetShuffleOrder(tag, shuffleOrder));
}
/**
* Schedules a shuffle setting action to be executed.
*
* @return The builder, for convenience.
......@@ -482,19 +382,18 @@ public final class ActionSchedule {
* @return The builder, for convenience.
*/
public Builder waitForTimelineChanged() {
return apply(new WaitForTimelineChanged(tag));
return apply(new WaitForTimelineChanged(tag, /* expectedTimeline= */ null));
}
/**
* Schedules a delay until the timeline changed to a specified expected timeline.
*
* @param expectedTimeline The expected timeline.
* @param expectedReason The expected reason of the timeline change.
* @param expectedTimeline The expected timeline to wait for. If null, wait for any timeline
* change.
* @return The builder, for convenience.
*/
public Builder waitForTimelineChanged(
Timeline expectedTimeline, @Player.TimelineChangeReason int expectedReason) {
return apply(new WaitForTimelineChanged(tag, expectedTimeline, expectedReason));
public Builder waitForTimelineChanged(Timeline expectedTimeline) {
return apply(new WaitForTimelineChanged(tag, expectedTimeline));
}
/**
......
......@@ -141,8 +141,7 @@ public abstract class ExoHostedTest implements AnalyticsListener, HostedTest {
pendingSchedule = null;
}
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = buildDrmSessionManager(userAgent);
player.setMediaItem(buildSource(host, Util.getUserAgent(host, userAgent), drmSessionManager));
player.prepare();
player.prepare(buildSource(host, Util.getUserAgent(host, userAgent), drmSessionManager));
}
@Override
......
......@@ -25,10 +25,8 @@ import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import java.util.List;
/**
* An abstract {@link ExoPlayer} implementation that throws {@link UnsupportedOperationException}
......@@ -99,11 +97,6 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void prepare() {
throw new UnsupportedOperationException();
}
@Override
public void prepare(MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
......@@ -114,77 +107,6 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void setMediaItems(List<MediaSource> mediaItems, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(List<MediaSource> mediaItems) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(
List<MediaSource> mediaItems, int startWindowIndex, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItem(MediaSource mediaItem, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItem(MediaSource mediaItem) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItem(MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItem(int index, MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItems(List<MediaSource> mediaSources) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItems(int index, List<MediaSource> mediaSources) {
throw new UnsupportedOperationException();
}
@Override
public void moveMediaItem(int currentIndex, int newIndex) {
throw new UnsupportedOperationException();
}
@Override
public void moveMediaItems(int fromIndex, int toIndex, int newIndex) {
throw new UnsupportedOperationException();
}
@Override
public MediaSource removeMediaItem(int index) {
throw new UnsupportedOperationException();
}
@Override
public void removeMediaItems(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
@Override
public void clearMediaItems() {
throw new UnsupportedOperationException();
}
@Override
public void setPlayWhenReady(boolean playWhenReady) {
throw new UnsupportedOperationException();
}
......@@ -205,11 +127,6 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
throw new UnsupportedOperationException();
}
@Override
public void setShuffleModeEnabled(boolean shuffleModeEnabled) {
throw new UnsupportedOperationException();
}
......
......@@ -26,7 +26,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.database.DatabaseProvider;
import com.google.android.exoplayer2.database.DefaultDatabaseProvider;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
......@@ -399,61 +398,4 @@ public class TestUtil {
}
return new DefaultExtractorInput(dataSource, position, length);
}
/**
* Checks whether the timelines are the same (does not compare {@link Timeline.Window#uid} and
* {@link Timeline.Period#uid}).
*
* @param firstTimeline The first {@link Timeline}.
* @param secondTimeline The second {@link Timeline} to compare with.
* @return {@code true} if both timelines are the same.
*/
public static boolean areTimelinesSame(Timeline firstTimeline, Timeline secondTimeline) {
if (firstTimeline == secondTimeline) {
return true;
}
if (secondTimeline.getWindowCount() != firstTimeline.getWindowCount()
|| secondTimeline.getPeriodCount() != firstTimeline.getPeriodCount()) {
return false;
}
Timeline.Window firstWindow = new Timeline.Window();
Timeline.Period firstPeriod = new Timeline.Period();
Timeline.Window secondWindow = new Timeline.Window();
Timeline.Period secondPeriod = new Timeline.Period();
for (int i = 0; i < firstTimeline.getWindowCount(); i++) {
if (!areWindowsSame(
firstTimeline.getWindow(i, firstWindow), secondTimeline.getWindow(i, secondWindow))) {
return false;
}
}
for (int i = 0; i < firstTimeline.getPeriodCount(); i++) {
if (!firstTimeline
.getPeriod(i, firstPeriod, /* setIds= */ false)
.equals(secondTimeline.getPeriod(i, secondPeriod, /* setIds= */ false))) {
return false;
}
}
return true;
}
/**
* Checks whether the windows are the same. This comparison does not compare the uid.
*
* @param first The first {@link Timeline.Window}.
* @param second The second {@link Timeline.Window}.
* @return true if both windows are the same.
*/
private static boolean areWindowsSame(Timeline.Window first, Timeline.Window second) {
return Util.areEqual(first.tag, second.tag)
&& Util.areEqual(first.manifest, second.manifest)
&& first.presentationStartTimeMs == second.presentationStartTimeMs
&& first.windowStartTimeMs == second.windowStartTimeMs
&& first.isSeekable == second.isSeekable
&& first.isDynamic == second.isDynamic
&& first.defaultPositionUs == second.defaultPositionUs
&& first.durationUs == second.durationUs
&& first.firstPeriodIndex == second.firstPeriodIndex
&& first.lastPeriodIndex == second.lastPeriodIndex
&& first.positionInFirstPeriodUs == second.positionInFirstPeriodUs;
}
}
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