Ensure MediaSourceFactory instances can be re-used
This fixes DefaultDrmSessionManager so it can be used by a new Player instance (by nulling out its reference to the playback thread, which is unique per-Player instance). This only works if the DefaultDrmSessionManager is 'fully released' before being used by the second Player instance, meaning that the reference count of the manager and all its sessions is zero. #exofixit #minor-release Issue: #9099 PiperOrigin-RevId: 395490506
This commit is contained in:
parent
b6e0ade939
commit
ee8df7afcb
@ -20,6 +20,10 @@
|
||||
* 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)).
|
||||
* Extractors:
|
||||
* Support TS packets without PTS flag
|
||||
([#9294](https://github.com/google/ExoPlayer/issues/9294)).
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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<SimpleExoPlayer> 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> 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();
|
||||
}
|
||||
}
|
@ -53,7 +53,6 @@ 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}.
|
||||
@ -298,8 +297,8 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||
@Nullable private ExoMediaDrm exoMediaDrm;
|
||||
@Nullable private DefaultDrmSession placeholderDrmSession;
|
||||
@Nullable private DefaultDrmSession noMultiSessionDrmSession;
|
||||
private @MonotonicNonNull Looper playbackLooper;
|
||||
private @MonotonicNonNull Handler playbackHandler;
|
||||
@Nullable private Looper playbackLooper;
|
||||
@Nullable private Handler playbackHandler;
|
||||
private int mode;
|
||||
@Nullable private byte[] offlineLicenseKeySetId;
|
||||
|
||||
@ -484,7 +483,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||
}
|
||||
releaseAllPreacquiredSessions();
|
||||
|
||||
maybeReleaseMediaDrm();
|
||||
maybeFullyReleaseManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -663,6 +662,7 @@ 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);
|
||||
}
|
||||
@ -779,12 +779,18 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||
return session;
|
||||
}
|
||||
|
||||
private void maybeReleaseMediaDrm() {
|
||||
private void maybeFullyReleaseManager() {
|
||||
if (exoMediaDrm != null
|
||||
&& prepareCallsCount == 0
|
||||
&& sessions.isEmpty()
|
||||
&& preacquiredSessionReferences.isEmpty()) {
|
||||
// This manager and all its sessions are fully released so we can release exoMediaDrm.
|
||||
// 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;
|
||||
}
|
||||
checkNotNull(exoMediaDrm).release();
|
||||
exoMediaDrm = null;
|
||||
}
|
||||
@ -935,7 +941,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||
keepaliveSessions.remove(session);
|
||||
}
|
||||
}
|
||||
maybeReleaseMediaDrm();
|
||||
maybeFullyReleaseManager();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ 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;
|
||||
@ -31,7 +32,13 @@ 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}. */
|
||||
/**
|
||||
* Factory for creating {@link MediaSource MediaSources} from {@link MediaItem MediaItems}.
|
||||
*
|
||||
* <p>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.
|
||||
*/
|
||||
public interface MediaSourceFactory {
|
||||
|
||||
/** @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} instead. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user