Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DrmSession Caching Proposal #2049

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -111,6 +112,7 @@ public Builder() {
playClearSamplesWithoutKeys = true;
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
sessionKeepaliveMs = DEFAULT_SESSION_KEEPALIVE_MS;
freeKeepAliveSessionsOnRelease = true;
}

/**
Expand Down Expand Up @@ -236,6 +238,16 @@ public Builder setSessionKeepaliveMs(long sessionKeepaliveMs) {
return this;
}

/**
* Sets the flag to enable {@link DrmSession DrmSessions} caching.
*
* <p>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(
Expand All @@ -247,7 +259,8 @@ public DefaultDrmSessionManager build(MediaDrmCallback mediaDrmCallback) {
useDrmSessionsForClearContentTrackTypes,
playClearSamplesWithoutKeys,
loadErrorHandlingPolicy,
sessionKeepaliveMs);
sessionKeepaliveMs,
freeKeepAliveSessionsOnRelease);
}
}

Expand Down Expand Up @@ -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<DefaultDrmSession> sessions;
private final Set<PreacquiredSessionReference> preacquiredSessionReferences;
Expand Down Expand Up @@ -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<String, String> 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;
Expand All @@ -356,6 +396,7 @@ private DefaultDrmSessionManager(
preacquiredSessionReferences = Sets.newIdentityHashSet();
keepaliveSessions = Sets.newIdentityHashSet();
this.sessionKeepaliveMs = sessionKeepaliveMs;
this.freeKeepAliveSessionsOnRelease = freeKeepAliveSessionsOnRelease;
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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) {
Expand All @@ -903,6 +980,14 @@ public void onReferenceCountDecremented(DefaultDrmSession session, int newRefere
if (noMultiSessionDrmSession == session) {
noMultiSessionDrmSession = null;
}
ImmutableSet<PreacquiredSessionReference> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
*
Expand Down