Fix how preacquired DRM sessions are released under resource contention
Previously the released preacquired sessions would start their keepalive timeout, and so no additional resources would be freed in time for the manager to retry the session acquisition. This change adds an additional purge of keepalive sessions *after* the preacquired sessions are released, which fixes the problem. #exofixit #minor-release PiperOrigin-RevId: 396613352
This commit is contained in:
parent
ee11d08760
commit
4d668f1b7b
@ -41,6 +41,9 @@
|
|||||||
* Request smaller decoder input buffers for Dolby Vision. This fixes an
|
* Request smaller decoder input buffers for Dolby Vision. This fixes an
|
||||||
issue that could cause UHD Dolby Vision playbacks to fail on some
|
issue that could cause UHD Dolby Vision playbacks to fail on some
|
||||||
devices, including Amazon Fire TV 4K.
|
devices, including Amazon Fire TV 4K.
|
||||||
|
* DRM:
|
||||||
|
* Fix `DefaultDrmSessionManager` to correctly eagerly release preacquired
|
||||||
|
DRM sessions when there's a shortage of DRM resources on the device.
|
||||||
* UI
|
* UI
|
||||||
* `SubtitleView` no longer implements `TextOutput`. `SubtitleView`
|
* `SubtitleView` no longer implements `TextOutput`. `SubtitleView`
|
||||||
implements `Player.Listener`, so can be registered to a player with
|
implements `Player.Listener`, so can be registered to a player with
|
||||||
|
@ -684,13 +684,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
|||||||
// If we're short on DRM session resources, first try eagerly releasing all our keepalive
|
// If we're short on DRM session resources, first try eagerly releasing all our keepalive
|
||||||
// sessions and then retry the acquisition.
|
// sessions and then retry the acquisition.
|
||||||
if (acquisitionFailedIndicatingResourceShortage(session) && !keepaliveSessions.isEmpty()) {
|
if (acquisitionFailedIndicatingResourceShortage(session) && !keepaliveSessions.isEmpty()) {
|
||||||
// Make a local copy, because sessions are removed from this.keepaliveSessions during
|
releaseAllKeepaliveSessions();
|
||||||
// release (via callback).
|
|
||||||
ImmutableSet<DefaultDrmSession> keepaliveSessions =
|
|
||||||
ImmutableSet.copyOf(this.keepaliveSessions);
|
|
||||||
for (DrmSession keepaliveSession : keepaliveSessions) {
|
|
||||||
keepaliveSession.release(/* eventDispatcher= */ null);
|
|
||||||
}
|
|
||||||
undoAcquisition(session, eventDispatcher);
|
undoAcquisition(session, eventDispatcher);
|
||||||
session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher);
|
session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher);
|
||||||
}
|
}
|
||||||
@ -702,6 +696,11 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
|||||||
&& shouldReleasePreacquiredSessionsBeforeRetrying
|
&& shouldReleasePreacquiredSessionsBeforeRetrying
|
||||||
&& !preacquiredSessionReferences.isEmpty()) {
|
&& !preacquiredSessionReferences.isEmpty()) {
|
||||||
releaseAllPreacquiredSessions();
|
releaseAllPreacquiredSessions();
|
||||||
|
if (!keepaliveSessions.isEmpty()) {
|
||||||
|
// Some preacquired sessions released above are now in their keepalive timeout phase. We
|
||||||
|
// release the keepalive references immediately.
|
||||||
|
releaseAllKeepaliveSessions();
|
||||||
|
}
|
||||||
undoAcquisition(session, eventDispatcher);
|
undoAcquisition(session, eventDispatcher);
|
||||||
session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher);
|
session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher);
|
||||||
}
|
}
|
||||||
@ -728,6 +727,15 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void releaseAllKeepaliveSessions() {
|
||||||
|
// Make a local copy, because sessions are removed from this.keepaliveSessions during
|
||||||
|
// release (via callback).
|
||||||
|
ImmutableSet<DefaultDrmSession> keepaliveSessions = ImmutableSet.copyOf(this.keepaliveSessions);
|
||||||
|
for (DrmSession keepaliveSession : keepaliveSessions) {
|
||||||
|
keepaliveSession.release(/* eventDispatcher= */ null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void releaseAllPreacquiredSessions() {
|
private void releaseAllPreacquiredSessions() {
|
||||||
// Make a local copy, because sessions are removed from this.preacquiredSessionReferences
|
// Make a local copy, because sessions are removed from this.preacquiredSessionReferences
|
||||||
// during release (via callback).
|
// during release (via callback).
|
||||||
|
@ -25,6 +25,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.drm.DrmSessionManager.DrmSessionReference;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.AppManagedProvider;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.AppManagedProvider;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeExoMediaDrm;
|
import com.google.android.exoplayer2.testutil.FakeExoMediaDrm;
|
||||||
@ -302,6 +303,64 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
assertThat(secondDrmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
|
assertThat(secondDrmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 10_000)
|
||||||
|
public void maxConcurrentSessionsExceeded_allPreacquiredAndKeepaliveSessionsEagerlyReleased()
|
||||||
|
throws Exception {
|
||||||
|
ImmutableList<DrmInitData.SchemeData> secondSchemeDatas =
|
||||||
|
ImmutableList.of(DRM_SCHEME_DATAS.get(0).copyWithData(TestUtil.createByteArray(4, 5, 6)));
|
||||||
|
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||||
|
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS, secondSchemeDatas);
|
||||||
|
Format secondFormatWithDrmInitData =
|
||||||
|
new Format.Builder().setDrmInitData(new DrmInitData(secondSchemeDatas)).build();
|
||||||
|
DrmSessionManager drmSessionManager =
|
||||||
|
new DefaultDrmSessionManager.Builder()
|
||||||
|
.setUuidAndExoMediaDrmProvider(
|
||||||
|
DRM_SCHEME_UUID,
|
||||||
|
uuid -> new FakeExoMediaDrm.Builder().setMaxConcurrentSessions(1).build())
|
||||||
|
.setSessionKeepaliveMs(10_000)
|
||||||
|
.setMultiSession(true)
|
||||||
|
.build(/* mediaDrmCallback= */ licenseServer);
|
||||||
|
|
||||||
|
drmSessionManager.prepare();
|
||||||
|
DrmSessionReference firstDrmSessionReference =
|
||||||
|
checkNotNull(
|
||||||
|
drmSessionManager.preacquireSession(
|
||||||
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
|
/* eventDispatcher= */ null,
|
||||||
|
FORMAT_WITH_DRM_INIT_DATA));
|
||||||
|
DrmSession firstDrmSession =
|
||||||
|
checkNotNull(
|
||||||
|
drmSessionManager.acquireSession(
|
||||||
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
|
/* eventDispatcher= */ null,
|
||||||
|
FORMAT_WITH_DRM_INIT_DATA));
|
||||||
|
waitForOpenedWithKeys(firstDrmSession);
|
||||||
|
firstDrmSession.release(/* eventDispatcher= */ null);
|
||||||
|
|
||||||
|
// The direct reference to firstDrmSession has been released, it's being kept alive by both
|
||||||
|
// firstDrmSessionReference and drmSessionManager's internal reference.
|
||||||
|
assertThat(firstDrmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
|
||||||
|
DrmSession secondDrmSession =
|
||||||
|
checkNotNull(
|
||||||
|
drmSessionManager.acquireSession(
|
||||||
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
|
/* eventDispatcher= */ null,
|
||||||
|
secondFormatWithDrmInitData));
|
||||||
|
// The drmSessionManager had to release both it's internal keep-alive reference and the
|
||||||
|
// reference represented by firstDrmSessionReference in order to acquire secondDrmSession.
|
||||||
|
assertThat(firstDrmSession.getState()).isEqualTo(DrmSession.STATE_RELEASED);
|
||||||
|
|
||||||
|
waitForOpenedWithKeys(secondDrmSession);
|
||||||
|
assertThat(secondDrmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
|
||||||
|
|
||||||
|
// Not needed (because the manager has already released this reference) but we call it anyway
|
||||||
|
// for completeness.
|
||||||
|
firstDrmSessionReference.release();
|
||||||
|
// Clean-up
|
||||||
|
secondDrmSession.release(/* eventDispatcher= */ null);
|
||||||
|
drmSessionManager.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout = 10_000)
|
@Test(timeout = 10_000)
|
||||||
public void sessionReacquired_keepaliveTimeOutCancelled() throws Exception {
|
public void sessionReacquired_keepaliveTimeOutCancelled() throws Exception {
|
||||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||||
@ -364,7 +423,7 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
|
|
||||||
drmSessionManager.prepare();
|
drmSessionManager.prepare();
|
||||||
|
|
||||||
DrmSessionManager.DrmSessionReference sessionReference =
|
DrmSessionReference sessionReference =
|
||||||
drmSessionManager.preacquireSession(
|
drmSessionManager.preacquireSession(
|
||||||
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
eventDispatcher,
|
eventDispatcher,
|
||||||
@ -413,7 +472,7 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
|
|
||||||
drmSessionManager.prepare();
|
drmSessionManager.prepare();
|
||||||
|
|
||||||
DrmSessionManager.DrmSessionReference sessionReference =
|
DrmSessionReference sessionReference =
|
||||||
drmSessionManager.preacquireSession(
|
drmSessionManager.preacquireSession(
|
||||||
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
/* eventDispatcher= */ null,
|
/* eventDispatcher= */ null,
|
||||||
@ -453,7 +512,7 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
|
|
||||||
drmSessionManager.prepare();
|
drmSessionManager.prepare();
|
||||||
|
|
||||||
DrmSessionManager.DrmSessionReference sessionReference =
|
DrmSessionReference sessionReference =
|
||||||
drmSessionManager.preacquireSession(
|
drmSessionManager.preacquireSession(
|
||||||
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
|
||||||
/* eventDispatcher= */ null,
|
/* eventDispatcher= */ null,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user