Commit 79b809b5 by Googler Committed by microkatz

Add setPlaybackLooper ExoPlayer builder method

The method allows clients to specify a pre-existing thread
to use for playback. This can be used to run multiple ExoPlayer
instances on the same playback thread.

PiperOrigin-RevId: 488980749
parent f52bb274
...@@ -24,6 +24,7 @@ import android.media.AudioDeviceInfo; ...@@ -24,6 +24,7 @@ import android.media.AudioDeviceInfo;
import android.media.AudioTrack; import android.media.AudioTrack;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.os.Looper; import android.os.Looper;
import android.os.Process;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
...@@ -473,6 +474,7 @@ public interface ExoPlayer extends Player { ...@@ -473,6 +474,7 @@ public interface ExoPlayer extends Player {
/* package */ long detachSurfaceTimeoutMs; /* package */ long detachSurfaceTimeoutMs;
/* package */ boolean pauseAtEndOfMediaItems; /* package */ boolean pauseAtEndOfMediaItems;
/* package */ boolean usePlatformDiagnostics; /* package */ boolean usePlatformDiagnostics;
@Nullable /* package */ Looper playbackLooper;
/* package */ boolean buildCalled; /* package */ boolean buildCalled;
/** /**
...@@ -515,6 +517,7 @@ public interface ExoPlayer extends Player { ...@@ -515,6 +517,7 @@ public interface ExoPlayer extends Player {
* <li>{@code pauseAtEndOfMediaItems}: {@code false} * <li>{@code pauseAtEndOfMediaItems}: {@code false}
* <li>{@code usePlatformDiagnostics}: {@code true} * <li>{@code usePlatformDiagnostics}: {@code true}
* <li>{@link Clock}: {@link Clock#DEFAULT} * <li>{@link Clock}: {@link Clock#DEFAULT}
* <li>{@code playbackLooper}: {@code null} (create new thread)
* </ul> * </ul>
* *
* @param context A {@link Context}. * @param context A {@link Context}.
...@@ -1098,6 +1101,23 @@ public interface ExoPlayer extends Player { ...@@ -1098,6 +1101,23 @@ public interface ExoPlayer extends Player {
} }
/** /**
* Sets the {@link Looper} that will be used for playback.
*
* <p>The backing thread should run with priority {@link Process#THREAD_PRIORITY_AUDIO} and
* should handle messages within 10ms.
*
* @param playbackLooper A {@link looper}.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
@CanIgnoreReturnValue
public Builder setPlaybackLooper(Looper playbackLooper) {
checkState(!buildCalled);
this.playbackLooper = playbackLooper;
return this;
}
/**
* Builds an {@link ExoPlayer} instance. * Builds an {@link ExoPlayer} instance.
* *
* @throws IllegalStateException If this method has already been called. * @throws IllegalStateException If this method has already been called.
......
...@@ -334,7 +334,8 @@ import java.util.concurrent.TimeoutException; ...@@ -334,7 +334,8 @@ import java.util.concurrent.TimeoutException;
applicationLooper, applicationLooper,
clock, clock,
playbackInfoUpdateListener, playbackInfoUpdateListener,
playerId); playerId,
builder.playbackLooper);
volume = 1; volume = 1;
repeatMode = Player.REPEAT_MODE_OFF; repeatMode = Player.REPEAT_MODE_OFF;
......
...@@ -181,7 +181,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -181,7 +181,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private final LoadControl loadControl; private final LoadControl loadControl;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final HandlerWrapper handler; private final HandlerWrapper handler;
private final HandlerThread internalPlaybackThread; @Nullable private final HandlerThread internalPlaybackThread;
private final Looper playbackLooper; private final Looper playbackLooper;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period; private final Timeline.Period period;
...@@ -236,7 +236,8 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -236,7 +236,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
Looper applicationLooper, Looper applicationLooper,
Clock clock, Clock clock,
PlaybackInfoUpdateListener playbackInfoUpdateListener, PlaybackInfoUpdateListener playbackInfoUpdateListener,
PlayerId playerId) { PlayerId playerId,
Looper playbackLooper) {
this.playbackInfoUpdateListener = playbackInfoUpdateListener; this.playbackInfoUpdateListener = playbackInfoUpdateListener;
this.renderers = renderers; this.renderers = renderers;
this.trackSelector = trackSelector; this.trackSelector = trackSelector;
...@@ -277,12 +278,18 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -277,12 +278,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
mediaSourceList = mediaSourceList =
new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler, playerId); new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler, playerId);
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can if (playbackLooper != null) {
// not normally change to this priority" is incorrect. internalPlaybackThread = null;
internalPlaybackThread = new HandlerThread("ExoPlayer:Playback", Process.THREAD_PRIORITY_AUDIO); this.playbackLooper = playbackLooper;
internalPlaybackThread.start(); } else {
playbackLooper = internalPlaybackThread.getLooper(); // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
handler = clock.createHandler(playbackLooper, this); // not normally change to this priority" is incorrect.
internalPlaybackThread =
new HandlerThread("ExoPlayer:Playback", Process.THREAD_PRIORITY_AUDIO);
internalPlaybackThread.start();
this.playbackLooper = internalPlaybackThread.getLooper();
}
handler = clock.createHandler(this.playbackLooper, this);
} }
public void experimentalSetForegroundModeTimeoutMs(long setForegroundModeTimeoutMs) { public void experimentalSetForegroundModeTimeoutMs(long setForegroundModeTimeoutMs) {
...@@ -385,7 +392,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -385,7 +392,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
@Override @Override
public synchronized void sendMessage(PlayerMessage message) { public synchronized void sendMessage(PlayerMessage message) {
if (released || !internalPlaybackThread.isAlive()) { if (released || !playbackLooper.getThread().isAlive()) {
Log.w(TAG, "Ignoring messages sent after release."); Log.w(TAG, "Ignoring messages sent after release.");
message.markAsProcessed(/* isDelivered= */ false); message.markAsProcessed(/* isDelivered= */ false);
return; return;
...@@ -400,7 +407,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -400,7 +407,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @return Whether the operations succeeded. If false, the operation timed out. * @return Whether the operations succeeded. If false, the operation timed out.
*/ */
public synchronized boolean setForegroundMode(boolean foregroundMode) { public synchronized boolean setForegroundMode(boolean foregroundMode) {
if (released || !internalPlaybackThread.isAlive()) { if (released || !playbackLooper.getThread().isAlive()) {
return true; return true;
} }
if (foregroundMode) { if (foregroundMode) {
...@@ -422,7 +429,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -422,7 +429,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @return Whether the release succeeded. If false, the release timed out. * @return Whether the release succeeded. If false, the release timed out.
*/ */
public synchronized boolean release() { public synchronized boolean release() {
if (released || !internalPlaybackThread.isAlive()) { if (released || !playbackLooper.getThread().isAlive()) {
return true; return true;
} }
handler.sendEmptyMessage(MSG_RELEASE); handler.sendEmptyMessage(MSG_RELEASE);
...@@ -1374,7 +1381,9 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1374,7 +1381,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* resetError= */ false); /* resetError= */ false);
loadControl.onReleased(); loadControl.onReleased();
setState(Player.STATE_IDLE); setState(Player.STATE_IDLE);
internalPlaybackThread.quit(); if (internalPlaybackThread != null) {
internalPlaybackThread.quit();
}
synchronized (this) { synchronized (this) {
released = true; released = true;
notifyAll(); notifyAll();
......
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