/** * @see DashSegmentIndex#getSegmentNum(long, long) */ public int getSegmentNum(long timeUs, long periodDurationUs) { final int firstSegmentNum = getFirstSegmentNum(); final int segmentCount = getSegmentCount(periodDurationUs); if (segmentCount == 0) { return firstSegmentNum; } if (segmentTimeline == null) { // All segments are of equal duration (with the possible exception of the last one). long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; int segmentNum = startNumber + (int) (timeUs / durationUs); // Ensure we stay within bounds. return segmentNum < firstSegmentNum ? firstSegmentNum : segmentCount == DashSegmentIndex.INDEX_UNBOUNDED ? segmentNum : Math.min(segmentNum, firstSegmentNum + segmentCount - 1); } else { // The index cannot be unbounded. Identify the segment using binary search. int lowIndex = firstSegmentNum; int highIndex = firstSegmentNum + segmentCount - 1; while (lowIndex <= highIndex) { int midIndex = lowIndex + (highIndex - lowIndex) / 2; long midTimeUs = getSegmentTimeUs(midIndex); if (midTimeUs < timeUs) { lowIndex = midIndex + 1; } else if (midTimeUs > timeUs) { highIndex = midIndex - 1; } else { return midIndex; } } return lowIndex == firstSegmentNum ? lowIndex : highIndex; } }
/** * @see DashSegmentIndex#getDurationUs(int, long) */ public final long getSegmentDurationUs(int sequenceNumber, long periodDurationUs) { if (segmentTimeline != null) { long duration = segmentTimeline.get(sequenceNumber - startNumber).duration; return (duration * C.MICROS_PER_SECOND) / timescale; } else { int segmentCount = getSegmentCount(periodDurationUs); return segmentCount != DashSegmentIndex.INDEX_UNBOUNDED && sequenceNumber == (getFirstSegmentNum() + segmentCount - 1) ? (periodDurationUs - getSegmentTimeUs(sequenceNumber)) : ((duration * C.MICROS_PER_SECOND) / timescale); } }
@Override public int getSegmentCount(long periodDurationUs) { if (segmentTimeline != null) { return segmentTimeline.size(); } else if (periodDurationUs != C.TIME_UNSET) { long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; return (int) Util.ceilDivide(periodDurationUs, durationUs); } else { return DashSegmentIndex.INDEX_UNBOUNDED; } }
/** * @see DashSegmentIndex#getSegmentNum(long, long) */ public int getSegmentNum(long timeUs, long periodDurationUs) { final int firstSegmentNum = getFirstSegmentNum(); int lowIndex = firstSegmentNum; int highIndex = getLastSegmentNum(periodDurationUs); if (segmentTimeline == null) { // All segments are of equal duration (with the possible exception of the last one). long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; int segmentNum = startNumber + (int) (timeUs / durationUs); // Ensure we stay within bounds. return segmentNum < lowIndex ? lowIndex : highIndex != DashSegmentIndex.INDEX_UNBOUNDED && segmentNum > highIndex ? highIndex : segmentNum; } else { // The high index cannot be unbounded. Identify the segment using binary search. while (lowIndex <= highIndex) { int midIndex = (lowIndex + highIndex) / 2; long midTimeUs = getSegmentTimeUs(midIndex); if (midTimeUs < timeUs) { lowIndex = midIndex + 1; } else if (midTimeUs > timeUs) { highIndex = midIndex - 1; } else { return midIndex; } } return lowIndex == firstSegmentNum ? lowIndex : highIndex; } }
@Override public int getLastSegmentNum(long periodDurationUs) { if (segmentTimeline != null) { return segmentTimeline.size() + startNumber - 1; } else if (periodDurationUs == C.TIME_UNSET) { return DashSegmentIndex.INDEX_UNBOUNDED; } else { long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; return startNumber + (int) Util.ceilDivide(periodDurationUs, durationUs) - 1; } }
/** * Returns DashSegmentIndex for given representation. */ private DashSegmentIndex getSegmentIndex(DataSource dataSource, DashManifest manifest, RepresentationKey key) throws IOException, InterruptedException { AdaptationSet adaptationSet = manifest.getPeriod(key.periodIndex).adaptationSets.get( key.adaptationSetIndex); Representation representation = adaptationSet.representations.get(key.representationIndex); DashSegmentIndex index = representation.getIndex(); if (index != null) { return index; } ChunkIndex seekMap = DashUtil.loadChunkIndex(dataSource, adaptationSet.type, representation); return seekMap == null ? null : new DashWrappingSegmentIndex(seekMap); }
@Override public DashSegmentIndex getIndex() { return segmentIndex; }
@Override public DashSegmentIndex getIndex() { return this; }
@Override protected List<Segment> getSegments(DataSource dataSource, DashManifest manifest, RepresentationKey[] keys, boolean allowIndexLoadErrors) throws InterruptedException, IOException { ArrayList<Segment> segments = new ArrayList<>(); for (RepresentationKey key : keys) { DashSegmentIndex index; try { index = getSegmentIndex(dataSource, manifest, key); if (index == null) { // Loading succeeded but there was no index. This is always a failure. throw new DownloadException("No index for representation: " + key); } } catch (IOException e) { if (allowIndexLoadErrors) { // Loading failed, but load errors are allowed. Advance to the next key. continue; } else { throw e; } } int segmentCount = index.getSegmentCount(C.TIME_UNSET); if (segmentCount == DashSegmentIndex.INDEX_UNBOUNDED) { throw new DownloadException("Unbounded index for representation: " + key); } Period period = manifest.getPeriod(key.periodIndex); Representation representation = period.adaptationSets.get(key.adaptationSetIndex) .representations.get(key.representationIndex); long startUs = C.msToUs(period.startMs); String baseUrl = representation.baseUrl; RangedUri initializationUri = representation.getInitializationUri(); if (initializationUri != null) { addSegment(segments, startUs, baseUrl, initializationUri); } RangedUri indexUri = representation.getIndexUri(); if (indexUri != null) { addSegment(segments, startUs, baseUrl, indexUri); } int firstSegmentNum = index.getFirstSegmentNum(); int lastSegmentNum = firstSegmentNum + segmentCount - 1; for (int j = firstSegmentNum; j <= lastSegmentNum; j++) { addSegment(segments, startUs + index.getTimeUs(j), baseUrl, index.getSegmentUrl(j)); } } return segments; }
/** * Returns an index if the representation provides one directly, or null otherwise. */ public abstract DashSegmentIndex getIndex();