Commit 2028fdd7 by bachinger Committed by Oliver Woodman

add media item based playlist methods

After this change users of ExoPlayerImpl and SimpleExoPlayer can use the media item base playlist API which converts the media item to a media source.

It adds the media item based methods to the ExoPlayer instead of the Player interface. This avoids a big change in the CastPlayer which requires migrating the cast extension to the MediaItem of the core module (follow up CLs).

PiperOrigin-RevId: 300575567
parent 1e387601
......@@ -24,8 +24,10 @@ import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.LoopingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
......@@ -138,6 +140,7 @@ public interface ExoPlayer extends Player {
private Clock clock;
private TrackSelector trackSelector;
private MediaSourceFactory mediaSourceFactory;
private LoadControl loadControl;
private BandwidthMeter bandwidthMeter;
private Looper looper;
......@@ -154,6 +157,7 @@ public interface ExoPlayer extends Player {
*
* <ul>
* <li>{@link TrackSelector}: {@link DefaultTrackSelector}
* <li>{@link MediaSourceFactory}: {@link DefaultMediaSourceFactory}
* <li>{@link LoadControl}: {@link DefaultLoadControl}
* <li>{@link BandwidthMeter}: {@link DefaultBandwidthMeter#getSingletonInstance(Context)}
* <li>{@link Looper}: The {@link Looper} associated with the current thread, or the {@link
......@@ -171,6 +175,7 @@ public interface ExoPlayer extends Player {
this(
renderers,
new DefaultTrackSelector(context),
DefaultMediaSourceFactory.newInstance(context),
new DefaultLoadControl(),
DefaultBandwidthMeter.getSingletonInstance(context),
Util.getLooper(),
......@@ -188,6 +193,7 @@ public interface ExoPlayer extends Player {
*
* @param renderers The {@link Renderer Renderers} to be used by the player.
* @param trackSelector A {@link TrackSelector}.
* @param mediaSourceFactory A {@link MediaSourceFactory}.
* @param loadControl A {@link LoadControl}.
* @param bandwidthMeter A {@link BandwidthMeter}.
* @param looper A {@link Looper} that must be used for all calls to the player.
......@@ -198,6 +204,7 @@ public interface ExoPlayer extends Player {
public Builder(
Renderer[] renderers,
TrackSelector trackSelector,
MediaSourceFactory mediaSourceFactory,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Looper looper,
......@@ -207,6 +214,7 @@ public interface ExoPlayer extends Player {
Assertions.checkArgument(renderers.length > 0);
this.renderers = renderers;
this.trackSelector = trackSelector;
this.mediaSourceFactory = mediaSourceFactory;
this.loadControl = loadControl;
this.bandwidthMeter = bandwidthMeter;
this.looper = looper;
......@@ -244,6 +252,19 @@ public interface ExoPlayer extends Player {
}
/**
* Sets the {@link MediaSourceFactory} that will be used by the player.
*
* @param mediaSourceFactory A {@link MediaSourceFactory}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setMediaSourceFactory(MediaSourceFactory mediaSourceFactory) {
Assertions.checkState(!buildCalled);
this.mediaSourceFactory = mediaSourceFactory;
return this;
}
/**
* Sets the {@link LoadControl} that will be used by the player.
*
* @param loadControl A {@link LoadControl}.
......@@ -331,7 +352,7 @@ public interface ExoPlayer extends Player {
/**
* Builds an {@link ExoPlayer} instance.
*
* @throws IllegalStateException If {@link #build()} has already been called.
* @throws IllegalStateException If {@code build} has already been called.
*/
public ExoPlayer build() {
Assertions.checkState(!buildCalled);
......@@ -340,6 +361,7 @@ public interface ExoPlayer extends Player {
new ExoPlayerImpl(
renderers,
trackSelector,
mediaSourceFactory,
loadControl,
bandwidthMeter,
analyticsCollector,
......@@ -362,9 +384,6 @@ public interface ExoPlayer extends Player {
@Deprecated
void retry();
/** Prepares the player. */
void prepare();
/** @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead. */
@Deprecated
void prepare(MediaSource mediaSource);
......@@ -375,6 +394,9 @@ public interface ExoPlayer extends Player {
@Deprecated
void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);
/** Prepares the player. */
void prepare();
/**
* Clears the playlist, adds the specified {@link MediaSource MediaSources} and resets the
* position to the default position.
......@@ -463,6 +485,93 @@ public interface ExoPlayer extends Player {
void addMediaSources(int index, List<MediaSource> mediaSources);
/**
* Clears the playlist, adds the specified {@link MediaItem MediaItems} and resets the position to
* the default position.
*
* @param mediaItems The new {@link MediaItem MediaItems}.
*/
void setMediaItems(List<MediaItem> mediaItems);
/**
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
*
* @param mediaItems The new {@link MediaItem MediaItems}.
* @param resetPosition Whether the playback position should be reset to the default position in
* the first {@link Timeline.Window}. If false, playback will start from the position defined
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
*/
void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition);
/**
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
*
* @param mediaItems The new {@link MediaItem MediaItems}.
* @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<MediaItem> mediaItems, int startWindowIndex, long startPositionMs);
/**
* Clears the playlist, adds the specified {@link MediaItem} and resets the position to the
* default position.
*
* @param mediaItem The new {@link MediaItem}.
*/
void setMediaItem(MediaItem mediaItem);
/**
* Clears the playlist and adds the specified {@link MediaItem}.
*
* @param mediaItem The new {@link MediaItem}.
* @param startPositionMs The position in milliseconds to start playback from.
*/
void setMediaItem(MediaItem mediaItem, long startPositionMs);
/**
* Clears the playlist and adds the specified {@link MediaItem}.
*
* @param mediaItem The new {@link MediaItem}.
* @param resetPosition Whether the playback position should be reset to the default position. If
* false, playback will start from the position defined by {@link #getCurrentWindowIndex()}
* and {@link #getCurrentPosition()}.
*/
void setMediaItem(MediaItem mediaItem, boolean resetPosition);
/**
* Adds a media item to the end of the playlist.
*
* @param mediaItem The {@link MediaItem} to add.
*/
void addMediaItem(MediaItem mediaItem);
/**
* Adds a media item at the given index of the playlist.
*
* @param index The index at which to add the item.
* @param mediaItem The {@link MediaItem} to add.
*/
void addMediaItem(int index, MediaItem mediaItem);
/**
* Adds a list of media items to the end of the playlist.
*
* @param mediaItems The {@link MediaItem MediaItems} to add.
*/
void addMediaItems(List<MediaItem> mediaItems);
/**
* 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 mediaItems The {@link MediaItem MediaItems} to add.
*/
void addMediaItems(int index, List<MediaItem> mediaItems);
/**
* Moves the media item at the current index to the new index.
*
* @param currentIndex The current index of the media item to move.
......
......@@ -18,6 +18,7 @@ package com.google.android.exoplayer2;
import android.content.Context;
import android.os.Looper;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
......@@ -197,6 +198,7 @@ public final class ExoPlayerFactory {
context,
renderersFactory,
trackSelector,
DefaultMediaSourceFactory.newInstance(context),
loadControl,
bandwidthMeter,
analyticsCollector,
......@@ -251,6 +253,7 @@ public final class ExoPlayerFactory {
return new ExoPlayerImpl(
renderers,
trackSelector,
DefaultMediaSourceFactory.newInstance(context),
loadControl,
bandwidthMeter,
/* analyticsCollector= */ null,
......
......@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection;
......@@ -69,6 +70,7 @@ import java.util.concurrent.TimeoutException;
private final ArrayDeque<Runnable> pendingListenerNotifications;
private final List<Playlist.MediaSourceHolder> mediaSourceHolders;
private final boolean useLazyPreparation;
private final MediaSourceFactory mediaSourceFactory;
@RepeatMode private int repeatMode;
private boolean shuffleModeEnabled;
......@@ -95,15 +97,16 @@ import java.util.concurrent.TimeoutException;
/**
* Constructs an instance. Must be called from a thread that has an associated {@link Looper}.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param analyticsCollector The {@link AnalyticsCollector} that will be used by the instance.
* @param renderers The {@link Renderer}s.
* @param trackSelector The {@link TrackSelector}.
* @param mediaSourceFactory The {@link MediaSourceFactory}.
* @param loadControl The {@link LoadControl}.
* @param bandwidthMeter The {@link BandwidthMeter}.
* @param analyticsCollector The {@link AnalyticsCollector}.
* @param useLazyPreparation Whether playlist items are prepared lazily. If false, all manifest
* loads and other initial preparation steps happen immediately. If true, these initial
* preparations are triggered only when the player starts buffering the media.
* @param clock The {@link Clock} that will be used by the instance.
* @param clock The {@link Clock}.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
......@@ -111,6 +114,7 @@ import java.util.concurrent.TimeoutException;
public ExoPlayerImpl(
Renderer[] renderers,
TrackSelector trackSelector,
MediaSourceFactory mediaSourceFactory,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
@Nullable AnalyticsCollector analyticsCollector,
......@@ -122,6 +126,7 @@ import java.util.concurrent.TimeoutException;
Assertions.checkState(renderers.length > 0);
this.renderers = Assertions.checkNotNull(renderers);
this.trackSelector = Assertions.checkNotNull(trackSelector);
this.mediaSourceFactory = mediaSourceFactory;
this.useLazyPreparation = useLazyPreparation;
repeatMode = Player.REPEAT_MODE_OFF;
listeners = new CopyOnWriteArrayList<>();
......@@ -307,6 +312,37 @@ import java.util.concurrent.TimeoutException;
}
@Override
public void setMediaItem(MediaItem mediaItem) {
setMediaItems(Collections.singletonList(mediaItem));
}
@Override
public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
setMediaItems(Collections.singletonList(mediaItem), /* startWindowIndex= */ 0, startPositionMs);
}
@Override
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
setMediaItems(Collections.singletonList(mediaItem), resetPosition);
}
@Override
public void setMediaItems(List<MediaItem> mediaItems) {
setMediaItems(mediaItems, /* resetPosition= */ true);
}
@Override
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
setMediaSources(createMediaSources(mediaItems), resetPosition);
}
@Override
public void setMediaItems(
List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs) {
setMediaSources(createMediaSources(mediaItems), startWindowIndex, startPositionMs);
}
@Override
public void setMediaSource(MediaSource mediaSource) {
setMediaSources(Collections.singletonList(mediaSource));
}
......@@ -329,7 +365,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void setMediaSources(List<MediaSource> mediaSources, boolean resetPosition) {
setMediaItemsInternal(
setMediaSourcesInternal(
mediaSources,
/* startWindowIndex= */ C.INDEX_UNSET,
/* startPositionMs= */ C.TIME_UNSET,
......@@ -339,11 +375,31 @@ import java.util.concurrent.TimeoutException;
@Override
public void setMediaSources(
List<MediaSource> mediaSources, int startWindowIndex, long startPositionMs) {
setMediaItemsInternal(
setMediaSourcesInternal(
mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false);
}
@Override
public void addMediaItem(int index, MediaItem mediaItem) {
addMediaItems(index, Collections.singletonList(mediaItem));
}
@Override
public void addMediaItem(MediaItem mediaItem) {
addMediaItems(Collections.singletonList(mediaItem));
}
@Override
public void addMediaItems(List<MediaItem> mediaItems) {
addMediaItems(/* index= */ mediaSourceHolders.size(), mediaItems);
}
@Override
public void addMediaItems(int index, List<MediaItem> mediaItems) {
addMediaSources(index, createMediaSources(mediaItems));
}
@Override
public void addMediaSource(MediaSource mediaSource) {
addMediaSources(Collections.singletonList(mediaSource));
}
......@@ -816,6 +872,14 @@ import java.util.concurrent.TimeoutException;
}
}
private List<MediaSource> createMediaSources(List<MediaItem> mediaItems) {
List<MediaSource> mediaSources = new ArrayList<>();
for (int i = 0; i < mediaItems.size(); i++) {
mediaSources.add(mediaSourceFactory.createMediaSource(mediaItems.get(i)));
}
return mediaSources;
}
@SuppressWarnings("deprecation")
private void handlePlaybackSpeed(float playbackSpeed, boolean operationAck) {
if (operationAck) {
......@@ -923,7 +987,7 @@ import java.util.concurrent.TimeoutException;
}
@SuppressWarnings("deprecation")
private void setMediaItemsInternal(
private void setMediaSourcesInternal(
List<MediaSource> mediaItems,
int startWindowIndex,
long startPositionMs,
......
......@@ -38,7 +38,9 @@ import com.google.android.exoplayer2.audio.AuxEffectInfo;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ShuffleOrder;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
......@@ -89,6 +91,7 @@ public class SimpleExoPlayer extends BasePlayer
private Clock clock;
private TrackSelector trackSelector;
private MediaSourceFactory mediaSourceFactory;
private LoadControl loadControl;
private BandwidthMeter bandwidthMeter;
private AnalyticsCollector analyticsCollector;
......@@ -108,6 +111,7 @@ public class SimpleExoPlayer extends BasePlayer
* <ul>
* <li>{@link RenderersFactory}: {@link DefaultRenderersFactory}
* <li>{@link TrackSelector}: {@link DefaultTrackSelector}
* <li>{@link MediaSourceFactory}: {@link DefaultMediaSourceFactory}
* <li>{@link LoadControl}: {@link DefaultLoadControl}
* <li>{@link BandwidthMeter}: {@link DefaultBandwidthMeter#getSingletonInstance(Context)}
* <li>{@link Looper}: The {@link Looper} associated with the current thread, or the {@link
......@@ -138,6 +142,7 @@ public class SimpleExoPlayer extends BasePlayer
context,
renderersFactory,
new DefaultTrackSelector(context),
DefaultMediaSourceFactory.newInstance(context),
new DefaultLoadControl(),
DefaultBandwidthMeter.getSingletonInstance(context),
Util.getLooper(),
......@@ -157,6 +162,7 @@ public class SimpleExoPlayer extends BasePlayer
* @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
* player.
* @param trackSelector A {@link TrackSelector}.
* @param mediaSourceFactory A {@link MediaSourceFactory}.
* @param loadControl A {@link LoadControl}.
* @param bandwidthMeter A {@link BandwidthMeter}.
* @param looper A {@link Looper} that must be used for all calls to the player.
......@@ -170,6 +176,7 @@ public class SimpleExoPlayer extends BasePlayer
Context context,
RenderersFactory renderersFactory,
TrackSelector trackSelector,
MediaSourceFactory mediaSourceFactory,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Looper looper,
......@@ -179,6 +186,7 @@ public class SimpleExoPlayer extends BasePlayer
this.context = context;
this.renderersFactory = renderersFactory;
this.trackSelector = trackSelector;
this.mediaSourceFactory = mediaSourceFactory;
this.loadControl = loadControl;
this.bandwidthMeter = bandwidthMeter;
this.looper = looper;
......@@ -201,6 +209,19 @@ public class SimpleExoPlayer extends BasePlayer
}
/**
* Sets the {@link MediaSourceFactory} that will be used by the player.
*
* @param mediaSourceFactory A {@link MediaSourceFactory}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setMediaSourceFactory(MediaSourceFactory mediaSourceFactory) {
Assertions.checkState(!buildCalled);
this.mediaSourceFactory = mediaSourceFactory;
return this;
}
/**
* Sets the {@link LoadControl} that will be used by the player.
*
* @param loadControl A {@link LoadControl}.
......@@ -350,6 +371,7 @@ public class SimpleExoPlayer extends BasePlayer
builder.context,
builder.renderersFactory,
builder.trackSelector,
builder.mediaSourceFactory,
builder.loadControl,
builder.bandwidthMeter,
builder.analyticsCollector,
......@@ -378,6 +400,7 @@ public class SimpleExoPlayer extends BasePlayer
Context context,
RenderersFactory renderersFactory,
TrackSelector trackSelector,
MediaSourceFactory mediaSourceFactory,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
AnalyticsCollector analyticsCollector,
......@@ -414,6 +437,7 @@ public class SimpleExoPlayer extends BasePlayer
new ExoPlayerImpl(
renderers,
trackSelector,
mediaSourceFactory,
loadControl,
bandwidthMeter,
analyticsCollector,
......@@ -1232,6 +1256,49 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void setMediaItems(List<MediaItem> mediaItems) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItems(mediaItems);
}
@Override
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItems(mediaItems, resetPosition);
}
@Override
public void setMediaItems(
List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItems(mediaItems, startWindowIndex, startPositionMs);
}
@Override
public void setMediaItem(MediaItem mediaItem) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItem(mediaItem);
}
@Override
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItem(mediaItem, resetPosition);
}
@Override
public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
player.setMediaItem(mediaItem, startPositionMs);
}
@Override
public void setMediaSources(List<MediaSource> mediaSources) {
verifyApplicationThread();
analyticsCollector.resetForNewPlaylist();
......@@ -1275,6 +1342,30 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void addMediaItems(List<MediaItem> mediaItems) {
verifyApplicationThread();
player.addMediaItems(mediaItems);
}
@Override
public void addMediaItems(int index, List<MediaItem> mediaItems) {
verifyApplicationThread();
player.addMediaItems(index, mediaItems);
}
@Override
public void addMediaItem(MediaItem mediaItem) {
verifyApplicationThread();
player.addMediaItem(mediaItem);
}
@Override
public void addMediaItem(int index, MediaItem mediaItem) {
verifyApplicationThread();
player.addMediaItem(index, mediaItem);
}
@Override
public void addMediaSource(MediaSource mediaSource) {
verifyApplicationThread();
player.addMediaSource(mediaSource);
......
......@@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
......@@ -136,6 +137,37 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void setMediaItem(MediaItem mediaItem) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(List<MediaItem> mediaItems) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaItems(
List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs) {
throw new UnsupportedOperationException();
}
@Override
public void setMediaSource(MediaSource mediaSource) {
throw new UnsupportedOperationException();
}
......@@ -167,6 +199,26 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void addMediaItem(MediaItem mediaItem) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItem(int index, MediaItem mediaItem) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItems(List<MediaItem> mediaItems) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaItems(int index, List<MediaItem> mediaItems) {
throw new UnsupportedOperationException();
}
@Override
public void addMediaSource(MediaSource mediaSource) {
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