From 19ab087c61f8dcc4142c2be975eb245ff89973b3 Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 10 Feb 2021 09:48:04 +0000 Subject: [PATCH] Cache the last DrmSessionManager instance inside the default provider 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 --- RELEASENOTES.md | 4 +++ .../drm/DefaultDrmSessionManagerProvider.java | 31 ++++++++++++++-- .../DefaultDrmSessionManagerProviderTest.java | 35 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 004b72cdcb..214b635a48 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerProvider.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerProvider.java index 10bd2953d5..18f668cc3d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerProvider.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerProvider.java @@ -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} @@ -60,12 +76,23 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager @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 diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/DefaultDrmSessionManagerProviderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/DefaultDrmSessionManagerProviderTest.java index 4e597b6371..0c830ca5ab 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/DefaultDrmSessionManagerProviderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/DefaultDrmSessionManagerProviderTest.java @@ -51,4 +51,39 @@ public class DefaultDrmSessionManagerProviderTest { 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); + } }