diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2237810a19..187c6114e6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -20,10 +20,6 @@ * Fix a bug when [depending on ExoPlayer locally](README.md#locally) with a relative path ([#9403](https://github.com/google/ExoPlayer/issues/9403)). - * Fix bug in `DefaultDrmSessionManager` which prevented - `MediaSourceFactory` instances from being re-used by `ExoPlayer` - instances with non-overlapping lifecycles - ([#9099](https://github.com/google/ExoPlayer/issues/9099)). * Better handle invalid seek requests. Seeks to positions that are before the start or after the end of the media are now handled as seeks to the start and end respectively diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactoryInstrumentationTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactoryInstrumentationTest.java deleted file mode 100644 index 921fd0f027..0000000000 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactoryInstrumentationTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2021 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.source; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.google.common.truth.Truth.assertThat; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.util.ConditionVariable; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Instrumentation tests for {@link DefaultMediaSourceFactory}. */ -@RunWith(AndroidJUnit4.class) -public final class DefaultMediaSourceFactoryInstrumentationTest { - - // https://github.com/google/ExoPlayer/issues/9099 - @Test - public void reuseMediaSourceFactoryBetweenPlayerInstances() throws Exception { - MediaItem mediaItem = - new MediaItem.Builder() - .setUri("asset:///media/mp4/sample.mp4") - .setDrmUuid(C.WIDEVINE_UUID) - .setDrmSessionForClearPeriods(true) - .build(); - AtomicReference player = new AtomicReference<>(); - DefaultMediaSourceFactory defaultMediaSourceFactory = - new DefaultMediaSourceFactory(getInstrumentation().getContext()); - getInstrumentation() - .runOnMainSync( - () -> - player.set( - new ExoPlayer.Builder(getInstrumentation().getContext()) - .setMediaSourceFactory(defaultMediaSourceFactory) - .build())); - playUntilEndAndRelease(player.get(), mediaItem); - getInstrumentation() - .runOnMainSync( - () -> - player.set( - new ExoPlayer.Builder(getInstrumentation().getContext()) - .setMediaSourceFactory(defaultMediaSourceFactory) - .build())); - playUntilEndAndRelease(player.get(), mediaItem); - } - - private void playUntilEndAndRelease(Player player, MediaItem mediaItem) - throws InterruptedException { - ConditionVariable playbackComplete = new ConditionVariable(); - AtomicReference playbackException = new AtomicReference<>(); - getInstrumentation() - .runOnMainSync( - () -> { - player.addListener( - new Player.Listener() { - @Override - public void onPlaybackStateChanged(@Player.State int playbackState) { - if (playbackState == Player.STATE_ENDED) { - playbackComplete.open(); - } - } - - @Override - public void onPlayerError(PlaybackException error) { - playbackException.set(error); - playbackComplete.open(); - } - }); - player.setMediaItem(mediaItem); - player.prepare(); - player.play(); - }); - - playbackComplete.block(); - getInstrumentation().runOnMainSync(player::release); - getInstrumentation().waitForIdleSync(); - assertThat(playbackException.get()).isNull(); - } -} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java index cd40555205..786c4a1302 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java @@ -53,6 +53,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A {@link DrmSessionManager} that supports playbacks using {@link ExoMediaDrm}. @@ -297,8 +298,8 @@ public class DefaultDrmSessionManager implements DrmSessionManager { @Nullable private ExoMediaDrm exoMediaDrm; @Nullable private DefaultDrmSession placeholderDrmSession; @Nullable private DefaultDrmSession noMultiSessionDrmSession; - @Nullable private Looper playbackLooper; - @Nullable private Handler playbackHandler; + private @MonotonicNonNull Looper playbackLooper; + private @MonotonicNonNull Handler playbackHandler; private int mode; @Nullable private byte[] offlineLicenseKeySetId; @@ -483,7 +484,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { } releaseAllPreacquiredSessions(); - maybeFullyReleaseManager(); + maybeReleaseMediaDrm(); } @Override @@ -662,7 +663,6 @@ public class DefaultDrmSessionManager implements DrmSessionManager { this.playbackLooper = playbackLooper; this.playbackHandler = new Handler(playbackLooper); } else { - // Check this manager is only being used by a single player at a time. checkState(this.playbackLooper == playbackLooper); checkNotNull(playbackHandler); } @@ -787,18 +787,12 @@ public class DefaultDrmSessionManager implements DrmSessionManager { return session; } - private void maybeFullyReleaseManager() { + private void maybeReleaseMediaDrm() { if (exoMediaDrm != null && prepareCallsCount == 0 && sessions.isEmpty() && preacquiredSessionReferences.isEmpty()) { - // This manager and all its sessions are fully released so we can null out the looper & - // handler references and release exoMediaDrm. - if (playbackLooper != null) { - checkNotNull(playbackHandler).removeCallbacksAndMessages(/* token= */ null); - playbackHandler = null; - playbackLooper = null; - } + // This manager and all its sessions are fully released so we can release exoMediaDrm. checkNotNull(exoMediaDrm).release(); exoMediaDrm = null; } @@ -949,7 +943,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { keepaliveSessions.remove(session); } } - maybeFullyReleaseManager(); + maybeReleaseMediaDrm(); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java index cbdae8aa41..755096bbe9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java @@ -19,7 +19,6 @@ import android.net.Uri; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider; import com.google.android.exoplayer2.drm.DrmSessionManager; @@ -32,13 +31,7 @@ import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import java.util.List; -/** - * Factory for creating {@link MediaSource MediaSources} from {@link MediaItem MediaItems}. - * - *

A factory must only be used by a single {@link Player} at a time. A factory can only be - * re-used by a second {@link Player} if the previous {@link Player} and all associated resources - * are fully released. - */ +/** Factory for creating {@link MediaSource MediaSources} from {@link MediaItem MediaItems}. */ public interface MediaSourceFactory { /** @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} instead. */