From 19d639eb9a79109ae04a93fb753a01b464375b05 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 11 May 2020 12:58:27 +0100 Subject: [PATCH] Make use of MediaItem in ProgressiveMediaSource PiperOrigin-RevId: 310889726 --- .../source/ExtractorMediaSource.java | 10 +- .../source/ProgressiveMediaSource.java | 49 +++-- .../source/SinglePeriodTimeline.java | 189 ++++++++++++++++-- .../source/ClippingMediaSourceTest.java | 18 +- .../source/SinglePeriodTimelineTest.java | 47 ++++- 5 files changed, 253 insertions(+), 60 deletions(-) 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 2e1c92067c..5b55b4f8f8 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 @@ -321,14 +321,16 @@ public final class ExtractorMediaSource extends CompositeMediaSource { @Nullable Object tag) { progressiveMediaSource = new ProgressiveMediaSource( - uri, + new MediaItem.Builder() + .setUri(uri) + .setCustomCacheKey(customCacheKey) + .setTag(tag) + .build(), dataSourceFactory, extractorsFactory, DrmSessionManager.getDummyDrmSessionManager(), loadableLoadErrorHandlingPolicy, - customCacheKey, - continueLoadingCheckIntervalBytes, - tag); + continueLoadingCheckIntervalBytes); } @Override 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 8885a716ba..259f293a94 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 @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; + import android.net.Uri; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -30,7 +32,6 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.util.Assertions; /** * Provides one period that loads data from a {@link Uri} and extracted using an {@link Extractor}. @@ -178,18 +179,21 @@ public final class ProgressiveMediaSource extends BaseMediaSource */ @Override public ProgressiveMediaSource createMediaSource(MediaItem mediaItem) { - Assertions.checkNotNull(mediaItem.playbackProperties); + checkNotNull(mediaItem.playbackProperties); + MediaItem.Builder builder = mediaItem.buildUpon(); + if (mediaItem.playbackProperties.tag == null) { + builder.setTag(tag); + } + if (mediaItem.playbackProperties.customCacheKey == null) { + builder.setCustomCacheKey(customCacheKey); + } return new ProgressiveMediaSource( - mediaItem.playbackProperties.uri, + builder.build(), dataSourceFactory, extractorsFactory, drmSessionManager, loadErrorHandlingPolicy, - mediaItem.playbackProperties.customCacheKey != null - ? mediaItem.playbackProperties.customCacheKey - : customCacheKey, - continueLoadingCheckIntervalBytes, - mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag); + continueLoadingCheckIntervalBytes); } @Override @@ -204,14 +208,13 @@ public final class ProgressiveMediaSource extends BaseMediaSource */ public static final int DEFAULT_LOADING_CHECK_INTERVAL_BYTES = 1024 * 1024; - private final Uri uri; + private final MediaItem mediaItem; + private final MediaItem.PlaybackProperties playbackProperties; private final DataSource.Factory dataSourceFactory; private final ExtractorsFactory extractorsFactory; private final DrmSessionManager drmSessionManager; private final LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy; - @Nullable private final String customCacheKey; private final int continueLoadingCheckIntervalBytes; - @Nullable private final Object tag; private boolean timelineIsPlaceholder; private long timelineDurationUs; @@ -221,30 +224,32 @@ public final class ProgressiveMediaSource extends BaseMediaSource // TODO: Make private when ExtractorMediaSource is deleted. /* package */ ProgressiveMediaSource( - Uri uri, + MediaItem mediaItem, DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory, DrmSessionManager drmSessionManager, LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy, - @Nullable String customCacheKey, - int continueLoadingCheckIntervalBytes, - @Nullable Object tag) { - this.uri = uri; + int continueLoadingCheckIntervalBytes) { + this.playbackProperties = checkNotNull(mediaItem.playbackProperties); + this.mediaItem = mediaItem; this.dataSourceFactory = dataSourceFactory; this.extractorsFactory = extractorsFactory; this.drmSessionManager = drmSessionManager; this.loadableLoadErrorHandlingPolicy = loadableLoadErrorHandlingPolicy; - this.customCacheKey = customCacheKey; this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes; this.timelineIsPlaceholder = true; this.timelineDurationUs = C.TIME_UNSET; - this.tag = tag; } @Override @Nullable public Object getTag() { - return tag; + return playbackProperties.tag; + } + + @Nullable + public Object getMediaItem() { + return mediaItem; } @Override @@ -266,7 +271,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource dataSource.addTransferListener(transferListener); } return new ProgressiveMediaPeriod( - uri, + playbackProperties.uri, dataSource, extractorsFactory.createExtractors(), drmSessionManager, @@ -274,7 +279,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource createEventDispatcher(id), this, allocator, - customCacheKey, + playbackProperties.customCacheKey, continueLoadingCheckIntervalBytes); } @@ -320,7 +325,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource /* isDynamic= */ false, /* isLive= */ timelineIsLive, /* manifest= */ null, - tag); + mediaItem); if (timelineIsPlaceholder) { // TODO: Actually prepare the extractors during prepatation so that we don't need a // placeholder. See https://github.com/google/ExoPlayer/issues/4727. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java index 5b47398dd5..0738fddbaa 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.util.Assertions; @@ -39,6 +40,7 @@ public final class SinglePeriodTimeline extends Timeline { private final boolean isLive; @Nullable private final Object tag; @Nullable private final Object manifest; + @Nullable private final MediaItem mediaItem; /** * Creates a timeline containing a single period and a window that spans it. @@ -50,19 +52,16 @@ public final class SinglePeriodTimeline extends Timeline { */ public SinglePeriodTimeline( long durationUs, boolean isSeekable, boolean isDynamic, boolean isLive) { - this(durationUs, isSeekable, isDynamic, isLive, /* manifest= */ null, /* tag= */ null); + this(durationUs, isSeekable, isDynamic, isLive, /* manifest= */ null, /* mediaItem= */ null); } /** - * Creates a timeline containing a single period and a window that spans it. - * - * @param durationUs The duration of the period, in microseconds. - * @param isSeekable Whether seeking is supported within the period. - * @param isDynamic Whether the window may change when the timeline is updated. - * @param isLive Whether the window is live. - * @param manifest The manifest. May be {@code null}. - * @param tag A tag used for {@link Window#tag}. + * @deprecated Use {@link #SinglePeriodTimeline(long, boolean, boolean, boolean, Object, + * MediaItem)} instead. */ + // Provide backwards compatibility. + @SuppressWarnings("deprecation") + @Deprecated public SinglePeriodTimeline( long durationUs, boolean isSeekable, @@ -83,21 +82,41 @@ public final class SinglePeriodTimeline extends Timeline { } /** - * Creates a timeline with one period, and a window of known duration starting at a specified - * position in the period. + * Creates a timeline containing a single period and a window that spans it. * - * @param periodDurationUs The duration of the period in microseconds. - * @param windowDurationUs The duration of the window in microseconds. - * @param windowPositionInPeriodUs The position of the start of the window in the period, in - * microseconds. - * @param windowDefaultStartPositionUs The default position relative to the start of the window at - * which to begin playback, in microseconds. - * @param isSeekable Whether seeking is supported within the window. + * @param durationUs The duration of the period, in microseconds. + * @param isSeekable Whether seeking is supported within the period. * @param isDynamic Whether the window may change when the timeline is updated. * @param isLive Whether the window is live. - * @param manifest The manifest. May be (@code null}. - * @param tag A tag used for {@link Timeline.Window#tag}. + * @param manifest The manifest. May be {@code null}. + * @param mediaItem A media item used for {@link Window#mediaItem}. */ + public SinglePeriodTimeline( + long durationUs, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + @Nullable Object manifest, + @Nullable MediaItem mediaItem) { + this( + durationUs, + durationUs, + /* windowPositionInPeriodUs= */ 0, + /* windowDefaultStartPositionUs= */ 0, + isSeekable, + isDynamic, + isLive, + manifest, + mediaItem); + } + + /** + * @deprecated Use {@link #SinglePeriodTimeline(long, long, long, long, boolean, boolean, boolean, + * Object, MediaItem)} instead. + */ + // Provide backwards compatibility. + @SuppressWarnings("deprecation") + @Deprecated public SinglePeriodTimeline( long periodDurationUs, long windowDurationUs, @@ -123,6 +142,81 @@ public final class SinglePeriodTimeline extends Timeline { tag); } + /** + * Creates a timeline with one period, and a window of known duration starting at a specified + * position in the period. + * + * @param periodDurationUs The duration of the period in microseconds. + * @param windowDurationUs The duration of the window in microseconds. + * @param windowPositionInPeriodUs The position of the start of the window in the period, in + * microseconds. + * @param windowDefaultStartPositionUs The default position relative to the start of the window at + * which to begin playback, in microseconds. + * @param isSeekable Whether seeking is supported within the window. + * @param isDynamic Whether the window may change when the timeline is updated. + * @param isLive Whether the window is live. + * @param manifest The manifest. May be (@code null}. + * @param mediaItem A media item used for {@link Timeline.Window#mediaItem}. + */ + public SinglePeriodTimeline( + long periodDurationUs, + long windowDurationUs, + long windowPositionInPeriodUs, + long windowDefaultStartPositionUs, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + @Nullable Object manifest, + @Nullable MediaItem mediaItem) { + this( + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ C.TIME_UNSET, + /* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET, + periodDurationUs, + windowDurationUs, + windowPositionInPeriodUs, + windowDefaultStartPositionUs, + isSeekable, + isDynamic, + isLive, + manifest, + mediaItem); + } + + /** + * @deprecated Use {@link #SinglePeriodTimeline(long, long, long, long, long, long, long, boolean, + * boolean, boolean, Object, MediaItem)} instead. + */ + @Deprecated + public SinglePeriodTimeline( + long presentationStartTimeMs, + long windowStartTimeMs, + long elapsedRealtimeEpochOffsetMs, + long periodDurationUs, + long windowDurationUs, + long windowPositionInPeriodUs, + long windowDefaultStartPositionUs, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + @Nullable Object manifest, + @Nullable Object tag) { + this( + presentationStartTimeMs, + windowStartTimeMs, + elapsedRealtimeEpochOffsetMs, + periodDurationUs, + windowDurationUs, + windowPositionInPeriodUs, + windowDefaultStartPositionUs, + isSeekable, + isDynamic, + isLive, + manifest, + /* mediaItem= */ null, + tag); + } + /** * Creates a timeline with one period, and a window of known duration starting at a specified * position in the period. @@ -144,7 +238,7 @@ public final class SinglePeriodTimeline extends Timeline { * @param isDynamic Whether the window may change when the timeline is updated. * @param isLive Whether the window is live. * @param manifest The manifest. May be {@code null}. - * @param tag A tag used for {@link Timeline.Window#tag}. + * @param mediaItem A media item used for {@link Timeline.Window#mediaItem}. */ public SinglePeriodTimeline( long presentationStartTimeMs, @@ -158,6 +252,36 @@ public final class SinglePeriodTimeline extends Timeline { boolean isDynamic, boolean isLive, @Nullable Object manifest, + @Nullable MediaItem mediaItem) { + this( + presentationStartTimeMs, + windowStartTimeMs, + elapsedRealtimeEpochOffsetMs, + periodDurationUs, + windowDurationUs, + windowPositionInPeriodUs, + windowDefaultStartPositionUs, + isSeekable, + isDynamic, + isLive, + manifest, + mediaItem, + /* tag= */ null); + } + + private SinglePeriodTimeline( + long presentationStartTimeMs, + long windowStartTimeMs, + long elapsedRealtimeEpochOffsetMs, + long periodDurationUs, + long windowDurationUs, + long windowPositionInPeriodUs, + long windowDefaultStartPositionUs, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + @Nullable Object manifest, + @Nullable MediaItem mediaItem, @Nullable Object tag) { this.presentationStartTimeMs = presentationStartTimeMs; this.windowStartTimeMs = windowStartTimeMs; @@ -170,6 +294,7 @@ public final class SinglePeriodTimeline extends Timeline { this.isDynamic = isDynamic; this.isLive = isLive; this.manifest = manifest; + this.mediaItem = mediaItem; this.tag = tag; } @@ -178,6 +303,8 @@ public final class SinglePeriodTimeline extends Timeline { return 1; } + // Provide backwards compatibility. + @SuppressWarnings("deprecation") @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { Assertions.checkIndex(windowIndex, 0, 1); @@ -194,9 +321,27 @@ public final class SinglePeriodTimeline extends Timeline { } } } + if (tag != null) { + // Support deprecated constructors. + return window.set( + Window.SINGLE_WINDOW_UID, + tag, + manifest, + presentationStartTimeMs, + windowStartTimeMs, + elapsedRealtimeEpochOffsetMs, + isSeekable, + isDynamic, + isLive, + windowDefaultStartPositionUs, + windowDurationUs, + /* firstPeriodIndex= */ 0, + /* lastPeriodIndex= */ 0, + windowPositionInPeriodUs); + } return window.set( Window.SINGLE_WINDOW_UID, - tag, + mediaItem, manifest, presentationStartTimeMs, windowStartTimeMs, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index ae0c431bd3..118842b239 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -235,7 +235,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline clippedTimeline = getClippedTimeline(timeline, /* durationUs= */ TEST_CLIP_AMOUNT_US); assertThat(clippedTimeline.getWindow(0, window).getDurationUs()).isEqualTo(TEST_CLIP_AMOUNT_US); @@ -258,7 +258,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline timeline2 = new SinglePeriodTimeline( /* periodDurationUs= */ 3 * TEST_PERIOD_DURATION_US, @@ -269,7 +269,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline[] clippedTimelines = getClippedTimelines( @@ -309,7 +309,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline timeline2 = new SinglePeriodTimeline( /* periodDurationUs= */ 4 * TEST_PERIOD_DURATION_US, @@ -320,7 +320,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline[] clippedTimelines = getClippedTimelines( @@ -360,7 +360,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline timeline2 = new SinglePeriodTimeline( /* periodDurationUs= */ 3 * TEST_PERIOD_DURATION_US, @@ -371,7 +371,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline[] clippedTimelines = getClippedTimelines( @@ -412,7 +412,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline timeline2 = new SinglePeriodTimeline( /* periodDurationUs= */ 4 * TEST_PERIOD_DURATION_US, @@ -423,7 +423,7 @@ public final class ClippingMediaSourceTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Timeline[] clippedTimelines = getClippedTimelines( diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java index fe4255c631..e2fa805cc6 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java @@ -17,9 +17,11 @@ package com.google.android.exoplayer2.source; import static com.google.common.truth.Truth.assertThat; +import android.net.Uri; import android.util.Pair; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; import org.junit.Before; @@ -66,7 +68,7 @@ public final class SinglePeriodTimelineTest { /* isDynamic= */ true, /* isLive= */ true, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); // Should return null with a positive position projection beyond window duration. Pair position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs + 1); @@ -90,7 +92,7 @@ public final class SinglePeriodTimelineTest { /* isDynamic= */ false, /* isLive= */ false, /* manifest= */ null, - /* tag= */ null); + /* tag= */ (Object) null); assertThat(timeline.getWindow(/* windowIndex= */ 0, window).tag).isNull(); assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ false).id).isNull(); @@ -100,6 +102,25 @@ public final class SinglePeriodTimelineTest { .isNotNull(); } + @Test + public void setNullMediaItem_returnsNullMediaItem_butUsesDefaultUid() { + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + /* durationUs= */ C.TIME_UNSET, + /* isSeekable= */ false, + /* isDynamic= */ false, + /* isLive= */ false, + /* manifest= */ null, + /* mediaItem= */ null); + + assertThat(timeline.getWindow(/* windowIndex= */ 0, window).mediaItem).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ false).id).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).id).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ false).uid).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).uid) + .isNotNull(); + } + @Test public void getWindow_setsTag() { Object tag = new Object(); @@ -115,6 +136,26 @@ public final class SinglePeriodTimelineTest { assertThat(timeline.getWindow(/* windowIndex= */ 0, window).tag).isEqualTo(tag); } + // Tests backward compatibility. + @SuppressWarnings("deprecation") + @Test + public void getWindow_setsMediaItemAndTag() { + MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(new Object()).build(); + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + /* durationUs= */ C.TIME_UNSET, + /* isSeekable= */ false, + /* isDynamic= */ false, + /* isLive= */ false, + /* manifest= */ null, + mediaItem); + + Window window = timeline.getWindow(/* windowIndex= */ 0, this.window); + + assertThat(window.mediaItem).isEqualTo(mediaItem); + assertThat(window.tag).isEqualTo(mediaItem.playbackProperties.tag); + } + @Test public void getIndexOfPeriod_returnsPeriod() { SinglePeriodTimeline timeline = @@ -124,7 +165,7 @@ public final class SinglePeriodTimelineTest { /* isDynamic= */ false, /* isLive= */ false, /* manifest= */ null, - /* tag= */ null); + /* mediaItem= */ null); Object uid = timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).uid; assertThat(timeline.getIndexOfPeriod(uid)).isEqualTo(0);