Skip to content

Commit

Permalink
Cache the last DrmSessionManager instance inside the default provider
Browse files Browse the repository at this point in the history
Without this a new manager is instantiated for every item in a playlist,
meaning the impact of caching improvements to DefaultDrmSessionManager
are reduced (since the cache doesn't persist across playlist items).

With this change, playlists of items with identical DRM config will use
the same manager instance (and thus share existing sessions).

Issue: #8523
PiperOrigin-RevId: 356690852
  • Loading branch information
icbaker authored and ojw28 committed Feb 12, 2021
1 parent 56feb96 commit 19ab087
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 2 deletions.
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
* Fix a bug where an assertion would fail if the player started to buffer
an ad media period before the ad URI was known then an ad state update
arrived that didn't set the ad URI.
* DRM:
* Re-use the previous `DrmSessionManager` instance when playing a playlist
(if possible)
([#8523](https://github.com/google/ExoPlayer/issues/8523)).

### 2.13.0 (2021-02-04)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,39 @@
package com.google.android.exoplayer2.drm;

import static com.google.android.exoplayer2.drm.DefaultDrmSessionManager.MODE_PLAYBACK;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;

import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import com.google.common.primitives.Ints;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

/** Default implementation of {@link DrmSessionManagerProvider}. */
@RequiresApi(18)
public final class DefaultDrmSessionManagerProvider implements DrmSessionManagerProvider {

private final Object lock;

@GuardedBy("lock")
private MediaItem.@MonotonicNonNull DrmConfiguration drmConfiguration;

@GuardedBy("lock")
private @MonotonicNonNull DrmSessionManager manager;

@Nullable private HttpDataSource.Factory drmHttpDataSourceFactory;
@Nullable private String userAgent;

public DefaultDrmSessionManagerProvider() {
lock = new Object();
}

/**
* Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback
* HttpMediaDrmCallbacks} which executes key and provisioning requests over HTTP. If {@code null}
Expand Down Expand Up @@ -60,12 +76,23 @@ public void setDrmUserAgent(@Nullable String userAgent) {

@Override
public DrmSessionManager get(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
checkNotNull(mediaItem.playbackProperties);
@Nullable
MediaItem.DrmConfiguration drmConfiguration = mediaItem.playbackProperties.drmConfiguration;
if (drmConfiguration == null || Util.SDK_INT < 18) {
return DrmSessionManager.DRM_UNSUPPORTED;
}

synchronized (lock) {
if (!Util.areEqual(drmConfiguration, this.drmConfiguration)) {
this.drmConfiguration = drmConfiguration;
this.manager = createManager(drmConfiguration);
}
return checkNotNull(this.manager);
}
}

private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
HttpDataSource.Factory dataSourceFactory =
drmHttpDataSourceFactory != null
? drmHttpDataSourceFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,39 @@ public void create_createsManager() {

assertThat(drmSessionManager).isNotEqualTo(DrmSessionManager.DRM_UNSUPPORTED);
}

@Test
public void create_reusesCachedInstanceWherePossible() {
MediaItem mediaItem1 =
new MediaItem.Builder()
.setUri("https://example.test/content-1")
.setDrmUuid(C.WIDEVINE_UUID)
.build();
// Same DRM info as item1, but different URL to check it doesn't prevent re-using a manager.
MediaItem mediaItem2 =
new MediaItem.Builder()
.setUri("https://example.test/content-2")
.setDrmUuid(C.WIDEVINE_UUID)
.build();
// Different DRM info to 1 and 2, needs a different manager instance.
MediaItem mediaItem3 =
new MediaItem.Builder()
.setUri("https://example.test/content-3")
.setDrmUuid(C.WIDEVINE_UUID)
.setDrmLicenseUri("https://example.test/license")
.build();

DefaultDrmSessionManagerProvider provider = new DefaultDrmSessionManagerProvider();
DrmSessionManager drmSessionManager1 = provider.get(mediaItem1);
DrmSessionManager drmSessionManager2 = provider.get(mediaItem2);
DrmSessionManager drmSessionManager3 = provider.get(mediaItem3);

// Get a manager for the first item again - expect it to be a different instance to last time
// since we only cache one.
DrmSessionManager drmSessionManager4 = provider.get(mediaItem1);

assertThat(drmSessionManager1).isSameInstanceAs(drmSessionManager2);
assertThat(drmSessionManager1).isNotSameInstanceAs(drmSessionManager3);
assertThat(drmSessionManager1).isNotSameInstanceAs(drmSessionManager4);
}
}

0 comments on commit 19ab087

Please sign in to comment.