mirror of
https://github.com/samsonjs/media.git
synced 2026-04-12 12:25:47 +00:00
Add server-client time offset to Window.
This offset allows to improve the calculated live offset because it can take known client-server time offsets into account. PiperOrigin-RevId: 285970738
This commit is contained in:
parent
a035c2e20a
commit
021291b38f
12 changed files with 77 additions and 30 deletions
|
|
@ -130,6 +130,7 @@ import java.util.Arrays;
|
|||
/* manifest= */ null,
|
||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
/* isSeekable= */ !isDynamic,
|
||||
isDynamic,
|
||||
isLive[windowIndex],
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ public abstract class BasePlayer implements Player {
|
|||
if (windowStartTimeMs == C.TIME_UNSET) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
return System.currentTimeMillis() - window.windowStartTimeMs - getContentPosition();
|
||||
return window.getCurrentUnixTimeMs() - window.windowStartTimeMs - getContentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Pair;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||
|
|
@ -136,19 +137,28 @@ public abstract class Timeline {
|
|||
|
||||
/**
|
||||
* The start time of the presentation to which this window belongs in milliseconds since the
|
||||
* epoch, or {@link C#TIME_UNSET} if unknown or not applicable. For informational purposes only.
|
||||
* Unix epoch, or {@link C#TIME_UNSET} if unknown or not applicable. For informational purposes
|
||||
* only.
|
||||
*/
|
||||
public long presentationStartTimeMs;
|
||||
|
||||
/**
|
||||
* The window's start time in milliseconds since the epoch, or {@link C#TIME_UNSET} if unknown
|
||||
* or not applicable. For informational purposes only.
|
||||
* The window's start time in milliseconds since the Unix epoch, or {@link C#TIME_UNSET} if
|
||||
* unknown or not applicable. For informational purposes only.
|
||||
*/
|
||||
public long windowStartTimeMs;
|
||||
|
||||
/**
|
||||
* Whether it's possible to seek within this window.
|
||||
* The offset between {@link SystemClock#elapsedRealtime()} and the time since the Unix epoch
|
||||
* according to the clock of the media origin server, or {@link C#TIME_UNSET} if unknown or not
|
||||
* applicable.
|
||||
*
|
||||
* <p>Note that the current Unix time can be retrieved using {@link #getCurrentUnixTimeMs()} and
|
||||
* is calculated as {@code SystemClock.elapsedRealtime() + elapsedRealtimeEpochOffsetMs}.
|
||||
*/
|
||||
public long elapsedRealtimeEpochOffsetMs;
|
||||
|
||||
/** Whether it's possible to seek within this window. */
|
||||
public boolean isSeekable;
|
||||
|
||||
// TODO: Split this to better describe which parts of the window might change. For example it
|
||||
|
|
@ -205,6 +215,7 @@ public abstract class Timeline {
|
|||
@Nullable Object manifest,
|
||||
long presentationStartTimeMs,
|
||||
long windowStartTimeMs,
|
||||
long elapsedRealtimeEpochOffsetMs,
|
||||
boolean isSeekable,
|
||||
boolean isDynamic,
|
||||
boolean isLive,
|
||||
|
|
@ -218,6 +229,7 @@ public abstract class Timeline {
|
|||
this.manifest = manifest;
|
||||
this.presentationStartTimeMs = presentationStartTimeMs;
|
||||
this.windowStartTimeMs = windowStartTimeMs;
|
||||
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
|
||||
this.isSeekable = isSeekable;
|
||||
this.isDynamic = isDynamic;
|
||||
this.isLive = isLive;
|
||||
|
|
@ -279,6 +291,16 @@ public abstract class Timeline {
|
|||
return positionInFirstPeriodUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in milliseconds since the Unix epoch.
|
||||
*
|
||||
* <p>This method applies {@link #elapsedRealtimeEpochOffsetMs known corrections} made available
|
||||
* by the media such that this time corresponds to the clock of the media origin server.
|
||||
*/
|
||||
public long getCurrentUnixTimeMs() {
|
||||
return Util.getNowUnixTimeMs(elapsedRealtimeEpochOffsetMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
|
|
@ -293,6 +315,7 @@ public abstract class Timeline {
|
|||
&& Util.areEqual(manifest, that.manifest)
|
||||
&& presentationStartTimeMs == that.presentationStartTimeMs
|
||||
&& windowStartTimeMs == that.windowStartTimeMs
|
||||
&& elapsedRealtimeEpochOffsetMs == that.elapsedRealtimeEpochOffsetMs
|
||||
&& isSeekable == that.isSeekable
|
||||
&& isDynamic == that.isDynamic
|
||||
&& isLive == that.isLive
|
||||
|
|
@ -311,6 +334,9 @@ public abstract class Timeline {
|
|||
result = 31 * result + (manifest == null ? 0 : manifest.hashCode());
|
||||
result = 31 * result + (int) (presentationStartTimeMs ^ (presentationStartTimeMs >>> 32));
|
||||
result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32));
|
||||
result =
|
||||
31 * result
|
||||
+ (int) (elapsedRealtimeEpochOffsetMs ^ (elapsedRealtimeEpochOffsetMs >>> 32));
|
||||
result = 31 * result + (isSeekable ? 1 : 0);
|
||||
result = 31 * result + (isDynamic ? 1 : 0);
|
||||
result = 31 * result + (isLive ? 1 : 0);
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
|||
/* manifest= */ null,
|
||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
/* isSeekable= */ false,
|
||||
// Dynamic window to indicate pending timeline updates.
|
||||
/* isDynamic= */ true,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
|
||||
private final long presentationStartTimeMs;
|
||||
private final long windowStartTimeMs;
|
||||
private final long elapsedRealtimeEpochOffsetMs;
|
||||
private final long periodDurationUs;
|
||||
private final long windowDurationUs;
|
||||
private final long windowPositionInPeriodUs;
|
||||
|
|
@ -110,6 +111,7 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
this(
|
||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
periodDurationUs,
|
||||
windowDurationUs,
|
||||
windowPositionInPeriodUs,
|
||||
|
|
@ -126,8 +128,12 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
* position in the period.
|
||||
*
|
||||
* @param presentationStartTimeMs The start time of the presentation in milliseconds since the
|
||||
* epoch.
|
||||
* @param windowStartTimeMs The window's start time in milliseconds since the epoch.
|
||||
* epoch, or {@link C#TIME_UNSET} if unknown or not applicable.
|
||||
* @param windowStartTimeMs The window's start time in milliseconds since the epoch, or {@link
|
||||
* C#TIME_UNSET} if unknown or not applicable.
|
||||
* @param elapsedRealtimeEpochOffsetMs The offset between {@link
|
||||
* android.os.SystemClock#elapsedRealtime()} and the time since the Unix epoch according to
|
||||
* the clock of the media origin server, or {@link C#TIME_UNSET} if unknown or not applicable.
|
||||
* @param periodDurationUs The duration of the period in microseconds.
|
||||
* @param windowDurationUs The duration of the window in microseconds.
|
||||
* @param windowPositionInPeriodUs The position of the start of the window in the period, in
|
||||
|
|
@ -143,6 +149,7 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
public SinglePeriodTimeline(
|
||||
long presentationStartTimeMs,
|
||||
long windowStartTimeMs,
|
||||
long elapsedRealtimeEpochOffsetMs,
|
||||
long periodDurationUs,
|
||||
long windowDurationUs,
|
||||
long windowPositionInPeriodUs,
|
||||
|
|
@ -154,6 +161,7 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
@Nullable Object tag) {
|
||||
this.presentationStartTimeMs = presentationStartTimeMs;
|
||||
this.windowStartTimeMs = windowStartTimeMs;
|
||||
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
|
||||
this.periodDurationUs = periodDurationUs;
|
||||
this.windowDurationUs = windowDurationUs;
|
||||
this.windowPositionInPeriodUs = windowPositionInPeriodUs;
|
||||
|
|
@ -192,13 +200,14 @@ public final class SinglePeriodTimeline extends Timeline {
|
|||
manifest,
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
elapsedRealtimeEpochOffsetMs,
|
||||
isSeekable,
|
||||
isDynamic,
|
||||
isLive,
|
||||
windowDefaultStartPositionUs,
|
||||
windowDurationUs,
|
||||
0,
|
||||
0,
|
||||
/* firstPeriodIndex= */ 0,
|
||||
/* lastPeriodIndex= */ 0,
|
||||
windowPositionInPeriodUs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import android.os.Build;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.SystemClock;
|
||||
import android.security.NetworkSecurityPolicy;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
|
@ -2045,6 +2046,19 @@ public final class Util {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in milliseconds since the epoch.
|
||||
*
|
||||
* @param elapsedRealtimeEpochOffsetMs The offset between {@link SystemClock#elapsedRealtime()}
|
||||
* and the time since the Unix epoch, or {@link C#TIME_UNSET} if unknown.
|
||||
* @return The Unix time in milliseconds since the epoch.
|
||||
*/
|
||||
public static long getNowUnixTimeMs(long elapsedRealtimeEpochOffsetMs) {
|
||||
return elapsedRealtimeEpochOffsetMs == C.TIME_UNSET
|
||||
? System.currentTimeMillis()
|
||||
: SystemClock.elapsedRealtime() + elapsedRealtimeEpochOffsetMs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getSystemProperty(String name) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ public class TimelineTest {
|
|||
window.manifest,
|
||||
window.presentationStartTimeMs,
|
||||
window.windowStartTimeMs,
|
||||
window.elapsedRealtimeEpochOffsetMs,
|
||||
window.isSeekable,
|
||||
window.isDynamic,
|
||||
window.isLive,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ public interface DashChunkSource extends ChunkSource {
|
|||
* @param trackSelection The track selection.
|
||||
* @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between
|
||||
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds,
|
||||
* specified as the server's unix time minus the local elapsed time. If unknown, set to 0.
|
||||
* specified as the server's unix time minus the local elapsed time. Or {@link
|
||||
* com.google.android.exoplayer2.C#TIME_UNSET} if unknown.
|
||||
* @param enableEventMessageTrack Whether to output an event message track.
|
||||
* @param closedCaptionFormats The {@link Format Formats} of closed caption tracks to be output.
|
||||
* @param transferListener The transfer listener which should be informed of any data transfers.
|
||||
|
|
|
|||
|
|
@ -621,6 +621,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
periodsById = new SparseArray<>();
|
||||
playerEmsgCallback = new DefaultPlayerEmsgCallback();
|
||||
expiredManifestPublishTimeUs = C.TIME_UNSET;
|
||||
elapsedRealtimeOffsetMs = C.TIME_UNSET;
|
||||
if (sideloadedManifest) {
|
||||
Assertions.checkState(!manifest.dynamic);
|
||||
manifestCallback = null;
|
||||
|
|
@ -723,7 +724,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
handler.removeCallbacksAndMessages(null);
|
||||
handler = null;
|
||||
}
|
||||
elapsedRealtimeOffsetMs = 0;
|
||||
elapsedRealtimeOffsetMs = C.TIME_UNSET;
|
||||
staleManifestReloadAttempt = 0;
|
||||
expiredManifestPublishTimeUs = C.TIME_UNSET;
|
||||
firstPeriodId = 0;
|
||||
|
|
@ -969,7 +970,8 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) {
|
||||
// The manifest describes an incomplete live stream. Update the start/end times to reflect the
|
||||
// live stream duration and the manifest's time shift buffer depth.
|
||||
long liveStreamDurationUs = getNowUnixTimeUs() - C.msToUs(manifest.availabilityStartTimeMs);
|
||||
long nowUnixTimeUs = C.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
|
||||
long liveStreamDurationUs = nowUnixTimeUs - C.msToUs(manifest.availabilityStartTimeMs);
|
||||
long liveStreamEndPositionInLastPeriodUs = liveStreamDurationUs
|
||||
- C.msToUs(manifest.getPeriod(lastPeriodIndex).startMs);
|
||||
currentEndTimeUs = Math.min(liveStreamEndPositionInLastPeriodUs, currentEndTimeUs);
|
||||
|
|
@ -1022,6 +1024,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
new DashTimeline(
|
||||
manifest.availabilityStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
elapsedRealtimeOffsetMs,
|
||||
firstPeriodId,
|
||||
currentStartTimeUs,
|
||||
windowDurationUs,
|
||||
|
|
@ -1093,14 +1096,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
manifestEventDispatcher.loadStarted(loadable.dataSpec, loadable.type, elapsedRealtimeMs);
|
||||
}
|
||||
|
||||
private long getNowUnixTimeUs() {
|
||||
if (elapsedRealtimeOffsetMs != 0) {
|
||||
return C.msToUs(SystemClock.elapsedRealtime() + elapsedRealtimeOffsetMs);
|
||||
} else {
|
||||
return C.msToUs(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PeriodSeekInfo {
|
||||
|
||||
public static PeriodSeekInfo createPeriodSeekInfo(
|
||||
|
|
@ -1170,6 +1165,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
|
||||
private final long presentationStartTimeMs;
|
||||
private final long windowStartTimeMs;
|
||||
private final long elapsedRealtimeEpochOffsetMs;
|
||||
|
||||
private final int firstPeriodId;
|
||||
private final long offsetInFirstPeriodUs;
|
||||
|
|
@ -1181,6 +1177,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
public DashTimeline(
|
||||
long presentationStartTimeMs,
|
||||
long windowStartTimeMs,
|
||||
long elapsedRealtimeEpochOffsetMs,
|
||||
int firstPeriodId,
|
||||
long offsetInFirstPeriodUs,
|
||||
long windowDurationUs,
|
||||
|
|
@ -1189,6 +1186,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
@Nullable Object windowTag) {
|
||||
this.presentationStartTimeMs = presentationStartTimeMs;
|
||||
this.windowStartTimeMs = windowStartTimeMs;
|
||||
this.elapsedRealtimeEpochOffsetMs = elapsedRealtimeEpochOffsetMs;
|
||||
this.firstPeriodId = firstPeriodId;
|
||||
this.offsetInFirstPeriodUs = offsetInFirstPeriodUs;
|
||||
this.windowDurationUs = windowDurationUs;
|
||||
|
|
@ -1228,6 +1226,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
manifest,
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
elapsedRealtimeEpochOffsetMs,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ isMovingLiveWindow(manifest),
|
||||
/* isLive= */ manifest.dynamic,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between
|
||||
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified
|
||||
* as the server's unix time minus the local elapsed time. If unknown, set to 0.
|
||||
* as the server's unix time minus the local elapsed time. Or {@link C#TIME_UNSET} if unknown.
|
||||
* @param maxSegmentsPerLoad The maximum number of segments to combine into a single request. Note
|
||||
* that segments will only be combined if their {@link Uri}s are the same and if their data
|
||||
* ranges are adjacent.
|
||||
|
|
@ -267,7 +267,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
return;
|
||||
}
|
||||
|
||||
long nowUnixTimeUs = getNowUnixTimeUs();
|
||||
long nowUnixTimeUs = C.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
|
||||
MediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
|
||||
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
|
||||
for (int i = 0; i < chunkIterators.length; i++) {
|
||||
|
|
@ -474,14 +474,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
? representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum) : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private long getNowUnixTimeUs() {
|
||||
if (elapsedRealtimeOffsetMs != 0) {
|
||||
return (SystemClock.elapsedRealtime() + elapsedRealtimeOffsetMs) * 1000;
|
||||
} else {
|
||||
return System.currentTimeMillis() * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {
|
||||
boolean resolveTimeToLiveEdgePossible = manifest.dynamic && liveEdgeTimeUs != C.TIME_UNSET;
|
||||
return resolveTimeToLiveEdgePossible ? liveEdgeTimeUs - playbackPositionUs : C.TIME_UNSET;
|
||||
|
|
|
|||
|
|
@ -468,6 +468,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||
new SinglePeriodTimeline(
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
periodDurationUs,
|
||||
/* windowDurationUs= */ playlist.durationUs,
|
||||
/* windowPositionInPeriodUs= */ offsetFromInitialStartTimeUs,
|
||||
|
|
@ -485,6 +486,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||
new SinglePeriodTimeline(
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
/* periodDurationUs= */ playlist.durationUs,
|
||||
/* windowDurationUs= */ playlist.durationUs,
|
||||
/* windowPositionInPeriodUs= */ 0,
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ public final class FakeTimeline extends Timeline {
|
|||
manifests[windowIndex],
|
||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
windowDefinition.isSeekable,
|
||||
windowDefinition.isDynamic,
|
||||
windowDefinition.isLive,
|
||||
|
|
|
|||
Loading…
Reference in a new issue