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 6552c4da08..1a03b779f6 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
@@ -126,16 +126,18 @@ import java.util.Arrays;
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
long durationUs = durationsUs[windowIndex];
boolean isDynamic = durationUs == C.TIME_UNSET;
+ MediaItem mediaItem =
+ new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build();
return window.set(
/* uid= */ ids[windowIndex],
- /* mediaItem= */ new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build(),
+ /* mediaItem= */ mediaItem,
/* manifest= */ null,
/* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* isSeekable= */ !isDynamic,
isDynamic,
- isLive[windowIndex],
+ isLive[windowIndex] ? mediaItem.liveConfiguration : null,
defaultPositionsUs[windowIndex],
durationUs,
/* firstPeriodIndex= */ windowIndex,
diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java
index 69bccfea73..842862fee8 100644
--- a/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java
+++ b/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java
@@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2;
+import static com.google.android.exoplayer2.util.Assertions.checkState;
+
import android.net.Uri;
import android.os.SystemClock;
import android.util.Pair;
@@ -74,10 +76,10 @@ import com.google.android.exoplayer2.util.Util;
*
A timeline for a live stream consists of a period whose duration is unknown, since it's
* continually extending as more content is broadcast. If content only remains available for a
* limited period of time then the window may start at a non-zero position, defining the region of
- * content that can still be played. The window will have {@link Window#isLive} set to true to
- * indicate it's a live stream and {@link Window#isDynamic} set to true as long as we expect changes
- * to the live window. Its default position is typically near to the live edge (indicated by the
- * black dot in the figure above).
+ * content that can still be played. The window will return true from {@link Window#isLive()} to
+ * indicate it's a live stream and {@link Window#isDynamic} will be set to true as long as we expect
+ * changes to the live window. Its default position is typically near to the live edge (indicated by
+ * the black dot in the figure above).
*
*
Live stream with indefinite availability
*
@@ -191,12 +193,14 @@ public abstract class Timeline {
/** Whether this window may change when the timeline is updated. */
public boolean isDynamic;
+ /** @deprecated Use {@link #isLive()} instead. */
+ @Deprecated public boolean isLive;
+
/**
- * Whether the media in this window is live. For informational purposes only.
- *
- * Check {@link #isDynamic} to know whether this window may still change.
+ * The {@link MediaItem.LiveConfiguration} that is used or null if {@link #isLive()} returns
+ * false.
*/
- public boolean isLive;
+ @Nullable public MediaItem.LiveConfiguration liveConfiguration;
/**
* Whether this window contains placeholder information because the real information has yet to
@@ -248,7 +252,7 @@ public abstract class Timeline {
long elapsedRealtimeEpochOffsetMs,
boolean isSeekable,
boolean isDynamic,
- boolean isLive,
+ @Nullable MediaItem.LiveConfiguration liveConfiguration,
long defaultPositionUs,
long durationUs,
int firstPeriodIndex,
@@ -266,7 +270,8 @@ public abstract class Timeline {
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
- this.isLive = isLive;
+ this.isLive = liveConfiguration != null;
+ this.liveConfiguration = liveConfiguration;
this.defaultPositionUs = defaultPositionUs;
this.durationUs = durationUs;
this.firstPeriodIndex = firstPeriodIndex;
@@ -336,6 +341,14 @@ public abstract class Timeline {
return Util.getNowUnixTimeMs(elapsedRealtimeEpochOffsetMs);
}
+ /** Returns whether this is a live stream. */
+ // Verifies whether the deprecated isLive member field is in a correct state.
+ @SuppressWarnings("deprecation")
+ public boolean isLive() {
+ checkState(isLive == (liveConfiguration != null));
+ return liveConfiguration != null;
+ }
+
// Provide backward compatibility for tag.
@Override
public boolean equals(@Nullable Object obj) {
@@ -349,12 +362,12 @@ public abstract class Timeline {
return Util.areEqual(uid, that.uid)
&& Util.areEqual(mediaItem, that.mediaItem)
&& Util.areEqual(manifest, that.manifest)
+ && Util.areEqual(liveConfiguration, that.liveConfiguration)
&& presentationStartTimeMs == that.presentationStartTimeMs
&& windowStartTimeMs == that.windowStartTimeMs
&& elapsedRealtimeEpochOffsetMs == that.elapsedRealtimeEpochOffsetMs
&& isSeekable == that.isSeekable
&& isDynamic == that.isDynamic
- && isLive == that.isLive
&& isPlaceholder == that.isPlaceholder
&& defaultPositionUs == that.defaultPositionUs
&& durationUs == that.durationUs
@@ -370,6 +383,7 @@ public abstract class Timeline {
result = 31 * result + uid.hashCode();
result = 31 * result + mediaItem.hashCode();
result = 31 * result + (manifest == null ? 0 : manifest.hashCode());
+ result = 31 * result + (liveConfiguration == null ? 0 : liveConfiguration.hashCode());
result = 31 * result + (int) (presentationStartTimeMs ^ (presentationStartTimeMs >>> 32));
result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32));
result =
@@ -377,7 +391,6 @@ public abstract class Timeline {
+ (int) (elapsedRealtimeEpochOffsetMs ^ (elapsedRealtimeEpochOffsetMs >>> 32));
result = 31 * result + (isSeekable ? 1 : 0);
result = 31 * result + (isDynamic ? 1 : 0);
- result = 31 * result + (isLive ? 1 : 0);
result = 31 * result + (isPlaceholder ? 1 : 0);
result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32));
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java
index 150a13e288..d99a320e32 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java
@@ -214,7 +214,7 @@ public abstract class BasePlayer implements Player {
@Override
public final boolean isCurrentWindowLive() {
Timeline timeline = getCurrentTimeline();
- return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isLive;
+ return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isLive();
}
@Override
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java
index 25c468330c..26fbdd3bcb 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java
@@ -99,7 +99,7 @@ public class DefaultControlDispatcher implements ControlDispatcher {
int nextWindowIndex = player.getNextWindowIndex();
if (nextWindowIndex != C.INDEX_UNSET) {
player.seekTo(nextWindowIndex, C.TIME_UNSET);
- } else if (timeline.getWindow(windowIndex, window).isLive) {
+ } else if (timeline.getWindow(windowIndex, window).isLive()) {
player.seekTo(windowIndex, C.TIME_UNSET);
}
return true;
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
index 7c3dd553b9..875f548a86 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2;
+import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -1050,7 +1051,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private long getLiveOffsetUs(Timeline timeline, Object periodUid, long periodPositionUs) {
int windowIndex = timeline.getPeriodByUid(periodUid, period).windowIndex;
timeline.getWindow(windowIndex, window);
- if (window.windowStartTimeMs == C.TIME_UNSET || !window.isLive || !window.isDynamic) {
+ if (window.windowStartTimeMs == C.TIME_UNSET || !window.isLive() || !window.isDynamic) {
return C.TIME_UNSET;
}
return C.msToUs(window.getCurrentUnixTimeMs() - window.windowStartTimeMs)
@@ -1067,7 +1068,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
timeline.getWindow(windowIndex, window);
- return window.isLive && window.isDynamic;
+ return window.isLive() && window.isDynamic;
}
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
@@ -1838,7 +1839,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
int windowIndex = newTimeline.getPeriodByUid(newPeriodId.periodUid, period).windowIndex;
newTimeline.getWindow(windowIndex, window);
- livePlaybackSpeedControl.setLiveConfiguration(window.mediaItem.liveConfiguration);
+ livePlaybackSpeedControl.setLiveConfiguration(castNonNull(window.liveConfiguration));
if (positionForTargetOffsetOverrideUs != C.TIME_UNSET) {
livePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(
getLiveOffsetUs(newTimeline, newPeriodId.periodUid, positionForTargetOffsetOverrideUs));
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Player.java b/library/core/src/main/java/com/google/android/exoplayer2/Player.java
index 36d64ec07e..c121e11159 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/Player.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/Player.java
@@ -1570,7 +1570,7 @@ public interface Player {
/**
* Returns whether the current window is live, or {@code false} if the {@link Timeline} is empty.
*
- * @see Timeline.Window#isLive
+ * @see Timeline.Window#isLive()
*/
boolean isCurrentWindowLive();
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java
index 0b8a668200..60df8413b1 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java
@@ -1003,7 +1003,7 @@ public final class DownloadHelper {
// Ignore dynamic updates.
return;
}
- if (timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window()).isLive) {
+ if (timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window()).isLive()) {
downloadHelperHandler
.obtainMessage(
DOWNLOAD_HELPER_CALLBACK_MESSAGE_FAILED,
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 5d2e1c6fb7..f9cf5af50d 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
@@ -374,7 +374,7 @@ public final class MaskingMediaSource extends CompositeMediaSource {
/* isSeekable= */ false,
// Dynamic window to indicate pending timeline updates.
/* isDynamic= */ true,
- /* isLive= */ false,
+ /* liveConfiguration= */ null,
/* defaultPositionUs= */ 0,
/* durationUs= */ C.TIME_UNSET,
/* firstPeriodIndex= */ 0,
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 19f09fde22..e18028571f 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
@@ -336,7 +336,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource
timelineDurationUs,
timelineIsSeekable,
/* isDynamic= */ false,
- /* isLive= */ timelineIsLive,
+ /* useLiveConfiguration= */ timelineIsLive,
/* manifest= */ null,
mediaItem);
if (timelineIsPlaceholder) {
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java
index 26b783f970..15861a1922 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java
@@ -132,7 +132,7 @@ public final class SilenceMediaSource extends BaseMediaSource {
durationUs,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
mediaItem));
}
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 54230a8b4f..a24dedea03 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
@@ -45,9 +45,9 @@ public final class SinglePeriodTimeline extends Timeline {
private final long windowDefaultStartPositionUs;
private final boolean isSeekable;
private final boolean isDynamic;
- private final boolean isLive;
@Nullable private final Object manifest;
@Nullable private final MediaItem mediaItem;
+ @Nullable private final MediaItem.LiveConfiguration liveConfiguration;
/**
* @deprecated Use {@link #SinglePeriodTimeline(long, boolean, boolean, boolean, Object,
@@ -81,7 +81,8 @@ public final class SinglePeriodTimeline extends Timeline {
* @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 useLiveConfiguration Whether the window is live and {@link MediaItem#liveConfiguration}
+ * is used to configure live playback behaviour.
* @param manifest The manifest. May be {@code null}.
* @param mediaItem A media item used for {@link Window#mediaItem}.
*/
@@ -89,7 +90,7 @@ public final class SinglePeriodTimeline extends Timeline {
long durationUs,
boolean isSeekable,
boolean isDynamic,
- boolean isLive,
+ boolean useLiveConfiguration,
@Nullable Object manifest,
MediaItem mediaItem) {
this(
@@ -99,7 +100,7 @@ public final class SinglePeriodTimeline extends Timeline {
/* windowDefaultStartPositionUs= */ 0,
isSeekable,
isDynamic,
- isLive,
+ useLiveConfiguration,
manifest,
mediaItem);
}
@@ -148,7 +149,8 @@ public final class SinglePeriodTimeline extends Timeline {
* 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 useLiveConfiguration Whether the window is live and {@link MediaItem#liveConfiguration}
+ * is used to configure live playback behaviour.
* @param manifest The manifest. May be (@code null}.
* @param mediaItem A media item used for {@link Timeline.Window#mediaItem}.
*/
@@ -159,7 +161,7 @@ public final class SinglePeriodTimeline extends Timeline {
long windowDefaultStartPositionUs,
boolean isSeekable,
boolean isDynamic,
- boolean isLive,
+ boolean useLiveConfiguration,
@Nullable Object manifest,
MediaItem mediaItem) {
this(
@@ -172,14 +174,14 @@ public final class SinglePeriodTimeline extends Timeline {
windowDefaultStartPositionUs,
isSeekable,
isDynamic,
- isLive,
manifest,
- mediaItem);
+ mediaItem,
+ useLiveConfiguration ? mediaItem.liveConfiguration : null);
}
/**
* @deprecated Use {@link #SinglePeriodTimeline(long, long, long, long, long, long, long, boolean,
- * boolean, boolean, Object, MediaItem)} instead.
+ * boolean, Object, MediaItem, MediaItem.LiveConfiguration)} instead.
*/
@Deprecated
public SinglePeriodTimeline(
@@ -205,9 +207,9 @@ public final class SinglePeriodTimeline extends Timeline {
windowDefaultStartPositionUs,
isSeekable,
isDynamic,
- isLive,
manifest,
- MEDIA_ITEM.buildUpon().setTag(tag).build());
+ MEDIA_ITEM.buildUpon().setTag(tag).build(),
+ isLive ? MEDIA_ITEM.liveConfiguration : null);
}
/**
@@ -229,9 +231,10 @@ public final class SinglePeriodTimeline extends Timeline {
* 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}.
+ * @param liveConfiguration The configuration for live playback behaviour, or {@code null} if the
+ * window is not live.
*/
public SinglePeriodTimeline(
long presentationStartTimeMs,
@@ -243,9 +246,9 @@ public final class SinglePeriodTimeline extends Timeline {
long windowDefaultStartPositionUs,
boolean isSeekable,
boolean isDynamic,
- boolean isLive,
@Nullable Object manifest,
- MediaItem mediaItem) {
+ MediaItem mediaItem,
+ @Nullable MediaItem.LiveConfiguration liveConfiguration) {
this.presentationStartTimeMs = presentationStartTimeMs;
this.windowStartTimeMs = windowStartTimeMs;
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
@@ -255,9 +258,9 @@ public final class SinglePeriodTimeline extends Timeline {
this.windowDefaultStartPositionUs = windowDefaultStartPositionUs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
- this.isLive = isLive;
this.manifest = manifest;
this.mediaItem = checkNotNull(mediaItem);
+ this.liveConfiguration = liveConfiguration;
}
@Override
@@ -291,7 +294,7 @@ public final class SinglePeriodTimeline extends Timeline {
elapsedRealtimeEpochOffsetMs,
isSeekable,
isDynamic,
- isLive,
+ liveConfiguration,
windowDefaultStartPositionUs,
windowDurationUs,
/* firstPeriodIndex= */ 0,
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
index 6cb8a451b3..70d2f925ce 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
@@ -291,7 +291,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
durationUs,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
mediaItem);
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
index ef845f5daa..6a6a2dcf42 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
@@ -54,7 +54,7 @@ public final class MediaPeriodQueueTest {
CONTENT_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
private static final Uri AD_URI = Uri.EMPTY;
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java b/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java
index e8585399cb..9dfd643712 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
@@ -91,7 +92,7 @@ public class TimelineTest {
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
- otherWindow.isLive = true;
+ otherWindow.liveConfiguration = LiveConfiguration.UNSET;
assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window();
@@ -129,7 +130,7 @@ public class TimelineTest {
window.elapsedRealtimeEpochOffsetMs,
window.isSeekable,
window.isDynamic,
- window.isLive,
+ window.liveConfiguration,
window.defaultPositionUs,
window.durationUs,
window.firstPeriodIndex,
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 8fce3b25ac..dda55e4f47 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
@@ -69,7 +69,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -90,7 +90,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ false,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -112,7 +112,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ false,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -134,7 +134,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -153,7 +153,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -189,7 +189,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -208,7 +208,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -226,7 +226,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -249,7 +249,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -272,7 +272,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 =
@@ -283,7 +283,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -323,7 +323,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 =
@@ -334,7 +334,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -374,7 +374,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 =
@@ -385,7 +385,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -426,7 +426,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 =
@@ -437,7 +437,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
@@ -556,7 +556,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
FakeMediaSource fakeMediaSource =
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 4fce17e336..09ac9b6df2 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
@@ -71,7 +71,7 @@ public final class SinglePeriodTimelineTest {
/* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ false,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
// Should return null with a positive position projection beyond window duration.
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdsMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdsMediaSourceTest.java
index 83386673af..b5748aaa42 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdsMediaSourceTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdsMediaSourceTest.java
@@ -58,7 +58,7 @@ public final class AdsMediaSourceTest {
PREROLL_AD_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
private static final Object PREROLL_AD_PERIOD_UID =
@@ -70,7 +70,7 @@ public final class AdsMediaSourceTest {
CONTENT_DURATION_US,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
/* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY));
private static final Object CONTENT_PERIOD_UID =
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 4dc0921b0a..8c4aaaa66e 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
@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
+import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -427,7 +428,7 @@ public final class DashMediaSource extends BaseMediaSource {
private static final String TAG = "DashMediaSource";
- private final MediaItem originalMediaItem;
+ private final MediaItem mediaItem;
private final boolean sideloadedManifest;
private final DataSource.Factory manifestDataSourceFactory;
private final DashChunkSource.Factory chunkSourceFactory;
@@ -452,7 +453,7 @@ public final class DashMediaSource extends BaseMediaSource {
private IOException manifestFatalError;
private Handler handler;
- private MediaItem updatedMediaItem;
+ private MediaItem.LiveConfiguration liveConfiguration;
private Uri manifestUri;
private Uri initialManifestUri;
private DashManifest manifest;
@@ -476,8 +477,8 @@ public final class DashMediaSource extends BaseMediaSource {
DrmSessionManager drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long fallbackTargetLiveOffsetMs) {
- this.originalMediaItem = mediaItem;
- this.updatedMediaItem = mediaItem;
+ this.mediaItem = mediaItem;
+ this.liveConfiguration = mediaItem.liveConfiguration;
this.manifestUri = checkNotNull(mediaItem.playbackProperties).uri;
this.initialManifestUri = mediaItem.playbackProperties.uri;
this.manifest = manifest;
@@ -531,12 +532,12 @@ public final class DashMediaSource extends BaseMediaSource {
@Override
@Nullable
public Object getTag() {
- return castNonNull(updatedMediaItem.playbackProperties).tag;
+ return castNonNull(mediaItem.playbackProperties).tag;
}
@Override
public MediaItem getMediaItem() {
- return updatedMediaItem;
+ return mediaItem;
}
@Override
@@ -938,8 +939,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* windowStartPeriodTimeUs= */ currentStartTimeUs,
/* windowEndPeriodTimeUs= */ currentEndTimeUs);
windowDefaultStartPositionUs =
- nowUnixTimeUs
- - C.msToUs(windowStartTimeMs + updatedMediaItem.liveConfiguration.targetOffsetMs);
+ nowUnixTimeUs - C.msToUs(windowStartTimeMs + liveConfiguration.targetOffsetMs);
long minimumDefaultStartPositionUs =
min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2);
if (windowDefaultStartPositionUs < minimumDefaultStartPositionUs) {
@@ -959,7 +959,8 @@ public final class DashMediaSource extends BaseMediaSource {
windowDurationUs,
windowDefaultStartPositionUs,
manifest,
- updatedMediaItem);
+ mediaItem,
+ manifest.dynamic ? liveConfiguration : null);
refreshSourceInfo(timeline);
if (!sideloadedManifest) {
@@ -996,8 +997,8 @@ public final class DashMediaSource extends BaseMediaSource {
private void updateMediaItemLiveConfiguration(
long nowPeriodTimeUs, long windowStartPeriodTimeUs, long windowEndPeriodTimeUs) {
long maxLiveOffsetMs;
- if (originalMediaItem.liveConfiguration.maxOffsetMs != C.TIME_UNSET) {
- maxLiveOffsetMs = originalMediaItem.liveConfiguration.maxOffsetMs;
+ if (mediaItem.liveConfiguration.maxOffsetMs != C.TIME_UNSET) {
+ maxLiveOffsetMs = mediaItem.liveConfiguration.maxOffsetMs;
} else if (manifest.serviceDescription != null
&& manifest.serviceDescription.maxOffsetMs != C.TIME_UNSET) {
maxLiveOffsetMs = manifest.serviceDescription.maxOffsetMs;
@@ -1005,8 +1006,8 @@ public final class DashMediaSource extends BaseMediaSource {
maxLiveOffsetMs = C.usToMs(nowPeriodTimeUs - windowStartPeriodTimeUs);
}
long minLiveOffsetMs;
- if (originalMediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) {
- minLiveOffsetMs = originalMediaItem.liveConfiguration.minOffsetMs;
+ if (mediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) {
+ minLiveOffsetMs = mediaItem.liveConfiguration.minOffsetMs;
} else if (manifest.serviceDescription != null
&& manifest.serviceDescription.minOffsetMs != C.TIME_UNSET) {
minLiveOffsetMs = manifest.serviceDescription.minOffsetMs;
@@ -1022,9 +1023,9 @@ public final class DashMediaSource extends BaseMediaSource {
}
}
long targetOffsetMs;
- if (updatedMediaItem.liveConfiguration.targetOffsetMs != C.TIME_UNSET) {
+ if (liveConfiguration.targetOffsetMs != C.TIME_UNSET) {
// Keep existing target offset even if the media configuration changes.
- targetOffsetMs = updatedMediaItem.liveConfiguration.targetOffsetMs;
+ targetOffsetMs = liveConfiguration.targetOffsetMs;
} else if (manifest.serviceDescription != null
&& manifest.serviceDescription.targetOffsetMs != C.TIME_UNSET) {
targetOffsetMs = manifest.serviceDescription.targetOffsetMs;
@@ -1048,26 +1049,20 @@ public final class DashMediaSource extends BaseMediaSource {
maxTargetOffsetForSafeDistanceToWindowStartMs, minLiveOffsetMs, maxLiveOffsetMs);
}
float minPlaybackSpeed = C.RATE_UNSET;
- if (originalMediaItem.liveConfiguration.minPlaybackSpeed != C.RATE_UNSET) {
- minPlaybackSpeed = originalMediaItem.liveConfiguration.minPlaybackSpeed;
+ if (mediaItem.liveConfiguration.minPlaybackSpeed != C.RATE_UNSET) {
+ minPlaybackSpeed = mediaItem.liveConfiguration.minPlaybackSpeed;
} else if (manifest.serviceDescription != null) {
minPlaybackSpeed = manifest.serviceDescription.minPlaybackSpeed;
}
float maxPlaybackSpeed = C.RATE_UNSET;
- if (originalMediaItem.liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET) {
- maxPlaybackSpeed = originalMediaItem.liveConfiguration.maxPlaybackSpeed;
+ if (mediaItem.liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET) {
+ maxPlaybackSpeed = mediaItem.liveConfiguration.maxPlaybackSpeed;
} else if (manifest.serviceDescription != null) {
maxPlaybackSpeed = manifest.serviceDescription.maxPlaybackSpeed;
}
- updatedMediaItem =
- originalMediaItem
- .buildUpon()
- .setLiveTargetOffsetMs(targetOffsetMs)
- .setLiveMinOffsetMs(minLiveOffsetMs)
- .setLiveMaxOffsetMs(maxLiveOffsetMs)
- .setLiveMinPlaybackSpeed(minPlaybackSpeed)
- .setLiveMaxPlaybackSpeed(maxPlaybackSpeed)
- .build();
+ liveConfiguration =
+ new MediaItem.LiveConfiguration(
+ targetOffsetMs, minLiveOffsetMs, maxLiveOffsetMs, minPlaybackSpeed, maxPlaybackSpeed);
}
private void scheduleManifestRefresh(long delayUntilNextLoadMs) {
@@ -1232,6 +1227,7 @@ public final class DashMediaSource extends BaseMediaSource {
private final long windowDefaultStartPositionUs;
private final DashManifest manifest;
private final MediaItem mediaItem;
+ @Nullable private final MediaItem.LiveConfiguration liveConfiguration;
public DashTimeline(
long presentationStartTimeMs,
@@ -1242,7 +1238,9 @@ public final class DashMediaSource extends BaseMediaSource {
long windowDurationUs,
long windowDefaultStartPositionUs,
DashManifest manifest,
- MediaItem mediaItem) {
+ MediaItem mediaItem,
+ @Nullable MediaItem.LiveConfiguration liveConfiguration) {
+ checkState(manifest.dynamic == (liveConfiguration != null));
this.presentationStartTimeMs = presentationStartTimeMs;
this.windowStartTimeMs = windowStartTimeMs;
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
@@ -1252,6 +1250,7 @@ public final class DashMediaSource extends BaseMediaSource {
this.windowDefaultStartPositionUs = windowDefaultStartPositionUs;
this.manifest = manifest;
this.mediaItem = mediaItem;
+ this.liveConfiguration = liveConfiguration;
}
@Override
@@ -1288,7 +1287,7 @@ public final class DashMediaSource extends BaseMediaSource {
elapsedRealtimeEpochOffsetMs,
/* isSeekable= */ true,
/* isDynamic= */ isMovingLiveWindow(manifest),
- /* isLive= */ manifest.dynamic,
+ liveConfiguration,
windowDefaultStartPositionUs,
windowDurationUs,
/* firstPeriodIndex= */ 0,
diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java
index 57f451b668..d1269e18a1 100644
--- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java
+++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java
@@ -287,14 +287,15 @@ public final class DashMediaSourceTest {
() -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION))
.createMediaSource(MediaItem.fromUri(Uri.EMPTY));
- MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs)
+ assertThat(liveConfiguration.targetOffsetMs)
.isEqualTo(DashMediaSource.DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS);
- assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(0L);
- assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
- assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
- assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
}
@Test
@@ -306,13 +307,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY));
- MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(1234L);
- assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(0L);
- assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
- assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
- assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.targetOffsetMs).isEqualTo(1234L);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
}
@Test
@@ -333,13 +335,10 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem);
- MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L);
- assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(500L);
- assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(20_000L);
- assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
- assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
+ assertThat(liveConfiguration).isEqualTo(mediaItem.liveConfiguration);
}
@Test
@@ -353,13 +352,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY));
- MediaItem mediaItem = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(2_000L);
- assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(500L);
- assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
- assertThat(mediaItem.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
- assertThat(mediaItem.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.targetOffsetMs).isEqualTo(2_000L);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(500L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
}
@Test
@@ -383,13 +383,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem);
- MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L);
- assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(200L);
- assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(999L);
- assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
- assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
+ assertThat(liveConfiguration.targetOffsetMs).isEqualTo(876L);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(200L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(999L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
}
@Test
@@ -401,13 +402,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY));
- MediaItem mediaItem = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(4_000L);
- assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(2_000L);
- assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(6_000L);
- assertThat(mediaItem.liveConfiguration.minPlaybackSpeed).isEqualTo(0.96f);
- assertThat(mediaItem.liveConfiguration.maxPlaybackSpeed).isEqualTo(1.04f);
+ assertThat(liveConfiguration.targetOffsetMs).isEqualTo(4_000L);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(2_000L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(6_000L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(0.96f);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(1.04f);
}
@Test
@@ -428,13 +430,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem);
- MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem;
+ MediaItem.LiveConfiguration liveConfiguration =
+ prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
- assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L);
- assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(100L);
- assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(999L);
- assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
- assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
+ assertThat(liveConfiguration.targetOffsetMs).isEqualTo(876L);
+ assertThat(liveConfiguration.minOffsetMs).isEqualTo(100L);
+ assertThat(liveConfiguration.maxOffsetMs).isEqualTo(999L);
+ assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
+ assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
}
@Test
@@ -448,7 +451,7 @@ public final class DashMediaSourceTest {
Window window = prepareAndWaitForTimelineRefresh(mediaSource);
// Expect the target live offset as defined in the manifest.
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(3000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(3000);
// Expect the default position at the first segment start before the live edge.
assertThat(window.getDefaultPositionMs()).isEqualTo(2_000);
}
@@ -466,7 +469,7 @@ public final class DashMediaSourceTest {
// Expect the default position at the first segment start below the minimum live start position.
assertThat(window.getDefaultPositionMs()).isEqualTo(4_000);
// Expect the target live offset reaching from now time to the minimum live start position.
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(9000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(9000);
}
@Test
@@ -483,7 +486,7 @@ public final class DashMediaSourceTest {
// Expect the default position at the start of the last segment.
assertThat(window.getDefaultPositionMs()).isEqualTo(12_000);
// Expect the target live offset reaching from now time to the end of the window.
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(60_000 - 16_000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(60_000 - 16_000);
}
private static Window prepareAndWaitForTimelineRefresh(MediaSource mediaSource)
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 61a8095176..bb5ce6c509 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
@@ -405,8 +405,9 @@ public final class HlsMediaSource extends BaseMediaSource
private final boolean useSessionKeys;
private final HlsPlaylistTracker playlistTracker;
private final long elapsedRealTimeOffsetMs;
+ private final MediaItem mediaItem;
- private MediaItem mediaItem;
+ private MediaItem.LiveConfiguration liveConfiguration;
@Nullable private TransferListener mediaTransferListener;
private HlsMediaSource(
@@ -423,6 +424,7 @@ public final class HlsMediaSource extends BaseMediaSource
boolean useSessionKeys) {
this.playbackProperties = checkNotNull(mediaItem.playbackProperties);
this.mediaItem = mediaItem;
+ this.liveConfiguration = mediaItem.liveConfiguration;
this.dataSourceFactory = dataSourceFactory;
this.extractorFactory = extractorFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@@ -515,8 +517,8 @@ public final class HlsMediaSource extends BaseMediaSource
if (playlistTracker.isLive()) {
long liveEdgeOffsetUs = getLiveEdgeOffsetUs(playlist);
long targetLiveOffsetUs =
- mediaItem.liveConfiguration.targetOffsetMs != C.TIME_UNSET
- ? C.msToUs(mediaItem.liveConfiguration.targetOffsetMs)
+ liveConfiguration.targetOffsetMs != C.TIME_UNSET
+ ? C.msToUs(liveConfiguration.targetOffsetMs)
: getTargetLiveOffsetUs(playlist, liveEdgeOffsetUs);
// Ensure target live offset is within the live window and greater than the live edge offset.
targetLiveOffsetUs =
@@ -546,9 +548,9 @@ public final class HlsMediaSource extends BaseMediaSource
windowDefaultStartPositionUs,
/* isSeekable= */ true,
/* isDynamic= */ !playlist.hasEndTag,
- /* isLive= */ true,
manifest,
- mediaItem);
+ mediaItem,
+ liveConfiguration);
} else /* not live */ {
if (windowDefaultStartPositionUs == C.TIME_UNSET) {
windowDefaultStartPositionUs = 0;
@@ -564,9 +566,9 @@ public final class HlsMediaSource extends BaseMediaSource
windowDefaultStartPositionUs,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
manifest,
- mediaItem);
+ mediaItem,
+ /* liveConfiguration= */ null);
}
refreshSourceInfo(timeline);
}
@@ -581,9 +583,7 @@ public final class HlsMediaSource extends BaseMediaSource
List segments = playlist.segments;
int segmentIndex = segments.size() - 1;
long minStartPositionUs =
- playlist.durationUs
- + liveEdgeOffsetUs
- - C.msToUs(mediaItem.liveConfiguration.targetOffsetMs);
+ playlist.durationUs + liveEdgeOffsetUs - C.msToUs(liveConfiguration.targetOffsetMs);
while (segmentIndex > 0
&& segments.get(segmentIndex).relativeStartTimeUs > minStartPositionUs) {
segmentIndex--;
@@ -593,8 +593,9 @@ public final class HlsMediaSource extends BaseMediaSource
private void maybeUpdateMediaItem(long targetLiveOffsetUs) {
long targetLiveOffsetMs = C.usToMs(targetLiveOffsetUs);
- if (targetLiveOffsetMs != mediaItem.liveConfiguration.targetOffsetMs) {
- mediaItem = mediaItem.buildUpon().setLiveTargetOffsetMs(targetLiveOffsetMs).build();
+ if (targetLiveOffsetMs != liveConfiguration.targetOffsetMs) {
+ liveConfiguration =
+ mediaItem.buildUpon().setLiveTargetOffsetMs(targetLiveOffsetMs).build().liveConfiguration;
}
}
diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaSourceTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaSourceTest.java
index 13ef9d75a4..fd2744280a 100644
--- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaSourceTest.java
+++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaSourceTest.java
@@ -183,7 +183,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from target duration (3 * 4 = 12 seconds) and then expressed
// in relation to the live edge (12 + 1 seconds).
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(13000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000);
assertThat(window.defaultPositionUs).isEqualTo(4000000);
}
@@ -219,7 +219,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from hold back and then expressed in relation to the live
// edge (+1 seconds).
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(13000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000);
assertThat(window.defaultPositionUs).isEqualTo(4000000);
}
@@ -257,7 +257,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from hold back and then expressed in relation to the live
// edge (+1 seconds).
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(13000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000);
assertThat(window.defaultPositionUs).isEqualTo(4000000);
}
@@ -288,7 +288,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from part hold back and then expressed in relation to the
// live edge (+1 seconds).
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(4000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(4000);
assertThat(window.defaultPositionUs).isEqualTo(0);
}
@@ -318,7 +318,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from the media item and not adjusted.
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(1000);
+ assertThat(window.liveConfiguration).isEqualTo(mediaItem.liveConfiguration);
assertThat(window.defaultPositionUs).isEqualTo(0);
}
@@ -351,7 +351,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
assertThat(mediaItem.liveConfiguration.targetOffsetMs)
.isGreaterThan(C.usToMs(window.durationUs));
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(9000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(9000);
}
@Test
@@ -385,7 +385,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is not adjusted to the live edge because the list does not have
// program date time.
- assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(12000);
+ assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(12000);
assertThat(window.defaultPositionUs).isEqualTo(4000000);
}
@@ -475,13 +475,13 @@ public class HlsMediaSourceTest {
runMainLooperUntil(() -> timelines.size() == 4);
Timeline.Window window = new Timeline.Window();
- assertThat(timelines.get(0).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs)
+ assertThat(timelines.get(0).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(12000);
- assertThat(timelines.get(1).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs)
+ assertThat(timelines.get(1).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(12000);
- assertThat(timelines.get(2).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs)
+ assertThat(timelines.get(2).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(8000);
- assertThat(timelines.get(3).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs)
+ assertThat(timelines.get(3).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(8000);
}
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 d24394edbc..3297bc0311 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
@@ -594,7 +594,7 @@ public final class SsMediaSource extends BaseMediaSource
/* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ manifest.isLive,
- /* isLive= */ manifest.isLive,
+ /* useLiveConfiguration= */ manifest.isLive,
manifest,
mediaItem);
} else if (manifest.isLive) {
@@ -617,7 +617,7 @@ public final class SsMediaSource extends BaseMediaSource
defaultStartPositionUs,
/* isSeekable= */ true,
/* isDynamic= */ true,
- /* isLive= */ true,
+ /* useLiveConfiguration= */ true,
manifest,
mediaItem);
} else {
@@ -631,7 +631,7 @@ public final class SsMediaSource extends BaseMediaSource
/* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ false,
- /* isLive= */ false,
+ /* useLiveConfiguration= */ false,
manifest,
mediaItem);
}
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 0f68783560..a440dd745d 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
@@ -329,7 +329,7 @@ public final class FakeTimeline extends Timeline {
/* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET,
windowDefinition.isSeekable,
windowDefinition.isDynamic,
- windowDefinition.isLive,
+ windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null,
windowDefinition.defaultPositionUs,
windowDefinition.durationUs,
periodOffsets[windowIndex],