From 77ed9302510471eb5c9a4e1c199c2071199a9ee6 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 6 Sep 2019 10:19:47 +0100 Subject: [PATCH] Add Timeline.Window.uid. This allows to uniquely identify a window within a Timeline. The value is set correctly for all Window instances, but is not used anywhere yet. PiperOrigin-RevId: 267556516 --- RELEASENOTES.md | 1 + .../exoplayer2/ext/cast/CastTimeline.java | 1 + .../google/android/exoplayer2/Timeline.java | 22 ++++++- .../source/AbstractConcatenatedTimeline.java | 18 +++-- .../exoplayer2/source/MaskingMediaSource.java | 65 ++++++++++++------- .../source/SinglePeriodTimeline.java | 1 + .../source/dash/DashMediaSource.java | 1 + .../exoplayer2/testutil/FakeTimeline.java | 1 + 8 files changed, 80 insertions(+), 30 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c7ccb30ad4..2263131204 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -64,6 +64,7 @@ [#4337](https://github.com/google/ExoPlayer/issues/4337)). * Publish `testutils` module to simplify unit testing with ExoPlayer ([#6267](https://github.com/google/ExoPlayer/issues/6267)). +* Add `uid` to `Timeline.Window` to uniquely identify window instances. ### 2.10.4 ### diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java index a0741b23cc..2857141f8f 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java @@ -114,6 +114,7 @@ import java.util.Arrays; long durationUs = durationsUs[windowIndex]; boolean isDynamic = durationUs == C.TIME_UNSET; return window.set( + /* uid= */ ids[windowIndex], /* tag= */ ids[windowIndex], /* manifest= */ null, /* presentationStartTimeMs= */ C.TIME_UNSET, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java index 172d9c4107..c496052f94 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -115,6 +115,17 @@ public abstract class Timeline { */ public static final class Window { + /** + * A {@link #uid} for a window that must be used for single-window {@link Timeline Timelines}. + */ + public static final Object SINGLE_WINDOW_UID = new Object(); + + /** + * A unique identifier for the window. Single-window {@link Timeline Timelines} must use {@link + * #SINGLE_WINDOW_UID}. + */ + public Object uid; + /** A tag for the window. Not necessarily unique. */ @Nullable public Object tag; @@ -175,8 +186,14 @@ public abstract class Timeline { */ public long positionInFirstPeriodUs; + /** Creates window. */ + public Window() { + uid = SINGLE_WINDOW_UID; + } + /** Sets the data held by this window. */ public Window set( + Object uid, @Nullable Object tag, @Nullable Object manifest, long presentationStartTimeMs, @@ -188,6 +205,7 @@ public abstract class Timeline { int firstPeriodIndex, int lastPeriodIndex, long positionInFirstPeriodUs) { + this.uid = uid; this.tag = tag; this.manifest = manifest; this.presentationStartTimeMs = presentationStartTimeMs; @@ -793,8 +811,8 @@ public abstract class Timeline { public abstract Period getPeriod(int periodIndex, Period period, boolean setIds); /** - * Returns the index of the period identified by its unique {@code id}, or {@link C#INDEX_UNSET} - * if the period is not in the timeline. + * Returns the index of the period identified by its unique {@link Period#uid}, or {@link + * C#INDEX_UNSET} if the period is not in the timeline. * * @param uid A unique identifier for a period. * @return The index of the period, or {@link C#INDEX_UNSET} if the period was not found. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java index 4e7b572384..703bb7e3a8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java @@ -53,14 +53,14 @@ import com.google.android.exoplayer2.util.Assertions; } /** - * Returns concatenated UID for a period in a child timeline. + * Returns a concatenated UID for a period or window in a child timeline. * - * @param childTimelineUid UID of the child timeline this period belongs to. - * @param childPeriodUid UID of the period in the child timeline. - * @return UID of the period in the concatenated timeline. + * @param childTimelineUid UID of the child timeline this period or window belongs to. + * @param childPeriodOrWindowUid UID of the period or window in the child timeline. + * @return UID of the period or window in the concatenated timeline. */ - public static Object getConcatenatedUid(Object childTimelineUid, Object childPeriodUid) { - return Pair.create(childTimelineUid, childPeriodUid); + public static Object getConcatenatedUid(Object childTimelineUid, Object childPeriodOrWindowUid) { + return Pair.create(childTimelineUid, childPeriodOrWindowUid); } /** @@ -195,6 +195,12 @@ import com.google.android.exoplayer2.util.Assertions; int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex); getTimelineByChildIndex(childIndex) .getWindow(windowIndex - firstWindowIndexInChild, window, defaultPositionProjectionUs); + Object childUid = getChildUidByChildIndex(childIndex); + // Don't create new objects if the child is using SINGLE_WINDOW_UID. + window.uid = + Window.SINGLE_WINDOW_UID.equals(window.uid) + ? childUid + : getConcatenatedUid(childUid, window.uid); window.firstPeriodIndex += firstPeriodIndexInChild; window.lastPeriodIndex += firstPeriodIndexInChild; return window; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java index 671bc9eb74..8727fc5ed9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java @@ -19,6 +19,7 @@ import android.util.Pair; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.TransferListener; @@ -134,7 +135,8 @@ public final class MaskingMediaSource extends CompositeMediaSource { timeline = timeline.cloneWithUpdatedTimeline(newTimeline); } else if (newTimeline.isEmpty()) { timeline = - MaskingTimeline.createWithRealTimeline(newTimeline, MaskingTimeline.DUMMY_EXTERNAL_ID); + MaskingTimeline.createWithRealTimeline( + newTimeline, Window.SINGLE_WINDOW_UID, MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID); } else { // Determine first period and the start position. // This will be: @@ -156,12 +158,13 @@ public final class MaskingMediaSource extends CompositeMediaSource { windowStartPositionUs = periodPreparePositionUs; } } + Object windowUid = window.uid; Pair periodPosition = newTimeline.getPeriodPosition( window, period, /* windowIndex= */ 0, windowStartPositionUs); Object periodUid = periodPosition.first; long periodPositionUs = periodPosition.second; - timeline = MaskingTimeline.createWithRealTimeline(newTimeline, periodUid); + timeline = MaskingTimeline.createWithRealTimeline(newTimeline, windowUid, periodUid); if (unpreparedMaskingMediaPeriod != null) { MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod; maskingPeriod.overridePreparePositionUs(periodPositionUs); @@ -190,14 +193,14 @@ public final class MaskingMediaSource extends CompositeMediaSource { } private Object getInternalPeriodUid(Object externalPeriodUid) { - return externalPeriodUid.equals(MaskingTimeline.DUMMY_EXTERNAL_ID) - ? timeline.replacedInternalId + return externalPeriodUid.equals(MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID) + ? timeline.replacedInternalPeriodUid : externalPeriodUid; } private Object getExternalPeriodUid(Object internalPeriodUid) { - return timeline.replacedInternalId.equals(internalPeriodUid) - ? MaskingTimeline.DUMMY_EXTERNAL_ID + return timeline.replacedInternalPeriodUid.equals(internalPeriodUid) + ? MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID : internalPeriodUid; } @@ -207,9 +210,10 @@ public final class MaskingMediaSource extends CompositeMediaSource { */ private static final class MaskingTimeline extends ForwardingTimeline { - public static final Object DUMMY_EXTERNAL_ID = new Object(); + public static final Object DUMMY_EXTERNAL_PERIOD_UID = new Object(); - private final Object replacedInternalId; + private final Object replacedInternalWindowUid; + private final Object replacedInternalPeriodUid; /** * Returns an instance with a dummy timeline using the provided window tag. @@ -217,7 +221,8 @@ public final class MaskingMediaSource extends CompositeMediaSource { * @param windowTag A window tag. */ public static MaskingTimeline createWithDummyTimeline(@Nullable Object windowTag) { - return new MaskingTimeline(new DummyTimeline(windowTag), DUMMY_EXTERNAL_ID); + return new MaskingTimeline( + new DummyTimeline(windowTag), Window.SINGLE_WINDOW_UID, DUMMY_EXTERNAL_PERIOD_UID); } /** @@ -225,16 +230,21 @@ public final class MaskingMediaSource extends CompositeMediaSource { * assigned dummy period ID. * * @param timeline The real timeline. + * @param firstWindowUid The window UID in the timeline which will be replaced by the already + * assigned {@link Window#SINGLE_WINDOW_UID}. * @param firstPeriodUid The period UID in the timeline which will be replaced by the already - * assigned dummy period UID. + * assigned {@link #DUMMY_EXTERNAL_PERIOD_UID}. */ - public static MaskingTimeline createWithRealTimeline(Timeline timeline, Object firstPeriodUid) { - return new MaskingTimeline(timeline, firstPeriodUid); + public static MaskingTimeline createWithRealTimeline( + Timeline timeline, Object firstWindowUid, Object firstPeriodUid) { + return new MaskingTimeline(timeline, firstWindowUid, firstPeriodUid); } - private MaskingTimeline(Timeline timeline, Object replacedInternalId) { + private MaskingTimeline( + Timeline timeline, Object replacedInternalWindowUid, Object replacedInternalPeriodUid) { super(timeline); - this.replacedInternalId = replacedInternalId; + this.replacedInternalWindowUid = replacedInternalWindowUid; + this.replacedInternalPeriodUid = replacedInternalPeriodUid; } /** @@ -243,7 +253,7 @@ public final class MaskingMediaSource extends CompositeMediaSource { * @param timeline The new timeline. */ public MaskingTimeline cloneWithUpdatedTimeline(Timeline timeline) { - return new MaskingTimeline(timeline, replacedInternalId); + return new MaskingTimeline(timeline, replacedInternalWindowUid, replacedInternalPeriodUid); } /** Returns the wrapped timeline. */ @@ -251,24 +261,34 @@ public final class MaskingMediaSource extends CompositeMediaSource { return timeline; } + @Override + public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { + timeline.getWindow(windowIndex, window, defaultPositionProjectionUs); + if (Util.areEqual(window.uid, replacedInternalWindowUid)) { + window.uid = Window.SINGLE_WINDOW_UID; + } + return window; + } + @Override public Period getPeriod(int periodIndex, Period period, boolean setIds) { timeline.getPeriod(periodIndex, period, setIds); - if (Util.areEqual(period.uid, replacedInternalId)) { - period.uid = DUMMY_EXTERNAL_ID; + if (Util.areEqual(period.uid, replacedInternalPeriodUid)) { + period.uid = DUMMY_EXTERNAL_PERIOD_UID; } return period; } @Override public int getIndexOfPeriod(Object uid) { - return timeline.getIndexOfPeriod(DUMMY_EXTERNAL_ID.equals(uid) ? replacedInternalId : uid); + return timeline.getIndexOfPeriod( + DUMMY_EXTERNAL_PERIOD_UID.equals(uid) ? replacedInternalPeriodUid : uid); } @Override public Object getUidOfPeriod(int periodIndex) { Object uid = timeline.getUidOfPeriod(periodIndex); - return Util.areEqual(uid, replacedInternalId) ? DUMMY_EXTERNAL_ID : uid; + return Util.areEqual(uid, replacedInternalPeriodUid) ? DUMMY_EXTERNAL_PERIOD_UID : uid; } } @@ -289,6 +309,7 @@ public final class MaskingMediaSource extends CompositeMediaSource { @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { return window.set( + Window.SINGLE_WINDOW_UID, tag, /* manifest= */ null, /* presentationStartTimeMs= */ C.TIME_UNSET, @@ -312,7 +333,7 @@ public final class MaskingMediaSource extends CompositeMediaSource { public Period getPeriod(int periodIndex, Period period, boolean setIds) { return period.set( /* id= */ 0, - /* uid= */ MaskingTimeline.DUMMY_EXTERNAL_ID, + /* uid= */ MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID, /* windowIndex= */ 0, /* durationUs = */ C.TIME_UNSET, /* positionInWindowUs= */ 0); @@ -320,12 +341,12 @@ public final class MaskingMediaSource extends CompositeMediaSource { @Override public int getIndexOfPeriod(Object uid) { - return uid == MaskingTimeline.DUMMY_EXTERNAL_ID ? 0 : C.INDEX_UNSET; + return uid == MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID ? 0 : C.INDEX_UNSET; } @Override public Object getUidOfPeriod(int periodIndex) { - return MaskingTimeline.DUMMY_EXTERNAL_ID; + return MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID; } } } 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 966f8e4c7a..49d67935a5 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 @@ -175,6 +175,7 @@ public final class SinglePeriodTimeline extends Timeline { } } return window.set( + Window.SINGLE_WINDOW_UID, tag, manifest, presentationStartTimeMs, 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 c9a039a4bd..f8bdfd5ec6 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 @@ -1216,6 +1216,7 @@ public final class DashMediaSource extends BaseMediaSource { && manifest.minUpdatePeriodMs != C.TIME_UNSET && manifest.durationMs == C.TIME_UNSET; return window.set( + Window.SINGLE_WINDOW_UID, windowTag, manifest, presentationStartTimeMs, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java index f0a0c77ff6..af672f0da3 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java @@ -182,6 +182,7 @@ public final class FakeTimeline extends Timeline { public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; return window.set( + /* uid= */ windowDefinition.id, /* tag= */ windowDefinition.id, manifests[windowIndex], /* presentationStartTimeMs= */ C.TIME_UNSET,