mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Remove set timeout on release() and setSurface()
Removes the experimental methods to set a timeout when releasing the player and setting the surface. PiperOrigin-RevId: 311703988
This commit is contained in:
parent
b9abe2d0d6
commit
78c850a885
5 changed files with 12 additions and 286 deletions
|
|
@ -149,8 +149,6 @@ public interface ExoPlayer extends Player {
|
||||||
private SeekParameters seekParameters;
|
private SeekParameters seekParameters;
|
||||||
private boolean pauseAtEndOfMediaItems;
|
private boolean pauseAtEndOfMediaItems;
|
||||||
private boolean buildCalled;
|
private boolean buildCalled;
|
||||||
|
|
||||||
private long releaseTimeoutMs;
|
|
||||||
private boolean throwWhenStuckBuffering;
|
private boolean throwWhenStuckBuffering;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -215,20 +213,6 @@ public interface ExoPlayer extends Player {
|
||||||
clock = Clock.DEFAULT;
|
clock = Clock.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a limit on the time a call to {@link ExoPlayer#release()} can spend. If a call to {@link
|
|
||||||
* ExoPlayer#release()} takes more than {@code timeoutMs} milliseconds to complete, the player
|
|
||||||
* will raise an error via {@link Player.EventListener#onPlayerError}.
|
|
||||||
*
|
|
||||||
* <p>This method is experimental, and will be renamed or removed in a future release.
|
|
||||||
*
|
|
||||||
* @param timeoutMs The time limit in milliseconds, or 0 for no limit.
|
|
||||||
*/
|
|
||||||
public Builder experimental_setReleaseTimeoutMs(long timeoutMs) {
|
|
||||||
releaseTimeoutMs = timeoutMs;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether the player should throw when it detects it's stuck buffering.
|
* Sets whether the player should throw when it detects it's stuck buffering.
|
||||||
*
|
*
|
||||||
|
|
@ -405,10 +389,6 @@ public interface ExoPlayer extends Player {
|
||||||
pauseAtEndOfMediaItems,
|
pauseAtEndOfMediaItems,
|
||||||
clock,
|
clock,
|
||||||
looper);
|
looper);
|
||||||
|
|
||||||
if (releaseTimeoutMs > 0) {
|
|
||||||
player.experimental_setReleaseTimeoutMs(releaseTimeoutMs);
|
|
||||||
}
|
|
||||||
if (throwWhenStuckBuffering) {
|
if (throwWhenStuckBuffering) {
|
||||||
player.experimental_throwWhenStuckBuffering();
|
player.experimental_throwWhenStuckBuffering();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayer.Builder}.
|
* An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayer.Builder}.
|
||||||
|
|
@ -178,20 +177,6 @@ import java.util.concurrent.TimeoutException;
|
||||||
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
|
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a limit on the time a call to {@link #release()} can spend. If a call to {@link #release()}
|
|
||||||
* takes more than {@code timeoutMs} milliseconds to complete, the player will raise an error via
|
|
||||||
* {@link Player.EventListener#onPlayerError}.
|
|
||||||
*
|
|
||||||
* <p>This method is experimental, and will be renamed or removed in a future release. It should
|
|
||||||
* only be called before the player is used.
|
|
||||||
*
|
|
||||||
* @param timeoutMs The time limit in milliseconds, or 0 for no limit.
|
|
||||||
*/
|
|
||||||
public void experimental_setReleaseTimeoutMs(long timeoutMs) {
|
|
||||||
internalPlayer.experimental_setReleaseTimeoutMs(timeoutMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the player to throw when it detects it's stuck buffering.
|
* Configures the player to throw when it detects it's stuck buffering.
|
||||||
*
|
*
|
||||||
|
|
@ -690,13 +675,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " ["
|
Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " ["
|
||||||
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] ["
|
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] ["
|
||||||
+ ExoPlayerLibraryInfo.registeredModules() + "]");
|
+ ExoPlayerLibraryInfo.registeredModules() + "]");
|
||||||
if (!internalPlayer.release()) {
|
internalPlayer.release();
|
||||||
notifyListeners(
|
|
||||||
listener ->
|
|
||||||
listener.onPlayerError(
|
|
||||||
ExoPlaybackException.createForUnexpected(
|
|
||||||
new RuntimeException(new TimeoutException("Player release timed out.")))));
|
|
||||||
}
|
|
||||||
applicationHandler.removeCallbacksAndMessages(null);
|
applicationHandler.removeCallbacksAndMessages(null);
|
||||||
playbackInfo =
|
playbackInfo =
|
||||||
getResetPlaybackInfo(
|
getResetPlaybackInfo(
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
private long rendererPositionUs;
|
private long rendererPositionUs;
|
||||||
private int nextPendingMessageIndexHint;
|
private int nextPendingMessageIndexHint;
|
||||||
private boolean deliverPendingMessageAtStartPositionRequired;
|
private boolean deliverPendingMessageAtStartPositionRequired;
|
||||||
|
|
||||||
private long releaseTimeoutMs;
|
|
||||||
private boolean throwWhenStuckBuffering;
|
private boolean throwWhenStuckBuffering;
|
||||||
|
|
||||||
public ExoPlayerImplInternal(
|
public ExoPlayerImplInternal(
|
||||||
|
|
@ -191,10 +189,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void experimental_setReleaseTimeoutMs(long releaseTimeoutMs) {
|
|
||||||
this.releaseTimeoutMs = releaseTimeoutMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void experimental_throwWhenStuckBuffering() {
|
public void experimental_throwWhenStuckBuffering() {
|
||||||
throwWhenStuckBuffering = true;
|
throwWhenStuckBuffering = true;
|
||||||
}
|
}
|
||||||
|
|
@ -322,23 +316,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean release() {
|
public synchronized void release() {
|
||||||
if (released || !internalPlaybackThread.isAlive()) {
|
if (released || !internalPlaybackThread.isAlive()) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.sendEmptyMessage(MSG_RELEASE);
|
handler.sendEmptyMessage(MSG_RELEASE);
|
||||||
try {
|
boolean wasInterrupted = false;
|
||||||
if (releaseTimeoutMs > 0) {
|
while (!released) {
|
||||||
waitUntilReleased(releaseTimeoutMs);
|
try {
|
||||||
} else {
|
wait();
|
||||||
waitUntilReleased();
|
} catch (InterruptedException e) {
|
||||||
|
wasInterrupted = true;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
}
|
||||||
|
if (wasInterrupted) {
|
||||||
|
// Restore the interrupted status.
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
return released;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Looper getPlaybackLooper() {
|
public Looper getPlaybackLooper() {
|
||||||
|
|
@ -504,63 +498,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
// Private methods.
|
// Private methods.
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks the current thread until {@link #releaseInternal()} is executed on the playback Thread.
|
|
||||||
*
|
|
||||||
* <p>If the current thread is interrupted while waiting for {@link #releaseInternal()} to
|
|
||||||
* complete, this method will delay throwing the {@link InterruptedException} to ensure that the
|
|
||||||
* underlying resources have been released, and will an {@link InterruptedException} <b>after</b>
|
|
||||||
* {@link #releaseInternal()} is complete.
|
|
||||||
*
|
|
||||||
* @throws {@link InterruptedException} if the current Thread was interrupted while waiting for
|
|
||||||
* {@link #releaseInternal()} to complete.
|
|
||||||
*/
|
|
||||||
private synchronized void waitUntilReleased() throws InterruptedException {
|
|
||||||
InterruptedException interruptedException = null;
|
|
||||||
while (!released) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
interruptedException = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interruptedException != null) {
|
|
||||||
throw interruptedException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks the current thread until {@link #releaseInternal()} is performed on the playback Thread
|
|
||||||
* or the specified amount of time has elapsed.
|
|
||||||
*
|
|
||||||
* <p>If the current thread is interrupted while waiting for {@link #releaseInternal()} to
|
|
||||||
* complete, this method will delay throwing the {@link InterruptedException} to ensure that the
|
|
||||||
* underlying resources have been released or the operation timed out, and will throw an {@link
|
|
||||||
* InterruptedException} afterwards.
|
|
||||||
*
|
|
||||||
* @param timeoutMs the time in milliseconds to wait for {@link #releaseInternal()} to complete.
|
|
||||||
* @throws {@link InterruptedException} if the current Thread was interrupted while waiting for
|
|
||||||
* {@link #releaseInternal()} to complete.
|
|
||||||
*/
|
|
||||||
private synchronized void waitUntilReleased(long timeoutMs) throws InterruptedException {
|
|
||||||
long deadlineMs = clock.elapsedRealtime() + timeoutMs;
|
|
||||||
long remainingMs = timeoutMs;
|
|
||||||
InterruptedException interruptedException = null;
|
|
||||||
while (!released && remainingMs > 0) {
|
|
||||||
try {
|
|
||||||
wait(remainingMs);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
interruptedException = e;
|
|
||||||
}
|
|
||||||
remainingMs = deadlineMs - clock.elapsedRealtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interruptedException != null) {
|
|
||||||
throw interruptedException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setState(int state) {
|
private void setState(int state) {
|
||||||
if (playbackInfo.playbackState != state) {
|
if (playbackInfo.playbackState != state) {
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(state);
|
playbackInfo = playbackInfo.copyWithPlaybackState(state);
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,7 @@ package com.google.android.exoplayer2;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
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 java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a player message which can be sent with a {@link Sender} and received by a {@link
|
* Defines a player message which can be sent with a {@link Sender} and received by a {@link
|
||||||
|
|
@ -292,28 +289,6 @@ public final class PlayerMessage {
|
||||||
return isDelivered;
|
return isDelivered;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
|
||||||
* the message or the specified waiting time elapses.
|
|
||||||
*
|
|
||||||
* <p>Note that this method can't be called if the current thread is the same thread used by the
|
|
||||||
* message handler set with {@link #setHandler(Handler)} as it would cause a deadlock.
|
|
||||||
*
|
|
||||||
* @param timeoutMs the maximum time to wait in milliseconds.
|
|
||||||
* @return Whether the message was delivered successfully.
|
|
||||||
* @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
|
|
||||||
* handler set with {@link #setHandler(Handler)}.
|
|
||||||
* @throws TimeoutException If the waiting time elapsed and this message has not been delivered
|
|
||||||
* and the player is still able to deliver the message.
|
|
||||||
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
|
||||||
* to be delivered.
|
|
||||||
*/
|
|
||||||
public synchronized boolean experimental_blockUntilDelivered(long timeoutMs)
|
|
||||||
throws InterruptedException, TimeoutException {
|
|
||||||
return experimental_blockUntilDelivered(timeoutMs, Clock.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the message as processed. Should only be called by a {@link Sender} and may be called
|
* Marks the message as processed. Should only be called by a {@link Sender} and may be called
|
||||||
* multiple times.
|
* multiple times.
|
||||||
|
|
@ -327,24 +302,4 @@ public final class PlayerMessage {
|
||||||
isProcessed = true;
|
isProcessed = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting()
|
|
||||||
/* package */ synchronized boolean experimental_blockUntilDelivered(long timeoutMs, Clock clock)
|
|
||||||
throws InterruptedException, TimeoutException {
|
|
||||||
Assertions.checkState(isSent);
|
|
||||||
Assertions.checkState(handler.getLooper().getThread() != Thread.currentThread());
|
|
||||||
|
|
||||||
long deadlineMs = clock.elapsedRealtime() + timeoutMs;
|
|
||||||
long remainingMs = timeoutMs;
|
|
||||||
while (!isProcessed && remainingMs > 0) {
|
|
||||||
wait(remainingMs);
|
|
||||||
remainingMs = deadlineMs - clock.elapsedRealtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isProcessed) {
|
|
||||||
throw new TimeoutException("Message delivery timed out.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return isDelivered;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.google.android.exoplayer2;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import com.google.android.exoplayer2.util.Clock;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
/** Unit test for {@link PlayerMessage}. */
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class PlayerMessageTest {
|
|
||||||
|
|
||||||
private static final long TIMEOUT_MS = 10;
|
|
||||||
|
|
||||||
@Mock Clock clock;
|
|
||||||
private HandlerThread handlerThread;
|
|
||||||
private PlayerMessage message;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
initMocks(this);
|
|
||||||
PlayerMessage.Sender sender = (message) -> {};
|
|
||||||
PlayerMessage.Target target = (messageType, payload) -> {};
|
|
||||||
handlerThread = new HandlerThread("TestHandler");
|
|
||||||
handlerThread.start();
|
|
||||||
Handler handler = new Handler(handlerThread.getLooper());
|
|
||||||
message =
|
|
||||||
new PlayerMessage(sender, target, Timeline.EMPTY, /* defaultWindowIndex= */ 0, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
handlerThread.quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void experimental_blockUntilDelivered_timesOut() throws Exception {
|
|
||||||
when(clock.elapsedRealtime()).thenReturn(0L).thenReturn(TIMEOUT_MS * 2);
|
|
||||||
|
|
||||||
try {
|
|
||||||
message.send().experimental_blockUntilDelivered(TIMEOUT_MS, clock);
|
|
||||||
fail();
|
|
||||||
} catch (TimeoutException expected) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure experimental_blockUntilDelivered() entered the blocking loop
|
|
||||||
verify(clock, Mockito.times(2)).elapsedRealtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void experimental_blockUntilDelivered_onAlreadyProcessed_succeeds() throws Exception {
|
|
||||||
when(clock.elapsedRealtime()).thenReturn(0L);
|
|
||||||
|
|
||||||
message.send().markAsProcessed(/* isDelivered= */ true);
|
|
||||||
|
|
||||||
assertThat(message.experimental_blockUntilDelivered(TIMEOUT_MS, clock)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void experimental_blockUntilDelivered_markAsProcessedWhileBlocked_succeeds()
|
|
||||||
throws Exception {
|
|
||||||
message.send();
|
|
||||||
|
|
||||||
// Use a separate Thread to mark the message as processed.
|
|
||||||
CountDownLatch prepareLatch = new CountDownLatch(1);
|
|
||||||
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
|
||||||
Future<Boolean> future =
|
|
||||||
executorService.submit(
|
|
||||||
() -> {
|
|
||||||
prepareLatch.await();
|
|
||||||
message.markAsProcessed(true);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
when(clock.elapsedRealtime())
|
|
||||||
.thenReturn(0L)
|
|
||||||
.then(
|
|
||||||
(invocation) -> {
|
|
||||||
// Signal the background thread to call PlayerMessage#markAsProcessed.
|
|
||||||
prepareLatch.countDown();
|
|
||||||
return TIMEOUT_MS - 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
assertThat(message.experimental_blockUntilDelivered(TIMEOUT_MS, clock)).isTrue();
|
|
||||||
// Ensure experimental_blockUntilDelivered() entered the blocking loop.
|
|
||||||
verify(clock, Mockito.atLeast(2)).elapsedRealtime();
|
|
||||||
future.get(1, TimeUnit.SECONDS);
|
|
||||||
} finally {
|
|
||||||
executorService.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue