Commit cd457116 by tonihei Committed by Andrew Lewis

Add builders for SimpleExoPlayer and ExoPlayer.

The current ExoPlayerFactory is growing too big and usage becomes increasingly
complicated because it's not possible to set individual components without
specifying many other defaults.

Adding new builder classes makes building easier and more future-proof.

PiperOrigin-RevId: 263339078
parent 69965ddf
Showing with 481 additions and 93 deletions
......@@ -34,6 +34,8 @@
* Reset `DefaultBandwidthMeter` to initial values on network change.
* Increase maximum buffer size for video in `DefaultLoadControl` to ensure high
quality video can be loaded up to the full default buffer duration.
* Replace `ExoPlayerFactory` by `SimpleExoPlayer.Builder` and
`ExoPlayer.Builder`.
### 2.10.4 ###
......
......@@ -20,7 +20,6 @@ import android.net.Uri;
import android.view.KeyEvent;
import android.view.View;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.EventListener;
......@@ -119,7 +118,7 @@ import java.util.Map;
mediaDrms = new IdentityHashMap<>();
trackSelector = new DefaultTrackSelector(context);
exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();
exoPlayer.addListener(this);
localPlayerView.setPlayer(exoPlayer);
......
......@@ -24,7 +24,6 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
......@@ -138,7 +137,9 @@ public class PlayerActivity extends GvrPlayerActivity implements PlaybackPrepare
lastSeenTrackGroupArray = null;
player =
ExoPlayerFactory.newSimpleInstance(/* context= */ this, renderersFactory, trackSelector);
new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
.setTrackSelector(trackSelector)
.build();
player.addListener(new PlayerEventListener());
player.setPlayWhenReady(startAutoPlay);
player.addAnalyticsListener(new EventLogger(trackSelector));
......
......@@ -20,7 +20,6 @@ import android.net.Uri;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
import com.google.android.exoplayer2.source.MediaSource;
......@@ -54,7 +53,7 @@ import com.google.android.exoplayer2.util.Util;
public void init(Context context, PlayerView playerView) {
// Create a player instance.
player = ExoPlayerFactory.newSimpleInstance(context);
player = new SimpleExoPlayer.Builder(context).build();
adsLoader.setPlayer(player);
playerView.setPlayer(player);
......
......@@ -33,7 +33,6 @@ import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory;
......@@ -371,7 +370,9 @@ public class PlayerActivity extends AppCompatActivity
lastSeenTrackGroupArray = null;
player =
ExoPlayerFactory.newSimpleInstance(/* context= */ this, renderersFactory, trackSelector);
new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
.setTrackSelector(trackSelector)
.build();
player.addListener(new PlayerEventListener());
player.setPlayWhenReady(startAutoPlay);
player.addAnalyticsListener(new EventLogger(trackSelector));
......
......@@ -122,22 +122,22 @@ Once you've followed the instructions above to check out, build and depend on
the extension, the next step is to tell ExoPlayer to use `FfmpegAudioRenderer`.
How you do this depends on which player API you're using:
* If you're passing a `DefaultRenderersFactory` to
`ExoPlayerFactory.newSimpleInstance`, you can enable using the extension by
setting the `extensionRendererMode` parameter of the `DefaultRenderersFactory`
constructor to `EXTENSION_RENDERER_MODE_ON`. This will use
`FfmpegAudioRenderer` for playback if `MediaCodecAudioRenderer` doesn't
support the input format. Pass `EXTENSION_RENDERER_MODE_PREFER` to give
`FfmpegAudioRenderer` priority over `MediaCodecAudioRenderer`.
* If you're passing a `DefaultRenderersFactory` to `SimpleExoPlayer.Builder`,
you can enable using the extension by setting the `extensionRendererMode`
parameter of the `DefaultRenderersFactory` constructor to
`EXTENSION_RENDERER_MODE_ON`. This will use `FfmpegAudioRenderer` for playback
if `MediaCodecAudioRenderer` doesn't support the input format. Pass
`EXTENSION_RENDERER_MODE_PREFER` to give `FfmpegAudioRenderer` priority over
`MediaCodecAudioRenderer`.
* If you've subclassed `DefaultRenderersFactory`, add an `FfmpegAudioRenderer`
to the output list in `buildAudioRenderers`. ExoPlayer will use the first
`Renderer` in the list that supports the input media format.
* If you've implemented your own `RenderersFactory`, return an
`FfmpegAudioRenderer` instance from `createRenderers`. ExoPlayer will use the
first `Renderer` in the returned array that supports the input media format.
* If you're using `ExoPlayerFactory.newInstance`, pass an `FfmpegAudioRenderer`
in the array of `Renderer`s. ExoPlayer will use the first `Renderer` in the
list that supports the input media format.
* If you're using `ExoPlayer.Builder`, pass an `FfmpegAudioRenderer` in the
array of `Renderer`s. ExoPlayer will use the first `Renderer` in the list that
supports the input media format.
Note: These instructions assume you're using `DefaultTrackSelector`. If you have
a custom track selector the choice of `Renderer` is up to your implementation,
......
......@@ -68,22 +68,22 @@ renderer.
### Using `LibflacAudioRenderer` ###
* If you're passing a `DefaultRenderersFactory` to
`ExoPlayerFactory.newSimpleInstance`, you can enable using the extension by
setting the `extensionRendererMode` parameter of the `DefaultRenderersFactory`
constructor to `EXTENSION_RENDERER_MODE_ON`. This will use
`LibflacAudioRenderer` for playback if `MediaCodecAudioRenderer` doesn't
support the input format. Pass `EXTENSION_RENDERER_MODE_PREFER` to give
`LibflacAudioRenderer` priority over `MediaCodecAudioRenderer`.
* If you're passing a `DefaultRenderersFactory` to `SimpleExoPlayer.Builder`,
you can enable using the extension by setting the `extensionRendererMode`
parameter of the `DefaultRenderersFactory` constructor to
`EXTENSION_RENDERER_MODE_ON`. This will use `LibflacAudioRenderer` for
playback if `MediaCodecAudioRenderer` doesn't support the input format. Pass
`EXTENSION_RENDERER_MODE_PREFER` to give `LibflacAudioRenderer` priority over
`MediaCodecAudioRenderer`.
* If you've subclassed `DefaultRenderersFactory`, add a `LibflacAudioRenderer`
to the output list in `buildAudioRenderers`. ExoPlayer will use the first
`Renderer` in the list that supports the input media format.
* If you've implemented your own `RenderersFactory`, return a
`LibflacAudioRenderer` instance from `createRenderers`. ExoPlayer will use the
first `Renderer` in the returned array that supports the input media format.
* If you're using `ExoPlayerFactory.newInstance`, pass a `LibflacAudioRenderer`
in the array of `Renderer`s. ExoPlayer will use the first `Renderer` in the
list that supports the input media format.
* If you're using `ExoPlayer.Builder`, pass a `LibflacAudioRenderer` in the
array of `Renderer`s. ExoPlayer will use the first `Renderer` in the list that
supports the input media format.
Note: These instructions assume you're using `DefaultTrackSelector`. If you have
a custom track selector the choice of `Renderer` is up to your implementation,
......
......@@ -24,13 +24,10 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.junit.Before;
import org.junit.Test;
......@@ -82,8 +79,7 @@ public class FlacPlaybackTest {
public void run() {
Looper.prepare();
LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
player = ExoPlayerFactory.newInstance(context, new Renderer[] {audioRenderer}, trackSelector);
player = new ExoPlayer.Builder(context, audioRenderer).build();
player.addListener(this);
MediaSource mediaSource =
new ProgressiveMediaSource.Factory(
......
......@@ -71,22 +71,22 @@ Once you've followed the instructions above to check out, build and depend on
the extension, the next step is to tell ExoPlayer to use `LibopusAudioRenderer`.
How you do this depends on which player API you're using:
* If you're passing a `DefaultRenderersFactory` to
`ExoPlayerFactory.newSimpleInstance`, you can enable using the extension by
setting the `extensionRendererMode` parameter of the `DefaultRenderersFactory`
constructor to `EXTENSION_RENDERER_MODE_ON`. This will use
`LibopusAudioRenderer` for playback if `MediaCodecAudioRenderer` doesn't
support the input format. Pass `EXTENSION_RENDERER_MODE_PREFER` to give
`LibopusAudioRenderer` priority over `MediaCodecAudioRenderer`.
* If you're passing a `DefaultRenderersFactory` to `SimpleExoPlayer.Builder`,
you can enable using the extension by setting the `extensionRendererMode`
parameter of the `DefaultRenderersFactory` constructor to
`EXTENSION_RENDERER_MODE_ON`. This will use `LibopusAudioRenderer` for
playback if `MediaCodecAudioRenderer` doesn't support the input format. Pass
`EXTENSION_RENDERER_MODE_PREFER` to give `LibopusAudioRenderer` priority over
`MediaCodecAudioRenderer`.
* If you've subclassed `DefaultRenderersFactory`, add a `LibopusAudioRenderer`
to the output list in `buildAudioRenderers`. ExoPlayer will use the first
`Renderer` in the list that supports the input media format.
* If you've implemented your own `RenderersFactory`, return a
`LibopusAudioRenderer` instance from `createRenderers`. ExoPlayer will use the
first `Renderer` in the returned array that supports the input media format.
* If you're using `ExoPlayerFactory.newInstance`, pass a `LibopusAudioRenderer`
in the array of `Renderer`s. ExoPlayer will use the first `Renderer` in the
list that supports the input media format.
* If you're using `ExoPlayer.Builder`, pass a `LibopusAudioRenderer` in the
array of `Renderer`s. ExoPlayer will use the first `Renderer` in the list that
supports the input media format.
Note: These instructions assume you're using `DefaultTrackSelector`. If you have
a custom track selector the choice of `Renderer` is up to your implementation,
......
......@@ -24,13 +24,10 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.junit.Before;
import org.junit.Test;
......@@ -82,8 +79,7 @@ public class OpusPlaybackTest {
public void run() {
Looper.prepare();
LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
player = ExoPlayerFactory.newInstance(context, new Renderer[] {audioRenderer}, trackSelector);
player = new ExoPlayer.Builder(context, audioRenderer).build();
player.addListener(this);
MediaSource mediaSource =
new ProgressiveMediaSource.Factory(
......
......@@ -85,22 +85,22 @@ Once you've followed the instructions above to check out, build and depend on
the extension, the next step is to tell ExoPlayer to use `LibvpxVideoRenderer`.
How you do this depends on which player API you're using:
* If you're passing a `DefaultRenderersFactory` to
`ExoPlayerFactory.newSimpleInstance`, you can enable using the extension by
setting the `extensionRendererMode` parameter of the `DefaultRenderersFactory`
constructor to `EXTENSION_RENDERER_MODE_ON`. This will use
`LibvpxVideoRenderer` for playback if `MediaCodecVideoRenderer` doesn't
support decoding the input VP9 stream. Pass `EXTENSION_RENDERER_MODE_PREFER`
to give `LibvpxVideoRenderer` priority over `MediaCodecVideoRenderer`.
* If you're passing a `DefaultRenderersFactory` to `SimpleExoPlayer.Builder`,
you can enable using the extension by setting the `extensionRendererMode`
parameter of the `DefaultRenderersFactory` constructor to
`EXTENSION_RENDERER_MODE_ON`. This will use `LibvpxVideoRenderer` for playback
if `MediaCodecVideoRenderer` doesn't support decoding the input VP9 stream.
Pass `EXTENSION_RENDERER_MODE_PREFER` to give `LibvpxVideoRenderer` priority
over `MediaCodecVideoRenderer`.
* If you've subclassed `DefaultRenderersFactory`, add a `LibvpxVideoRenderer`
to the output list in `buildVideoRenderers`. ExoPlayer will use the first
`Renderer` in the list that supports the input media format.
* If you've implemented your own `RenderersFactory`, return a
`LibvpxVideoRenderer` instance from `createRenderers`. ExoPlayer will use the
first `Renderer` in the returned array that supports the input media format.
* If you're using `ExoPlayerFactory.newInstance`, pass a `LibvpxVideoRenderer`
in the array of `Renderer`s. ExoPlayer will use the first `Renderer` in the
list that supports the input media format.
* If you're using `ExoPlayer.Builder`, pass a `LibvpxVideoRenderer` in the array
of `Renderer`s. ExoPlayer will use the first `Renderer` in the list that
supports the input media format.
Note: These instructions assume you're using `DefaultTrackSelector`. If you have
a custom track selector the choice of `Renderer` is up to your implementation,
......
......@@ -25,13 +25,10 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Log;
import org.junit.Before;
......@@ -115,8 +112,7 @@ public class VpxPlaybackTest {
public void run() {
Looper.prepare();
LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(0);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
player = ExoPlayerFactory.newInstance(context, new Renderer[] {videoRenderer}, trackSelector);
player = new ExoPlayer.Builder(context, videoRenderer).build();
player.addListener(this);
MediaSource mediaSource =
new ProgressiveMediaSource.Factory(
......
......@@ -104,7 +104,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
/**
* @deprecated Use {@link #DefaultRenderersFactory(Context)} and pass {@link DrmSessionManager}
* directly to {@link SimpleExoPlayer} or {@link ExoPlayerFactory}.
* directly to {@link SimpleExoPlayer.Builder}.
*/
@Deprecated
@SuppressWarnings("deprecation")
......@@ -127,7 +127,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
/**
* @deprecated Use {@link #DefaultRenderersFactory(Context)} and {@link
* #setExtensionRendererMode(int)}, and pass {@link DrmSessionManager} directly to {@link
* SimpleExoPlayer} or {@link ExoPlayerFactory}.
* SimpleExoPlayer.Builder}.
*/
@Deprecated
@SuppressWarnings("deprecation")
......@@ -154,7 +154,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
/**
* @deprecated Use {@link #DefaultRenderersFactory(Context)}, {@link
* #setExtensionRendererMode(int)} and {@link #setAllowedVideoJoiningTimeMs(long)}, and pass
* {@link DrmSessionManager} directly to {@link SimpleExoPlayer} or {@link ExoPlayerFactory}.
* {@link DrmSessionManager} directly to {@link SimpleExoPlayer.Builder}.
*/
@Deprecated
public DefaultRenderersFactory(
......
......@@ -15,8 +15,10 @@
*/
package com.google.android.exoplayer2;
import android.content.Context;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.source.ClippingMediaSource;
......@@ -29,12 +31,17 @@ import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
/**
* An extensible media player that plays {@link MediaSource}s. Instances can be obtained from {@link
* ExoPlayerFactory}.
* SimpleExoPlayer.Builder} or {@link ExoPlayer.Builder}.
*
* <h3>Player components</h3>
*
......@@ -117,6 +124,162 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
*/
public interface ExoPlayer extends Player {
/**
* A builder for {@link ExoPlayer} instances.
*
* <p>See {@link #Builder(Context, Renderer...)} for the list of default values.
*/
final class Builder {
private final Renderer[] renderers;
private Clock clock;
private TrackSelector trackSelector;
private LoadControl loadControl;
private BandwidthMeter bandwidthMeter;
private Looper looper;
private boolean buildCalled;
/**
* Creates a builder with a list of {@link Renderer Renderers}.
*
* <p>The builder uses the following default values:
*
* <ul>
* <li>{@link RenderersFactory}: {@link DefaultRenderersFactory}
* <li>{@link TrackSelector}: {@link DefaultTrackSelector}
* <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
* Looper} of the application's main thread if the current thread doesn't have a {@link
* Looper}
* <li>{@link Clock}: {@link Clock#DEFAULT}
* </ul>
*
* @param context A {@link Context}.
* @param renderers The {@link Renderer Renderers} to be used by the player.
*/
public Builder(Context context, Renderer... renderers) {
this(
renderers,
new DefaultTrackSelector(context),
new DefaultLoadControl(),
DefaultBandwidthMeter.getSingletonInstance(context),
Util.getLooper(),
Clock.DEFAULT);
}
/**
* Creates a builder with the specified custom components.
*
* <p>Note that this constructor is only useful if you try to ensure that ExoPlayer's default
* components can be removed by ProGuard or R8. For most components except renderers, there is
* only a marginal benefit of doing that.
*
* @param renderers The {@link Renderer Renderers} to be used by the player.
* @param trackSelector A {@link TrackSelector}.
* @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.
* @param clock A {@link Clock}. Should always be {@link Clock#DEFAULT}.
*/
public Builder(
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Looper looper,
Clock clock) {
Assertions.checkArgument(renderers.length > 0);
this.renderers = renderers;
this.trackSelector = trackSelector;
this.loadControl = loadControl;
this.bandwidthMeter = bandwidthMeter;
this.looper = looper;
this.clock = clock;
}
/**
* Sets the {@link TrackSelector} that will be used by the player.
*
* @param trackSelector A {@link TrackSelector}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setTrackSelector(TrackSelector trackSelector) {
Assertions.checkState(!buildCalled);
this.trackSelector = trackSelector;
return this;
}
/**
* Sets the {@link LoadControl} that will be used by the player.
*
* @param loadControl A {@link LoadControl}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setLoadControl(LoadControl loadControl) {
Assertions.checkState(!buildCalled);
this.loadControl = loadControl;
return this;
}
/**
* Sets the {@link BandwidthMeter} that will be used by the player.
*
* @param bandwidthMeter A {@link BandwidthMeter}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setBandwidthMeter(BandwidthMeter bandwidthMeter) {
Assertions.checkState(!buildCalled);
this.bandwidthMeter = bandwidthMeter;
return this;
}
/**
* Sets the {@link Looper} that must be used for all calls to the player and that is used to
* call listeners on.
*
* @param looper A {@link Looper}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setLooper(Looper looper) {
Assertions.checkState(!buildCalled);
this.looper = looper;
return this;
}
/**
* Sets the {@link Clock} that will be used by the player. Should only be set for testing
* purposes.
*
* @param clock A {@link Clock}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
@VisibleForTesting
public Builder setClock(Clock clock) {
Assertions.checkState(!buildCalled);
this.clock = clock;
return this;
}
/**
* Builds an {@link ExoPlayer} instance.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public ExoPlayer build() {
Assertions.checkState(!buildCalled);
buildCalled = true;
return new ExoPlayerImpl(
renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
}
}
/** Returns the {@link Looper} associated with the playback thread. */
Looper getPlaybackLooper();
......
......@@ -37,7 +37,9 @@ import com.google.android.exoplayer2.util.Util;
import java.util.ArrayDeque;
import java.util.concurrent.CopyOnWriteArrayList;
/** An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayerFactory}. */
/**
* An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayer.Builder}.
*/
/* package */ final class ExoPlayerImpl extends BasePlayer implements ExoPlayer {
private static final String TAG = "ExoPlayerImpl";
......
......@@ -24,6 +24,7 @@ import android.media.PlaybackParams;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
......@@ -45,9 +46,11 @@ import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Log;
......@@ -63,7 +66,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
/**
* An {@link ExoPlayer} implementation that uses default {@link Renderer} components. Instances can
* be obtained from {@link ExoPlayerFactory}.
* be obtained from {@link SimpleExoPlayer.Builder}.
*/
public class SimpleExoPlayer extends BasePlayer
implements ExoPlayer,
......@@ -76,6 +79,229 @@ public class SimpleExoPlayer extends BasePlayer
@Deprecated
public interface VideoListener extends com.google.android.exoplayer2.video.VideoListener {}
/**
* A builder for {@link SimpleExoPlayer} instances.
*
* <p>See {@link #Builder(Context)} for the list of default values.
*/
public static final class Builder {
private final Context context;
private final RenderersFactory renderersFactory;
private Clock clock;
private TrackSelector trackSelector;
private LoadControl loadControl;
private DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
private BandwidthMeter bandwidthMeter;
private AnalyticsCollector analyticsCollector;
private Looper looper;
private boolean buildCalled;
/**
* Creates a builder.
*
* <p>Use {@link #Builder(Context, RenderersFactory)} instead, if you intend to provide a custom
* {@link RenderersFactory}. This is to ensure that ProGuard or R8 can remove ExoPlayer's {@link
* DefaultRenderersFactory} from the APK.
*
* <p>The builder uses the following default values:
*
* <ul>
* <li>{@link RenderersFactory}: {@link DefaultRenderersFactory}
* <li>{@link TrackSelector}: {@link DefaultTrackSelector}
* <li>{@link LoadControl}: {@link DefaultLoadControl}
* <li>{@link DrmSessionManager}: {@link DrmSessionManager#getDummyDrmSessionManager()}
* <li>{@link BandwidthMeter}: {@link DefaultBandwidthMeter#getSingletonInstance(Context)}
* <li>{@link Looper}: The {@link Looper} associated with the current thread, or the {@link
* Looper} of the application's main thread if the current thread doesn't have a {@link
* Looper}
* <li>{@link AnalyticsCollector}: {@link AnalyticsCollector} with {@link Clock#DEFAULT}
* <li>{@link Clock}: {@link Clock#DEFAULT}
* </ul>
*
* @param context A {@link Context}.
*/
public Builder(Context context) {
this(context, new DefaultRenderersFactory(context));
}
/**
* Creates a builder with a custom {@link RenderersFactory}.
*
* <p>See {@link #Builder(Context)} for a list of default values.
*
* @param context A {@link Context}.
* @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
* player.
*/
public Builder(Context context, RenderersFactory renderersFactory) {
this(
context,
renderersFactory,
new DefaultTrackSelector(context),
new DefaultLoadControl(),
DrmSessionManager.getDummyDrmSessionManager(),
DefaultBandwidthMeter.getSingletonInstance(context),
Util.getLooper(),
new AnalyticsCollector(Clock.DEFAULT),
Clock.DEFAULT);
}
/**
* Creates a builder with the specified custom components.
*
* <p>Note that this constructor is only useful if you try to ensure that ExoPlayer's default
* components can be removed by ProGuard or R8. For most components except renderers, there is
* only a marginal benefit of doing that.
*
* @param context A {@link Context}.
* @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
* player.
* @param trackSelector A {@link TrackSelector}.
* @param loadControl A {@link LoadControl}.
* @param drmSessionManager A {@link DrmSessionManager}.
* @param bandwidthMeter A {@link BandwidthMeter}.
* @param looper A {@link Looper} that must be used for all calls to the player.
* @param analyticsCollector An {@link AnalyticsCollector}.
* @param clock A {@link Clock}. Should always be {@link Clock#DEFAULT}.
*/
public Builder(
Context context,
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
BandwidthMeter bandwidthMeter,
Looper looper,
AnalyticsCollector analyticsCollector,
Clock clock) {
this.context = context;
this.renderersFactory = renderersFactory;
this.trackSelector = trackSelector;
this.loadControl = loadControl;
this.drmSessionManager = drmSessionManager;
this.bandwidthMeter = bandwidthMeter;
this.looper = looper;
this.analyticsCollector = analyticsCollector;
this.clock = clock;
}
/**
* Sets the {@link TrackSelector} that will be used by the player.
*
* @param trackSelector A {@link TrackSelector}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setTrackSelector(TrackSelector trackSelector) {
Assertions.checkState(!buildCalled);
this.trackSelector = trackSelector;
return this;
}
/**
* Sets the {@link LoadControl} that will be used by the player.
*
* @param loadControl A {@link LoadControl}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setLoadControl(LoadControl loadControl) {
Assertions.checkState(!buildCalled);
this.loadControl = loadControl;
return this;
}
/**
* Sets the {@link DrmSessionManager} that will be used for DRM protected playbacks.
*
* @param drmSessionManager A {@link DrmSessionManager}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmSessionManager(DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
Assertions.checkState(!buildCalled);
this.drmSessionManager = drmSessionManager;
return this;
}
/**
* Sets the {@link BandwidthMeter} that will be used by the player.
*
* @param bandwidthMeter A {@link BandwidthMeter}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setBandwidthMeter(BandwidthMeter bandwidthMeter) {
Assertions.checkState(!buildCalled);
this.bandwidthMeter = bandwidthMeter;
return this;
}
/**
* Sets the {@link Looper} that must be used for all calls to the player and that is used to
* call listeners on.
*
* @param looper A {@link Looper}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setLooper(Looper looper) {
Assertions.checkState(!buildCalled);
this.looper = looper;
return this;
}
/**
* Sets the {@link AnalyticsCollector} that will collect and forward all player events.
*
* @param analyticsCollector An {@link AnalyticsCollector}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setAnalyticsCollector(AnalyticsCollector analyticsCollector) {
Assertions.checkState(!buildCalled);
this.analyticsCollector = analyticsCollector;
return this;
}
/**
* Sets the {@link Clock} that will be used by the player. Should only be set for testing
* purposes.
*
* @param clock A {@link Clock}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
@VisibleForTesting
public Builder setClock(Clock clock) {
Assertions.checkState(!buildCalled);
this.clock = clock;
return this;
}
/**
* Builds a {@link SimpleExoPlayer} instance.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public SimpleExoPlayer build() {
Assertions.checkState(!buildCalled);
buildCalled = true;
return new SimpleExoPlayer(
context,
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
bandwidthMeter,
analyticsCollector,
clock,
looper);
}
}
private static final String TAG = "SimpleExoPlayer";
protected final Renderer[] renderers;
......
......@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.trackselection;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
......@@ -64,7 +63,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
/**
* @deprecated Use {@link #Factory()} instead. Custom bandwidth meter should be directly passed
* to the player in {@link ExoPlayerFactory}.
* to the player in {@link SimpleExoPlayer.Builder}.
*/
@Deprecated
@SuppressWarnings("deprecation")
......@@ -112,7 +111,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
/**
* @deprecated Use {@link #Factory(int, int, int, float)} instead. Custom bandwidth meter should
* be directly passed to the player in {@link ExoPlayerFactory}.
* be directly passed to the player in {@link SimpleExoPlayer.Builder}.
*/
@Deprecated
@SuppressWarnings("deprecation")
......@@ -181,7 +180,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
/**
* @deprecated Use {@link #Factory(int, int, int, float, float, long, Clock)} instead. Custom
* bandwidth meter should be directly passed to the player in {@link ExoPlayerFactory}.
* bandwidth meter should be directly passed to the player in {@link
* SimpleExoPlayer.Builder}.
*/
@Deprecated
public Factory(
......
......@@ -26,7 +26,6 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Renderer;
......@@ -1408,7 +1407,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/**
* @deprecated Use {@link #DefaultTrackSelector(Context)} instead. The bandwidth meter should be
* passed directly to the player in {@link ExoPlayerFactory}.
* passed directly to the player in {@link SimpleExoPlayer.Builder}.
*/
@Deprecated
@SuppressWarnings("deprecation")
......
......@@ -79,6 +79,8 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
/** Default maximum weight for the sliding window. */
public static final int DEFAULT_SLIDING_WINDOW_MAX_WEIGHT = 2000;
@Nullable private static DefaultBandwidthMeter singletonInstance;
/** Builder for a bandwidth meter. */
public static final class Builder {
......@@ -214,6 +216,19 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList
}
}
/**
* Returns a singleton instance of a {@link DefaultBandwidthMeter} with default configuration.
*
* @param context A {@link Context}.
* @return The singleton instance.
*/
public static synchronized DefaultBandwidthMeter getSingletonInstance(Context context) {
if (singletonInstance == null) {
singletonInstance = new DefaultBandwidthMeter.Builder(context).build();
}
return singletonInstance;
}
private static final int ELAPSED_MILLIS_FOR_ESTIMATE = 2000;
private static final int BYTES_TRANSFERRED_FOR_ESTIMATE = 512 * 1024;
......
......@@ -23,8 +23,6 @@ import android.media.UnsupportedSchemeException;
import android.net.Uri;
import android.view.Surface;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.SimpleExoPlayer;
......@@ -290,12 +288,10 @@ public final class DashTestRunner {
MappingTrackSelector trackSelector,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(
host,
new DebugRenderersFactory(host),
trackSelector,
new DefaultLoadControl(),
drmSessionManager);
new SimpleExoPlayer.Builder(host, new DebugRenderersFactory(host))
.setTrackSelector(trackSelector)
.setDrmSessionManager(drmSessionManager)
.build();
player.setVideoSurface(surface);
return player;
}
......
......@@ -22,13 +22,10 @@ import android.os.Looper;
import android.os.SystemClock;
import android.view.Surface;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.audio.DefaultAudioSink;
......@@ -245,14 +242,14 @@ public abstract class ExoHostedTest implements AnalyticsListener, HostedTest {
Surface surface,
MappingTrackSelector trackSelector,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
RenderersFactory renderersFactory =
new DefaultRenderersFactory(
host,
DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF,
/* allowedVideoJoiningTimeMs= */ 0);
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(host);
renderersFactory.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
renderersFactory.setAllowedVideoJoiningTimeMs(/* allowedVideoJoiningTimeMs= */ 0);
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(
host, renderersFactory, trackSelector, new DefaultLoadControl(), drmSessionManager);
new SimpleExoPlayer.Builder(host, renderersFactory)
.setTrackSelector(trackSelector)
.setDrmSessionManager(drmSessionManager)
.build();
player.setVideoSurface(surface);
return player;
}
......
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