Add live configuration to Timeline.Window
Issue: #5011 PiperOrigin-RevId: 346828103
This commit is contained in:
parent
392b3ab573
commit
05c928f96d
@ -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,
|
||||||
|
@ -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));
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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 =
|
||||||
|
@ -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.
|
||||||
|
@ -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 =
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user