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