Commit c107017a by bachinger Committed by kim-vde

Ensure implicit manifest updates arrives asap

PiperOrigin-RevId: 333714978
parent 397fe8f3
......@@ -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(
......
......@@ -102,6 +102,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.
*
* <p>If true is returned, each segment is defined explicitly by the index data, and all of the
......
......@@ -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;
......@@ -57,6 +58,11 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
}
@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,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;
/**
......@@ -72,6 +73,11 @@ import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
}
@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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment