Add live configuration to Timeline.Window

Issue: #5011
PiperOrigin-RevId: 346828103
This commit is contained in:
bachinger 2020-12-10 19:31:47 +00:00 committed by Ian Baker
parent 392b3ab573
commit 05c928f96d
23 changed files with 190 additions and 167 deletions

View File

@ -126,16 +126,18 @@ import java.util.Arrays;
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
long durationUs = durationsUs[windowIndex]; long durationUs = durationsUs[windowIndex];
boolean isDynamic = durationUs == C.TIME_UNSET; boolean isDynamic = durationUs == C.TIME_UNSET;
MediaItem mediaItem =
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build();
return window.set( return window.set(
/* uid= */ ids[windowIndex], /* uid= */ ids[windowIndex],
/* mediaItem= */ new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build(), /* mediaItem= */ mediaItem,
/* manifest= */ null, /* manifest= */ null,
/* presentationStartTimeMs= */ C.TIME_UNSET, /* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET, /* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET, /* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* isSeekable= */ !isDynamic, /* isSeekable= */ !isDynamic,
isDynamic, isDynamic,
isLive[windowIndex], isLive[windowIndex] ? mediaItem.liveConfiguration : null,
defaultPositionsUs[windowIndex], defaultPositionsUs[windowIndex],
durationUs, durationUs,
/* firstPeriodIndex= */ windowIndex, /* firstPeriodIndex= */ windowIndex,

View File

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.net.Uri; import android.net.Uri;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Pair; import android.util.Pair;
@ -74,10 +76,10 @@ import com.google.android.exoplayer2.util.Util;
* <p>A timeline for a live stream consists of a period whose duration is unknown, since it's * <p>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 * 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 * 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 * 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} set to true as long as we expect changes * indicate it's a live stream and {@link Window#isDynamic} will be set to true as long as we expect
* to the live window. Its default position is typically near to the live edge (indicated by the * changes to the live window. Its default position is typically near to the live edge (indicated by
* black dot in the figure above). * the black dot in the figure above).
* *
* <h3>Live stream with indefinite availability</h3> * <h3>Live stream with indefinite availability</h3>
* *
@ -191,12 +193,14 @@ public abstract class Timeline {
/** Whether this window may change when the timeline is updated. */ /** Whether this window may change when the timeline is updated. */
public boolean isDynamic; public boolean isDynamic;
/** @deprecated Use {@link #isLive()} instead. */
@Deprecated public boolean isLive;
/** /**
* Whether the media in this window is live. For informational purposes only. * The {@link MediaItem.LiveConfiguration} that is used or null if {@link #isLive()} returns
* * false.
* <p>Check {@link #isDynamic} to know whether this window may still change.
*/ */
public boolean isLive; @Nullable public MediaItem.LiveConfiguration liveConfiguration;
/** /**
* Whether this window contains placeholder information because the real information has yet to * Whether this window contains placeholder information because the real information has yet to
@ -248,7 +252,7 @@ public abstract class Timeline {
long elapsedRealtimeEpochOffsetMs, long elapsedRealtimeEpochOffsetMs,
boolean isSeekable, boolean isSeekable,
boolean isDynamic, boolean isDynamic,
boolean isLive, @Nullable MediaItem.LiveConfiguration liveConfiguration,
long defaultPositionUs, long defaultPositionUs,
long durationUs, long durationUs,
int firstPeriodIndex, int firstPeriodIndex,
@ -266,7 +270,8 @@ public abstract class Timeline {
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs; this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
this.isSeekable = isSeekable; this.isSeekable = isSeekable;
this.isDynamic = isDynamic; this.isDynamic = isDynamic;
this.isLive = isLive; this.isLive = liveConfiguration != null;
this.liveConfiguration = liveConfiguration;
this.defaultPositionUs = defaultPositionUs; this.defaultPositionUs = defaultPositionUs;
this.durationUs = durationUs; this.durationUs = durationUs;
this.firstPeriodIndex = firstPeriodIndex; this.firstPeriodIndex = firstPeriodIndex;
@ -336,6 +341,14 @@ public abstract class Timeline {
return Util.getNowUnixTimeMs(elapsedRealtimeEpochOffsetMs); 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. // Provide backward compatibility for tag.
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
@ -349,12 +362,12 @@ public abstract class Timeline {
return Util.areEqual(uid, that.uid) return Util.areEqual(uid, that.uid)
&& Util.areEqual(mediaItem, that.mediaItem) && Util.areEqual(mediaItem, that.mediaItem)
&& Util.areEqual(manifest, that.manifest) && Util.areEqual(manifest, that.manifest)
&& Util.areEqual(liveConfiguration, that.liveConfiguration)
&& presentationStartTimeMs == that.presentationStartTimeMs && presentationStartTimeMs == that.presentationStartTimeMs
&& windowStartTimeMs == that.windowStartTimeMs && windowStartTimeMs == that.windowStartTimeMs
&& elapsedRealtimeEpochOffsetMs == that.elapsedRealtimeEpochOffsetMs && elapsedRealtimeEpochOffsetMs == that.elapsedRealtimeEpochOffsetMs
&& isSeekable == that.isSeekable && isSeekable == that.isSeekable
&& isDynamic == that.isDynamic && isDynamic == that.isDynamic
&& isLive == that.isLive
&& isPlaceholder == that.isPlaceholder && isPlaceholder == that.isPlaceholder
&& defaultPositionUs == that.defaultPositionUs && defaultPositionUs == that.defaultPositionUs
&& durationUs == that.durationUs && durationUs == that.durationUs
@ -370,6 +383,7 @@ public abstract class Timeline {
result = 31 * result + uid.hashCode(); result = 31 * result + uid.hashCode();
result = 31 * result + mediaItem.hashCode(); result = 31 * result + mediaItem.hashCode();
result = 31 * result + (manifest == null ? 0 : manifest.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) (presentationStartTimeMs ^ (presentationStartTimeMs >>> 32));
result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32)); result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32));
result = result =
@ -377,7 +391,6 @@ public abstract class Timeline {
+ (int) (elapsedRealtimeEpochOffsetMs ^ (elapsedRealtimeEpochOffsetMs >>> 32)); + (int) (elapsedRealtimeEpochOffsetMs ^ (elapsedRealtimeEpochOffsetMs >>> 32));
result = 31 * result + (isSeekable ? 1 : 0); result = 31 * result + (isSeekable ? 1 : 0);
result = 31 * result + (isDynamic ? 1 : 0); result = 31 * result + (isDynamic ? 1 : 0);
result = 31 * result + (isLive ? 1 : 0);
result = 31 * result + (isPlaceholder ? 1 : 0); result = 31 * result + (isPlaceholder ? 1 : 0);
result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32)); result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32));
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32)); result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));

View File

@ -214,7 +214,7 @@ public abstract class BasePlayer implements Player {
@Override @Override
public final boolean isCurrentWindowLive() { public final boolean isCurrentWindowLive() {
Timeline timeline = getCurrentTimeline(); Timeline timeline = getCurrentTimeline();
return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isLive; return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isLive();
} }
@Override @Override

View File

@ -99,7 +99,7 @@ public class DefaultControlDispatcher implements ControlDispatcher {
int nextWindowIndex = player.getNextWindowIndex(); int nextWindowIndex = player.getNextWindowIndex();
if (nextWindowIndex != C.INDEX_UNSET) { if (nextWindowIndex != C.INDEX_UNSET) {
player.seekTo(nextWindowIndex, C.TIME_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); player.seekTo(windowIndex, C.TIME_UNSET);
} }
return true; return true;

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2; 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.max;
import static java.lang.Math.min; 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) { private long getLiveOffsetUs(Timeline timeline, Object periodUid, long periodPositionUs) {
int windowIndex = timeline.getPeriodByUid(periodUid, period).windowIndex; int windowIndex = timeline.getPeriodByUid(periodUid, period).windowIndex;
timeline.getWindow(windowIndex, window); 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.TIME_UNSET;
} }
return C.msToUs(window.getCurrentUnixTimeMs() - window.windowStartTimeMs) 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; int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
return window.isLive && window.isDynamic; return window.isLive() && window.isDynamic;
} }
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) { 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; int windowIndex = newTimeline.getPeriodByUid(newPeriodId.periodUid, period).windowIndex;
newTimeline.getWindow(windowIndex, window); newTimeline.getWindow(windowIndex, window);
livePlaybackSpeedControl.setLiveConfiguration(window.mediaItem.liveConfiguration); livePlaybackSpeedControl.setLiveConfiguration(castNonNull(window.liveConfiguration));
if (positionForTargetOffsetOverrideUs != C.TIME_UNSET) { if (positionForTargetOffsetOverrideUs != C.TIME_UNSET) {
livePlaybackSpeedControl.setTargetLiveOffsetOverrideUs( livePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(
getLiveOffsetUs(newTimeline, newPeriodId.periodUid, positionForTargetOffsetOverrideUs)); getLiveOffsetUs(newTimeline, newPeriodId.periodUid, positionForTargetOffsetOverrideUs));

View File

@ -1570,7 +1570,7 @@ public interface Player {
/** /**
* Returns whether the current window is live, or {@code false} if the {@link Timeline} is empty. * 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(); boolean isCurrentWindowLive();

View File

@ -1003,7 +1003,7 @@ public final class DownloadHelper {
// Ignore dynamic updates. // Ignore dynamic updates.
return; return;
} }
if (timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window()).isLive) { if (timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window()).isLive()) {
downloadHelperHandler downloadHelperHandler
.obtainMessage( .obtainMessage(
DOWNLOAD_HELPER_CALLBACK_MESSAGE_FAILED, DOWNLOAD_HELPER_CALLBACK_MESSAGE_FAILED,

View File

@ -374,7 +374,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
/* isSeekable= */ false, /* isSeekable= */ false,
// Dynamic window to indicate pending timeline updates. // Dynamic window to indicate pending timeline updates.
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ false, /* liveConfiguration= */ null,
/* defaultPositionUs= */ 0, /* defaultPositionUs= */ 0,
/* durationUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET,
/* firstPeriodIndex= */ 0, /* firstPeriodIndex= */ 0,

View File

@ -336,7 +336,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource
timelineDurationUs, timelineDurationUs,
timelineIsSeekable, timelineIsSeekable,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ timelineIsLive, /* useLiveConfiguration= */ timelineIsLive,
/* manifest= */ null, /* manifest= */ null,
mediaItem); mediaItem);
if (timelineIsPlaceholder) { if (timelineIsPlaceholder) {

View File

@ -132,7 +132,7 @@ public final class SilenceMediaSource extends BaseMediaSource {
durationUs, durationUs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
mediaItem)); mediaItem));
} }

View File

@ -45,9 +45,9 @@ public final class SinglePeriodTimeline extends Timeline {
private final long windowDefaultStartPositionUs; private final long windowDefaultStartPositionUs;
private final boolean isSeekable; private final boolean isSeekable;
private final boolean isDynamic; private final boolean isDynamic;
private final boolean isLive;
@Nullable private final Object manifest; @Nullable private final Object manifest;
@Nullable private final MediaItem mediaItem; @Nullable private final MediaItem mediaItem;
@Nullable private final MediaItem.LiveConfiguration liveConfiguration;
/** /**
* @deprecated Use {@link #SinglePeriodTimeline(long, boolean, boolean, boolean, Object, * @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 durationUs The duration of the period, in microseconds.
* @param isSeekable Whether seeking is supported within the period. * @param isSeekable Whether seeking is supported within the period.
* @param isDynamic Whether the window may change when the timeline is updated. * @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 manifest The manifest. May be {@code null}.
* @param mediaItem A media item used for {@link Window#mediaItem}. * @param mediaItem A media item used for {@link Window#mediaItem}.
*/ */
@ -89,7 +90,7 @@ public final class SinglePeriodTimeline extends Timeline {
long durationUs, long durationUs,
boolean isSeekable, boolean isSeekable,
boolean isDynamic, boolean isDynamic,
boolean isLive, boolean useLiveConfiguration,
@Nullable Object manifest, @Nullable Object manifest,
MediaItem mediaItem) { MediaItem mediaItem) {
this( this(
@ -99,7 +100,7 @@ public final class SinglePeriodTimeline extends Timeline {
/* windowDefaultStartPositionUs= */ 0, /* windowDefaultStartPositionUs= */ 0,
isSeekable, isSeekable,
isDynamic, isDynamic,
isLive, useLiveConfiguration,
manifest, manifest,
mediaItem); mediaItem);
} }
@ -148,7 +149,8 @@ public final class SinglePeriodTimeline extends Timeline {
* which to begin playback, in microseconds. * which to begin playback, in microseconds.
* @param isSeekable Whether seeking is supported within the window. * @param isSeekable Whether seeking is supported within the window.
* @param isDynamic Whether the window may change when the timeline is updated. * @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 manifest The manifest. May be (@code null}.
* @param mediaItem A media item used for {@link Timeline.Window#mediaItem}. * @param mediaItem A media item used for {@link Timeline.Window#mediaItem}.
*/ */
@ -159,7 +161,7 @@ public final class SinglePeriodTimeline extends Timeline {
long windowDefaultStartPositionUs, long windowDefaultStartPositionUs,
boolean isSeekable, boolean isSeekable,
boolean isDynamic, boolean isDynamic,
boolean isLive, boolean useLiveConfiguration,
@Nullable Object manifest, @Nullable Object manifest,
MediaItem mediaItem) { MediaItem mediaItem) {
this( this(
@ -172,14 +174,14 @@ public final class SinglePeriodTimeline extends Timeline {
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
isSeekable, isSeekable,
isDynamic, isDynamic,
isLive,
manifest, manifest,
mediaItem); mediaItem,
useLiveConfiguration ? mediaItem.liveConfiguration : null);
} }
/** /**
* @deprecated Use {@link #SinglePeriodTimeline(long, long, long, long, long, long, long, boolean, * @deprecated Use {@link #SinglePeriodTimeline(long, long, long, long, long, long, long, boolean,
* boolean, boolean, Object, MediaItem)} instead. * boolean, Object, MediaItem, MediaItem.LiveConfiguration)} instead.
*/ */
@Deprecated @Deprecated
public SinglePeriodTimeline( public SinglePeriodTimeline(
@ -205,9 +207,9 @@ public final class SinglePeriodTimeline extends Timeline {
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
isSeekable, isSeekable,
isDynamic, isDynamic,
isLive,
manifest, 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. * which to begin playback, in microseconds.
* @param isSeekable Whether seeking is supported within the window. * @param isSeekable Whether seeking is supported within the window.
* @param isDynamic Whether the window may change when the timeline is updated. * @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 manifest The manifest. May be {@code null}.
* @param mediaItem A media item used for {@link Timeline.Window#mediaItem}. * @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( public SinglePeriodTimeline(
long presentationStartTimeMs, long presentationStartTimeMs,
@ -243,9 +246,9 @@ public final class SinglePeriodTimeline extends Timeline {
long windowDefaultStartPositionUs, long windowDefaultStartPositionUs,
boolean isSeekable, boolean isSeekable,
boolean isDynamic, boolean isDynamic,
boolean isLive,
@Nullable Object manifest, @Nullable Object manifest,
MediaItem mediaItem) { MediaItem mediaItem,
@Nullable MediaItem.LiveConfiguration liveConfiguration) {
this.presentationStartTimeMs = presentationStartTimeMs; this.presentationStartTimeMs = presentationStartTimeMs;
this.windowStartTimeMs = windowStartTimeMs; this.windowStartTimeMs = windowStartTimeMs;
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs; this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
@ -255,9 +258,9 @@ public final class SinglePeriodTimeline extends Timeline {
this.windowDefaultStartPositionUs = windowDefaultStartPositionUs; this.windowDefaultStartPositionUs = windowDefaultStartPositionUs;
this.isSeekable = isSeekable; this.isSeekable = isSeekable;
this.isDynamic = isDynamic; this.isDynamic = isDynamic;
this.isLive = isLive;
this.manifest = manifest; this.manifest = manifest;
this.mediaItem = checkNotNull(mediaItem); this.mediaItem = checkNotNull(mediaItem);
this.liveConfiguration = liveConfiguration;
} }
@Override @Override
@ -291,7 +294,7 @@ public final class SinglePeriodTimeline extends Timeline {
elapsedRealtimeEpochOffsetMs, elapsedRealtimeEpochOffsetMs,
isSeekable, isSeekable,
isDynamic, isDynamic,
isLive, liveConfiguration,
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
windowDurationUs, windowDurationUs,
/* firstPeriodIndex= */ 0, /* firstPeriodIndex= */ 0,

View File

@ -291,7 +291,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
durationUs, durationUs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
mediaItem); mediaItem);
} }

View File

@ -54,7 +54,7 @@ public final class MediaPeriodQueueTest {
CONTENT_DURATION_US, CONTENT_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
private static final Uri AD_URI = Uri.EMPTY; private static final Uri AD_URI = Uri.EMPTY;

View File

@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4; 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;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
@ -91,7 +92,7 @@ public class TimelineTest {
assertThat(window).isNotEqualTo(otherWindow); assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window(); otherWindow = new Timeline.Window();
otherWindow.isLive = true; otherWindow.liveConfiguration = LiveConfiguration.UNSET;
assertThat(window).isNotEqualTo(otherWindow); assertThat(window).isNotEqualTo(otherWindow);
otherWindow = new Timeline.Window(); otherWindow = new Timeline.Window();
@ -129,7 +130,7 @@ public class TimelineTest {
window.elapsedRealtimeEpochOffsetMs, window.elapsedRealtimeEpochOffsetMs,
window.isSeekable, window.isSeekable,
window.isDynamic, window.isDynamic,
window.isLive, window.liveConfiguration,
window.defaultPositionUs, window.defaultPositionUs,
window.durationUs, window.durationUs,
window.firstPeriodIndex, window.firstPeriodIndex,

View File

@ -69,7 +69,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -90,7 +90,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ false, /* isSeekable= */ false,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -112,7 +112,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ false, /* isSeekable= */ false,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -134,7 +134,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -153,7 +153,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -189,7 +189,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ TEST_PERIOD_DURATION_US, /* durationUs= */ TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -208,7 +208,7 @@ public final class ClippingMediaSourceTest {
/* durationUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -226,7 +226,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -249,7 +249,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -272,7 +272,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 = Timeline timeline2 =
@ -283,7 +283,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -323,7 +323,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 = Timeline timeline2 =
@ -334,7 +334,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -374,7 +374,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 = Timeline timeline2 =
@ -385,7 +385,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -426,7 +426,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
Timeline timeline2 = Timeline timeline2 =
@ -437,7 +437,7 @@ public final class ClippingMediaSourceTest {
/* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US, /* windowDefaultStartPositionUs= */ TEST_CLIP_AMOUNT_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
@ -556,7 +556,7 @@ public final class ClippingMediaSourceTest {
TEST_PERIOD_DURATION_US, TEST_PERIOD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
FakeMediaSource fakeMediaSource = FakeMediaSource fakeMediaSource =

View File

@ -71,7 +71,7 @@ public final class SinglePeriodTimelineTest {
/* windowDefaultStartPositionUs= */ 0, /* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ false, /* isSeekable= */ false,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
// Should return null with a positive position projection beyond window duration. // Should return null with a positive position projection beyond window duration.

View File

@ -58,7 +58,7 @@ public final class AdsMediaSourceTest {
PREROLL_AD_DURATION_US, PREROLL_AD_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
private static final Object PREROLL_AD_PERIOD_UID = private static final Object PREROLL_AD_PERIOD_UID =
@ -70,7 +70,7 @@ public final class AdsMediaSourceTest {
CONTENT_DURATION_US, CONTENT_DURATION_US,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.EMPTY));
private static final Object CONTENT_PERIOD_UID = private static final Object CONTENT_PERIOD_UID =

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash; package com.google.android.exoplayer2.source.dash;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; 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 com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.Math.min; import static java.lang.Math.min;
@ -427,7 +428,7 @@ public final class DashMediaSource extends BaseMediaSource {
private static final String TAG = "DashMediaSource"; private static final String TAG = "DashMediaSource";
private final MediaItem originalMediaItem; private final MediaItem mediaItem;
private final boolean sideloadedManifest; private final boolean sideloadedManifest;
private final DataSource.Factory manifestDataSourceFactory; private final DataSource.Factory manifestDataSourceFactory;
private final DashChunkSource.Factory chunkSourceFactory; private final DashChunkSource.Factory chunkSourceFactory;
@ -452,7 +453,7 @@ public final class DashMediaSource extends BaseMediaSource {
private IOException manifestFatalError; private IOException manifestFatalError;
private Handler handler; private Handler handler;
private MediaItem updatedMediaItem; private MediaItem.LiveConfiguration liveConfiguration;
private Uri manifestUri; private Uri manifestUri;
private Uri initialManifestUri; private Uri initialManifestUri;
private DashManifest manifest; private DashManifest manifest;
@ -476,8 +477,8 @@ public final class DashMediaSource extends BaseMediaSource {
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long fallbackTargetLiveOffsetMs) { long fallbackTargetLiveOffsetMs) {
this.originalMediaItem = mediaItem; this.mediaItem = mediaItem;
this.updatedMediaItem = mediaItem; this.liveConfiguration = mediaItem.liveConfiguration;
this.manifestUri = checkNotNull(mediaItem.playbackProperties).uri; this.manifestUri = checkNotNull(mediaItem.playbackProperties).uri;
this.initialManifestUri = mediaItem.playbackProperties.uri; this.initialManifestUri = mediaItem.playbackProperties.uri;
this.manifest = manifest; this.manifest = manifest;
@ -531,12 +532,12 @@ public final class DashMediaSource extends BaseMediaSource {
@Override @Override
@Nullable @Nullable
public Object getTag() { public Object getTag() {
return castNonNull(updatedMediaItem.playbackProperties).tag; return castNonNull(mediaItem.playbackProperties).tag;
} }
@Override @Override
public MediaItem getMediaItem() { public MediaItem getMediaItem() {
return updatedMediaItem; return mediaItem;
} }
@Override @Override
@ -938,8 +939,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* windowStartPeriodTimeUs= */ currentStartTimeUs, /* windowStartPeriodTimeUs= */ currentStartTimeUs,
/* windowEndPeriodTimeUs= */ currentEndTimeUs); /* windowEndPeriodTimeUs= */ currentEndTimeUs);
windowDefaultStartPositionUs = windowDefaultStartPositionUs =
nowUnixTimeUs nowUnixTimeUs - C.msToUs(windowStartTimeMs + liveConfiguration.targetOffsetMs);
- C.msToUs(windowStartTimeMs + updatedMediaItem.liveConfiguration.targetOffsetMs);
long minimumDefaultStartPositionUs = long minimumDefaultStartPositionUs =
min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2); min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2);
if (windowDefaultStartPositionUs < minimumDefaultStartPositionUs) { if (windowDefaultStartPositionUs < minimumDefaultStartPositionUs) {
@ -959,7 +959,8 @@ public final class DashMediaSource extends BaseMediaSource {
windowDurationUs, windowDurationUs,
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
manifest, manifest,
updatedMediaItem); mediaItem,
manifest.dynamic ? liveConfiguration : null);
refreshSourceInfo(timeline); refreshSourceInfo(timeline);
if (!sideloadedManifest) { if (!sideloadedManifest) {
@ -996,8 +997,8 @@ public final class DashMediaSource extends BaseMediaSource {
private void updateMediaItemLiveConfiguration( private void updateMediaItemLiveConfiguration(
long nowPeriodTimeUs, long windowStartPeriodTimeUs, long windowEndPeriodTimeUs) { long nowPeriodTimeUs, long windowStartPeriodTimeUs, long windowEndPeriodTimeUs) {
long maxLiveOffsetMs; long maxLiveOffsetMs;
if (originalMediaItem.liveConfiguration.maxOffsetMs != C.TIME_UNSET) { if (mediaItem.liveConfiguration.maxOffsetMs != C.TIME_UNSET) {
maxLiveOffsetMs = originalMediaItem.liveConfiguration.maxOffsetMs; maxLiveOffsetMs = mediaItem.liveConfiguration.maxOffsetMs;
} else if (manifest.serviceDescription != null } else if (manifest.serviceDescription != null
&& manifest.serviceDescription.maxOffsetMs != C.TIME_UNSET) { && manifest.serviceDescription.maxOffsetMs != C.TIME_UNSET) {
maxLiveOffsetMs = manifest.serviceDescription.maxOffsetMs; maxLiveOffsetMs = manifest.serviceDescription.maxOffsetMs;
@ -1005,8 +1006,8 @@ public final class DashMediaSource extends BaseMediaSource {
maxLiveOffsetMs = C.usToMs(nowPeriodTimeUs - windowStartPeriodTimeUs); maxLiveOffsetMs = C.usToMs(nowPeriodTimeUs - windowStartPeriodTimeUs);
} }
long minLiveOffsetMs; long minLiveOffsetMs;
if (originalMediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) { if (mediaItem.liveConfiguration.minOffsetMs != C.TIME_UNSET) {
minLiveOffsetMs = originalMediaItem.liveConfiguration.minOffsetMs; minLiveOffsetMs = mediaItem.liveConfiguration.minOffsetMs;
} else if (manifest.serviceDescription != null } else if (manifest.serviceDescription != null
&& manifest.serviceDescription.minOffsetMs != C.TIME_UNSET) { && manifest.serviceDescription.minOffsetMs != C.TIME_UNSET) {
minLiveOffsetMs = manifest.serviceDescription.minOffsetMs; minLiveOffsetMs = manifest.serviceDescription.minOffsetMs;
@ -1022,9 +1023,9 @@ public final class DashMediaSource extends BaseMediaSource {
} }
} }
long targetOffsetMs; 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. // Keep existing target offset even if the media configuration changes.
targetOffsetMs = updatedMediaItem.liveConfiguration.targetOffsetMs; targetOffsetMs = liveConfiguration.targetOffsetMs;
} else if (manifest.serviceDescription != null } else if (manifest.serviceDescription != null
&& manifest.serviceDescription.targetOffsetMs != C.TIME_UNSET) { && manifest.serviceDescription.targetOffsetMs != C.TIME_UNSET) {
targetOffsetMs = manifest.serviceDescription.targetOffsetMs; targetOffsetMs = manifest.serviceDescription.targetOffsetMs;
@ -1048,26 +1049,20 @@ public final class DashMediaSource extends BaseMediaSource {
maxTargetOffsetForSafeDistanceToWindowStartMs, minLiveOffsetMs, maxLiveOffsetMs); maxTargetOffsetForSafeDistanceToWindowStartMs, minLiveOffsetMs, maxLiveOffsetMs);
} }
float minPlaybackSpeed = C.RATE_UNSET; float minPlaybackSpeed = C.RATE_UNSET;
if (originalMediaItem.liveConfiguration.minPlaybackSpeed != C.RATE_UNSET) { if (mediaItem.liveConfiguration.minPlaybackSpeed != C.RATE_UNSET) {
minPlaybackSpeed = originalMediaItem.liveConfiguration.minPlaybackSpeed; minPlaybackSpeed = mediaItem.liveConfiguration.minPlaybackSpeed;
} else if (manifest.serviceDescription != null) { } else if (manifest.serviceDescription != null) {
minPlaybackSpeed = manifest.serviceDescription.minPlaybackSpeed; minPlaybackSpeed = manifest.serviceDescription.minPlaybackSpeed;
} }
float maxPlaybackSpeed = C.RATE_UNSET; float maxPlaybackSpeed = C.RATE_UNSET;
if (originalMediaItem.liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET) { if (mediaItem.liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET) {
maxPlaybackSpeed = originalMediaItem.liveConfiguration.maxPlaybackSpeed; maxPlaybackSpeed = mediaItem.liveConfiguration.maxPlaybackSpeed;
} else if (manifest.serviceDescription != null) { } else if (manifest.serviceDescription != null) {
maxPlaybackSpeed = manifest.serviceDescription.maxPlaybackSpeed; maxPlaybackSpeed = manifest.serviceDescription.maxPlaybackSpeed;
} }
updatedMediaItem = liveConfiguration =
originalMediaItem new MediaItem.LiveConfiguration(
.buildUpon() targetOffsetMs, minLiveOffsetMs, maxLiveOffsetMs, minPlaybackSpeed, maxPlaybackSpeed);
.setLiveTargetOffsetMs(targetOffsetMs)
.setLiveMinOffsetMs(minLiveOffsetMs)
.setLiveMaxOffsetMs(maxLiveOffsetMs)
.setLiveMinPlaybackSpeed(minPlaybackSpeed)
.setLiveMaxPlaybackSpeed(maxPlaybackSpeed)
.build();
} }
private void scheduleManifestRefresh(long delayUntilNextLoadMs) { private void scheduleManifestRefresh(long delayUntilNextLoadMs) {
@ -1232,6 +1227,7 @@ public final class DashMediaSource extends BaseMediaSource {
private final long windowDefaultStartPositionUs; private final long windowDefaultStartPositionUs;
private final DashManifest manifest; private final DashManifest manifest;
private final MediaItem mediaItem; private final MediaItem mediaItem;
@Nullable private final MediaItem.LiveConfiguration liveConfiguration;
public DashTimeline( public DashTimeline(
long presentationStartTimeMs, long presentationStartTimeMs,
@ -1242,7 +1238,9 @@ public final class DashMediaSource extends BaseMediaSource {
long windowDurationUs, long windowDurationUs,
long windowDefaultStartPositionUs, long windowDefaultStartPositionUs,
DashManifest manifest, DashManifest manifest,
MediaItem mediaItem) { MediaItem mediaItem,
@Nullable MediaItem.LiveConfiguration liveConfiguration) {
checkState(manifest.dynamic == (liveConfiguration != null));
this.presentationStartTimeMs = presentationStartTimeMs; this.presentationStartTimeMs = presentationStartTimeMs;
this.windowStartTimeMs = windowStartTimeMs; this.windowStartTimeMs = windowStartTimeMs;
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs; this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
@ -1252,6 +1250,7 @@ public final class DashMediaSource extends BaseMediaSource {
this.windowDefaultStartPositionUs = windowDefaultStartPositionUs; this.windowDefaultStartPositionUs = windowDefaultStartPositionUs;
this.manifest = manifest; this.manifest = manifest;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.liveConfiguration = liveConfiguration;
} }
@Override @Override
@ -1288,7 +1287,7 @@ public final class DashMediaSource extends BaseMediaSource {
elapsedRealtimeEpochOffsetMs, elapsedRealtimeEpochOffsetMs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ isMovingLiveWindow(manifest), /* isDynamic= */ isMovingLiveWindow(manifest),
/* isLive= */ manifest.dynamic, liveConfiguration,
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
windowDurationUs, windowDurationUs,
/* firstPeriodIndex= */ 0, /* firstPeriodIndex= */ 0,

View File

@ -287,14 +287,15 @@ public final class DashMediaSourceTest {
() -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION)) () -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION))
.createMediaSource(MediaItem.fromUri(Uri.EMPTY)); .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); .isEqualTo(DashMediaSource.DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS);
assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(0L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L);
assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(58_000L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
} }
@Test @Test
@ -306,13 +307,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY)); .createMediaSource(MediaItem.fromUri(Uri.EMPTY));
MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(1234L); assertThat(liveConfiguration.targetOffsetMs).isEqualTo(1234L);
assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(0L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L);
assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(58_000L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
} }
@Test @Test
@ -333,13 +335,10 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem); .createMediaSource(mediaItem);
MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L); assertThat(liveConfiguration).isEqualTo(mediaItem.liveConfiguration);
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);
} }
@Test @Test
@ -353,13 +352,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY)); .createMediaSource(MediaItem.fromUri(Uri.EMPTY));
MediaItem mediaItem = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(2_000L); assertThat(liveConfiguration.targetOffsetMs).isEqualTo(2_000L);
assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(500L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(500L);
assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(58_000L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L);
assertThat(mediaItem.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
assertThat(mediaItem.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
} }
@Test @Test
@ -383,13 +383,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem); .createMediaSource(mediaItem);
MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L); assertThat(liveConfiguration.targetOffsetMs).isEqualTo(876L);
assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(200L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(200L);
assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(999L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(999L);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(23f); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(42f); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
} }
@Test @Test
@ -401,13 +402,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(MediaItem.fromUri(Uri.EMPTY)); .createMediaSource(MediaItem.fromUri(Uri.EMPTY));
MediaItem mediaItem = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(4_000L); assertThat(liveConfiguration.targetOffsetMs).isEqualTo(4_000L);
assertThat(mediaItem.liveConfiguration.minOffsetMs).isEqualTo(2_000L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(2_000L);
assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(6_000L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(6_000L);
assertThat(mediaItem.liveConfiguration.minPlaybackSpeed).isEqualTo(0.96f); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(0.96f);
assertThat(mediaItem.liveConfiguration.maxPlaybackSpeed).isEqualTo(1.04f); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(1.04f);
} }
@Test @Test
@ -428,13 +430,14 @@ public final class DashMediaSourceTest {
.setFallbackTargetLiveOffsetMs(1234L) .setFallbackTargetLiveOffsetMs(1234L)
.createMediaSource(mediaItem); .createMediaSource(mediaItem);
MediaItem mediaItemFromSource = prepareAndWaitForTimelineRefresh(mediaSource).mediaItem; MediaItem.LiveConfiguration liveConfiguration =
prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration;
assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(876L); assertThat(liveConfiguration.targetOffsetMs).isEqualTo(876L);
assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(100L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(100L);
assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(999L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(999L);
assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(23f); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(23f);
assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(42f); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(42f);
} }
@Test @Test
@ -448,7 +451,7 @@ public final class DashMediaSourceTest {
Window window = prepareAndWaitForTimelineRefresh(mediaSource); Window window = prepareAndWaitForTimelineRefresh(mediaSource);
// Expect the target live offset as defined in the manifest. // 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. // Expect the default position at the first segment start before the live edge.
assertThat(window.getDefaultPositionMs()).isEqualTo(2_000); 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. // Expect the default position at the first segment start below the minimum live start position.
assertThat(window.getDefaultPositionMs()).isEqualTo(4_000); assertThat(window.getDefaultPositionMs()).isEqualTo(4_000);
// Expect the target live offset reaching from now time to the minimum live start position. // 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 @Test
@ -483,7 +486,7 @@ public final class DashMediaSourceTest {
// Expect the default position at the start of the last segment. // Expect the default position at the start of the last segment.
assertThat(window.getDefaultPositionMs()).isEqualTo(12_000); assertThat(window.getDefaultPositionMs()).isEqualTo(12_000);
// Expect the target live offset reaching from now time to the end of the window. // 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) private static Window prepareAndWaitForTimelineRefresh(MediaSource mediaSource)

View File

@ -405,8 +405,9 @@ public final class HlsMediaSource extends BaseMediaSource
private final boolean useSessionKeys; private final boolean useSessionKeys;
private final HlsPlaylistTracker playlistTracker; private final HlsPlaylistTracker playlistTracker;
private final long elapsedRealTimeOffsetMs; private final long elapsedRealTimeOffsetMs;
private final MediaItem mediaItem;
private MediaItem mediaItem; private MediaItem.LiveConfiguration liveConfiguration;
@Nullable private TransferListener mediaTransferListener; @Nullable private TransferListener mediaTransferListener;
private HlsMediaSource( private HlsMediaSource(
@ -423,6 +424,7 @@ public final class HlsMediaSource extends BaseMediaSource
boolean useSessionKeys) { boolean useSessionKeys) {
this.playbackProperties = checkNotNull(mediaItem.playbackProperties); this.playbackProperties = checkNotNull(mediaItem.playbackProperties);
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.liveConfiguration = mediaItem.liveConfiguration;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.extractorFactory = extractorFactory; this.extractorFactory = extractorFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@ -515,8 +517,8 @@ public final class HlsMediaSource extends BaseMediaSource
if (playlistTracker.isLive()) { if (playlistTracker.isLive()) {
long liveEdgeOffsetUs = getLiveEdgeOffsetUs(playlist); long liveEdgeOffsetUs = getLiveEdgeOffsetUs(playlist);
long targetLiveOffsetUs = long targetLiveOffsetUs =
mediaItem.liveConfiguration.targetOffsetMs != C.TIME_UNSET liveConfiguration.targetOffsetMs != C.TIME_UNSET
? C.msToUs(mediaItem.liveConfiguration.targetOffsetMs) ? C.msToUs(liveConfiguration.targetOffsetMs)
: getTargetLiveOffsetUs(playlist, liveEdgeOffsetUs); : getTargetLiveOffsetUs(playlist, liveEdgeOffsetUs);
// Ensure target live offset is within the live window and greater than the live edge offset. // Ensure target live offset is within the live window and greater than the live edge offset.
targetLiveOffsetUs = targetLiveOffsetUs =
@ -546,9 +548,9 @@ public final class HlsMediaSource extends BaseMediaSource
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ !playlist.hasEndTag, /* isDynamic= */ !playlist.hasEndTag,
/* isLive= */ true,
manifest, manifest,
mediaItem); mediaItem,
liveConfiguration);
} else /* not live */ { } else /* not live */ {
if (windowDefaultStartPositionUs == C.TIME_UNSET) { if (windowDefaultStartPositionUs == C.TIME_UNSET) {
windowDefaultStartPositionUs = 0; windowDefaultStartPositionUs = 0;
@ -564,9 +566,9 @@ public final class HlsMediaSource extends BaseMediaSource
windowDefaultStartPositionUs, windowDefaultStartPositionUs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false,
manifest, manifest,
mediaItem); mediaItem,
/* liveConfiguration= */ null);
} }
refreshSourceInfo(timeline); refreshSourceInfo(timeline);
} }
@ -581,9 +583,7 @@ public final class HlsMediaSource extends BaseMediaSource
List<HlsMediaPlaylist.Segment> segments = playlist.segments; List<HlsMediaPlaylist.Segment> segments = playlist.segments;
int segmentIndex = segments.size() - 1; int segmentIndex = segments.size() - 1;
long minStartPositionUs = long minStartPositionUs =
playlist.durationUs playlist.durationUs + liveEdgeOffsetUs - C.msToUs(liveConfiguration.targetOffsetMs);
+ liveEdgeOffsetUs
- C.msToUs(mediaItem.liveConfiguration.targetOffsetMs);
while (segmentIndex > 0 while (segmentIndex > 0
&& segments.get(segmentIndex).relativeStartTimeUs > minStartPositionUs) { && segments.get(segmentIndex).relativeStartTimeUs > minStartPositionUs) {
segmentIndex--; segmentIndex--;
@ -593,8 +593,9 @@ public final class HlsMediaSource extends BaseMediaSource
private void maybeUpdateMediaItem(long targetLiveOffsetUs) { private void maybeUpdateMediaItem(long targetLiveOffsetUs) {
long targetLiveOffsetMs = C.usToMs(targetLiveOffsetUs); long targetLiveOffsetMs = C.usToMs(targetLiveOffsetUs);
if (targetLiveOffsetMs != mediaItem.liveConfiguration.targetOffsetMs) { if (targetLiveOffsetMs != liveConfiguration.targetOffsetMs) {
mediaItem = mediaItem.buildUpon().setLiveTargetOffsetMs(targetLiveOffsetMs).build(); liveConfiguration =
mediaItem.buildUpon().setLiveTargetOffsetMs(targetLiveOffsetMs).build().liveConfiguration;
} }
} }

View File

@ -183,7 +183,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); 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 // 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). // 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); assertThat(window.defaultPositionUs).isEqualTo(4000000);
} }
@ -219,7 +219,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); 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 // The target live offset is picked from hold back and then expressed in relation to the live
// edge (+1 seconds). // edge (+1 seconds).
assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(13000); assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000);
assertThat(window.defaultPositionUs).isEqualTo(4000000); assertThat(window.defaultPositionUs).isEqualTo(4000000);
} }
@ -257,7 +257,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); 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 // The target live offset is picked from hold back and then expressed in relation to the live
// edge (+1 seconds). // edge (+1 seconds).
assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(13000); assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000);
assertThat(window.defaultPositionUs).isEqualTo(4000000); assertThat(window.defaultPositionUs).isEqualTo(4000000);
} }
@ -288,7 +288,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); 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 // The target live offset is picked from part hold back and then expressed in relation to the
// live edge (+1 seconds). // live edge (+1 seconds).
assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(4000); assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(4000);
assertThat(window.defaultPositionUs).isEqualTo(0); assertThat(window.defaultPositionUs).isEqualTo(0);
} }
@ -318,7 +318,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
// The target live offset is picked from the media item and not adjusted. // 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); assertThat(window.defaultPositionUs).isEqualTo(0);
} }
@ -351,7 +351,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
assertThat(mediaItem.liveConfiguration.targetOffsetMs) assertThat(mediaItem.liveConfiguration.targetOffsetMs)
.isGreaterThan(C.usToMs(window.durationUs)); .isGreaterThan(C.usToMs(window.durationUs));
assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(9000); assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(9000);
} }
@Test @Test
@ -385,7 +385,7 @@ public class HlsMediaSourceTest {
Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); 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 // The target live offset is not adjusted to the live edge because the list does not have
// program date time. // program date time.
assertThat(window.mediaItem.liveConfiguration.targetOffsetMs).isEqualTo(12000); assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(12000);
assertThat(window.defaultPositionUs).isEqualTo(4000000); assertThat(window.defaultPositionUs).isEqualTo(4000000);
} }
@ -475,13 +475,13 @@ public class HlsMediaSourceTest {
runMainLooperUntil(() -> timelines.size() == 4); runMainLooperUntil(() -> timelines.size() == 4);
Timeline.Window window = new Timeline.Window(); 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); .isEqualTo(12000);
assertThat(timelines.get(1).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs) assertThat(timelines.get(1).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(12000); .isEqualTo(12000);
assertThat(timelines.get(2).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs) assertThat(timelines.get(2).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(8000); .isEqualTo(8000);
assertThat(timelines.get(3).getWindow(0, window).mediaItem.liveConfiguration.targetOffsetMs) assertThat(timelines.get(3).getWindow(0, window).liveConfiguration.targetOffsetMs)
.isEqualTo(8000); .isEqualTo(8000);
} }

View File

@ -594,7 +594,7 @@ public final class SsMediaSource extends BaseMediaSource
/* windowDefaultStartPositionUs= */ 0, /* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ manifest.isLive, /* isDynamic= */ manifest.isLive,
/* isLive= */ manifest.isLive, /* useLiveConfiguration= */ manifest.isLive,
manifest, manifest,
mediaItem); mediaItem);
} else if (manifest.isLive) { } else if (manifest.isLive) {
@ -617,7 +617,7 @@ public final class SsMediaSource extends BaseMediaSource
defaultStartPositionUs, defaultStartPositionUs,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ true, /* isDynamic= */ true,
/* isLive= */ true, /* useLiveConfiguration= */ true,
manifest, manifest,
mediaItem); mediaItem);
} else { } else {
@ -631,7 +631,7 @@ public final class SsMediaSource extends BaseMediaSource
/* windowDefaultStartPositionUs= */ 0, /* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ true, /* isSeekable= */ true,
/* isDynamic= */ false, /* isDynamic= */ false,
/* isLive= */ false, /* useLiveConfiguration= */ false,
manifest, manifest,
mediaItem); mediaItem);
} }

View File

@ -329,7 +329,7 @@ public final class FakeTimeline extends Timeline {
/* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET, /* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET,
windowDefinition.isSeekable, windowDefinition.isSeekable,
windowDefinition.isDynamic, windowDefinition.isDynamic,
windowDefinition.isLive, windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null,
windowDefinition.defaultPositionUs, windowDefinition.defaultPositionUs,
windowDefinition.durationUs, windowDefinition.durationUs,
periodOffsets[windowIndex], periodOffsets[windowIndex],