mirror of
https://github.com/samsonjs/media.git
synced 2026-04-01 10:35:48 +00:00
Ensure implicit manifest updates arrives asap
PiperOrigin-RevId: 333714978
This commit is contained in:
parent
397fe8f305
commit
c107017a4b
8 changed files with 287 additions and 151 deletions
|
|
@ -50,6 +50,8 @@ import com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCal
|
|||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
|
@ -68,10 +70,12 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||
import com.google.android.exoplayer2.util.SntpClient;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.math.LongMath;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
|
|
@ -441,7 +445,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
* MediaSourceCaller#onSourceInfoRefreshed(MediaSource, Timeline)} when the source's {@link
|
||||
* Timeline} is changing dynamically (for example, for incomplete live streams).
|
||||
*/
|
||||
private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000;
|
||||
private static final long DEFAULT_NOTIFY_MANIFEST_INTERVAL_MS = 5000;
|
||||
/**
|
||||
* The minimum default start position for live streams, relative to the start of the live window.
|
||||
*/
|
||||
|
|
@ -1106,7 +1110,10 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
handler.removeCallbacks(simulateManifestRefreshRunnable);
|
||||
// If the window is changing implicitly, post a simulated manifest refresh to update it.
|
||||
if (windowChangingImplicitly) {
|
||||
handler.postDelayed(simulateManifestRefreshRunnable, NOTIFY_MANIFEST_INTERVAL_MS);
|
||||
handler.postDelayed(
|
||||
simulateManifestRefreshRunnable,
|
||||
getIntervalUntilNextManifestRefreshMs(
|
||||
manifest, Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs)));
|
||||
}
|
||||
if (manifestLoadPending) {
|
||||
startLoadingManifest();
|
||||
|
|
@ -1165,6 +1172,38 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
loadable.type);
|
||||
}
|
||||
|
||||
private static long getIntervalUntilNextManifestRefreshMs(
|
||||
DashManifest manifest, long nowUnixTimeMs) {
|
||||
int periodIndex = manifest.getPeriodCount() - 1;
|
||||
Period period = manifest.getPeriod(periodIndex);
|
||||
long periodStartUs = C.msToUs(period.startMs);
|
||||
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
|
||||
long nowUnixTimeUs = C.msToUs(nowUnixTimeMs);
|
||||
long availabilityStartTimeUs = C.msToUs(manifest.availabilityStartTimeMs);
|
||||
long intervalUs = C.msToUs(DEFAULT_NOTIFY_MANIFEST_INTERVAL_MS);
|
||||
for (int i = 0; i < period.adaptationSets.size(); i++) {
|
||||
List<Representation> representations = period.adaptationSets.get(i).representations;
|
||||
if (representations.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@Nullable DashSegmentIndex index = representations.get(0).getIndex();
|
||||
if (index != null) {
|
||||
long nextSegmentShiftUnixTimeUs =
|
||||
availabilityStartTimeUs
|
||||
+ periodStartUs
|
||||
+ index.getNextSegmentAvailableTimeUs(periodDurationUs, nowUnixTimeUs);
|
||||
long requiredIntervalUs = nextSegmentShiftUnixTimeUs - nowUnixTimeUs;
|
||||
// Avoid multiple refreshes within a very small amount of time.
|
||||
if (requiredIntervalUs < intervalUs - 100_000
|
||||
|| (requiredIntervalUs > intervalUs && requiredIntervalUs < intervalUs + 100_000)) {
|
||||
intervalUs = requiredIntervalUs;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Round up to compensate for a potential loss in the us to ms conversion.
|
||||
return LongMath.divide(intervalUs, 1000, RoundingMode.CEILING);
|
||||
}
|
||||
|
||||
private static final class PeriodSeekInfo {
|
||||
|
||||
public static PeriodSeekInfo createPeriodSeekInfo(
|
||||
|
|
|
|||
|
|
@ -101,6 +101,18 @@ public interface DashSegmentIndex {
|
|||
*/
|
||||
int getAvailableSegmentCount(long periodDurationUs, long nowUnixTimeUs);
|
||||
|
||||
/**
|
||||
* Returns the time, in microseconds, at which a new segment becomes available, or {@link
|
||||
* C#TIME_UNSET} if not applicable.
|
||||
*
|
||||
* @param periodDurationUs The duration of the enclosing period in microseconds, or {@link
|
||||
* C#TIME_UNSET} if the period's duration is not yet known.
|
||||
* @param nowUnixTimeUs The current time in milliseconds since the Unix epoch.
|
||||
* @return The time, in microseconds, at which a new segment becomes available, or {@link
|
||||
* C#TIME_UNSET} if not applicable.
|
||||
*/
|
||||
long getNextSegmentAvailableTimeUs(long periodDurationUs, long nowUnixTimeUs);
|
||||
|
||||
/**
|
||||
* Returns true if segments are defined explicitly by the index.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
|
||||
|
||||
|
|
@ -56,6 +57,11 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
|
|||
return chunkIndex.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSegmentAvailableTimeUs(long periodDurationUs, long nowUnixTimeUs) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeUs(long segmentNum) {
|
||||
return chunkIndex.timesUs[(int) segmentNum] - timeOffsetUs;
|
||||
|
|
|
|||
|
|
@ -291,9 +291,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
parseSegmentList(
|
||||
xpp,
|
||||
/* parent= */ null,
|
||||
periodStartUnixTimeMs,
|
||||
durationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBaseAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, /* parentAvailabilityTimeOffsetUs= */ C.TIME_UNSET);
|
||||
|
|
@ -302,9 +304,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
xpp,
|
||||
/* parent= */ null,
|
||||
ImmutableList.of(),
|
||||
periodStartUnixTimeMs,
|
||||
durationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AssetIdentifier")) {
|
||||
assetIdentifier = parseDescriptor(xpp, "AssetIdentifier");
|
||||
} else {
|
||||
|
|
@ -407,9 +411,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
essentialProperties,
|
||||
supplementalProperties,
|
||||
segmentBase,
|
||||
periodStartUnixTimeMs,
|
||||
periodDurationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
contentType =
|
||||
checkContentTypeConsistency(
|
||||
contentType, MimeTypes.getTrackType(representationInfo.format.sampleMimeType));
|
||||
|
|
@ -423,9 +429,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
parseSegmentList(
|
||||
xpp,
|
||||
(SegmentList) segmentBase,
|
||||
periodStartUnixTimeMs,
|
||||
periodDurationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBaseAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, segmentBaseAvailabilityTimeOffsetUs);
|
||||
|
|
@ -434,9 +442,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
xpp,
|
||||
(SegmentTemplate) segmentBase,
|
||||
supplementalProperties,
|
||||
periodStartUnixTimeMs,
|
||||
periodDurationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) {
|
||||
inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream"));
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "Label")) {
|
||||
|
|
@ -455,9 +465,7 @@ public class DashManifestParser extends DefaultHandler
|
|||
label,
|
||||
drmSchemeType,
|
||||
drmSchemeDatas,
|
||||
inbandEventStreams,
|
||||
periodStartUnixTimeMs,
|
||||
timeShiftBufferDepthMs));
|
||||
inbandEventStreams));
|
||||
}
|
||||
|
||||
return buildAdaptationSet(
|
||||
|
|
@ -599,9 +607,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
List<Descriptor> adaptationSetEssentialProperties,
|
||||
List<Descriptor> adaptationSetSupplementalProperties,
|
||||
@Nullable SegmentBase segmentBase,
|
||||
long periodStartUnixTimeMs,
|
||||
long periodDurationMs,
|
||||
long baseUrlAvailabilityTimeOffsetUs,
|
||||
long segmentBaseAvailabilityTimeOffsetUs)
|
||||
long segmentBaseAvailabilityTimeOffsetUs,
|
||||
long timeShiftBufferDepthMs)
|
||||
throws XmlPullParserException, IOException {
|
||||
String id = xpp.getAttributeValue(null, "id");
|
||||
int bandwidth = parseInt(xpp, "bandwidth", Format.NO_VALUE);
|
||||
|
|
@ -641,9 +651,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
parseSegmentList(
|
||||
xpp,
|
||||
(SegmentList) segmentBase,
|
||||
periodStartUnixTimeMs,
|
||||
periodDurationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBaseAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, segmentBaseAvailabilityTimeOffsetUs);
|
||||
|
|
@ -652,9 +664,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
xpp,
|
||||
(SegmentTemplate) segmentBase,
|
||||
adaptationSetSupplementalProperties,
|
||||
periodStartUnixTimeMs,
|
||||
periodDurationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
segmentBaseAvailabilityTimeOffsetUs);
|
||||
segmentBaseAvailabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthMs);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
Pair<String, SchemeData> contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection.first != null) {
|
||||
|
|
@ -754,9 +768,7 @@ public class DashManifestParser extends DefaultHandler
|
|||
@Nullable String label,
|
||||
@Nullable String extraDrmSchemeType,
|
||||
ArrayList<SchemeData> extraDrmSchemeDatas,
|
||||
ArrayList<Descriptor> extraInbandEventStreams,
|
||||
long periodStartUnixTimeMs,
|
||||
long timeShiftBufferDepthMs) {
|
||||
ArrayList<Descriptor> extraInbandEventStreams) {
|
||||
Format.Builder formatBuilder = representationInfo.format.buildUpon();
|
||||
if (label != null) {
|
||||
formatBuilder.setLabel(label);
|
||||
|
|
@ -778,9 +790,7 @@ public class DashManifestParser extends DefaultHandler
|
|||
formatBuilder.build(),
|
||||
representationInfo.baseUrl,
|
||||
representationInfo.segmentBase,
|
||||
inbandEventStreams,
|
||||
periodStartUnixTimeMs,
|
||||
timeShiftBufferDepthMs);
|
||||
inbandEventStreams);
|
||||
}
|
||||
|
||||
// SegmentBase, SegmentList and SegmentTemplate parsing.
|
||||
|
|
@ -825,9 +835,11 @@ public class DashManifestParser extends DefaultHandler
|
|||
protected SegmentList parseSegmentList(
|
||||
XmlPullParser xpp,
|
||||
@Nullable SegmentList parent,
|
||||
long periodStartUnixTimeMs,
|
||||
long periodDurationMs,
|
||||
long baseUrlAvailabilityTimeOffsetUs,
|
||||
long segmentBaseAvailabilityTimeOffsetUs)
|
||||
long segmentBaseAvailabilityTimeOffsetUs,
|
||||
long timeShiftBufferDepthMs)
|
||||
throws XmlPullParserException, IOException {
|
||||
|
||||
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
|
||||
|
|
@ -873,7 +885,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
duration,
|
||||
timeline,
|
||||
availabilityTimeOffsetUs,
|
||||
segments);
|
||||
segments,
|
||||
timeShiftBufferDepthMs,
|
||||
periodStartUnixTimeMs);
|
||||
}
|
||||
|
||||
protected SegmentList buildSegmentList(
|
||||
|
|
@ -884,7 +898,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
long duration,
|
||||
@Nullable List<SegmentTimelineElement> timeline,
|
||||
long availabilityTimeOffsetUs,
|
||||
@Nullable List<RangedUri> segments) {
|
||||
@Nullable List<RangedUri> segments,
|
||||
long timeShiftBufferDepthMs,
|
||||
long periodStartUnixTimeMs) {
|
||||
return new SegmentList(
|
||||
initialization,
|
||||
timescale,
|
||||
|
|
@ -893,16 +909,20 @@ public class DashManifestParser extends DefaultHandler
|
|||
duration,
|
||||
timeline,
|
||||
availabilityTimeOffsetUs,
|
||||
segments);
|
||||
segments,
|
||||
C.msToUs(timeShiftBufferDepthMs),
|
||||
C.msToUs(periodStartUnixTimeMs));
|
||||
}
|
||||
|
||||
protected SegmentTemplate parseSegmentTemplate(
|
||||
XmlPullParser xpp,
|
||||
@Nullable SegmentTemplate parent,
|
||||
List<Descriptor> adaptationSetSupplementalProperties,
|
||||
long periodStartUnixTimeMs,
|
||||
long periodDurationMs,
|
||||
long baseUrlAvailabilityTimeOffsetUs,
|
||||
long segmentBaseAvailabilityTimeOffsetUs)
|
||||
long segmentBaseAvailabilityTimeOffsetUs,
|
||||
long timeShiftBufferDepthMs)
|
||||
throws XmlPullParserException, IOException {
|
||||
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
|
||||
long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset",
|
||||
|
|
@ -949,7 +969,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
timeline,
|
||||
availabilityTimeOffsetUs,
|
||||
initializationTemplate,
|
||||
mediaTemplate);
|
||||
mediaTemplate,
|
||||
timeShiftBufferDepthMs,
|
||||
periodStartUnixTimeMs);
|
||||
}
|
||||
|
||||
protected SegmentTemplate buildSegmentTemplate(
|
||||
|
|
@ -962,7 +984,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
List<SegmentTimelineElement> timeline,
|
||||
long availabilityTimeOffsetUs,
|
||||
@Nullable UrlTemplate initializationTemplate,
|
||||
@Nullable UrlTemplate mediaTemplate) {
|
||||
@Nullable UrlTemplate mediaTemplate,
|
||||
long timeShiftBufferDepthMs,
|
||||
long periodStartUnixTimeMs) {
|
||||
return new SegmentTemplate(
|
||||
initialization,
|
||||
timescale,
|
||||
|
|
@ -973,7 +997,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
timeline,
|
||||
availabilityTimeOffsetUs,
|
||||
initializationTemplate,
|
||||
mediaTemplate);
|
||||
mediaTemplate,
|
||||
C.msToUs(timeShiftBufferDepthMs),
|
||||
C.msToUs(periodStartUnixTimeMs));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash.manifest;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
|
@ -73,14 +71,7 @@ public abstract class Representation {
|
|||
*/
|
||||
public static Representation newInstance(
|
||||
long revisionId, Format format, String baseUrl, SegmentBase segmentBase) {
|
||||
return newInstance(
|
||||
revisionId,
|
||||
format,
|
||||
baseUrl,
|
||||
segmentBase,
|
||||
/* inbandEventStreams= */ null,
|
||||
/* periodStartUnixTimeMs= */ C.TIME_UNSET,
|
||||
/* timeShiftBufferDepthMs= */ C.TIME_UNSET);
|
||||
return newInstance(revisionId, format, baseUrl, segmentBase, /* inbandEventStreams= */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,9 +82,6 @@ public abstract class Representation {
|
|||
* @param baseUrl The base URL.
|
||||
* @param segmentBase A segment base element for the representation.
|
||||
* @param inbandEventStreams The in-band event streams in the representation. May be null.
|
||||
* @param periodStartUnixTimeMs The start time of the enclosing {@link Period} in milliseconds
|
||||
* since the Unix epoch, or {@link C#TIME_UNSET} is not applicable.
|
||||
* @param timeShiftBufferDepthMs The {@link DashManifest#timeShiftBufferDepthMs}.
|
||||
* @return The constructed instance.
|
||||
*/
|
||||
public static Representation newInstance(
|
||||
|
|
@ -101,17 +89,13 @@ public abstract class Representation {
|
|||
Format format,
|
||||
String baseUrl,
|
||||
SegmentBase segmentBase,
|
||||
@Nullable List<Descriptor> inbandEventStreams,
|
||||
long periodStartUnixTimeMs,
|
||||
long timeShiftBufferDepthMs) {
|
||||
@Nullable List<Descriptor> inbandEventStreams) {
|
||||
return newInstance(
|
||||
revisionId,
|
||||
format,
|
||||
baseUrl,
|
||||
segmentBase,
|
||||
inbandEventStreams,
|
||||
periodStartUnixTimeMs,
|
||||
timeShiftBufferDepthMs,
|
||||
/* cacheKey= */ null);
|
||||
}
|
||||
|
||||
|
|
@ -123,9 +107,6 @@ public abstract class Representation {
|
|||
* @param baseUrl The base URL of the representation.
|
||||
* @param segmentBase A segment base element for the representation.
|
||||
* @param inbandEventStreams The in-band event streams in the representation. May be null.
|
||||
* @param periodStartUnixTimeMs The start time of the enclosing {@link Period} in milliseconds
|
||||
* since the Unix epoch, or {@link C#TIME_UNSET} is not applicable.
|
||||
* @param timeShiftBufferDepthMs The {@link DashManifest#timeShiftBufferDepthMs}.
|
||||
* @param cacheKey An optional key to be returned from {@link #getCacheKey()}, or null. This
|
||||
* parameter is ignored if {@code segmentBase} consists of multiple segments.
|
||||
* @return The constructed instance.
|
||||
|
|
@ -136,8 +117,6 @@ public abstract class Representation {
|
|||
String baseUrl,
|
||||
SegmentBase segmentBase,
|
||||
@Nullable List<Descriptor> inbandEventStreams,
|
||||
long periodStartUnixTimeMs,
|
||||
long timeShiftBufferDepthMs,
|
||||
@Nullable String cacheKey) {
|
||||
if (segmentBase instanceof SingleSegmentBase) {
|
||||
return new SingleSegmentRepresentation(
|
||||
|
|
@ -150,13 +129,7 @@ public abstract class Representation {
|
|||
C.LENGTH_UNSET);
|
||||
} else if (segmentBase instanceof MultiSegmentBase) {
|
||||
return new MultiSegmentRepresentation(
|
||||
revisionId,
|
||||
format,
|
||||
baseUrl,
|
||||
(MultiSegmentBase) segmentBase,
|
||||
inbandEventStreams,
|
||||
periodStartUnixTimeMs,
|
||||
timeShiftBufferDepthMs);
|
||||
revisionId, format, baseUrl, (MultiSegmentBase) segmentBase, inbandEventStreams);
|
||||
} else {
|
||||
throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or "
|
||||
+ "MultiSegmentBase");
|
||||
|
|
@ -309,8 +282,6 @@ public abstract class Representation {
|
|||
implements DashSegmentIndex {
|
||||
|
||||
@VisibleForTesting /* package */ final MultiSegmentBase segmentBase;
|
||||
private final long periodStartUnixTimeUs;
|
||||
private final long timeShiftBufferDepthUs;
|
||||
|
||||
/**
|
||||
* Creates the multi-segment Representation.
|
||||
|
|
@ -320,22 +291,15 @@ public abstract class Representation {
|
|||
* @param baseUrl The base URL of the representation.
|
||||
* @param segmentBase The segment base underlying the representation.
|
||||
* @param inbandEventStreams The in-band event streams in the representation. May be null.
|
||||
* @param periodStartUnixTimeMs The start time of the enclosing {@link Period} in milliseconds
|
||||
* since the Unix epoch, or {@link C#TIME_UNSET} is not applicable.
|
||||
* @param timeShiftBufferDepthMs The {@link DashManifest#timeShiftBufferDepthMs}.
|
||||
*/
|
||||
public MultiSegmentRepresentation(
|
||||
long revisionId,
|
||||
Format format,
|
||||
String baseUrl,
|
||||
MultiSegmentBase segmentBase,
|
||||
@Nullable List<Descriptor> inbandEventStreams,
|
||||
long periodStartUnixTimeMs,
|
||||
long timeShiftBufferDepthMs) {
|
||||
@Nullable List<Descriptor> inbandEventStreams) {
|
||||
super(revisionId, format, baseUrl, segmentBase, inbandEventStreams);
|
||||
this.segmentBase = segmentBase;
|
||||
this.periodStartUnixTimeUs = C.msToUs(periodStartUnixTimeMs);
|
||||
this.timeShiftBufferDepthUs = C.msToUs(timeShiftBufferDepthMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -384,17 +348,7 @@ public abstract class Representation {
|
|||
|
||||
@Override
|
||||
public long getFirstAvailableSegmentNum(long periodDurationUs, long nowUnixTimeUs) {
|
||||
long segmentCount = segmentBase.getSegmentCount(periodDurationUs);
|
||||
if (segmentCount != INDEX_UNBOUNDED || timeShiftBufferDepthUs == C.TIME_UNSET) {
|
||||
return segmentBase.getFirstSegmentNum();
|
||||
}
|
||||
// The index is itself unbounded. We need to use the current time to calculate the range of
|
||||
// available segments.
|
||||
long liveEdgeTimeInPeriodUs = nowUnixTimeUs - periodStartUnixTimeUs;
|
||||
long timeShiftBufferStartInPeriodUs = liveEdgeTimeInPeriodUs - timeShiftBufferDepthUs;
|
||||
long timeShiftBufferStartSegmentNum =
|
||||
getSegmentNum(timeShiftBufferStartInPeriodUs, periodDurationUs);
|
||||
return max(getFirstSegmentNum(), timeShiftBufferStartSegmentNum);
|
||||
return segmentBase.getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -404,18 +358,12 @@ public abstract class Representation {
|
|||
|
||||
@Override
|
||||
public int getAvailableSegmentCount(long periodDurationUs, long nowUnixTimeUs) {
|
||||
int segmentCount = segmentBase.getSegmentCount(periodDurationUs);
|
||||
if (segmentCount != INDEX_UNBOUNDED) {
|
||||
return segmentCount;
|
||||
}
|
||||
// The index is itself unbounded. We need to use the current time to calculate the range of
|
||||
// available segments.
|
||||
long liveEdgeTimeInPeriodUs = nowUnixTimeUs - periodStartUnixTimeUs;
|
||||
long availabilityEndTimeUs = liveEdgeTimeInPeriodUs + segmentBase.availabilityTimeOffsetUs;
|
||||
// getSegmentNum(availabilityEndTimeUs) will not be completed yet.
|
||||
long firstIncompleteSegmentNum = getSegmentNum(availabilityEndTimeUs, periodDurationUs);
|
||||
long firstAvailableSegmentNum = getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs);
|
||||
return (int) (firstIncompleteSegmentNum - firstAvailableSegmentNum);
|
||||
return segmentBase.getAvailableSegmentCount(periodDurationUs, nowUnixTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSegmentAvailableTimeUs(long periodDurationUs, long nowUnixTimeUs) {
|
||||
return segmentBase.getNextSegmentAvailableTimeUs(periodDurationUs, nowUnixTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,9 +15,12 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash.manifest;
|
||||
|
||||
import static com.google.android.exoplayer2.source.dash.DashSegmentIndex.INDEX_UNBOUNDED;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -119,6 +122,8 @@ public abstract class SegmentBase {
|
|||
/* package */ final long startNumber;
|
||||
/* package */ final long duration;
|
||||
@Nullable /* package */ final List<SegmentTimelineElement> segmentTimeline;
|
||||
private final long timeShiftBufferDepthUs;
|
||||
private final long periodStartUnixTimeUs;
|
||||
|
||||
/**
|
||||
* Offset to the current realtime at which segments become available, in microseconds, or {@link
|
||||
|
|
@ -127,7 +132,7 @@ public abstract class SegmentBase {
|
|||
* <p>Segments will be available once their end time ≤ currentRealTime +
|
||||
* availabilityTimeOffset.
|
||||
*/
|
||||
/* package */ final long availabilityTimeOffsetUs;
|
||||
@VisibleForTesting /* package */ final long availabilityTimeOffsetUs;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
|
|
@ -144,6 +149,9 @@ public abstract class SegmentBase {
|
|||
* parameter.
|
||||
* @param availabilityTimeOffsetUs The offset to the current realtime at which segments become
|
||||
* available in microseconds, or {@link C#TIME_UNSET} if not applicable.
|
||||
* @param timeShiftBufferDepthUs The time shift buffer depth in microseconds.
|
||||
* @param periodStartUnixTimeUs The start of the enclosing period in microseconds since the Unix
|
||||
* epoch.
|
||||
*/
|
||||
public MultiSegmentBase(
|
||||
@Nullable RangedUri initialization,
|
||||
|
|
@ -152,15 +160,19 @@ public abstract class SegmentBase {
|
|||
long startNumber,
|
||||
long duration,
|
||||
@Nullable List<SegmentTimelineElement> segmentTimeline,
|
||||
long availabilityTimeOffsetUs) {
|
||||
long availabilityTimeOffsetUs,
|
||||
long timeShiftBufferDepthUs,
|
||||
long periodStartUnixTimeUs) {
|
||||
super(initialization, timescale, presentationTimeOffset);
|
||||
this.startNumber = startNumber;
|
||||
this.duration = duration;
|
||||
this.segmentTimeline = segmentTimeline;
|
||||
this.availabilityTimeOffsetUs = availabilityTimeOffsetUs;
|
||||
this.timeShiftBufferDepthUs = timeShiftBufferDepthUs;
|
||||
this.periodStartUnixTimeUs = periodStartUnixTimeUs;
|
||||
}
|
||||
|
||||
/** @see DashSegmentIndex#getSegmentNum(long, long) */
|
||||
/** See {@link DashSegmentIndex#getSegmentNum(long, long)}. */
|
||||
public long getSegmentNum(long timeUs, long periodDurationUs) {
|
||||
final long firstSegmentNum = getFirstSegmentNum();
|
||||
final long segmentCount = getSegmentCount(periodDurationUs);
|
||||
|
|
@ -174,7 +186,7 @@ public abstract class SegmentBase {
|
|||
// Ensure we stay within bounds.
|
||||
return segmentNum < firstSegmentNum
|
||||
? firstSegmentNum
|
||||
: segmentCount == DashSegmentIndex.INDEX_UNBOUNDED
|
||||
: segmentCount == INDEX_UNBOUNDED
|
||||
? segmentNum
|
||||
: min(segmentNum, firstSegmentNum + segmentCount - 1);
|
||||
} else {
|
||||
|
|
@ -196,21 +208,21 @@ public abstract class SegmentBase {
|
|||
}
|
||||
}
|
||||
|
||||
/** @see DashSegmentIndex#getDurationUs(long, long) */
|
||||
/** See {@link DashSegmentIndex#getDurationUs(long, long)}. */
|
||||
public final long getSegmentDurationUs(long sequenceNumber, long periodDurationUs) {
|
||||
if (segmentTimeline != null) {
|
||||
long duration = segmentTimeline.get((int) (sequenceNumber - startNumber)).duration;
|
||||
return (duration * C.MICROS_PER_SECOND) / timescale;
|
||||
} else {
|
||||
int segmentCount = getSegmentCount(periodDurationUs);
|
||||
return segmentCount != DashSegmentIndex.INDEX_UNBOUNDED
|
||||
&& sequenceNumber == (getFirstSegmentNum() + segmentCount - 1)
|
||||
return segmentCount != INDEX_UNBOUNDED
|
||||
&& sequenceNumber == (getFirstSegmentNum() + segmentCount - 1)
|
||||
? (periodDurationUs - getSegmentTimeUs(sequenceNumber))
|
||||
: ((duration * C.MICROS_PER_SECOND) / timescale);
|
||||
}
|
||||
}
|
||||
|
||||
/** @see DashSegmentIndex#getTimeUs(long) */
|
||||
/** See {@link DashSegmentIndex#getTimeUs(long)}. */
|
||||
public final long getSegmentTimeUs(long sequenceNumber) {
|
||||
long unscaledSegmentTime;
|
||||
if (segmentTimeline != null) {
|
||||
|
|
@ -227,27 +239,66 @@ public abstract class SegmentBase {
|
|||
* Returns a {@link RangedUri} defining the location of a segment for the given index in the
|
||||
* given representation.
|
||||
*
|
||||
* @see DashSegmentIndex#getSegmentUrl(long)
|
||||
* <p>See {@link DashSegmentIndex#getSegmentUrl(long)}.
|
||||
*/
|
||||
public abstract RangedUri getSegmentUrl(Representation representation, long index);
|
||||
|
||||
/** @see DashSegmentIndex#getFirstSegmentNum() */
|
||||
/** See {@link DashSegmentIndex#getFirstSegmentNum()}. */
|
||||
public long getFirstSegmentNum() {
|
||||
return startNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#getSegmentCount(long)
|
||||
*/
|
||||
public abstract int getSegmentCount(long periodDurationUs);
|
||||
/** See {@link DashSegmentIndex#getFirstAvailableSegmentNum(long, long)}. */
|
||||
public long getFirstAvailableSegmentNum(long periodDurationUs, long nowUnixTimeUs) {
|
||||
long segmentCount = getSegmentCount(periodDurationUs);
|
||||
if (segmentCount != INDEX_UNBOUNDED || timeShiftBufferDepthUs == C.TIME_UNSET) {
|
||||
return getFirstSegmentNum();
|
||||
}
|
||||
// The index is itself unbounded. We need to use the current time to calculate the range of
|
||||
// available segments.
|
||||
long liveEdgeTimeInPeriodUs = nowUnixTimeUs - periodStartUnixTimeUs;
|
||||
long timeShiftBufferStartInPeriodUs = liveEdgeTimeInPeriodUs - timeShiftBufferDepthUs;
|
||||
long timeShiftBufferStartSegmentNum =
|
||||
getSegmentNum(timeShiftBufferStartInPeriodUs, periodDurationUs);
|
||||
return max(getFirstSegmentNum(), timeShiftBufferStartSegmentNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DashSegmentIndex#isExplicit()
|
||||
*/
|
||||
/** See {@link DashSegmentIndex#getAvailableSegmentCount(long, long)}. */
|
||||
public int getAvailableSegmentCount(long periodDurationUs, long nowUnixTimeUs) {
|
||||
int segmentCount = getSegmentCount(periodDurationUs);
|
||||
if (segmentCount != INDEX_UNBOUNDED) {
|
||||
return segmentCount;
|
||||
}
|
||||
// The index is itself unbounded. We need to use the current time to calculate the range of
|
||||
// available segments.
|
||||
long liveEdgeTimeInPeriodUs = nowUnixTimeUs - periodStartUnixTimeUs;
|
||||
long availabilityTimeOffsetUs = liveEdgeTimeInPeriodUs + this.availabilityTimeOffsetUs;
|
||||
// getSegmentNum(availabilityTimeOffsetUs) will not be completed yet.
|
||||
long firstIncompleteSegmentNum = getSegmentNum(availabilityTimeOffsetUs, periodDurationUs);
|
||||
long firstAvailableSegmentNum = getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs);
|
||||
return (int) (firstIncompleteSegmentNum - firstAvailableSegmentNum);
|
||||
}
|
||||
|
||||
/** See {@link DashSegmentIndex#getNextSegmentAvailableTimeUs(long, long)}. */
|
||||
public long getNextSegmentAvailableTimeUs(long periodDurationUs, long nowUnixTimeUs) {
|
||||
if (segmentTimeline != null) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
long firstIncompleteSegmentNum =
|
||||
getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs)
|
||||
+ getAvailableSegmentCount(periodDurationUs, nowUnixTimeUs);
|
||||
return getSegmentTimeUs(firstIncompleteSegmentNum)
|
||||
+ getSegmentDurationUs(firstIncompleteSegmentNum, periodDurationUs)
|
||||
- availabilityTimeOffsetUs;
|
||||
}
|
||||
|
||||
/** See {@link DashSegmentIndex#isExplicit()} */
|
||||
public boolean isExplicit() {
|
||||
return segmentTimeline != null;
|
||||
}
|
||||
|
||||
/** See {@link DashSegmentIndex#getSegmentCount(long)}. */
|
||||
public abstract int getSegmentCount(long periodDurationUs);
|
||||
}
|
||||
|
||||
/** A {@link MultiSegmentBase} that uses a SegmentList to define its segments. */
|
||||
|
|
@ -271,6 +322,9 @@ public abstract class SegmentBase {
|
|||
* @param availabilityTimeOffsetUs The offset to the current realtime at which segments become
|
||||
* available in microseconds, or {@link C#TIME_UNSET} if not applicable.
|
||||
* @param mediaSegments A list of {@link RangedUri}s indicating the locations of the segments.
|
||||
* @param timeShiftBufferDepthUs The time shift buffer depth in microseconds.
|
||||
* @param periodStartUnixTimeUs The start of the enclosing period in microseconds since the Unix
|
||||
* epoch.
|
||||
*/
|
||||
public SegmentList(
|
||||
RangedUri initialization,
|
||||
|
|
@ -280,7 +334,9 @@ public abstract class SegmentBase {
|
|||
long duration,
|
||||
@Nullable List<SegmentTimelineElement> segmentTimeline,
|
||||
long availabilityTimeOffsetUs,
|
||||
@Nullable List<RangedUri> mediaSegments) {
|
||||
@Nullable List<RangedUri> mediaSegments,
|
||||
long timeShiftBufferDepthUs,
|
||||
long periodStartUnixTimeUs) {
|
||||
super(
|
||||
initialization,
|
||||
timescale,
|
||||
|
|
@ -288,7 +344,9 @@ public abstract class SegmentBase {
|
|||
startNumber,
|
||||
duration,
|
||||
segmentTimeline,
|
||||
availabilityTimeOffsetUs);
|
||||
availabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthUs,
|
||||
periodStartUnixTimeUs);
|
||||
this.mediaSegments = mediaSegments;
|
||||
}
|
||||
|
||||
|
|
@ -339,6 +397,9 @@ public abstract class SegmentBase {
|
|||
* such data exists. If non-null then the {@code initialization} parameter is ignored. If
|
||||
* null then {@code initialization} will be used.
|
||||
* @param mediaTemplate A template defining the location of each media segment.
|
||||
* @param timeShiftBufferDepthUs The time shift buffer depth in microseconds.
|
||||
* @param periodStartUnixTimeUs The start of the enclosing period in microseconds since the Unix
|
||||
* epoch.
|
||||
*/
|
||||
public SegmentTemplate(
|
||||
RangedUri initialization,
|
||||
|
|
@ -350,7 +411,9 @@ public abstract class SegmentBase {
|
|||
@Nullable List<SegmentTimelineElement> segmentTimeline,
|
||||
long availabilityTimeOffsetUs,
|
||||
@Nullable UrlTemplate initializationTemplate,
|
||||
@Nullable UrlTemplate mediaTemplate) {
|
||||
@Nullable UrlTemplate mediaTemplate,
|
||||
long timeShiftBufferDepthUs,
|
||||
long periodStartUnixTimeUs) {
|
||||
super(
|
||||
initialization,
|
||||
timescale,
|
||||
|
|
@ -358,7 +421,9 @@ public abstract class SegmentBase {
|
|||
startNumber,
|
||||
duration,
|
||||
segmentTimeline,
|
||||
availabilityTimeOffsetUs);
|
||||
availabilityTimeOffsetUs,
|
||||
timeShiftBufferDepthUs,
|
||||
periodStartUnixTimeUs);
|
||||
this.initializationTemplate = initializationTemplate;
|
||||
this.mediaTemplate = mediaTemplate;
|
||||
this.endNumber = endNumber;
|
||||
|
|
@ -399,7 +464,7 @@ public abstract class SegmentBase {
|
|||
long durationUs = (duration * C.MICROS_PER_SECOND) / timescale;
|
||||
return (int) Util.ceilDivide(periodDurationUs, durationUs);
|
||||
} else {
|
||||
return DashSegmentIndex.INDEX_UNBOUNDED;
|
||||
return INDEX_UNBOUNDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash.manifest;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
|
||||
|
||||
/**
|
||||
|
|
@ -71,6 +72,11 @@ import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
|
|||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSegmentAvailableTimeUs(long periodDurationUs, long nowUnixTimeUs) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicit() {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -19,16 +19,15 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link Representation}. */
|
||||
/** Unit test for {@link SegmentBase}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class RepresentationTest {
|
||||
public final class SegmentBaseTest {
|
||||
|
||||
@Test
|
||||
public void getFirstAvailableSegmentNum_multiSegmentRepresentationWithUnboundedTemplate() {
|
||||
public void getFirstAvailableSegmentNum_unboundedSegmentTemplate() {
|
||||
long periodStartUnixTimeUs = 123_000_000_000_000L;
|
||||
SegmentBase.SegmentTemplate segmentTemplate =
|
||||
new SegmentBase.SegmentTemplate(
|
||||
|
|
@ -41,50 +40,43 @@ public final class RepresentationTest {
|
|||
/* segmentTimeline= */ null,
|
||||
/* availabilityTimeOffsetUs= */ 500_000,
|
||||
/* initializationTemplate= */ null,
|
||||
/* mediaTemplate= */ null);
|
||||
Representation.MultiSegmentRepresentation representation =
|
||||
new Representation.MultiSegmentRepresentation(
|
||||
/* revisionId= */ 0,
|
||||
new Format.Builder().build(),
|
||||
/* baseUrl= */ "https://baseUrl/",
|
||||
segmentTemplate,
|
||||
/* inbandEventStreams= */ null,
|
||||
/* periodStartUnixTimeMs= */ C.usToMs(periodStartUnixTimeUs),
|
||||
/* timeShiftBufferDepthMs= */ 6_000);
|
||||
/* mediaTemplate= */ null,
|
||||
/* timeShiftBufferDepthUs= */ 6_000_000,
|
||||
/* periodStartUnixTimeUs= */ periodStartUnixTimeUs);
|
||||
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs - 10_000_000))
|
||||
.isEqualTo(42);
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET, /* nowUnixTimeUs= */ periodStartUnixTimeUs))
|
||||
.isEqualTo(42);
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 7_999_999))
|
||||
.isEqualTo(42);
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 8_000_000))
|
||||
.isEqualTo(43);
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 9_999_999))
|
||||
.isEqualTo(43);
|
||||
assertThat(
|
||||
representation.getFirstAvailableSegmentNum(
|
||||
segmentTemplate.getFirstAvailableSegmentNum(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 10_000_000))
|
||||
.isEqualTo(44);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableSegmentCount_multiSegmentRepresentationWithUnboundedTemplate() {
|
||||
public void getAvailableSegmentCount_unboundedSegmentTemplate() {
|
||||
long periodStartUnixTimeUs = 123_000_000_000_000L;
|
||||
SegmentBase.SegmentTemplate segmentTemplate =
|
||||
new SegmentBase.SegmentTemplate(
|
||||
|
|
@ -97,55 +89,97 @@ public final class RepresentationTest {
|
|||
/* segmentTimeline= */ null,
|
||||
/* availabilityTimeOffsetUs= */ 500_000,
|
||||
/* initializationTemplate= */ null,
|
||||
/* mediaTemplate= */ null);
|
||||
Representation.MultiSegmentRepresentation representation =
|
||||
new Representation.MultiSegmentRepresentation(
|
||||
/* revisionId= */ 0,
|
||||
new Format.Builder().build(),
|
||||
/* baseUrl= */ "https://baseUrl/",
|
||||
segmentTemplate,
|
||||
/* inbandEventStreams= */ null,
|
||||
/* periodStartUnixTimeMs= */ C.usToMs(periodStartUnixTimeUs),
|
||||
/* timeShiftBufferDepthMs= */ 6_000);
|
||||
/* mediaTemplate= */ null,
|
||||
/* timeShiftBufferDepthUs= */ 6_000_000,
|
||||
/* periodStartUnixTimeUs= */ periodStartUnixTimeUs);
|
||||
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs - 10_000_000))
|
||||
.isEqualTo(0);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET, /* nowUnixTimeUs= */ periodStartUnixTimeUs))
|
||||
.isEqualTo(0);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 1_499_999))
|
||||
.isEqualTo(0);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 1_500_000))
|
||||
.isEqualTo(1);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 7_499_999))
|
||||
.isEqualTo(3);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 7_500_000))
|
||||
.isEqualTo(4);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 7_999_999))
|
||||
.isEqualTo(4);
|
||||
assertThat(
|
||||
representation.getAvailableSegmentCount(
|
||||
segmentTemplate.getAvailableSegmentCount(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 8_000_000))
|
||||
.isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNextSegmentShiftTimeUse_unboundedSegmentTemplate() {
|
||||
long periodStartUnixTimeUs = 123_000_000_000_000L;
|
||||
SegmentBase.SegmentTemplate segmentTemplate =
|
||||
new SegmentBase.SegmentTemplate(
|
||||
/* initialization= */ null,
|
||||
/* timescale= */ 1000,
|
||||
/* presentationTimeOffset= */ 0,
|
||||
/* startNumber= */ 42,
|
||||
/* endNumber= */ C.INDEX_UNSET,
|
||||
/* duration= */ 2000,
|
||||
/* segmentTimeline= */ null,
|
||||
/* availabilityTimeOffsetUs= */ 500_000,
|
||||
/* initializationTemplate= */ null,
|
||||
/* mediaTemplate= */ null,
|
||||
/* timeShiftBufferDepthUs= */ 6_000_000,
|
||||
/* periodStartUnixTimeUs= */ periodStartUnixTimeUs);
|
||||
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs - 10_000_000))
|
||||
.isEqualTo(1_500_000);
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET, /* nowUnixTimeUs= */ periodStartUnixTimeUs))
|
||||
.isEqualTo(1_500_000);
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 1_499_999))
|
||||
.isEqualTo(1_500_000);
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 1_500_000))
|
||||
.isEqualTo(3_500_000);
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 17_499_999))
|
||||
.isEqualTo(17_500_000);
|
||||
assertThat(
|
||||
segmentTemplate.getNextSegmentAvailableTimeUs(
|
||||
/* periodDurationUs= */ C.TIME_UNSET,
|
||||
/* nowUnixTimeUs= */ periodStartUnixTimeUs + 17_500_000))
|
||||
.isEqualTo(19_500_000);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue