diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java
index 63c69282a79..b76ac032437 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java
@@ -85,6 +85,7 @@ public static final class Builder {
private boolean playClearSamplesWithoutKeys;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long sessionKeepaliveMs;
+ private boolean freeKeepAliveSessionsOnRelease;
/**
* Creates a builder with default values. The default values are:
@@ -111,6 +112,7 @@ public Builder() {
playClearSamplesWithoutKeys = true;
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
sessionKeepaliveMs = DEFAULT_SESSION_KEEPALIVE_MS;
+ freeKeepAliveSessionsOnRelease = true;
}
/**
@@ -236,6 +238,16 @@ public Builder setSessionKeepaliveMs(long sessionKeepaliveMs) {
return this;
}
+ /**
+ * Sets the flag to enable {@link DrmSession DrmSessions} caching.
+ *
+ *
It is useful to keep sessions around during quick channel changes.
+ */
+ public Builder setFreeKeepAliveSessionsOnRelease(boolean enable) {
+ this.freeKeepAliveSessionsOnRelease = enable;
+ return this;
+ }
+
/** Builds a {@link DefaultDrmSessionManager} instance. */
public DefaultDrmSessionManager build(MediaDrmCallback mediaDrmCallback) {
return new DefaultDrmSessionManager(
@@ -247,7 +259,8 @@ public DefaultDrmSessionManager build(MediaDrmCallback mediaDrmCallback) {
useDrmSessionsForClearContentTrackTypes,
playClearSamplesWithoutKeys,
loadErrorHandlingPolicy,
- sessionKeepaliveMs);
+ sessionKeepaliveMs,
+ freeKeepAliveSessionsOnRelease);
}
}
@@ -312,6 +325,8 @@ private MissingSchemeDataException(UUID uuid) {
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final ReferenceCountListenerImpl referenceCountListener;
private final long sessionKeepaliveMs;
+ private boolean freeKeepAliveSessionsOnRelease;
+ private boolean isFinalRelease;
private final List sessions;
private final Set preacquiredSessionReferences;
@@ -339,6 +354,31 @@ private DefaultDrmSessionManager(
boolean playClearSamplesWithoutKeys,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long sessionKeepaliveMs) {
+
+ this(
+ uuid,
+ exoMediaDrmProvider,
+ callback,
+ keyRequestParameters,
+ multiSession,
+ useDrmSessionsForClearContentTrackTypes,
+ playClearSamplesWithoutKeys,
+ loadErrorHandlingPolicy,
+ sessionKeepaliveMs,
+ true);
+ }
+
+ private DefaultDrmSessionManager(
+ UUID uuid,
+ ExoMediaDrm.Provider exoMediaDrmProvider,
+ MediaDrmCallback callback,
+ HashMap keyRequestParameters,
+ boolean multiSession,
+ int[] useDrmSessionsForClearContentTrackTypes,
+ boolean playClearSamplesWithoutKeys,
+ LoadErrorHandlingPolicy loadErrorHandlingPolicy,
+ long sessionKeepaliveMs,
+ boolean freeKeepAliveSessionsOnRelease) {
checkNotNull(uuid);
checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead");
this.uuid = uuid;
@@ -356,6 +396,7 @@ private DefaultDrmSessionManager(
preacquiredSessionReferences = Sets.newIdentityHashSet();
keepaliveSessions = Sets.newIdentityHashSet();
this.sessionKeepaliveMs = sessionKeepaliveMs;
+ this.freeKeepAliveSessionsOnRelease = freeKeepAliveSessionsOnRelease;
}
/**
@@ -413,9 +454,29 @@ public final void prepare() {
@Override
public final void release() {
verifyPlaybackThread(/* allowBeforeSetPlayer= */ true);
- if (--prepareCallsCount != 0) {
- return;
+ if (freeKeepAliveSessionsOnRelease) {
+ if (--prepareCallsCount != 0) {
+ return;
+ }
+ releaseKeepAliveSessionsIfEnabled();
+ } else if (isFinalRelease) {
+
+ while (!sessions.isEmpty() || !keepaliveSessions.isEmpty()) {
+ // Release all keepalive acquisitions if keepalive is enabled.
+ releaseKeepAliveSessionsIfEnabled();
+ }
+ prepareCallsCount = 0;
}
+ releaseAllPreacquiredSessions();
+
+ maybeReleaseMediaDrm();
+
+ if (isFinalRelease) {
+ assert exoMediaDrm == null;
+ }
+ }
+
+ private void releaseKeepAliveSessionsIfEnabled() {
// Release all keepalive acquisitions if keepalive is enabled.
if (sessionKeepaliveMs != C.TIME_UNSET) {
// Make a local copy, because sessions are removed from this.sessions during release (via
@@ -425,9 +486,11 @@ public final void release() {
sessions.get(i).release(/* eventDispatcher= */ null);
}
}
- releaseAllPreacquiredSessions();
+ }
- maybeReleaseMediaDrm();
+ /** Releases all the sessions. This is called from onStop() */
+ public void releaseAllSessions() {
+ isFinalRelease = true;
}
@Override
@@ -631,6 +694,7 @@ private DefaultDrmSession createAndAcquireSessionWithRetry(
// If we're short on DRM session resources, first try eagerly releasing all our keepalive
// sessions and then retry the acquisition.
if (acquisitionFailedIndicatingResourceShortage(session) && !keepaliveSessions.isEmpty()) {
+ Log.w(TAG, "aquire resource shortage and keepaliveSession size: " + keepaliveSessions.size());
releaseAllKeepaliveSessions();
undoAcquisition(session, eventDispatcher);
session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher);
@@ -642,6 +706,11 @@ private DefaultDrmSession createAndAcquireSessionWithRetry(
if (acquisitionFailedIndicatingResourceShortage(session)
&& shouldReleasePreacquiredSessionsBeforeRetrying
&& !preacquiredSessionReferences.isEmpty()) {
+ Log.w(
+ TAG,
+ "aquire resource shortage and preacquiredSessionReferences size: "
+ + preacquiredSessionReferences.size());
+
releaseAllPreacquiredSessions();
if (!keepaliveSessions.isEmpty()) {
// Some preacquired sessions released above are now in their keepalive timeout phase. We
@@ -877,7 +946,12 @@ private class ReferenceCountListenerImpl implements DefaultDrmSession.ReferenceC
public void onReferenceCountIncremented(DefaultDrmSession session, int newReferenceCount) {
if (sessionKeepaliveMs != C.TIME_UNSET) {
// The session has been acquired elsewhere so we want to cancel our timeout.
- keepaliveSessions.remove(session);
+ boolean removed = keepaliveSessions.remove(session);
+ if (removed) {
+ Log.d(
+ TAG,
+ "Using cached session, ref count: " + newReferenceCount + ", session: " + session);
+ }
checkNotNull(playbackHandler).removeCallbacksAndMessages(session);
}
}
@@ -891,7 +965,10 @@ public void onReferenceCountDecremented(DefaultDrmSession session, int newRefere
keepaliveSessions.add(session);
checkNotNull(playbackHandler)
.postAtTime(
- () -> session.release(/* eventDispatcher= */ null),
+ () -> {
+ Log.d(TAG, "keepAlive expired for session: " + session);
+ session.release(/* eventDispatcher= */ null);
+ },
session,
/* uptimeMillis= */ SystemClock.uptimeMillis() + sessionKeepaliveMs);
} else if (newReferenceCount == 0) {
@@ -903,6 +980,14 @@ public void onReferenceCountDecremented(DefaultDrmSession session, int newRefere
if (noMultiSessionDrmSession == session) {
noMultiSessionDrmSession = null;
}
+ ImmutableSet references =
+ ImmutableSet.copyOf(preacquiredSessionReferences);
+ for (PreacquiredSessionReference reference : references) {
+ if (reference.session == session) {
+ reference.isReleased = true;
+ preacquiredSessionReferences.remove(reference);
+ }
+ }
provisioningManagerImpl.onSessionFullyReleased(session);
if (sessionKeepaliveMs != C.TIME_UNSET) {
checkNotNull(playbackHandler).removeCallbacksAndMessages(session);
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManagerProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManagerProvider.java
index ad6ad520b3b..e2cbd1c297f 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManagerProvider.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManagerProvider.java
@@ -45,6 +45,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
@Nullable private DataSource.Factory drmHttpDataSourceFactory;
@Nullable private String userAgent;
@Nullable private LoadErrorHandlingPolicy drmLoadErrorHandlingPolicy;
+ private boolean freeKeepAliveSessionsOnRelease;
public DefaultDrmSessionManagerProvider() {
lock = new Object();
@@ -83,6 +84,22 @@ public void setDrmLoadErrorHandlingPolicy(LoadErrorHandlingPolicy drmLoadErrorHa
this.drmLoadErrorHandlingPolicy = drmLoadErrorHandlingPolicy;
}
+ /**
+ * Set the flag to indicate {@link DefaultDrmSessionManager} to cache the DrmSessions
+ *
+ * @param enable - true means caching ie enabled, false is the default behaviour
+ */
+ public void setFreeKeepAliveSessionsOnRelease(boolean enable) {
+ this.freeKeepAliveSessionsOnRelease = enable;
+ }
+
+ /** Releases all the cached sessions in {@link DrmSessionManager} */
+ public void releaseDrmSession() {
+ if (this.manager != null) {
+ manager.releaseAllSessions();
+ }
+ }
+
@Override
public DrmSessionManager get(MediaItem mediaItem) {
checkNotNull(mediaItem.localConfiguration);
@@ -95,6 +112,9 @@ public DrmSessionManager get(MediaItem mediaItem) {
synchronized (lock) {
if (!Util.areEqual(drmConfiguration, this.drmConfiguration)) {
this.drmConfiguration = drmConfiguration;
+ if (manager != null) {
+ manager.releaseAllSessions();
+ }
this.manager = createManager(drmConfiguration);
}
return checkNotNull(this.manager);
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java
index 6880f97d47d..1fd85d9431b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java
@@ -94,6 +94,14 @@ default void release() {
*/
void setPlayer(Looper playbackLooper, PlayerId playerId);
+ /**
+ * Releases all the aquired resources if default is set to preserve keepAlive on {@link
+ * #release()}. This is called from the Activity's onStop()
+ */
+ default void releaseAllSessions() {
+ // Do nothing.
+ }
+
/**
* Pre-acquires a DRM session for the specified {@link Format}.
*