diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 44d471bb27..f94123426e 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -42,7 +42,6 @@ import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.demo.Sample.UriSample; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; -import com.google.android.exoplayer2.offline.DownloadHelper; import com.google.android.exoplayer2.offline.DownloadRequest; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; @@ -473,7 +472,12 @@ public class PlayerActivity extends AppCompatActivity .getDownloadTracker() .getDownloadRequest(mediaItem.playbackProperties.sourceUri); if (downloadRequest != null) { - return DownloadHelper.createMediaSource(downloadRequest, dataSourceFactory); + mediaItem = + mediaItem + .buildUpon() + .setStreamKeys(downloadRequest.streamKeys) + .setCustomCacheKey(downloadRequest.customCacheKey) + .build(); } return mediaSourceFactory .setDrmHttpDataSourceFactory(drmDataSourceFactory) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/MediaItem.java b/library/common/src/main/java/com/google/android/exoplayer2/MediaItem.java index a78a2d06ee..f484d3f80e 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/MediaItem.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/MediaItem.java @@ -69,17 +69,47 @@ public final class MediaItem { private boolean drmPlayClearContentWithoutKey; private List drmSessionForClearTypes; private List streamKeys; + @Nullable private String customCacheKey; private List subtitles; @Nullable private Object tag; @Nullable private MediaMetadata mediaMetadata; /** Creates a builder. */ public Builder() { - streamKeys = Collections.emptyList(); - subtitles = Collections.emptyList(); + clipEndPositionMs = C.TIME_END_OF_SOURCE; drmSessionForClearTypes = Collections.emptyList(); drmLicenseRequestHeaders = Collections.emptyMap(); - clipEndPositionMs = C.TIME_END_OF_SOURCE; + streamKeys = Collections.emptyList(); + subtitles = Collections.emptyList(); + } + + private Builder(MediaItem mediaItem) { + this(); + clipEndPositionMs = mediaItem.clippingProperties.endPositionMs; + clipRelativeToLiveWindow = mediaItem.clippingProperties.relativeToLiveWindow; + clipRelativeToDefaultPosition = mediaItem.clippingProperties.relativeToDefaultPosition; + clipStartsAtKeyFrame = mediaItem.clippingProperties.startsAtKeyFrame; + clipStartPositionMs = mediaItem.clippingProperties.startPositionMs; + mediaId = mediaItem.mediaId; + mediaMetadata = mediaItem.mediaMetadata; + @Nullable PlaybackProperties playbackProperties = mediaItem.playbackProperties; + if (playbackProperties != null) { + customCacheKey = playbackProperties.customCacheKey; + mimeType = playbackProperties.mimeType; + sourceUri = playbackProperties.sourceUri; + streamKeys = playbackProperties.streamKeys; + subtitles = playbackProperties.subtitles; + tag = playbackProperties.tag; + @Nullable DrmConfiguration drmConfiguration = playbackProperties.drmConfiguration; + if (drmConfiguration != null) { + drmLicenseUri = drmConfiguration.licenseUri; + drmLicenseRequestHeaders = drmConfiguration.requestHeaders; + drmMultiSession = drmConfiguration.multiSession; + drmPlayClearContentWithoutKey = drmConfiguration.playClearContentWithoutKey; + drmSessionForClearTypes = drmConfiguration.sessionForClearTypes; + drmUuid = drmConfiguration.uuid; + } + } } /** @@ -296,6 +326,17 @@ public final class MediaItem { return this; } + /** + * Sets the optional custom cache key (only used for progressive streams). + * + *

If a {@link PlaybackProperties#sourceUri} is set, the custom cache key is used to create a + * {@link PlaybackProperties} object. Otherwise it will be ignored. + */ + public Builder setCustomCacheKey(@Nullable String customCacheKey) { + this.customCacheKey = customCacheKey; + return this; + } + /** * Sets the optional subtitles. * @@ -352,6 +393,7 @@ public final class MediaItem { drmSessionForClearTypes) : null, streamKeys, + customCacheKey, subtitles, tag); mediaId = mediaId != null ? mediaId : sourceUri.toString(); @@ -461,6 +503,9 @@ public final class MediaItem { /** Optional stream keys by which the manifest is filtered. */ public final List streamKeys; + /** Optional custom cache key (only used for progressive streams). */ + @Nullable public final String customCacheKey; + /** Optional subtitles to be sideloaded. */ public final List subtitles; @@ -476,12 +521,14 @@ public final class MediaItem { @Nullable String mimeType, @Nullable DrmConfiguration drmConfiguration, List streamKeys, + @Nullable String customCacheKey, List subtitles, @Nullable Object tag) { this.sourceUri = sourceUri; this.mimeType = mimeType; this.drmConfiguration = drmConfiguration; this.streamKeys = streamKeys; + this.customCacheKey = customCacheKey; this.subtitles = subtitles; this.tag = tag; } @@ -500,6 +547,7 @@ public final class MediaItem { && Util.areEqual(mimeType, other.mimeType) && Util.areEqual(drmConfiguration, other.drmConfiguration) && streamKeys.equals(other.streamKeys) + && Util.areEqual(customCacheKey, other.customCacheKey) && subtitles.equals(other.subtitles) && Util.areEqual(tag, other.tag); } @@ -510,6 +558,7 @@ public final class MediaItem { result = 31 * result + (mimeType == null ? 0 : mimeType.hashCode()); result = 31 * result + (drmConfiguration == null ? 0 : drmConfiguration.hashCode()); result = 31 * result + streamKeys.hashCode(); + result = 31 * result + (customCacheKey == null ? 0 : customCacheKey.hashCode()); result = 31 * result + subtitles.hashCode(); result = 31 * result + (tag == null ? 0 : tag.hashCode()); return result; @@ -674,6 +723,11 @@ public final class MediaItem { this.clippingProperties = clippingProperties; } + /** Returns a {@link Builder} initialized with the values of this instance. */ + public Builder buildUpon() { + return new Builder(this); + } + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { diff --git a/library/common/src/test/java/com/google/android/exoplayer2/MediaItemTest.java b/library/common/src/test/java/com/google/android/exoplayer2/MediaItemTest.java index bb470114d3..adfbc60085 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/MediaItemTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/MediaItemTest.java @@ -141,6 +141,14 @@ public class MediaItemTest { .build()); } + @Test + public void builderSetCustomCacheKey_setsCustomCacheKey() { + MediaItem mediaItem = + new MediaItem.Builder().setSourceUri(URI_STRING).setCustomCacheKey("key").build(); + + assertThat(mediaItem.playbackProperties.customCacheKey).isEqualTo("key"); + } + @Test public void builderSetStreamKeys_setsStreamKeys() { List streamKeys = new ArrayList<>(); @@ -267,4 +275,40 @@ public class MediaItemTest { assertThat(mediaItem.mediaMetadata).isEqualTo(mediaMetadata); } + + @Test + public void buildUpon_equalsToOriginal() { + MediaItem mediaItem = + new MediaItem.Builder() + .setClipEndPositionMs(1000) + .setClipRelativeToDefaultPosition(true) + .setClipRelativeToLiveWindow(true) + .setClipStartPositionMs(100) + .setClipStartsAtKeyFrame(true) + .setCustomCacheKey("key") + .setDrmUuid(C.WIDEVINE_UUID) + .setDrmLicenseUri(URI_STRING + "/license") + .setDrmLicenseRequestHeaders( + Collections.singletonMap("Referer", "http://www.google.com")) + .setDrmMultiSession(true) + .setDrmPlayClearContentWithoutKey(true) + .setDrmSessionForClearTypes(Collections.singletonList(C.TRACK_TYPE_AUDIO)) + .setMediaId("mediaId") + .setMediaMetadata(new MediaMetadata.Builder().setTitle("title").build()) + .setMimeType(MimeTypes.APPLICATION_MP4) + .setSourceUri(URI_STRING) + .setStreamKeys(Collections.singletonList(new StreamKey(1, 0, 0))) + .setSubtitles( + Collections.singletonList( + new MediaItem.Subtitle( + Uri.parse(URI_STRING + "/en"), + MimeTypes.APPLICATION_TTML, + /* language= */ "en"))) + .setTag(new Object()) + .build(); + + MediaItem copy = mediaItem.buildUpon().build(); + + assertThat(copy).isEqualTo(mediaItem); + } } 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 b2404db56c..ef9a965e76 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 @@ -95,13 +95,10 @@ public final class ProgressiveMediaSource extends BaseMediaSource } /** - * Sets the custom key that uniquely identifies the original stream. Used for cache indexing. - * The default value is {@code null}. - * - * @param customCacheKey A custom key that uniquely identifies the original stream. Used for - * cache indexing. - * @return This factory, for convenience. + * @deprecated Use {@link MediaItem.Builder#setCustomCacheKey(String)} and {@link + * #createMediaSource(MediaItem)} instead. */ + @Deprecated public Factory setCustomCacheKey(@Nullable String customCacheKey) { this.customCacheKey = customCacheKey; return this; @@ -188,7 +185,9 @@ public final class ProgressiveMediaSource extends BaseMediaSource extractorsFactory, drmSessionManager, loadErrorHandlingPolicy, - customCacheKey, + mediaItem.playbackProperties.customCacheKey != null + ? mediaItem.playbackProperties.customCacheKey + : customCacheKey, continueLoadingCheckIntervalBytes, mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag); }