From e55b345595cd42c73dacbbba9706edbc7b07dc43 Mon Sep 17 00:00:00 2001 From: bachinger Date: Thu, 20 Aug 2020 21:51:38 +0100 Subject: [PATCH] Make all media source factories create a drm session PiperOrigin-RevId: 327691347 --- .../source/DefaultMediaSourceFactory.java | 119 ++---------------- .../source/ExtractorMediaSource.java | 18 +++ .../source/MediaSourceDrmHelper.java | 105 ++++++++++++++++ .../exoplayer2/source/MediaSourceFactory.java | 54 +++++++- .../source/ProgressiveMediaSource.java | 34 ++--- .../source/MediaSourceDrmHelperTest.java | 53 ++++++++ .../source/dash/DashMediaSource.java | 37 +++--- .../exoplayer2/source/hls/HlsMediaSource.java | 35 +++--- .../source/smoothstreaming/SsMediaSource.java | 37 +++--- 9 files changed, 318 insertions(+), 174 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceDrmHelper.java create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/source/MediaSourceDrmHelperTest.java diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java index afbf05fa9be..566f7fb1c72 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java @@ -15,28 +15,19 @@ */ package com.google.android.exoplayer2.source; -import static com.google.android.exoplayer2.drm.DefaultDrmSessionManager.MODE_PLAYBACK; - import android.content.Context; import android.net.Uri; -import android.os.Build; import android.util.SparseArray; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.MediaItem.DrmConfiguration; -import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; -import com.google.android.exoplayer2.drm.FrameworkMediaDrm; -import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; -import com.google.android.exoplayer2.drm.MediaDrmCallback; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.source.ads.AdsLoader; import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; @@ -44,10 +35,8 @@ import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; -import com.google.common.primitives.Ints; import java.util.Arrays; import java.util.List; -import java.util.Map; /** * The default {@link MediaSourceFactory} implementation. @@ -79,21 +68,6 @@ * the stream. * * - *

DrmSessionManager creation for protected content

- * - *

For a media item with a {@link DrmConfiguration}, a {@link DefaultDrmSessionManager} is - * created based on that configuration. The following setter can be used to optionally configure the - * creation: - * - *

- * - *

For media items without a {@link DrmConfiguration}, the {@link DrmSessionManager} passed to - * {@link #setDrmSessionManager(DrmSessionManager)} will be used. - * *

Ad support for media items with ad tag uri

* *

For a media item with an ad tag uri, an {@link AdSupportProvider} needs to be passed to {@link @@ -167,21 +141,14 @@ public static DefaultMediaSourceFactory newInstance( } private static final String TAG = "DefaultMediaSourceFactory"; - private static final String DEFAULT_USER_AGENT = - ExoPlayerLibraryInfo.VERSION_SLASHY - + " (Linux;Android " - + Build.VERSION.RELEASE - + ") " - + ExoPlayerLibraryInfo.VERSION_SLASHY; + private final MediaSourceDrmHelper mediaSourceDrmHelper; private final DataSource.Factory dataSourceFactory; @Nullable private final AdSupportProvider adSupportProvider; private final SparseArray mediaSourceFactories; @C.ContentType private final int[] supportedTypes; - private DrmSessionManager drmSessionManager; - @Nullable private HttpDataSource.Factory drmHttpDataSourceFactory; - private String userAgent; + @Nullable private DrmSessionManager drmSessionManager; @Nullable private List streamKeys; /** @@ -196,8 +163,7 @@ public DefaultMediaSourceFactory( DataSource.Factory dataSourceFactory, @Nullable AdSupportProvider adSupportProvider) { this.dataSourceFactory = dataSourceFactory; this.adSupportProvider = adSupportProvider; - drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); - userAgent = DEFAULT_USER_AGENT; + mediaSourceDrmHelper = new MediaSourceDrmHelper(); mediaSourceFactories = loadDelegates(dataSourceFactory); supportedTypes = new int[mediaSourceFactories.size()]; for (int i = 0; i < mediaSourceFactories.size(); i++) { @@ -205,49 +171,23 @@ public DefaultMediaSourceFactory( } } - /** - * Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback - * HttpMediaDrmCallbacks} which executes key and provisioning requests over HTTP. If {@code null} - * is passed the {@link DefaultHttpDataSourceFactory} is used. - * - * @param drmHttpDataSourceFactory The HTTP data source factory or {@code null} to use {@link - * DefaultHttpDataSourceFactory}. - * @return This factory, for convenience. - */ + @Override public DefaultMediaSourceFactory setDrmHttpDataSourceFactory( @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { - this.drmHttpDataSourceFactory = drmHttpDataSourceFactory; + mediaSourceDrmHelper.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory); return this; } - /** - * Sets the optional user agent to be used for DRM requests. - * - *

In case a factory has been set by {@link - * #setDrmHttpDataSourceFactory(HttpDataSource.Factory)}, this user agent is ignored. - * - * @param userAgent The user agent to be used for DRM requests. - * @return This factory, for convenience. - */ + @Override public DefaultMediaSourceFactory setDrmUserAgent(@Nullable String userAgent) { - this.userAgent = userAgent != null ? userAgent : DEFAULT_USER_AGENT; + mediaSourceDrmHelper.setDrmUserAgent(userAgent); return this; } - /** - * Sets the {@link DrmSessionManager} to use for media items that do not specify a {@link - * DrmConfiguration}. The default value is {@link DrmSessionManager#DUMMY}. - * - * @param drmSessionManager The {@link DrmSessionManager}. - * @return This factory, for convenience. - */ @Override public DefaultMediaSourceFactory setDrmSessionManager( @Nullable DrmSessionManager drmSessionManager) { - this.drmSessionManager = - drmSessionManager != null - ? drmSessionManager - : DrmSessionManager.getDummyDrmSessionManager(); + this.drmSessionManager = drmSessionManager; return this; } @@ -292,7 +232,8 @@ public MediaSource createMediaSource(MediaItem mediaItem) { @Nullable MediaSourceFactory mediaSourceFactory = mediaSourceFactories.get(type); Assertions.checkNotNull( mediaSourceFactory, "No suitable media source factory found for content type: " + type); - mediaSourceFactory.setDrmSessionManager(createDrmSessionManager(mediaItem)); + mediaSourceFactory.setDrmSessionManager( + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem)); mediaSourceFactory.setStreamKeys( !mediaItem.playbackProperties.streamKeys.isEmpty() ? mediaItem.playbackProperties.streamKeys @@ -318,46 +259,6 @@ public MediaSource createMediaSource(MediaItem mediaItem) { // internal methods - private DrmSessionManager createDrmSessionManager(MediaItem mediaItem) { - Assertions.checkNotNull(mediaItem.playbackProperties); - if (mediaItem.playbackProperties.drmConfiguration == null - || mediaItem.playbackProperties.drmConfiguration.licenseUri == null - || Util.SDK_INT < 18) { - return drmSessionManager; - } - DefaultDrmSessionManager drmSessionManager = - new DefaultDrmSessionManager.Builder() - .setUuidAndExoMediaDrmProvider( - mediaItem.playbackProperties.drmConfiguration.uuid, - FrameworkMediaDrm.DEFAULT_PROVIDER) - .setMultiSession(mediaItem.playbackProperties.drmConfiguration.multiSession) - .setPlayClearSamplesWithoutKeys( - mediaItem.playbackProperties.drmConfiguration.playClearContentWithoutKey) - .setUseDrmSessionsForClearContent( - Ints.toArray(mediaItem.playbackProperties.drmConfiguration.sessionForClearTypes)) - .build(createHttpMediaDrmCallback(mediaItem.playbackProperties.drmConfiguration)); - - drmSessionManager.setMode( - MODE_PLAYBACK, mediaItem.playbackProperties.drmConfiguration.getKeySetId()); - - return drmSessionManager; - } - - private MediaDrmCallback createHttpMediaDrmCallback(MediaItem.DrmConfiguration drmConfiguration) { - Assertions.checkNotNull(drmConfiguration.licenseUri); - HttpMediaDrmCallback drmCallback = - new HttpMediaDrmCallback( - drmConfiguration.licenseUri.toString(), - drmConfiguration.forceDefaultLicenseUri, - drmHttpDataSourceFactory != null - ? drmHttpDataSourceFactory - : new DefaultHttpDataSourceFactory(userAgent)); - for (Map.Entry entry : drmConfiguration.requestHeaders.entrySet()) { - drmCallback.setKeyRequestProperty(entry.getKey(), entry.getValue()); - } - return drmCallback; - } - private static MediaSource maybeClipMediaSource(MediaItem mediaItem, MediaSource mediaSource) { if (mediaItem.clippingProperties.startPositionMs == 0 && mediaItem.clippingProperties.endPositionMs == C.TIME_END_OF_SOURCE diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index 1e8129bf3a8..38146c92b24 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; @@ -168,6 +169,23 @@ public Factory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManage throw new UnsupportedOperationException(); } + /** + * @deprecated Use {@link ProgressiveMediaSource.Factory#setDrmHttpDataSourceFactory} instead. + */ + @Deprecated + @Override + public MediaSourceFactory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + throw new UnsupportedOperationException(); + } + + /** @deprecated Use {@link ProgressiveMediaSource.Factory#setDrmUserAgent} instead. */ + @Deprecated + @Override + public MediaSourceFactory setDrmUserAgent(@Nullable String userAgent) { + throw new UnsupportedOperationException(); + } + /** @deprecated Use {@link #createMediaSource(MediaItem)} instead. */ @SuppressWarnings("deprecation") @Deprecated diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceDrmHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceDrmHelper.java new file mode 100644 index 00000000000..29325d789ed --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceDrmHelper.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source; + +import static com.google.android.exoplayer2.drm.DefaultDrmSessionManager.MODE_PLAYBACK; +import static com.google.android.exoplayer2.util.Util.castNonNull; + +import android.os.Build; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.ExoPlayerLibraryInfo; +import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; +import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.FrameworkMediaDrm; +import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; +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; + +/** A helper to create a {@link DrmSessionManager} from a {@link MediaItem}. */ +public final class MediaSourceDrmHelper { + + private static final String DEFAULT_USER_AGENT = + ExoPlayerLibraryInfo.VERSION_SLASHY + + " (Linux;Android " + + Build.VERSION.RELEASE + + ") " + + ExoPlayerLibraryInfo.VERSION_SLASHY; + + @Nullable private HttpDataSource.Factory drmHttpDataSourceFactory; + @Nullable private String userAgent; + + /** + * Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback + * HttpMediaDrmCallbacks} which executes key and provisioning requests over HTTP. If {@code null} + * is passed the {@link DefaultHttpDataSourceFactory} is used. + * + * @param drmHttpDataSourceFactory The HTTP data source factory or {@code null} to use {@link + * DefaultHttpDataSourceFactory}. + */ + public void setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + this.drmHttpDataSourceFactory = drmHttpDataSourceFactory; + } + + /** + * Sets the optional user agent to be used for DRM requests. + * + *

In case a factory has been set by {@link + * #setDrmHttpDataSourceFactory(HttpDataSource.Factory)}, this user agent is ignored. + * + * @param userAgent The user agent to be used for DRM requests. + */ + public void setDrmUserAgent(@Nullable String userAgent) { + this.userAgent = userAgent; + } + + /** Creates a {@link DrmSessionManager} for the given media item. */ + public DrmSessionManager create(MediaItem mediaItem) { + Assertions.checkNotNull(mediaItem.playbackProperties); + @Nullable + MediaItem.DrmConfiguration drmConfiguration = mediaItem.playbackProperties.drmConfiguration; + if (drmConfiguration == null || drmConfiguration.licenseUri == null || Util.SDK_INT < 18) { + return DrmSessionManager.getDummyDrmSessionManager(); + } + HttpDataSource.Factory dataSourceFactory = + drmHttpDataSourceFactory != null + ? drmHttpDataSourceFactory + : new DefaultHttpDataSourceFactory(userAgent != null ? userAgent : DEFAULT_USER_AGENT); + HttpMediaDrmCallback httpDrmCallback = + new HttpMediaDrmCallback( + castNonNull(drmConfiguration.licenseUri).toString(), + drmConfiguration.forceDefaultLicenseUri, + dataSourceFactory); + for (Map.Entry entry : drmConfiguration.requestHeaders.entrySet()) { + httpDrmCallback.setKeyRequestProperty(entry.getKey(), entry.getValue()); + } + DefaultDrmSessionManager drmSessionManager = + new DefaultDrmSessionManager.Builder() + .setUuidAndExoMediaDrmProvider( + drmConfiguration.uuid, FrameworkMediaDrm.DEFAULT_PROVIDER) + .setMultiSession(drmConfiguration.multiSession) + .setPlayClearSamplesWithoutKeys(drmConfiguration.playClearContentWithoutKey) + .setUseDrmSessionsForClearContent(Ints.toArray(drmConfiguration.sessionForClearTypes)) + .build(httpDrmCallback); + drmSessionManager.setMode(MODE_PLAYBACK, drmConfiguration.getKeySetId()); + return drmSessionManager; + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java index e1c52c097b1..4175121d385 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceFactory.java @@ -19,13 +19,34 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.offline.StreamKey; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import java.util.List; -/** Factory for creating {@link MediaSource}s from URIs. */ +/** + * Factory for creating {@link MediaSource}s from URIs. + * + *

DrmSessionManager creation for protected content

+ * + *

In case a {@link DrmSessionManager} is passed to {@link + * #setDrmSessionManager(DrmSessionManager)}, it will be used regardless of the drm configuration of + * the media item. + * + *

For a media item with a {@link MediaItem.DrmConfiguration}, a {@link DefaultDrmSessionManager} + * is created based on that configuration. The following setter can be used to optionally configure + * the creation: + * + *

+ */ public interface MediaSourceFactory { /** @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} instead. */ @@ -35,13 +56,40 @@ default MediaSourceFactory setStreamKeys(@Nullable List streamKeys) { } /** - * Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. + * Sets the {@link DrmSessionManager} to use for all media items regardless of their {@link + * MediaItem.DrmConfiguration}. * * @param drmSessionManager The {@link DrmSessionManager}. * @return This factory, for convenience. */ MediaSourceFactory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager); + /** + * Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback + * HttpMediaDrmCallbacks} to execute key and provisioning requests over HTTP. + * + *

In case a {@link DrmSessionManager} has been set by {@link + * #setDrmSessionManager(DrmSessionManager)}, this data source factory is ignored. + * + * @param drmHttpDataSourceFactory The HTTP data source factory, or {@code null} to use {@link + * DefaultHttpDataSourceFactory}. + * @return This factory, for convenience. + */ + MediaSourceFactory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory); + + /** + * Sets the optional user agent to be used for DRM requests. + * + *

In case a factory has been set by {@link + * #setDrmHttpDataSourceFactory(HttpDataSource.Factory)} or a {@link DrmSessionManager} has been + * set by {@link #setDrmSessionManager(DrmSessionManager)}, this user agent is ignored. + * + * @param userAgent The user agent to be used for DRM requests. + * @return This factory, for convenience. + */ + MediaSourceFactory setDrmUserAgent(@Nullable String userAgent); + /** * Sets an optional {@link LoadErrorHandlingPolicy}. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java index 125891f09cf..4d7230cc3ae 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java @@ -22,7 +22,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.Extractor; @@ -30,6 +29,7 @@ import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.TransferListener; @@ -51,9 +51,10 @@ public final class ProgressiveMediaSource extends BaseMediaSource public static final class Factory implements MediaSourceFactory { private final DataSource.Factory dataSourceFactory; + private final MediaSourceDrmHelper mediaSourceDrmHelper; private ExtractorsFactory extractorsFactory; - private DrmSessionManager drmSessionManager; + @Nullable private DrmSessionManager drmSessionManager; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private int continueLoadingCheckIntervalBytes; @Nullable private String customCacheKey; @@ -78,7 +79,7 @@ public Factory(DataSource.Factory dataSourceFactory) { public Factory(DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory) { this.dataSourceFactory = dataSourceFactory; this.extractorsFactory = extractorsFactory; - drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); + mediaSourceDrmHelper = new MediaSourceDrmHelper(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); continueLoadingCheckIntervalBytes = DEFAULT_LOADING_CHECK_INTERVAL_BYTES; } @@ -146,19 +147,22 @@ public Factory setContinueLoadingCheckIntervalBytes(int continueLoadingCheckInte return this; } - /** - * Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. The - * default value is {@link DrmSessionManager#DUMMY}. - * - * @param drmSessionManager The {@link DrmSessionManager}. - * @return This factory, for convenience. - */ @Override public Factory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager) { - this.drmSessionManager = - drmSessionManager != null - ? drmSessionManager - : DrmSessionManager.getDummyDrmSessionManager(); + this.drmSessionManager = drmSessionManager; + return this; + } + + @Override + public Factory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + mediaSourceDrmHelper.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory); + return this; + } + + @Override + public Factory setDrmUserAgent(@Nullable String userAgent) { + mediaSourceDrmHelper.setDrmUserAgent(userAgent); return this; } @@ -194,7 +198,7 @@ public ProgressiveMediaSource createMediaSource(MediaItem mediaItem) { mediaItem, dataSourceFactory, extractorsFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, continueLoadingCheckIntervalBytes); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/MediaSourceDrmHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/MediaSourceDrmHelperTest.java new file mode 100644 index 00000000000..45384f05ec5 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/MediaSourceDrmHelperTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.drm.DrmSessionManager; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link MediaSourceDrmHelper}. */ +@RunWith(AndroidJUnit4.class) +public class MediaSourceDrmHelperTest { + + @Test + public void create_noDrmProperties_createsNoopManager() { + DrmSessionManager drmSessionManager = + new MediaSourceDrmHelper().create(MediaItem.fromUri(Uri.EMPTY)); + + assertThat(drmSessionManager).isEqualTo(DrmSessionManager.DUMMY); + } + + @Test + public void create_createsManager() { + MediaItem mediaItem = + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setDrmLicenseUri(Uri.EMPTY) + .setDrmUuid(C.WIDEVINE_UUID) + .build(); + + DrmSessionManager drmSessionManager = new MediaSourceDrmHelper().create(mediaItem); + + assertThat(drmSessionManager).isNotEqualTo(DrmSessionManager.DUMMY); + } +} diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 260c11fa626..2f5b169e30d 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -30,7 +30,6 @@ import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.FilteringManifestParser; @@ -42,6 +41,7 @@ import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MediaSourceDrmHelper; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceFactory; @@ -54,6 +54,7 @@ import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo; import com.google.android.exoplayer2.upstream.Loader; @@ -91,9 +92,10 @@ public final class DashMediaSource extends BaseMediaSource { public static final class Factory implements MediaSourceFactory { private final DashChunkSource.Factory chunkSourceFactory; + private final MediaSourceDrmHelper mediaSourceDrmHelper; @Nullable private final DataSource.Factory manifestDataSourceFactory; - private DrmSessionManager drmSessionManager; + @Nullable private DrmSessionManager drmSessionManager; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private long livePresentationDelayMs; @@ -126,7 +128,7 @@ public Factory( @Nullable DataSource.Factory manifestDataSourceFactory) { this.chunkSourceFactory = checkNotNull(chunkSourceFactory); this.manifestDataSourceFactory = manifestDataSourceFactory; - drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); + mediaSourceDrmHelper = new MediaSourceDrmHelper(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS; compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); @@ -155,19 +157,22 @@ public Factory setStreamKeys(@Nullable List streamKeys) { return this; } - /** - * Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. The - * default value is {@link DrmSessionManager#DUMMY}. - * - * @param drmSessionManager The {@link DrmSessionManager}. - * @return This factory, for convenience. - */ @Override public Factory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager) { - this.drmSessionManager = - drmSessionManager != null - ? drmSessionManager - : DrmSessionManager.getDummyDrmSessionManager(); + this.drmSessionManager = drmSessionManager; + return this; + } + + @Override + public Factory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + mediaSourceDrmHelper.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory); + return this; + } + + @Override + public Factory setDrmUserAgent(@Nullable String userAgent) { + mediaSourceDrmHelper.setDrmUserAgent(userAgent); return this; } @@ -312,7 +317,7 @@ public DashMediaSource createMediaSource(DashManifest manifest, MediaItem mediaI /* manifestParser= */ null, chunkSourceFactory, compositeSequenceableLoaderFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, livePresentationDelayMs, livePresentationDelayOverridesManifest); @@ -403,7 +408,7 @@ public DashMediaSource createMediaSource(MediaItem mediaItem) { manifestParser, chunkSourceFactory, compositeSequenceableLoaderFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, livePresentationDelayMs, livePresentationDelayOverridesManifest); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 02d7ebd9a08..b58f3da928f 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -26,7 +26,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.Extractor; @@ -36,6 +35,7 @@ import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MediaSourceDrmHelper; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.SequenceableLoader; @@ -49,6 +49,7 @@ import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; @@ -93,12 +94,13 @@ public final class HlsMediaSource extends BaseMediaSource public static final class Factory implements MediaSourceFactory { private final HlsDataSourceFactory hlsDataSourceFactory; + private final MediaSourceDrmHelper mediaSourceDrmHelper; private HlsExtractorFactory extractorFactory; private HlsPlaylistParserFactory playlistParserFactory; private HlsPlaylistTracker.Factory playlistTrackerFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; - private DrmSessionManager drmSessionManager; + @Nullable private DrmSessionManager drmSessionManager; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private boolean allowChunklessPreparation; @MetadataType private int metadataType; @@ -125,10 +127,10 @@ public Factory(DataSource.Factory dataSourceFactory) { */ public Factory(HlsDataSourceFactory hlsDataSourceFactory) { this.hlsDataSourceFactory = checkNotNull(hlsDataSourceFactory); + mediaSourceDrmHelper = new MediaSourceDrmHelper(); playlistParserFactory = new DefaultHlsPlaylistParserFactory(); playlistTrackerFactory = DefaultHlsPlaylistTracker.FACTORY; extractorFactory = HlsExtractorFactory.DEFAULT; - drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); metadataType = METADATA_TYPE_ID3; @@ -285,19 +287,22 @@ public Factory setUseSessionKeys(boolean useSessionKeys) { return this; } - /** - * Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. The - * default value is {@link DrmSessionManager#DUMMY}. - * - * @param drmSessionManager The {@link DrmSessionManager}. - * @return This factory, for convenience. - */ @Override public Factory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager) { - this.drmSessionManager = - drmSessionManager != null - ? drmSessionManager - : DrmSessionManager.getDummyDrmSessionManager(); + this.drmSessionManager = drmSessionManager; + return this; + } + + @Override + public Factory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + mediaSourceDrmHelper.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory); + return this; + } + + @Override + public MediaSourceFactory setDrmUserAgent(@Nullable String userAgent) { + mediaSourceDrmHelper.setDrmUserAgent(userAgent); return this; } @@ -374,7 +379,7 @@ public HlsMediaSource createMediaSource(MediaItem mediaItem) { hlsDataSourceFactory, extractorFactory, compositeSequenceableLoaderFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, playlistTrackerFactory.createTracker( hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory), diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 4a1f5c353c4..a2ebb06936c 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -27,7 +27,6 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.FilteringManifestParser; @@ -39,6 +38,7 @@ import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MediaSourceDrmHelper; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceFactory; @@ -50,6 +50,7 @@ import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo; import com.google.android.exoplayer2.upstream.Loader; @@ -77,10 +78,11 @@ public final class SsMediaSource extends BaseMediaSource public static final class Factory implements MediaSourceFactory { private final SsChunkSource.Factory chunkSourceFactory; + private final MediaSourceDrmHelper mediaSourceDrmHelper; @Nullable private final DataSource.Factory manifestDataSourceFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; - private DrmSessionManager drmSessionManager; + @Nullable private DrmSessionManager drmSessionManager; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private long livePresentationDelayMs; @Nullable private ParsingLoadable.Parser manifestParser; @@ -111,7 +113,7 @@ public Factory( @Nullable DataSource.Factory manifestDataSourceFactory) { this.chunkSourceFactory = checkNotNull(chunkSourceFactory); this.manifestDataSourceFactory = manifestDataSourceFactory; - drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); + mediaSourceDrmHelper = new MediaSourceDrmHelper(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS; compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); @@ -197,19 +199,22 @@ public Factory setCompositeSequenceableLoaderFactory( return this; } - /** - * Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. The - * default value is {@link DrmSessionManager#DUMMY}. - * - * @param drmSessionManager The {@link DrmSessionManager}. - * @return This factory, for convenience. - */ @Override public Factory setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager) { - this.drmSessionManager = - drmSessionManager != null - ? drmSessionManager - : DrmSessionManager.getDummyDrmSessionManager(); + this.drmSessionManager = drmSessionManager; + return this; + } + + @Override + public Factory setDrmHttpDataSourceFactory( + @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { + mediaSourceDrmHelper.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory); + return this; + } + + @Override + public Factory setDrmUserAgent(@Nullable String userAgent) { + mediaSourceDrmHelper.setDrmUserAgent(userAgent); return this; } @@ -280,7 +285,7 @@ public SsMediaSource createMediaSource(SsManifest manifest, MediaItem mediaItem) /* manifestParser= */ null, chunkSourceFactory, compositeSequenceableLoaderFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, livePresentationDelayMs); } @@ -357,7 +362,7 @@ public SsMediaSource createMediaSource(MediaItem mediaItem) { manifestParser, chunkSourceFactory, compositeSequenceableLoaderFactory, - drmSessionManager, + drmSessionManager != null ? drmSessionManager : mediaSourceDrmHelper.create(mediaItem), loadErrorHandlingPolicy, livePresentationDelayMs); }