Commit 36fa9d5a by bachinger Committed by Oliver Woodman

add top-level playlist API

Design doc: https://docs.google.com/document/d/11h0S91KI5TB3NNZUtsCzg0S7r6nyTnF_tDZZAtmY93g/edit

Issue: #6161, #5155
PiperOrigin-RevId: 286020313
parent 43bbc172
Showing with 493 additions and 141 deletions
......@@ -34,6 +34,7 @@
([6773](https://github.com/google/ExoPlayer/issues/6773)).
* Suppress ProGuard warnings for compile-time `javax.annotation` package
([#6771](https://github.com/google/ExoPlayer/issues/6771)).
* Add playlist API ([#6161](https://github.com/google/ExoPlayer/issues/6161)).
### 2.11.0 (2019-12-11) ###
......
......@@ -51,7 +51,6 @@ 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.MergingMediaSource;
......@@ -82,6 +81,8 @@ import java.lang.reflect.Constructor;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.util.ArrayList;
import java.util.List;
/** An activity that plays media using {@link SimpleExoPlayer}. */
public class PlayerActivity extends AppCompatActivity
......@@ -147,7 +148,7 @@ public class PlayerActivity extends AppCompatActivity
private DataSource.Factory dataSourceFactory;
private SimpleExoPlayer player;
private MediaSource mediaSource;
private List<MediaSource> mediaSources;
private DefaultTrackSelector trackSelector;
private DefaultTrackSelector.Parameters trackSelectorParameters;
private DebugTextViewHelper debugViewHelper;
......@@ -349,12 +350,10 @@ public class PlayerActivity extends AppCompatActivity
private void initializePlayer() {
if (player == null) {
Intent intent = getIntent();
mediaSource = createTopLevelMediaSource(intent);
if (mediaSource == null) {
mediaSources = createTopLevelMediaSources(intent);
if (mediaSources.isEmpty()) {
return;
}
TrackSelection.Factory trackSelectionFactory;
String abrAlgorithm = intent.getStringExtra(ABR_ALGORITHM_EXTRA);
if (abrAlgorithm == null || ABR_ALGORITHM_DEFAULT.equals(abrAlgorithm)) {
......@@ -395,13 +394,12 @@ public class PlayerActivity extends AppCompatActivity
if (haveStartPosition) {
player.seekTo(startWindow, startPosition);
}
player.setMediaSource(mediaSource);
player.setMediaSources(mediaSources, /* resetPosition= */ !haveStartPosition);
player.prepare();
updateButtonVisibility();
}
@Nullable
private MediaSource createTopLevelMediaSource(Intent intent) {
private List<MediaSource> createTopLevelMediaSources(Intent intent) {
String action = intent.getAction();
boolean actionIsListView = ACTION_VIEW_LIST.equals(action);
if (!actionIsListView && !ACTION_VIEW.equals(action)) {
......@@ -429,10 +427,10 @@ public class PlayerActivity extends AppCompatActivity
}
}
MediaSource[] mediaSources = new MediaSource[samples.length];
for (int i = 0; i < samples.length; i++) {
mediaSources[i] = createLeafMediaSource(samples[i]);
Sample.SubtitleInfo subtitleInfo = samples[i].subtitleInfo;
List<MediaSource> mediaSources = new ArrayList<>();
for (UriSample sample : samples) {
MediaSource mediaSource = createLeafMediaSource(sample);
Sample.SubtitleInfo subtitleInfo = sample.subtitleInfo;
if (subtitleInfo != null) {
Format subtitleFormat =
Format.createTextSampleFormat(
......@@ -443,33 +441,30 @@ public class PlayerActivity extends AppCompatActivity
MediaSource subtitleMediaSource =
new SingleSampleMediaSource.Factory(dataSourceFactory)
.createMediaSource(subtitleInfo.uri, subtitleFormat, C.TIME_UNSET);
mediaSources[i] = new MergingMediaSource(mediaSources[i], subtitleMediaSource);
mediaSource = new MergingMediaSource(mediaSource, subtitleMediaSource);
}
mediaSources.add(mediaSource);
}
MediaSource mediaSource =
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
if (seenAdsTagUri) {
if (seenAdsTagUri && mediaSources.size() == 1) {
Uri adTagUri = samples[0].adTagUri;
if (actionIsListView) {
showToast(R.string.unsupported_ads_in_concatenation);
if (!adTagUri.equals(loadedAdTagUri)) {
releaseAdsLoader();
loadedAdTagUri = adTagUri;
}
MediaSource adsMediaSource = createAdsMediaSource(mediaSources.get(0), adTagUri);
if (adsMediaSource != null) {
mediaSources.set(0, adsMediaSource);
} else {
if (!adTagUri.equals(loadedAdTagUri)) {
releaseAdsLoader();
loadedAdTagUri = adTagUri;
}
MediaSource adsMediaSource = createAdsMediaSource(mediaSource, adTagUri);
if (adsMediaSource != null) {
mediaSource = adsMediaSource;
} 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 {
releaseAdsLoader();
}
return mediaSource;
return mediaSources;
}
private MediaSource createLeafMediaSource(UriSample parameters) {
......@@ -557,7 +552,7 @@ public class PlayerActivity extends AppCompatActivity
debugViewHelper = null;
player.release();
player = null;
mediaSource = null;
mediaSources = null;
trackSelector = null;
}
if (adsLoader != null) {
......
......@@ -110,7 +110,6 @@ 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.
......@@ -173,7 +172,6 @@ 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);
}
......@@ -641,15 +639,13 @@ public final class CastPlayer extends BasePlayer {
private void updateTimelineAndNotifyIfChanged() {
if (updateTimeline()) {
@Player.TimelineChangeReason
int reason =
waitingForInitialTimeline
? Player.TIMELINE_CHANGE_REASON_PREPARED
: Player.TIMELINE_CHANGE_REASON_DYNAMIC;
waitingForInitialTimeline = false;
// TODO: Differentiate TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED and
// TIMELINE_CHANGE_REASON_SOURCE_UPDATE [see internal: b/65152553].
notificationsBatch.add(
new ListenerNotificationTask(
listener -> listener.onTimelineChanged(currentTimeline, reason)));
listener ->
listener.onTimelineChanged(
currentTimeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE)));
}
}
......
......@@ -288,7 +288,7 @@ public class ImaAdsLoaderTest {
this.adPlaybackState = adPlaybackState;
fakeExoPlayer.updateTimeline(
new SinglePeriodAdTimeline(contentTimeline, adPlaybackState),
Player.TIMELINE_CHANGE_REASON_DYNAMIC);
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
}
@Override
......
......@@ -297,6 +297,7 @@ public final class ExoPlayerFactory {
drmSessionManager,
bandwidthMeter,
analyticsCollector,
/* useLazyPreparation= */ true,
Clock.DEFAULT,
looper);
}
......@@ -345,6 +346,13 @@ public final class ExoPlayerFactory {
BandwidthMeter bandwidthMeter,
Looper looper) {
return new ExoPlayerImpl(
renderers, trackSelector, loadControl, bandwidthMeter, Clock.DEFAULT, looper);
renderers,
trackSelector,
loadControl,
bandwidthMeter,
/* analyticsCollector= */ null,
/* useLazyPreparation= */ true,
Clock.DEFAULT,
looper);
}
}
......@@ -19,7 +19,6 @@ 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;
......@@ -56,7 +55,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
private final boolean[] mayRetainStreamFlags;
private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector trackSelector;
private final MediaSource mediaSource;
private final Playlist playlist;
@Nullable private MediaPeriodHolder next;
private TrackGroupArray trackGroups;
......@@ -70,7 +69,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 mediaSource The media source that produced the media period.
* @param playlist The playlist.
* @param info Information used to identify this media period in its timeline period.
* @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each
* renderer.
......@@ -80,13 +79,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
long rendererPositionOffsetUs,
TrackSelector trackSelector,
Allocator allocator,
MediaSource mediaSource,
Playlist playlist,
MediaPeriodInfo info,
TrackSelectorResult emptyTrackSelectorResult) {
this.rendererCapabilities = rendererCapabilities;
this.rendererPositionOffsetUs = rendererPositionOffsetUs;
this.trackSelector = trackSelector;
this.mediaSource = mediaSource;
this.playlist = playlist;
this.uid = info.id.periodUid;
this.info = info;
this.trackGroups = TrackGroupArray.EMPTY;
......@@ -94,8 +93,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
sampleStreams = new SampleStream[rendererCapabilities.length];
mayRetainStreamFlags = new boolean[rendererCapabilities.length];
mediaPeriod =
createMediaPeriod(
info.id, mediaSource, allocator, info.startPositionUs, info.endPositionUs);
createMediaPeriod(info.id, playlist, allocator, info.startPositionUs, info.endPositionUs);
}
/**
......@@ -305,7 +303,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, mediaSource, mediaPeriod);
releaseMediaPeriod(info.endPositionUs, playlist, mediaPeriod);
}
/**
......@@ -402,11 +400,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Returns a media period corresponding to the given {@code id}. */
private static MediaPeriod createMediaPeriod(
MediaPeriodId id,
MediaSource mediaSource,
Playlist playlist,
Allocator allocator,
long startPositionUs,
long endPositionUs) {
MediaPeriod mediaPeriod = mediaSource.createPeriod(id, allocator, startPositionUs);
MediaPeriod mediaPeriod = playlist.createPeriod(id, allocator, startPositionUs);
if (endPositionUs != C.TIME_UNSET && endPositionUs != C.TIME_END_OF_SOURCE) {
mediaPeriod =
new ClippingMediaPeriod(
......@@ -417,12 +415,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Releases the given {@code mediaPeriod}, logging and suppressing any errors. */
private static void releaseMediaPeriod(
long endPositionUs, MediaSource mediaSource, MediaPeriod mediaPeriod) {
long endPositionUs, Playlist playlist, MediaPeriod mediaPeriod) {
try {
if (endPositionUs != C.TIME_UNSET && endPositionUs != C.TIME_END_OF_SOURCE) {
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
playlist.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
} else {
mediaSource.releasePeriod(mediaPeriod);
playlist.releasePeriod(mediaPeriod);
}
} catch (RuntimeException e) {
// There's nothing we can do.
......
......@@ -19,7 +19,6 @@ 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;
......@@ -134,7 +133,7 @@ import com.google.android.exoplayer2.util.Assertions;
* @param rendererCapabilities The renderer capabilities.
* @param trackSelector The track selector.
* @param allocator The allocator.
* @param mediaSource The media source that produced the media period.
* @param playlist The playlist.
* @param info Information used to identify this media period in its timeline period.
* @param emptyTrackSelectorResult A {@link TrackSelectorResult} with empty selections for each
* renderer.
......@@ -143,7 +142,7 @@ import com.google.android.exoplayer2.util.Assertions;
RendererCapabilities[] rendererCapabilities,
TrackSelector trackSelector,
Allocator allocator,
MediaSource mediaSource,
Playlist playlist,
MediaPeriodInfo info,
TrackSelectorResult emptyTrackSelectorResult) {
long rendererPositionOffsetUs =
......@@ -158,7 +157,7 @@ import com.google.android.exoplayer2.util.Assertions;
rendererPositionOffsetUs,
trackSelector,
allocator,
mediaSource,
playlist,
info,
emptyTrackSelectorResult);
if (loading != null) {
......
......@@ -162,7 +162,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
public MediaPeriodId getDummyFirstMediaPeriodId(
boolean shuffleModeEnabled, Timeline.Window window, Timeline.Period period) {
if (timeline.isEmpty()) {
return DUMMY_MEDIA_PERIOD_ID;
return getDummyPeriodForEmptyTimeline();
}
int firstWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
int firstPeriodIndex = timeline.getWindow(firstWindowIndex, window).firstPeriodIndex;
......@@ -178,6 +178,11 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
return new MediaPeriodId(timeline.getUidOfPeriod(firstPeriodIndex), windowSequenceNumber);
}
/** Returns dummy period id for an empty timeline. */
public MediaPeriodId getDummyPeriodForEmptyTimeline() {
return DUMMY_MEDIA_PERIOD_ID;
}
/**
* Copies playback info with new playing position.
*
......
......@@ -381,7 +381,8 @@ public interface Player {
* {@link #onPositionDiscontinuity(int)}.
*
* @param timeline The latest timeline. Never null, but may be empty.
* @param manifest The latest manifest. May be null.
* @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 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,
......@@ -619,25 +620,17 @@ public interface Player {
int DISCONTINUITY_REASON_INTERNAL = 4;
/**
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PREPARED}, {@link
* #TIMELINE_CHANGE_REASON_RESET} or {@link #TIMELINE_CHANGE_REASON_DYNAMIC}.
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
* #TIMELINE_CHANGE_REASON_SOURCE_UPDATE}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
TIMELINE_CHANGE_REASON_PREPARED,
TIMELINE_CHANGE_REASON_RESET,
TIMELINE_CHANGE_REASON_DYNAMIC
})
@IntDef({TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, TIMELINE_CHANGE_REASON_SOURCE_UPDATE})
@interface TimelineChangeReason {}
/** 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;
/** 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;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable
......
......@@ -135,11 +135,8 @@ public class AnalyticsCollector
}
}
/**
* 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() {
/** Resets the analytics collector for a new playlist. */
public final void resetForNewPlaylist() {
// Copying the list is needed because onMediaPeriodReleased will modify the list.
List<MediaPeriodInfo> mediaPeriodInfos =
new ArrayList<>(mediaPeriodQueueTracker.mediaPeriodInfoQueue);
......@@ -806,9 +803,13 @@ public class AnalyticsCollector
/** Updates the queue with a newly created media period. */
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
boolean isInTimeline = timeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET;
int periodIndex = timeline.getIndexOfPeriod(mediaPeriodId.periodUid);
boolean isInTimeline = periodIndex != C.INDEX_UNSET;
MediaPeriodInfo mediaPeriodInfo =
new MediaPeriodInfo(mediaPeriodId, isInTimeline ? timeline : Timeline.EMPTY, windowIndex);
new MediaPeriodInfo(
mediaPeriodId,
isInTimeline ? timeline : Timeline.EMPTY,
isInTimeline ? timeline.getPeriod(periodIndex, period).windowIndex : windowIndex);
mediaPeriodInfoQueue.add(mediaPeriodInfo);
mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo);
lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0);
......@@ -824,7 +825,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 resetForNewMediaSource().
// The media period has already been removed from the queue in resetForNewPlaylist().
return false;
}
mediaPeriodInfoQueue.remove(mediaPeriodInfo);
......
......@@ -617,12 +617,10 @@ public class EventLogger implements AnalyticsListener {
private static String getTimelineChangeReasonString(@Player.TimelineChangeReason int reason) {
switch (reason) {
case Player.TIMELINE_CHANGE_REASON_PREPARED:
return "PREPARED";
case Player.TIMELINE_CHANGE_REASON_RESET:
return "RESET";
case Player.TIMELINE_CHANGE_REASON_DYNAMIC:
return "DYNAMIC";
case Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE:
return "SOURCE_UPDATE";
case Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED:
return "PLAYLIST_CHANGED";
default:
return "?";
}
......
......@@ -26,6 +26,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.ForwardingTimeline;
import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaPeriod;
......@@ -49,6 +50,22 @@ import java.util.List;
*/
public class FakeMediaSource extends BaseMediaSource {
/** A forwarding timeline to provide an initial timeline for fake multi window sources. */
public static class InitialTimeline extends ForwardingTimeline {
public InitialTimeline(Timeline timeline) {
super(timeline);
}
@Override
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
Window childWindow = timeline.getWindow(windowIndex, window, defaultPositionProjectionUs);
childWindow.isDynamic = true;
childWindow.isSeekable = false;
return childWindow;
}
}
private static final DataSpec FAKE_DATA_SPEC = new DataSpec(Uri.parse("http://manifest.uri"));
private static final int MANIFEST_LOAD_BYTES = 100;
......@@ -92,6 +109,19 @@ public class FakeMediaSource extends BaseMediaSource {
return hasTimeline ? timeline.getWindow(0, new Timeline.Window()).tag : null;
}
@Nullable
@Override
public Timeline getInitialTimeline() {
return timeline == null || timeline == Timeline.EMPTY || timeline.getWindowCount() == 1
? null
: new InitialTimeline(timeline);
}
@Override
public boolean isSingleWindow() {
return timeline == null || timeline == Timeline.EMPTY || timeline.getWindowCount() == 1;
}
@Override
public synchronized void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
assertThat(preparedSource).isFalse();
......@@ -249,5 +279,4 @@ public class FakeMediaSource extends BaseMediaSource {
}
return new TrackGroupArray(trackGroups);
}
}
......@@ -25,8 +25,10 @@ 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}
......@@ -91,21 +93,36 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
/** @deprecated Use {@link #prepare()} instead. */
@Deprecated
@Override
public void retry() {
throw new UnsupportedOperationException();
}
/**
* @deprecated Use {@link #setMediaSource(MediaSource)} and {@link ExoPlayer#prepare()} instead.
*/
@Deprecated
@Override
public void prepare() {
throw new UnsupportedOperationException();
}
/**
* @deprecated Use {@link #setMediaSource(MediaSource)} and {@link ExoPlayer#prepare()} instead.
*/
@Deprecated
@Override
public void prepare(MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Use {@link #setMediaSource(MediaSource, boolean)} and {@link ExoPlayer#prepare()}
* instead.
*/
@Deprecated
@Override
public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) {
throw new UnsupportedOperationException();
......@@ -122,6 +139,72 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void setMediaSource(MediaSource mediaSource, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaSources(List<MediaSource> mediaSources) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaSources(List<MediaSource> mediaSources, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaSources(
List<MediaSource> mediaSources, int startWindowIndex, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaSource(MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaSource(int index, MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaSources(List<MediaSource> mediaSources) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaSources(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();
}
......@@ -142,6 +225,11 @@ 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();
}
......
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