mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Use Clock to create Handler for delivering messages.
This ensures the message devilery is governed by the clock. Also replace setting a Handler with a Looper to facilititate this change. PiperOrigin-RevId: 353019729
This commit is contained in:
parent
a10e9de484
commit
4cbd4e2e2a
10 changed files with 102 additions and 70 deletions
|
|
@ -138,6 +138,7 @@
|
||||||
([#8430](https://github.com/google/ExoPlayer/issues/8430)).
|
([#8430](https://github.com/google/ExoPlayer/issues/8430)).
|
||||||
* Remove `setVideoDecoderOutputBufferRenderer` from Player API. Use
|
* Remove `setVideoDecoderOutputBufferRenderer` from Player API. Use
|
||||||
`setVideoSurfaceView` and `clearVideoSurfaceView` instead.
|
`setVideoSurfaceView` and `clearVideoSurfaceView` instead.
|
||||||
|
* Replace `PlayerMessage.setHandler` with `PlayerMessage.setLooper`.
|
||||||
* Extractors:
|
* Extractors:
|
||||||
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
|
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
|
||||||
allow decoder capability checks based on codec profile/level
|
allow decoder capability checks based on codec profile/level
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,9 @@ public interface ExoPlayer extends Player {
|
||||||
/** Returns the {@link Looper} associated with the playback thread. */
|
/** Returns the {@link Looper} associated with the playback thread. */
|
||||||
Looper getPlaybackLooper();
|
Looper getPlaybackLooper();
|
||||||
|
|
||||||
|
/** Returns the {@link Clock} used for playback. */
|
||||||
|
Clock getClock();
|
||||||
|
|
||||||
/** @deprecated Use {@link #prepare()} instead. */
|
/** @deprecated Use {@link #prepare()} instead. */
|
||||||
@Deprecated
|
@Deprecated
|
||||||
void retry();
|
void retry();
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ import java.util.List;
|
||||||
private final Handler playbackInfoUpdateHandler;
|
private final Handler playbackInfoUpdateHandler;
|
||||||
private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener;
|
private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener;
|
||||||
private final ExoPlayerImplInternal internalPlayer;
|
private final ExoPlayerImplInternal internalPlayer;
|
||||||
private final Handler internalPlayerHandler;
|
|
||||||
private final ListenerSet<Player.EventListener, Player.Events> listeners;
|
private final ListenerSet<Player.EventListener, Player.Events> listeners;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
private final List<MediaSourceHolderSnapshot> mediaSourceHolderSnapshots;
|
private final List<MediaSourceHolderSnapshot> mediaSourceHolderSnapshots;
|
||||||
|
|
@ -79,6 +78,7 @@ import java.util.List;
|
||||||
@Nullable private final AnalyticsCollector analyticsCollector;
|
@Nullable private final AnalyticsCollector analyticsCollector;
|
||||||
private final Looper applicationLooper;
|
private final Looper applicationLooper;
|
||||||
private final BandwidthMeter bandwidthMeter;
|
private final BandwidthMeter bandwidthMeter;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
@RepeatMode private int repeatMode;
|
@RepeatMode private int repeatMode;
|
||||||
private boolean shuffleModeEnabled;
|
private boolean shuffleModeEnabled;
|
||||||
|
|
@ -149,6 +149,7 @@ import java.util.List;
|
||||||
this.seekParameters = seekParameters;
|
this.seekParameters = seekParameters;
|
||||||
this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems;
|
this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems;
|
||||||
this.applicationLooper = applicationLooper;
|
this.applicationLooper = applicationLooper;
|
||||||
|
this.clock = clock;
|
||||||
repeatMode = Player.REPEAT_MODE_OFF;
|
repeatMode = Player.REPEAT_MODE_OFF;
|
||||||
Player playerForListeners = wrappingPlayer != null ? wrappingPlayer : this;
|
Player playerForListeners = wrappingPlayer != null ? wrappingPlayer : this;
|
||||||
listeners =
|
listeners =
|
||||||
|
|
@ -193,7 +194,6 @@ import java.util.List;
|
||||||
applicationLooper,
|
applicationLooper,
|
||||||
clock,
|
clock,
|
||||||
playbackInfoUpdateListener);
|
playbackInfoUpdateListener);
|
||||||
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -260,6 +260,11 @@ import java.util.List;
|
||||||
return applicationLooper;
|
return applicationLooper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clock getClock() {
|
||||||
|
return clock;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(Player.EventListener listener) {
|
public void addListener(Player.EventListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
|
|
@ -755,7 +760,8 @@ import java.util.List;
|
||||||
target,
|
target,
|
||||||
playbackInfo.timeline,
|
playbackInfo.timeline,
|
||||||
getCurrentWindowIndex(),
|
getCurrentWindowIndex(),
|
||||||
internalPlayerHandler);
|
clock,
|
||||||
|
internalPlayer.getPlaybackLooper());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1468,7 +1468,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessageToTarget(PlayerMessage message) throws ExoPlaybackException {
|
private void sendMessageToTarget(PlayerMessage message) throws ExoPlaybackException {
|
||||||
if (message.getHandler().getLooper() == playbackLooper) {
|
if (message.getLooper() == playbackLooper) {
|
||||||
deliverMessage(message);
|
deliverMessage(message);
|
||||||
if (playbackInfo.playbackState == Player.STATE_READY
|
if (playbackInfo.playbackState == Player.STATE_READY
|
||||||
|| playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
|| playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
||||||
|
|
@ -1481,21 +1481,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessageToTargetThread(final PlayerMessage message) {
|
private void sendMessageToTargetThread(final PlayerMessage message) {
|
||||||
Handler handler = message.getHandler();
|
Looper looper = message.getLooper();
|
||||||
if (!handler.getLooper().getThread().isAlive()) {
|
if (!looper.getThread().isAlive()) {
|
||||||
Log.w("TAG", "Trying to send message on a dead thread.");
|
Log.w("TAG", "Trying to send message on a dead thread.");
|
||||||
message.markAsProcessed(/* isDelivered= */ false);
|
message.markAsProcessed(/* isDelivered= */ false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler.post(
|
clock
|
||||||
() -> {
|
.createHandler(looper, /* callback= */ null)
|
||||||
try {
|
.post(
|
||||||
deliverMessage(message);
|
() -> {
|
||||||
} catch (ExoPlaybackException e) {
|
try {
|
||||||
Log.e(TAG, "Unexpected error delivering message on external thread.", e);
|
deliverMessage(message);
|
||||||
throw new RuntimeException(e);
|
} catch (ExoPlaybackException e) {
|
||||||
}
|
Log.e(TAG, "Unexpected error delivering message on external thread.", e);
|
||||||
});
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deliverMessage(PlayerMessage message) throws ExoPlaybackException {
|
private void deliverMessage(PlayerMessage message) throws ExoPlaybackException {
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
package com.google.android.exoplayer2;
|
package com.google.android.exoplayer2;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Clock;
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
@ -55,11 +55,12 @@ public final class PlayerMessage {
|
||||||
|
|
||||||
private final Target target;
|
private final Target target;
|
||||||
private final Sender sender;
|
private final Sender sender;
|
||||||
|
private final Clock clock;
|
||||||
private final Timeline timeline;
|
private final Timeline timeline;
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
@Nullable private Object payload;
|
@Nullable private Object payload;
|
||||||
private Handler handler;
|
private Looper looper;
|
||||||
private int windowIndex;
|
private int windowIndex;
|
||||||
private long positionMs;
|
private long positionMs;
|
||||||
private boolean deleteAfterDelivery;
|
private boolean deleteAfterDelivery;
|
||||||
|
|
@ -77,7 +78,8 @@ public final class PlayerMessage {
|
||||||
* set to {@link Timeline#EMPTY}, any position can be specified.
|
* set to {@link Timeline#EMPTY}, any position can be specified.
|
||||||
* @param defaultWindowIndex The default window index in the {@code timeline} when no other window
|
* @param defaultWindowIndex The default window index in the {@code timeline} when no other window
|
||||||
* index is specified.
|
* index is specified.
|
||||||
* @param defaultHandler The default handler to send the message on when no other handler is
|
* @param clock The {@link Clock}.
|
||||||
|
* @param defaultLooper The default {@link Looper} to send the message on when no other looper is
|
||||||
* specified.
|
* specified.
|
||||||
*/
|
*/
|
||||||
public PlayerMessage(
|
public PlayerMessage(
|
||||||
|
|
@ -85,11 +87,13 @@ public final class PlayerMessage {
|
||||||
Target target,
|
Target target,
|
||||||
Timeline timeline,
|
Timeline timeline,
|
||||||
int defaultWindowIndex,
|
int defaultWindowIndex,
|
||||||
Handler defaultHandler) {
|
Clock clock,
|
||||||
|
Looper defaultLooper) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.timeline = timeline;
|
this.timeline = timeline;
|
||||||
this.handler = defaultHandler;
|
this.looper = defaultLooper;
|
||||||
|
this.clock = clock;
|
||||||
this.windowIndex = defaultWindowIndex;
|
this.windowIndex = defaultWindowIndex;
|
||||||
this.positionMs = C.TIME_UNSET;
|
this.positionMs = C.TIME_UNSET;
|
||||||
this.deleteAfterDelivery = true;
|
this.deleteAfterDelivery = true;
|
||||||
|
|
@ -142,22 +146,28 @@ public final class PlayerMessage {
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated Use {@link #setLooper(Looper)} instead. */
|
||||||
|
@Deprecated
|
||||||
|
public PlayerMessage setHandler(Handler handler) {
|
||||||
|
return setLooper(handler.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the handler the message is delivered on.
|
* Sets the {@link Looper} the message is delivered on.
|
||||||
*
|
*
|
||||||
* @param handler A {@link Handler}.
|
* @param looper A {@link Looper}.
|
||||||
* @return This message.
|
* @return This message.
|
||||||
* @throws IllegalStateException If {@link #send()} has already been called.
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
||||||
*/
|
*/
|
||||||
public PlayerMessage setHandler(Handler handler) {
|
public PlayerMessage setLooper(Looper looper) {
|
||||||
Assertions.checkState(!isSent);
|
Assertions.checkState(!isSent);
|
||||||
this.handler = handler;
|
this.looper = looper;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the handler the message is delivered on. */
|
/** Returns the {@link Looper} the message is delivered on. */
|
||||||
public Handler getHandler() {
|
public Looper getLooper() {
|
||||||
return handler;
|
return looper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -287,19 +297,19 @@ public final class PlayerMessage {
|
||||||
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
||||||
* the message.
|
* the message.
|
||||||
*
|
*
|
||||||
* <p>Note that this method can't be called if the current thread is the same thread used by the
|
* <p>Note that this method must not be called if the current thread is the same thread used by
|
||||||
* message handler set with {@link #setHandler(Handler)} as it would cause a deadlock.
|
* the message {@link #getLooper() looper} as it would cause a deadlock.
|
||||||
*
|
*
|
||||||
* @return Whether the message was delivered successfully.
|
* @return Whether the message was delivered successfully.
|
||||||
* @throws IllegalStateException If this method is called before {@link #send()}.
|
* @throws IllegalStateException If this method is called before {@link #send()}.
|
||||||
* @throws IllegalStateException If this method is called on the same thread used by the message
|
* @throws IllegalStateException If this method is called on the same thread used by the message
|
||||||
* handler set with {@link #setHandler(Handler)}.
|
* {@link #getLooper() looper}.
|
||||||
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
||||||
* to be delivered.
|
* to be delivered.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean blockUntilDelivered() throws InterruptedException {
|
public synchronized boolean blockUntilDelivered() throws InterruptedException {
|
||||||
Assertions.checkState(isSent);
|
Assertions.checkState(isSent);
|
||||||
Assertions.checkState(handler.getLooper().getThread() != Thread.currentThread());
|
Assertions.checkState(looper.getThread() != Thread.currentThread());
|
||||||
while (!isProcessed) {
|
while (!isProcessed) {
|
||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
|
|
@ -310,14 +320,14 @@ public final class PlayerMessage {
|
||||||
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
||||||
* the message or the specified timeout elapsed.
|
* the message or the specified timeout elapsed.
|
||||||
*
|
*
|
||||||
* <p>Note that this method can't be called if the current thread is the same thread used by the
|
* <p>Note that this method must not be called if the current thread is the same thread used by
|
||||||
* message handler set with {@link #setHandler(Handler)} as it would cause a deadlock.
|
* the message {@link #getLooper() looper} as it would cause a deadlock.
|
||||||
*
|
*
|
||||||
* @param timeoutMs The timeout in milliseconds.
|
* @param timeoutMs The timeout in milliseconds.
|
||||||
* @return Whether the message was delivered successfully.
|
* @return Whether the message was delivered successfully.
|
||||||
* @throws IllegalStateException If this method is called before {@link #send()}.
|
* @throws IllegalStateException If this method is called before {@link #send()}.
|
||||||
* @throws IllegalStateException If this method is called on the same thread used by the message
|
* @throws IllegalStateException If this method is called on the same thread used by the message
|
||||||
* handler set with {@link #setHandler(Handler)}.
|
* {@link #getLooper() looper}.
|
||||||
* @throws TimeoutException If the {@code timeoutMs} elapsed and this message has not been
|
* @throws TimeoutException If the {@code timeoutMs} elapsed and this message has not been
|
||||||
* delivered and the player is still able to deliver the message.
|
* delivered and the player is still able to deliver the message.
|
||||||
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
||||||
|
|
@ -325,14 +335,8 @@ public final class PlayerMessage {
|
||||||
*/
|
*/
|
||||||
public synchronized boolean blockUntilDelivered(long timeoutMs)
|
public synchronized boolean blockUntilDelivered(long timeoutMs)
|
||||||
throws InterruptedException, TimeoutException {
|
throws InterruptedException, TimeoutException {
|
||||||
return blockUntilDelivered(timeoutMs, Clock.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting()
|
|
||||||
/* package */ synchronized boolean blockUntilDelivered(long timeoutMs, Clock clock)
|
|
||||||
throws InterruptedException, TimeoutException {
|
|
||||||
Assertions.checkState(isSent);
|
Assertions.checkState(isSent);
|
||||||
Assertions.checkState(handler.getLooper().getThread() != Thread.currentThread());
|
Assertions.checkState(looper.getThread() != Thread.currentThread());
|
||||||
|
|
||||||
long deadlineMs = clock.elapsedRealtime() + timeoutMs;
|
long deadlineMs = clock.elapsedRealtime() + timeoutMs;
|
||||||
long remainingMs = timeoutMs;
|
long remainingMs = timeoutMs;
|
||||||
|
|
@ -340,11 +344,9 @@ public final class PlayerMessage {
|
||||||
wait(remainingMs);
|
wait(remainingMs);
|
||||||
remainingMs = deadlineMs - clock.elapsedRealtime();
|
remainingMs = deadlineMs - clock.elapsedRealtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isProcessed) {
|
if (!isProcessed) {
|
||||||
throw new TimeoutException("Message delivery timed out.");
|
throw new TimeoutException("Message delivery timed out.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return isDelivered;
|
return isDelivered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1202,6 +1202,11 @@ public class SimpleExoPlayer extends BasePlayer
|
||||||
return player.getApplicationLooper();
|
return player.getApplicationLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clock getClock() {
|
||||||
|
return player.getClock();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(Player.EventListener listener) {
|
public void addListener(Player.EventListener listener) {
|
||||||
// Don't verify application thread. We allow calls to this method from any thread.
|
// Don't verify application thread. We allow calls to this method from any thread.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.util.Clock;
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
|
|
@ -55,9 +54,14 @@ public class PlayerMessageTest {
|
||||||
PlayerMessage.Target target = (messageType, payload) -> {};
|
PlayerMessage.Target target = (messageType, payload) -> {};
|
||||||
handlerThread = new HandlerThread("TestHandler");
|
handlerThread = new HandlerThread("TestHandler");
|
||||||
handlerThread.start();
|
handlerThread.start();
|
||||||
Handler handler = new Handler(handlerThread.getLooper());
|
|
||||||
message =
|
message =
|
||||||
new PlayerMessage(sender, target, Timeline.EMPTY, /* defaultWindowIndex= */ 0, handler);
|
new PlayerMessage(
|
||||||
|
sender,
|
||||||
|
target,
|
||||||
|
Timeline.EMPTY,
|
||||||
|
/* defaultWindowIndex= */ 0,
|
||||||
|
clock,
|
||||||
|
handlerThread.getLooper());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
@ -69,8 +73,7 @@ public class PlayerMessageTest {
|
||||||
public void blockUntilDelivered_timesOut() throws Exception {
|
public void blockUntilDelivered_timesOut() throws Exception {
|
||||||
when(clock.elapsedRealtime()).thenReturn(0L).thenReturn(TIMEOUT_MS * 2);
|
when(clock.elapsedRealtime()).thenReturn(0L).thenReturn(TIMEOUT_MS * 2);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(TimeoutException.class, () -> message.send().blockUntilDelivered(TIMEOUT_MS));
|
||||||
TimeoutException.class, () -> message.send().blockUntilDelivered(TIMEOUT_MS, clock));
|
|
||||||
|
|
||||||
// Ensure blockUntilDelivered() entered the blocking loop.
|
// Ensure blockUntilDelivered() entered the blocking loop.
|
||||||
verify(clock, Mockito.times(2)).elapsedRealtime();
|
verify(clock, Mockito.times(2)).elapsedRealtime();
|
||||||
|
|
@ -82,7 +85,7 @@ public class PlayerMessageTest {
|
||||||
|
|
||||||
message.send().markAsProcessed(/* isDelivered= */ true);
|
message.send().markAsProcessed(/* isDelivered= */ true);
|
||||||
|
|
||||||
assertThat(message.blockUntilDelivered(TIMEOUT_MS, clock)).isTrue();
|
assertThat(message.blockUntilDelivered(TIMEOUT_MS)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -110,7 +113,7 @@ public class PlayerMessageTest {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assertThat(message.blockUntilDelivered(TIMEOUT_MS, clock)).isTrue();
|
assertThat(message.blockUntilDelivered(TIMEOUT_MS)).isTrue();
|
||||||
// Ensure blockUntilDelivered() entered the blocking loop.
|
// Ensure blockUntilDelivered() entered the blocking loop.
|
||||||
verify(clock, Mockito.atLeast(2)).elapsedRealtime();
|
verify(clock, Mockito.atLeast(2)).elapsedRealtime();
|
||||||
future.get(1, SECONDS);
|
future.get(1, SECONDS);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package com.google.android.exoplayer2.robolectric;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
|
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
|
|
@ -297,20 +296,22 @@ public class TestPlayerRunHelper {
|
||||||
public static void playUntilPosition(ExoPlayer player, int windowIndex, long positionMs)
|
public static void playUntilPosition(ExoPlayer player, int windowIndex, long positionMs)
|
||||||
throws TimeoutException {
|
throws TimeoutException {
|
||||||
verifyMainTestThread(player);
|
verifyMainTestThread(player);
|
||||||
Handler testHandler = Util.createHandlerForCurrentOrMainLooper();
|
Looper applicationLooper = Util.getCurrentOrMainLooper();
|
||||||
|
|
||||||
AtomicBoolean messageHandled = new AtomicBoolean(false);
|
AtomicBoolean messageHandled = new AtomicBoolean(false);
|
||||||
player
|
player
|
||||||
.createMessage(
|
.createMessage(
|
||||||
(messageType, payload) -> {
|
(messageType, payload) -> {
|
||||||
// Block playback thread until pause command has been sent from test thread.
|
// Block playback thread until pause command has been sent from test thread.
|
||||||
ConditionVariable blockPlaybackThreadCondition = new ConditionVariable();
|
ConditionVariable blockPlaybackThreadCondition = new ConditionVariable();
|
||||||
testHandler.post(
|
player
|
||||||
() -> {
|
.getClock()
|
||||||
player.pause();
|
.createHandler(applicationLooper, /* callback= */ null)
|
||||||
messageHandled.set(true);
|
.post(
|
||||||
blockPlaybackThreadCondition.open();
|
() -> {
|
||||||
});
|
player.pause();
|
||||||
|
messageHandled.set(true);
|
||||||
|
blockPlaybackThreadCondition.open();
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
blockPlaybackThreadCondition.block();
|
blockPlaybackThreadCondition.block();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
@ -354,7 +355,7 @@ public class TestPlayerRunHelper {
|
||||||
AtomicBoolean receivedMessageCallback = new AtomicBoolean(false);
|
AtomicBoolean receivedMessageCallback = new AtomicBoolean(false);
|
||||||
player
|
player
|
||||||
.createMessage((type, data) -> receivedMessageCallback.set(true))
|
.createMessage((type, data) -> receivedMessageCallback.set(true))
|
||||||
.setHandler(Util.createHandlerForCurrentOrMainLooper())
|
.setLooper(Util.getCurrentOrMainLooper())
|
||||||
.send();
|
.send();
|
||||||
runMainLooperUntil(receivedMessageCallback::get);
|
runMainLooperUntil(receivedMessageCallback::get);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.testutil;
|
package com.google.android.exoplayer2.testutil;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Looper;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
|
@ -603,7 +603,7 @@ public abstract class Action {
|
||||||
} else {
|
} else {
|
||||||
message.setPosition(positionMs);
|
message.setPosition(positionMs);
|
||||||
}
|
}
|
||||||
message.setHandler(Util.createHandlerForCurrentOrMainLooper());
|
message.setLooper(Util.getCurrentOrMainLooper());
|
||||||
message.setDeleteAfterDelivery(deleteAfterDelivery);
|
message.setDeleteAfterDelivery(deleteAfterDelivery);
|
||||||
message.send();
|
message.send();
|
||||||
}
|
}
|
||||||
|
|
@ -685,18 +685,21 @@ public abstract class Action {
|
||||||
@Nullable Surface surface,
|
@Nullable Surface surface,
|
||||||
HandlerWrapper handler,
|
HandlerWrapper handler,
|
||||||
@Nullable ActionNode nextAction) {
|
@Nullable ActionNode nextAction) {
|
||||||
Handler testThreadHandler = Util.createHandlerForCurrentOrMainLooper();
|
|
||||||
// Schedule a message on the playback thread to ensure the player is paused immediately.
|
// Schedule a message on the playback thread to ensure the player is paused immediately.
|
||||||
|
Looper applicationLooper = Util.getCurrentOrMainLooper();
|
||||||
player
|
player
|
||||||
.createMessage(
|
.createMessage(
|
||||||
(messageType, payload) -> {
|
(messageType, payload) -> {
|
||||||
// Block playback thread until pause command has been sent from test thread.
|
// Block playback thread until pause command has been sent from test thread.
|
||||||
ConditionVariable blockPlaybackThreadCondition = new ConditionVariable();
|
ConditionVariable blockPlaybackThreadCondition = new ConditionVariable();
|
||||||
testThreadHandler.post(
|
player
|
||||||
() -> {
|
.getClock()
|
||||||
player.pause();
|
.createHandler(applicationLooper, /* callback= */ null)
|
||||||
blockPlaybackThreadCondition.open();
|
.post(
|
||||||
});
|
() -> {
|
||||||
|
player.pause();
|
||||||
|
blockPlaybackThreadCondition.open();
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
blockPlaybackThreadCondition.block();
|
blockPlaybackThreadCondition.block();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
@ -712,7 +715,7 @@ public abstract class Action {
|
||||||
(messageType, payload) ->
|
(messageType, payload) ->
|
||||||
nextAction.schedule(player, trackSelector, surface, handler))
|
nextAction.schedule(player, trackSelector, surface, handler))
|
||||||
.setPosition(windowIndex, positionMs)
|
.setPosition(windowIndex, positionMs)
|
||||||
.setHandler(testThreadHandler)
|
.setLooper(applicationLooper)
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
player.play();
|
player.play();
|
||||||
|
|
@ -1049,7 +1052,7 @@ public abstract class Action {
|
||||||
player
|
player
|
||||||
.createMessage(
|
.createMessage(
|
||||||
(type, data) -> nextAction.schedule(player, trackSelector, surface, handler))
|
(type, data) -> nextAction.schedule(player, trackSelector, surface, handler))
|
||||||
.setHandler(Util.createHandlerForCurrentOrMainLooper())
|
.setLooper(Util.getCurrentOrMainLooper())
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||||
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,6 +76,11 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clock getClock() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(Player.EventListener listener) {
|
public void addListener(Player.EventListener listener) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue