Use long segment indices for DASH

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183389701
This commit is contained in:
aquilescanta 2018-01-26 07:56:29 -08:00 committed by Oliver Woodman
parent 46c4ca7ddb
commit 8ba3335145
15 changed files with 171 additions and 123 deletions

View File

@ -37,9 +37,15 @@ public abstract class BaseMediaChunk extends MediaChunk {
* @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk. * @param chunkIndex The index of the chunk.
*/ */
public BaseMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, public BaseMediaChunk(
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, DataSource dataSource,
int chunkIndex) { DataSpec dataSpec,
Format trackFormat,
int trackSelectionReason,
Object trackSelectionData,
long startTimeUs,
long endTimeUs,
long chunkIndex) {
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
endTimeUs, chunkIndex); endTimeUs, chunkIndex);
} }

View File

@ -53,9 +53,18 @@ public class ContainerMediaChunk extends BaseMediaChunk {
* @param sampleOffsetUs An offset to add to the sample timestamps parsed by the extractor. * @param sampleOffsetUs An offset to add to the sample timestamps parsed by the extractor.
* @param extractorWrapper A wrapped extractor to use for parsing the data. * @param extractorWrapper A wrapped extractor to use for parsing the data.
*/ */
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, public ContainerMediaChunk(
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, DataSource dataSource,
int chunkIndex, int chunkCount, long sampleOffsetUs, ChunkExtractorWrapper extractorWrapper) { DataSpec dataSpec,
Format trackFormat,
int trackSelectionReason,
Object trackSelectionData,
long startTimeUs,
long endTimeUs,
long chunkIndex,
int chunkCount,
long sampleOffsetUs,
ChunkExtractorWrapper extractorWrapper) {
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
endTimeUs, chunkIndex); endTimeUs, chunkIndex);
this.chunkCount = chunkCount; this.chunkCount = chunkCount;

View File

@ -50,9 +50,17 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
* constants. * constants.
* @param sampleFormat The {@link Format} of the sample in the chunk. * @param sampleFormat The {@link Format} of the sample in the chunk.
*/ */
public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, public SingleSampleMediaChunk(
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, DataSource dataSource,
int chunkIndex, int trackType, Format sampleFormat) { DataSpec dataSpec,
Format trackFormat,
int trackSelectionReason,
Object trackSelectionData,
long startTimeUs,
long endTimeUs,
long chunkIndex,
int trackType,
Format sampleFormat) {
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
endTimeUs, chunkIndex); endTimeUs, chunkIndex);
this.trackType = trackType; this.trackType = trackType;

View File

@ -64,7 +64,7 @@ public class DashManifestParserTest extends InstrumentationTestCase {
if (representation instanceof Representation.MultiSegmentRepresentation) { if (representation instanceof Representation.MultiSegmentRepresentation) {
Representation.MultiSegmentRepresentation multiSegmentRepresentation = Representation.MultiSegmentRepresentation multiSegmentRepresentation =
(Representation.MultiSegmentRepresentation) representation; (Representation.MultiSegmentRepresentation) representation;
int firstSegmentIndex = multiSegmentRepresentation.getFirstSegmentNum(); long firstSegmentIndex = multiSegmentRepresentation.getFirstSegmentNum();
RangedUri uri = multiSegmentRepresentation.getSegmentUrl(firstSegmentIndex); RangedUri uri = multiSegmentRepresentation.getSegmentUrl(firstSegmentIndex);
assertThat( assertThat(
uri.resolveUriString(representation.baseUrl) uri.resolveUriString(representation.baseUrl)

View File

@ -898,11 +898,11 @@ public final class DashMediaSource implements MediaSource {
availableStartTimeUs = 0; availableStartTimeUs = 0;
availableEndTimeUs = 0; availableEndTimeUs = 0;
} else if (!seenEmptyIndex) { } else if (!seenEmptyIndex) {
int firstSegmentNum = index.getFirstSegmentNum(); long firstSegmentNum = index.getFirstSegmentNum();
long adaptationSetAvailableStartTimeUs = index.getTimeUs(firstSegmentNum); long adaptationSetAvailableStartTimeUs = index.getTimeUs(firstSegmentNum);
availableStartTimeUs = Math.max(availableStartTimeUs, adaptationSetAvailableStartTimeUs); availableStartTimeUs = Math.max(availableStartTimeUs, adaptationSetAvailableStartTimeUs);
if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED) { if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED) {
int lastSegmentNum = firstSegmentNum + segmentCount - 1; long lastSegmentNum = firstSegmentNum + segmentCount - 1;
long adaptationSetAvailableEndTimeUs = index.getTimeUs(lastSegmentNum) long adaptationSetAvailableEndTimeUs = index.getTimeUs(lastSegmentNum)
+ index.getDurationUs(lastSegmentNum, durationUs); + index.getDurationUs(lastSegmentNum, durationUs);
availableEndTimeUs = Math.min(availableEndTimeUs, adaptationSetAvailableEndTimeUs); availableEndTimeUs = Math.min(availableEndTimeUs, adaptationSetAvailableEndTimeUs);
@ -1027,7 +1027,7 @@ public final class DashMediaSource implements MediaSource {
// Video adaptation set does not include a non-empty index for snapping. // Video adaptation set does not include a non-empty index for snapping.
return windowDefaultStartPositionUs; return windowDefaultStartPositionUs;
} }
int segmentNum = snapIndex.getSegmentNum(defaultStartPositionInPeriodUs, periodDurationUs); long segmentNum = snapIndex.getSegmentNum(defaultStartPositionInPeriodUs, periodDurationUs);
return windowDefaultStartPositionUs + snapIndex.getTimeUs(segmentNum) return windowDefaultStartPositionUs + snapIndex.getTimeUs(segmentNum)
- defaultStartPositionInPeriodUs; - defaultStartPositionInPeriodUs;
} }

View File

@ -32,11 +32,11 @@ public interface DashSegmentIndex {
* Otherwise, returns the segment number of the segment containing the given media time. * Otherwise, returns the segment number of the segment containing the given media time.
* *
* @param timeUs The time in microseconds. * @param timeUs The time in microseconds.
* @param periodDurationUs The duration of the enclosing period in microseconds, or * @param periodDurationUs The duration of the enclosing period in microseconds, or {@link
* {@link C#TIME_UNSET} if the period's duration is not yet known. * C#TIME_UNSET} if the period's duration is not yet known.
* @return The segment number of the corresponding segment. * @return The segment number of the corresponding segment.
*/ */
int getSegmentNum(long timeUs, long periodDurationUs); long getSegmentNum(long timeUs, long periodDurationUs);
/** /**
* Returns the start time of a segment. * Returns the start time of a segment.
@ -44,17 +44,17 @@ public interface DashSegmentIndex {
* @param segmentNum The segment number. * @param segmentNum The segment number.
* @return The corresponding start time in microseconds. * @return The corresponding start time in microseconds.
*/ */
long getTimeUs(int segmentNum); long getTimeUs(long segmentNum);
/** /**
* Returns the duration of a segment. * Returns the duration of a segment.
* *
* @param segmentNum The segment number. * @param segmentNum The segment number.
* @param periodDurationUs The duration of the enclosing period in microseconds, or * @param periodDurationUs The duration of the enclosing period in microseconds, or {@link
* {@link C#TIME_UNSET} if the period's duration is not yet known. * C#TIME_UNSET} if the period's duration is not yet known.
* @return The duration of the segment, in microseconds. * @return The duration of the segment, in microseconds.
*/ */
long getDurationUs(int segmentNum, long periodDurationUs); long getDurationUs(long segmentNum, long periodDurationUs);
/** /**
* Returns a {@link RangedUri} defining the location of a segment. * Returns a {@link RangedUri} defining the location of a segment.
@ -62,14 +62,14 @@ public interface DashSegmentIndex {
* @param segmentNum The segment number. * @param segmentNum The segment number.
* @return The {@link RangedUri} defining the location of the data. * @return The {@link RangedUri} defining the location of the data.
*/ */
RangedUri getSegmentUrl(int segmentNum); RangedUri getSegmentUrl(long segmentNum);
/** /**
* Returns the segment number of the first segment. * Returns the segment number of the first segment.
* *
* @return The segment number of the first segment. * @return The segment number of the first segment.
*/ */
int getFirstSegmentNum(); long getFirstSegmentNum();
/** /**
* Returns the number of segments in the index, or {@link #INDEX_UNBOUNDED}. * Returns the number of segments in the index, or {@link #INDEX_UNBOUNDED}.

View File

@ -34,7 +34,7 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
} }
@Override @Override
public int getFirstSegmentNum() { public long getFirstSegmentNum() {
return 0; return 0;
} }
@ -44,22 +44,23 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
} }
@Override @Override
public long getTimeUs(int segmentNum) { public long getTimeUs(long segmentNum) {
return chunkIndex.timesUs[segmentNum]; return chunkIndex.timesUs[(int) segmentNum];
} }
@Override @Override
public long getDurationUs(int segmentNum, long periodDurationUs) { public long getDurationUs(long segmentNum, long periodDurationUs) {
return chunkIndex.durationsUs[segmentNum]; return chunkIndex.durationsUs[(int) segmentNum];
} }
@Override @Override
public RangedUri getSegmentUrl(int segmentNum) { public RangedUri getSegmentUrl(long segmentNum) {
return new RangedUri(null, chunkIndex.offsets[segmentNum], chunkIndex.sizes[segmentNum]); return new RangedUri(
null, chunkIndex.offsets[(int) segmentNum], chunkIndex.sizes[(int) segmentNum]);
} }
@Override @Override
public int getSegmentNum(long timeUs, long periodDurationUs) { public long getSegmentNum(long timeUs, long periodDurationUs) {
return chunkIndex.getChunkIndex(timeUs); return chunkIndex.getChunkIndex(timeUs);
} }

View File

@ -187,7 +187,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
// Segments are aligned across representations, so any segment index will do. // Segments are aligned across representations, so any segment index will do.
for (RepresentationHolder representationHolder : representationHolders) { for (RepresentationHolder representationHolder : representationHolders) {
if (representationHolder.segmentIndex != null) { if (representationHolder.segmentIndex != null) {
int segmentNum = representationHolder.getSegmentNum(positionUs); long segmentNum = representationHolder.getSegmentNum(positionUs);
long firstSyncUs = representationHolder.getSegmentStartTimeUs(segmentNum); long firstSyncUs = representationHolder.getSegmentStartTimeUs(segmentNum);
long secondSyncUs = long secondSyncUs =
firstSyncUs < positionUs && segmentNum < representationHolder.getSegmentCount() - 1 firstSyncUs < positionUs && segmentNum < representationHolder.getSegmentCount() - 1
@ -284,8 +284,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
return; return;
} }
int firstAvailableSegmentNum = representationHolder.getFirstSegmentNum(); long firstAvailableSegmentNum = representationHolder.getFirstSegmentNum();
int lastAvailableSegmentNum; long lastAvailableSegmentNum;
if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) { if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) {
// The index is itself unbounded. We need to use the current time to calculate the range of // The index is itself unbounded. We need to use the current time to calculate the range of
// available segments. // available segments.
@ -306,12 +306,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
updateLiveEdgeTimeUs(representationHolder, lastAvailableSegmentNum); updateLiveEdgeTimeUs(representationHolder, lastAvailableSegmentNum);
int segmentNum; long segmentNum;
if (previous == null) { if (previous == null) {
segmentNum = Util.constrainValue(representationHolder.getSegmentNum(loadPositionUs), segmentNum = Util.constrainValue(representationHolder.getSegmentNum(loadPositionUs),
firstAvailableSegmentNum, lastAvailableSegmentNum); firstAvailableSegmentNum, lastAvailableSegmentNum);
} else { } else {
segmentNum = (int) previous.getNextChunkIndex(); segmentNum = previous.getNextChunkIndex();
if (segmentNum < firstAvailableSegmentNum) { if (segmentNum < firstAvailableSegmentNum) {
// This is before the first chunk in the current manifest. // This is before the first chunk in the current manifest.
fatalError = new BehindLiveWindowException(); fatalError = new BehindLiveWindowException();
@ -326,7 +326,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
return; return;
} }
int maxSegmentCount = Math.min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1); int maxSegmentCount =
(int) Math.min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1);
out.chunk = newMediaChunk(representationHolder, dataSource, trackType, out.chunk = newMediaChunk(representationHolder, dataSource, trackType,
trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(),
trackSelection.getSelectionData(), segmentNum, maxSegmentCount); trackSelection.getSelectionData(), segmentNum, maxSegmentCount);
@ -370,7 +371,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
representationHolders[trackSelection.indexOf(chunk.trackFormat)]; representationHolders[trackSelection.indexOf(chunk.trackFormat)];
int segmentCount = representationHolder.getSegmentCount(); int segmentCount = representationHolder.getSegmentCount();
if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED && segmentCount != 0) { if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED && segmentCount != 0) {
int lastAvailableSegmentNum = representationHolder.getFirstSegmentNum() + segmentCount - 1; long lastAvailableSegmentNum = representationHolder.getFirstSegmentNum() + segmentCount - 1;
if (((MediaChunk) chunk).getNextChunkIndex() > lastAvailableSegmentNum) { if (((MediaChunk) chunk).getNextChunkIndex() > lastAvailableSegmentNum) {
missingLastSegment = true; missingLastSegment = true;
return true; return true;
@ -393,8 +394,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
return representations; return representations;
} }
private void updateLiveEdgeTimeUs(RepresentationHolder representationHolder, private void updateLiveEdgeTimeUs(
int lastAvailableSegmentNum) { RepresentationHolder representationHolder, long lastAvailableSegmentNum) {
liveEdgeTimeUs = manifest.dynamic liveEdgeTimeUs = manifest.dynamic
? representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum) : C.TIME_UNSET; ? representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum) : C.TIME_UNSET;
} }
@ -433,9 +434,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper);
} }
protected static Chunk newMediaChunk(RepresentationHolder representationHolder, protected static Chunk newMediaChunk(
DataSource dataSource, int trackType, Format trackFormat, int trackSelectionReason, RepresentationHolder representationHolder,
Object trackSelectionData, int firstSegmentNum, int maxSegmentCount) { DataSource dataSource,
int trackType,
Format trackFormat,
int trackSelectionReason,
Object trackSelectionData,
long firstSegmentNum,
int maxSegmentCount) {
Representation representation = representationHolder.representation; Representation representation = representationHolder.representation;
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum); long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum); RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
@ -481,7 +488,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
public DashSegmentIndex segmentIndex; public DashSegmentIndex segmentIndex;
private long periodDurationUs; private long periodDurationUs;
private int segmentNumShift; private long segmentNumShift;
/* package */ RepresentationHolder( /* package */ RepresentationHolder(
long periodDurationUs, long periodDurationUs,
@ -547,10 +554,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
return; return;
} }
int oldIndexLastSegmentNum = oldIndex.getFirstSegmentNum() + oldIndexSegmentCount - 1; long oldIndexLastSegmentNum = oldIndex.getFirstSegmentNum() + oldIndexSegmentCount - 1;
long oldIndexEndTimeUs = oldIndex.getTimeUs(oldIndexLastSegmentNum) long oldIndexEndTimeUs = oldIndex.getTimeUs(oldIndexLastSegmentNum)
+ oldIndex.getDurationUs(oldIndexLastSegmentNum, periodDurationUs); + oldIndex.getDurationUs(oldIndexLastSegmentNum, periodDurationUs);
int newIndexFirstSegmentNum = newIndex.getFirstSegmentNum(); long newIndexFirstSegmentNum = newIndex.getFirstSegmentNum();
long newIndexStartTimeUs = newIndex.getTimeUs(newIndexFirstSegmentNum); long newIndexStartTimeUs = newIndex.getTimeUs(newIndexFirstSegmentNum);
if (oldIndexEndTimeUs == newIndexStartTimeUs) { if (oldIndexEndTimeUs == newIndexStartTimeUs) {
// The new index continues where the old one ended, with no overlap. // The new index continues where the old one ended, with no overlap.
@ -566,7 +573,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
} }
public int getFirstSegmentNum() { public long getFirstSegmentNum() {
return segmentIndex.getFirstSegmentNum() + segmentNumShift; return segmentIndex.getFirstSegmentNum() + segmentNumShift;
} }
@ -574,20 +581,20 @@ public class DefaultDashChunkSource implements DashChunkSource {
return segmentIndex.getSegmentCount(periodDurationUs); return segmentIndex.getSegmentCount(periodDurationUs);
} }
public long getSegmentStartTimeUs(int segmentNum) { public long getSegmentStartTimeUs(long segmentNum) {
return segmentIndex.getTimeUs(segmentNum - segmentNumShift); return segmentIndex.getTimeUs(segmentNum - segmentNumShift);
} }
public long getSegmentEndTimeUs(int segmentNum) { public long getSegmentEndTimeUs(long segmentNum) {
return getSegmentStartTimeUs(segmentNum) return getSegmentStartTimeUs(segmentNum)
+ segmentIndex.getDurationUs(segmentNum - segmentNumShift, periodDurationUs); + segmentIndex.getDurationUs(segmentNum - segmentNumShift, periodDurationUs);
} }
public int getSegmentNum(long positionUs) { public long getSegmentNum(long positionUs) {
return segmentIndex.getSegmentNum(positionUs, periodDurationUs) + segmentNumShift; return segmentIndex.getSegmentNum(positionUs, periodDurationUs) + segmentNumShift;
} }
public RangedUri getSegmentUrl(int segmentNum) { public RangedUri getSegmentUrl(long segmentNum) {
return segmentIndex.getSegmentUrl(segmentNum - segmentNumShift); return segmentIndex.getSegmentUrl(segmentNum - segmentNumShift);
} }

View File

@ -602,7 +602,7 @@ public class DashManifestParser extends DefaultHandler
long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset", long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset",
parent != null ? parent.presentationTimeOffset : 0); parent != null ? parent.presentationTimeOffset : 0);
long duration = parseLong(xpp, "duration", parent != null ? parent.duration : C.TIME_UNSET); long duration = parseLong(xpp, "duration", parent != null ? parent.duration : C.TIME_UNSET);
int startNumber = parseInt(xpp, "startNumber", parent != null ? parent.startNumber : 1); long startNumber = parseLong(xpp, "startNumber", parent != null ? parent.startNumber : 1);
RangedUri initialization = null; RangedUri initialization = null;
List<SegmentTimelineElement> timeline = null; List<SegmentTimelineElement> timeline = null;
@ -632,9 +632,14 @@ public class DashManifestParser extends DefaultHandler
startNumber, duration, timeline, segments); startNumber, duration, timeline, segments);
} }
protected SegmentList buildSegmentList(RangedUri initialization, long timescale, protected SegmentList buildSegmentList(
long presentationTimeOffset, int startNumber, long duration, RangedUri initialization,
List<SegmentTimelineElement> timeline, List<RangedUri> segments) { long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> timeline,
List<RangedUri> segments) {
return new SegmentList(initialization, timescale, presentationTimeOffset, return new SegmentList(initialization, timescale, presentationTimeOffset,
startNumber, duration, timeline, segments); startNumber, duration, timeline, segments);
} }
@ -645,7 +650,7 @@ public class DashManifestParser extends DefaultHandler
long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset", long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset",
parent != null ? parent.presentationTimeOffset : 0); parent != null ? parent.presentationTimeOffset : 0);
long duration = parseLong(xpp, "duration", parent != null ? parent.duration : C.TIME_UNSET); long duration = parseLong(xpp, "duration", parent != null ? parent.duration : C.TIME_UNSET);
int startNumber = parseInt(xpp, "startNumber", parent != null ? parent.startNumber : 1); long startNumber = parseLong(xpp, "startNumber", parent != null ? parent.startNumber : 1);
UrlTemplate mediaTemplate = parseUrlTemplate(xpp, "media", UrlTemplate mediaTemplate = parseUrlTemplate(xpp, "media",
parent != null ? parent.mediaTemplate : null); parent != null ? parent.mediaTemplate : null);
UrlTemplate initializationTemplate = parseUrlTemplate(xpp, "initialization", UrlTemplate initializationTemplate = parseUrlTemplate(xpp, "initialization",
@ -672,9 +677,14 @@ public class DashManifestParser extends DefaultHandler
startNumber, duration, timeline, initializationTemplate, mediaTemplate); startNumber, duration, timeline, initializationTemplate, mediaTemplate);
} }
protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale, protected SegmentTemplate buildSegmentTemplate(
long presentationTimeOffset, int startNumber, long duration, RangedUri initialization,
List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate, long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> timeline,
UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate) { UrlTemplate mediaTemplate) {
return new SegmentTemplate(initialization, timescale, presentationTimeOffset, return new SegmentTemplate(initialization, timescale, presentationTimeOffset,
startNumber, duration, timeline, initializationTemplate, mediaTemplate); startNumber, duration, timeline, initializationTemplate, mediaTemplate);

View File

@ -292,27 +292,27 @@ public abstract class Representation {
// DashSegmentIndex implementation. // DashSegmentIndex implementation.
@Override @Override
public RangedUri getSegmentUrl(int segmentIndex) { public RangedUri getSegmentUrl(long segmentIndex) {
return segmentBase.getSegmentUrl(this, segmentIndex); return segmentBase.getSegmentUrl(this, segmentIndex);
} }
@Override @Override
public int getSegmentNum(long timeUs, long periodDurationUs) { public long getSegmentNum(long timeUs, long periodDurationUs) {
return segmentBase.getSegmentNum(timeUs, periodDurationUs); return segmentBase.getSegmentNum(timeUs, periodDurationUs);
} }
@Override @Override
public long getTimeUs(int segmentIndex) { public long getTimeUs(long segmentIndex) {
return segmentBase.getSegmentTimeUs(segmentIndex); return segmentBase.getSegmentTimeUs(segmentIndex);
} }
@Override @Override
public long getDurationUs(int segmentIndex, long periodDurationUs) { public long getDurationUs(long segmentIndex, long periodDurationUs) {
return segmentBase.getSegmentDurationUs(segmentIndex, periodDurationUs); return segmentBase.getSegmentDurationUs(segmentIndex, periodDurationUs);
} }
@Override @Override
public int getFirstSegmentNum() { public long getFirstSegmentNum() {
return segmentBase.getFirstSegmentNum(); return segmentBase.getFirstSegmentNum();
} }

View File

@ -99,7 +99,7 @@ public abstract class SegmentBase {
*/ */
public abstract static class MultiSegmentBase extends SegmentBase { public abstract static class MultiSegmentBase extends SegmentBase {
/* package */ final int startNumber; /* package */ final long startNumber;
/* package */ final long duration; /* package */ final long duration;
/* package */ final List<SegmentTimelineElement> segmentTimeline; /* package */ final List<SegmentTimelineElement> segmentTimeline;
@ -111,43 +111,46 @@ public abstract class SegmentBase {
* division of this value and {@code timescale}. * division of this value and {@code timescale}.
* @param startNumber The sequence number of the first segment. * @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The * @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If * value in seconds is the division of this value and {@code timescale}. If {@code
* {@code segmentTimeline} is non-null then this parameter is ignored. * segmentTimeline} is non-null then this parameter is ignored.
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then * @param segmentTimeline A segment timeline corresponding to the segments. If null, then
* segments are assumed to be of fixed duration as specified by the {@code duration} * segments are assumed to be of fixed duration as specified by the {@code duration}
* parameter. * parameter.
*/ */
public MultiSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset, public MultiSegmentBase(
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline) { RangedUri initialization,
long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline) {
super(initialization, timescale, presentationTimeOffset); super(initialization, timescale, presentationTimeOffset);
this.startNumber = startNumber; this.startNumber = startNumber;
this.duration = duration; this.duration = duration;
this.segmentTimeline = segmentTimeline; this.segmentTimeline = segmentTimeline;
} }
/** /** @see DashSegmentIndex#getSegmentNum(long, long) */
* @see DashSegmentIndex#getSegmentNum(long, long) public long getSegmentNum(long timeUs, long periodDurationUs) {
*/ final long firstSegmentNum = getFirstSegmentNum();
public int getSegmentNum(long timeUs, long periodDurationUs) { final long segmentCount = getSegmentCount(periodDurationUs);
final int firstSegmentNum = getFirstSegmentNum();
final int segmentCount = getSegmentCount(periodDurationUs);
if (segmentCount == 0) { if (segmentCount == 0) {
return firstSegmentNum; return firstSegmentNum;
} }
if (segmentTimeline == null) { if (segmentTimeline == null) {
// All segments are of equal duration (with the possible exception of the last one). // All segments are of equal duration (with the possible exception of the last one).
long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; long durationUs = (duration * C.MICROS_PER_SECOND) / timescale;
int segmentNum = startNumber + (int) (timeUs / durationUs); long segmentNum = startNumber + timeUs / durationUs;
// Ensure we stay within bounds. // Ensure we stay within bounds.
return segmentNum < firstSegmentNum ? firstSegmentNum return segmentNum < firstSegmentNum ? firstSegmentNum
: segmentCount == DashSegmentIndex.INDEX_UNBOUNDED ? segmentNum : segmentCount == DashSegmentIndex.INDEX_UNBOUNDED ? segmentNum
: Math.min(segmentNum, firstSegmentNum + segmentCount - 1); : Math.min(segmentNum, firstSegmentNum + segmentCount - 1);
} else { } else {
// The index cannot be unbounded. Identify the segment using binary search. // The index cannot be unbounded. Identify the segment using binary search.
int lowIndex = firstSegmentNum; long lowIndex = firstSegmentNum;
int highIndex = firstSegmentNum + segmentCount - 1; long highIndex = firstSegmentNum + segmentCount - 1;
while (lowIndex <= highIndex) { while (lowIndex <= highIndex) {
int midIndex = lowIndex + (highIndex - lowIndex) / 2; long midIndex = lowIndex + (highIndex - lowIndex) / 2;
long midTimeUs = getSegmentTimeUs(midIndex); long midTimeUs = getSegmentTimeUs(midIndex);
if (midTimeUs < timeUs) { if (midTimeUs < timeUs) {
lowIndex = midIndex + 1; lowIndex = midIndex + 1;
@ -161,12 +164,10 @@ public abstract class SegmentBase {
} }
} }
/** /** @see DashSegmentIndex#getDurationUs(long, long) */
* @see DashSegmentIndex#getDurationUs(int, long) public final long getSegmentDurationUs(long sequenceNumber, long periodDurationUs) {
*/
public final long getSegmentDurationUs(int sequenceNumber, long periodDurationUs) {
if (segmentTimeline != null) { if (segmentTimeline != null) {
long duration = segmentTimeline.get(sequenceNumber - startNumber).duration; long duration = segmentTimeline.get((int) (sequenceNumber - startNumber)).duration;
return (duration * C.MICROS_PER_SECOND) / timescale; return (duration * C.MICROS_PER_SECOND) / timescale;
} else { } else {
int segmentCount = getSegmentCount(periodDurationUs); int segmentCount = getSegmentCount(periodDurationUs);
@ -177,14 +178,13 @@ public abstract class SegmentBase {
} }
} }
/** /** @see DashSegmentIndex#getTimeUs(long) */
* @see DashSegmentIndex#getTimeUs(int) public final long getSegmentTimeUs(long sequenceNumber) {
*/
public final long getSegmentTimeUs(int sequenceNumber) {
long unscaledSegmentTime; long unscaledSegmentTime;
if (segmentTimeline != null) { if (segmentTimeline != null) {
unscaledSegmentTime = segmentTimeline.get(sequenceNumber - startNumber).startTime unscaledSegmentTime =
- presentationTimeOffset; segmentTimeline.get((int) (sequenceNumber - startNumber)).startTime
- presentationTimeOffset;
} else { } else {
unscaledSegmentTime = (sequenceNumber - startNumber) * duration; unscaledSegmentTime = (sequenceNumber - startNumber) * duration;
} }
@ -195,14 +195,12 @@ public abstract class SegmentBase {
* Returns a {@link RangedUri} defining the location of a segment for the given index in the * Returns a {@link RangedUri} defining the location of a segment for the given index in the
* given representation. * given representation.
* *
* @see DashSegmentIndex#getSegmentUrl(int) * @see DashSegmentIndex#getSegmentUrl(long)
*/ */
public abstract RangedUri getSegmentUrl(Representation representation, int index); public abstract RangedUri getSegmentUrl(Representation representation, long index);
/** /** @see DashSegmentIndex#getFirstSegmentNum() */
* @see DashSegmentIndex#getFirstSegmentNum() public long getFirstSegmentNum() {
*/
public int getFirstSegmentNum() {
return startNumber; return startNumber;
} }
@ -235,15 +233,20 @@ public abstract class SegmentBase {
* division of this value and {@code timescale}. * division of this value and {@code timescale}.
* @param startNumber The sequence number of the first segment. * @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The * @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If * value in seconds is the division of this value and {@code timescale}. If {@code
* {@code segmentTimeline} is non-null then this parameter is ignored. * segmentTimeline} is non-null then this parameter is ignored.
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then * @param segmentTimeline A segment timeline corresponding to the segments. If null, then
* segments are assumed to be of fixed duration as specified by the {@code duration} * segments are assumed to be of fixed duration as specified by the {@code duration}
* parameter. * parameter.
* @param mediaSegments A list of {@link RangedUri}s indicating the locations of the segments. * @param mediaSegments A list of {@link RangedUri}s indicating the locations of the segments.
*/ */
public SegmentList(RangedUri initialization, long timescale, long presentationTimeOffset, public SegmentList(
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline, RangedUri initialization,
long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline,
List<RangedUri> mediaSegments) { List<RangedUri> mediaSegments) {
super(initialization, timescale, presentationTimeOffset, startNumber, duration, super(initialization, timescale, presentationTimeOffset, startNumber, duration,
segmentTimeline); segmentTimeline);
@ -251,8 +254,8 @@ public abstract class SegmentBase {
} }
@Override @Override
public RangedUri getSegmentUrl(Representation representation, int sequenceNumber) { public RangedUri getSegmentUrl(Representation representation, long sequenceNumber) {
return mediaSegments.get(sequenceNumber - startNumber); return mediaSegments.get((int) (sequenceNumber - startNumber));
} }
@Override @Override
@ -284,8 +287,8 @@ public abstract class SegmentBase {
* division of this value and {@code timescale}. * division of this value and {@code timescale}.
* @param startNumber The sequence number of the first segment. * @param startNumber The sequence number of the first segment.
* @param duration The duration of each segment in the case of fixed duration segments. The * @param duration The duration of each segment in the case of fixed duration segments. The
* value in seconds is the division of this value and {@code timescale}. If * value in seconds is the division of this value and {@code timescale}. If {@code
* {@code segmentTimeline} is non-null then this parameter is ignored. * segmentTimeline} is non-null then this parameter is ignored.
* @param segmentTimeline A segment timeline corresponding to the segments. If null, then * @param segmentTimeline A segment timeline corresponding to the segments. If null, then
* segments are assumed to be of fixed duration as specified by the {@code duration} * segments are assumed to be of fixed duration as specified by the {@code duration}
* parameter. * parameter.
@ -294,9 +297,15 @@ public abstract class SegmentBase {
* null then {@code initialization} will be used. * null then {@code initialization} will be used.
* @param mediaTemplate A template defining the location of each media segment. * @param mediaTemplate A template defining the location of each media segment.
*/ */
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset, public SegmentTemplate(
int startNumber, long duration, List<SegmentTimelineElement> segmentTimeline, RangedUri initialization,
UrlTemplate initializationTemplate, UrlTemplate mediaTemplate) { long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline,
UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate) {
super(initialization, timescale, presentationTimeOffset, startNumber, super(initialization, timescale, presentationTimeOffset, startNumber,
duration, segmentTimeline); duration, segmentTimeline);
this.initializationTemplate = initializationTemplate; this.initializationTemplate = initializationTemplate;
@ -315,10 +324,10 @@ public abstract class SegmentBase {
} }
@Override @Override
public RangedUri getSegmentUrl(Representation representation, int sequenceNumber) { public RangedUri getSegmentUrl(Representation representation, long sequenceNumber) {
long time; long time;
if (segmentTimeline != null) { if (segmentTimeline != null) {
time = segmentTimeline.get(sequenceNumber - startNumber).startTime; time = segmentTimeline.get((int) (sequenceNumber - startNumber)).startTime;
} else { } else {
time = (sequenceNumber - startNumber) * duration; time = (sequenceNumber - startNumber) * duration;
} }

View File

@ -32,27 +32,27 @@ import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
} }
@Override @Override
public int getSegmentNum(long timeUs, long periodDurationUs) { public long getSegmentNum(long timeUs, long periodDurationUs) {
return 0; return 0;
} }
@Override @Override
public long getTimeUs(int segmentNum) { public long getTimeUs(long segmentNum) {
return 0; return 0;
} }
@Override @Override
public long getDurationUs(int segmentNum, long periodDurationUs) { public long getDurationUs(long segmentNum, long periodDurationUs) {
return periodDurationUs; return periodDurationUs;
} }
@Override @Override
public RangedUri getSegmentUrl(int segmentNum) { public RangedUri getSegmentUrl(long segmentNum) {
return uri; return uri;
} }
@Override @Override
public int getFirstSegmentNum() { public long getFirstSegmentNum() {
return 0; return 0;
} }

View File

@ -71,8 +71,8 @@ public final class UrlTemplate {
/** /**
* Constructs a Uri from the template, substituting in the provided arguments. * Constructs a Uri from the template, substituting in the provided arguments.
* <p> *
* Arguments whose corresponding identifiers are not present in the template will be ignored. * <p>Arguments whose corresponding identifiers are not present in the template will be ignored.
* *
* @param representationId The representation identifier. * @param representationId The representation identifier.
* @param segmentNumber The segment number. * @param segmentNumber The segment number.
@ -80,7 +80,7 @@ public final class UrlTemplate {
* @param time The time as specified by the segment timeline. * @param time The time as specified by the segment timeline.
* @return The built Uri. * @return The built Uri.
*/ */
public String buildUri(String representationId, int segmentNumber, int bandwidth, long time) { public String buildUri(String representationId, long segmentNumber, int bandwidth, long time) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i = 0; i < identifierCount; i++) { for (int i = 0; i < identifierCount; i++) {
builder.append(urlPieces[i]); builder.append(urlPieces[i]);

View File

@ -138,9 +138,9 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
addSegment(segments, startUs, baseUrl, indexUri); addSegment(segments, startUs, baseUrl, indexUri);
} }
int firstSegmentNum = index.getFirstSegmentNum(); long firstSegmentNum = index.getFirstSegmentNum();
int lastSegmentNum = firstSegmentNum + segmentCount - 1; long lastSegmentNum = firstSegmentNum + segmentCount - 1;
for (int j = firstSegmentNum; j <= lastSegmentNum; j++) { for (long j = firstSegmentNum; j <= lastSegmentNum; j++) {
addSegment(segments, startUs + index.getTimeUs(j), baseUrl, index.getSegmentUrl(j)); addSegment(segments, startUs + index.getTimeUs(j), baseUrl, index.getSegmentUrl(j));
} }
} }

View File

@ -450,11 +450,9 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
private static Segment getFirstOldOverlappingSegment(HlsMediaPlaylist oldPlaylist, private static Segment getFirstOldOverlappingSegment(HlsMediaPlaylist oldPlaylist,
HlsMediaPlaylist loadedPlaylist) { HlsMediaPlaylist loadedPlaylist) {
long mediaSequenceOffset = loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence; int mediaSequenceOffset = (int) (loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence);
List<Segment> oldSegments = oldPlaylist.segments; List<Segment> oldSegments = oldPlaylist.segments;
return mediaSequenceOffset < oldSegments.size() return mediaSequenceOffset < oldSegments.size() ? oldSegments.get(mediaSequenceOffset) : null;
? oldSegments.get((int) mediaSequenceOffset)
: null;
} }
/** /**