diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index b4cd9a399d..89a28bd764 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -149,8 +149,6 @@ public interface ExoPlayer extends Player { private SeekParameters seekParameters; private boolean pauseAtEndOfMediaItems; private boolean buildCalled; - - private long releaseTimeoutMs; private boolean throwWhenStuckBuffering; /** @@ -215,20 +213,6 @@ public interface ExoPlayer extends Player { 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}. - * - *
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. * @@ -405,10 +389,6 @@ public interface ExoPlayer extends Player { pauseAtEndOfMediaItems, clock, looper); - - if (releaseTimeoutMs > 0) { - player.experimental_setReleaseTimeoutMs(releaseTimeoutMs); - } if (throwWhenStuckBuffering) { player.experimental_throwWhenStuckBuffering(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 26357a18dc..e98da39a10 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -45,7 +45,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeoutException; /** * 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()); } - /** - * 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}. - * - *
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. * @@ -690,13 +675,7 @@ import java.util.concurrent.TimeoutException; Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " [" + ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] [" + ExoPlayerLibraryInfo.registeredModules() + "]"); - if (!internalPlayer.release()) { - notifyListeners( - listener -> - listener.onPlayerError( - ExoPlaybackException.createForUnexpected( - new RuntimeException(new TimeoutException("Player release timed out."))))); - } + internalPlayer.release(); applicationHandler.removeCallbacksAndMessages(null); playbackInfo = getResetPlaybackInfo( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 53c8a5d080..5d698b8f66 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -133,8 +133,6 @@ import java.util.concurrent.atomic.AtomicBoolean; private long rendererPositionUs; private int nextPendingMessageIndexHint; private boolean deliverPendingMessageAtStartPositionRequired; - - private long releaseTimeoutMs; private boolean throwWhenStuckBuffering; 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() { throwWhenStuckBuffering = true; } @@ -322,23 +316,23 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - public synchronized boolean release() { + public synchronized void release() { if (released || !internalPlaybackThread.isAlive()) { - return true; + return; } - handler.sendEmptyMessage(MSG_RELEASE); - try { - if (releaseTimeoutMs > 0) { - waitUntilReleased(releaseTimeoutMs); - } else { - waitUntilReleased(); + boolean wasInterrupted = false; + while (!released) { + try { + wait(); + } catch (InterruptedException e) { + wasInterrupted = true; } - } catch (InterruptedException e) { + } + if (wasInterrupted) { + // Restore the interrupted status. Thread.currentThread().interrupt(); } - - return released; } public Looper getPlaybackLooper() { @@ -504,63 +498,6 @@ import java.util.concurrent.atomic.AtomicBoolean; // Private methods. - /** - * Blocks the current thread until {@link #releaseInternal()} is executed on the playback Thread. - * - *
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} after - * {@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. - * - *
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) { if (playbackInfo.playbackState != state) { playbackInfo = playbackInfo.copyWithPlaybackState(state); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java index be7c7ce973..9837cb59da 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java @@ -17,10 +17,7 @@ package com.google.android.exoplayer2; import android.os.Handler; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; 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 @@ -292,28 +289,6 @@ public final class PlayerMessage { 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. - * - *
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
* multiple times.
@@ -327,24 +302,4 @@ public final class PlayerMessage {
isProcessed = true;
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;
- }
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/PlayerMessageTest.java b/library/core/src/test/java/com/google/android/exoplayer2/PlayerMessageTest.java
deleted file mode 100644
index 874a8c5a5a..0000000000
--- a/library/core/src/test/java/com/google/android/exoplayer2/PlayerMessageTest.java
+++ /dev/null
@@ -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