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) {
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,

View File

@ -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;
* <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
* 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).
*
* <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. */
public boolean isDynamic;
/** @deprecated Use {@link #isLive()} instead. */
@Deprecated public boolean isLive;
/**
* Whether the media in this window is live. For informational purposes only.
*
* <p>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));

View File

@ -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

View File

@ -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;

View File

@ -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));

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.
*
* @see Timeline.Window#isLive
* @see Timeline.Window#isLive()
*/
boolean isCurrentWindowLive();

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -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,

View File

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

View File

@ -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;

View File

@ -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,

View File

@ -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 =

View File

@ -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.

View File

@ -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 =

View File

@ -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,

View File

@ -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)

View File

@ -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<HlsMediaPlaylist.Segment> 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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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],