Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i] = new RepresentationHolder( periodDurationUs, trackType,
@Override public void updateManifest(DashManifest newManifest, int newPeriodIndex) { try { manifest = newManifest; periodIndex = newPeriodIndex; long periodDurationUs = manifest.getPeriodDurationUs(periodIndex); List<Representation> representations = getRepresentations(); for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(trackSelection.getIndexInTrackGroup(i)); representationHolders[i] = representationHolders[i].copyWithNewRepresentation(periodDurationUs, representation); } } catch (BehindLiveWindowException e) { fatalError = e; } }
} else { long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(manifest, periodIndex, nowUnixTimeUs); long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(manifest, periodIndex, nowUnixTimeUs); long segmentNum = getSegmentNum( boolean periodEnded = periodDurationUs != C.TIME_UNSET; if (representationHolder.getSegmentCount() == 0) { representationHolder.getFirstAvailableSegmentNum(manifest, periodIndex, nowUnixTimeUs); long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(manifest, periodIndex, nowUnixTimeUs); if (periodEnded && representationHolder.getSegmentStartTimeUs(segmentNum) >= periodDurationUs) { if (periodDurationUs != C.TIME_UNSET) { while (maxSegmentCount > 1 && representationHolder.getSegmentStartTimeUs(segmentNum + maxSegmentCount - 1) >= periodDurationUs) {
long seekTimeUs) { Representation representation = representationHolder.representation; long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum); RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum); String baseUrl = representation.baseUrl; if (representationHolder.extractorWrapper == null) { long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum); DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(baseUrl), segmentUri.start, segmentUri.length, representation.getCacheKey()); int segmentCount = 1; for (int i = 1; i < maxSegmentCount; i++) { RangedUri nextSegmentUri = representationHolder.getSegmentUrl(firstSegmentNum + i); RangedUri mergedSegmentUri = segmentUri.attemptMerge(nextSegmentUri, baseUrl); if (mergedSegmentUri == null) { segmentCount++; long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum + segmentCount - 1); long periodDurationUs = representationHolder.periodDurationUs; long clippedEndTimeUs =
return new RepresentationHolder( newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, oldIndex); return new RepresentationHolder( newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex); if (oldIndexSegmentCount == 0) { return new RepresentationHolder( newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex); - newIndexFirstSegmentNum; return new RepresentationHolder( newPeriodDurationUs, newRepresentation, extractorWrapper, newSegmentNumShift, newIndex);
@Override public boolean onChunkLoadError( Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) { if (!cancelable) { return false; } if (playerTrackEmsgHandler != null && playerTrackEmsgHandler.maybeRefreshManifestOnLoadingError(chunk)) { return true; } // Workaround for missing segment at the end of the period if (!manifest.dynamic && chunk instanceof MediaChunk && e instanceof InvalidResponseCodeException && ((InvalidResponseCodeException) e).responseCode == 404) { RepresentationHolder representationHolder = representationHolders[trackSelection.indexOf(chunk.trackFormat)]; int segmentCount = representationHolder.getSegmentCount(); if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED && segmentCount != 0) { long lastAvailableSegmentNum = representationHolder.getFirstSegmentNum() + segmentCount - 1; if (((MediaChunk) chunk).getNextChunkIndex() > lastAvailableSegmentNum) { missingLastSegment = true; return true; } } } return blacklistDurationMs != C.TIME_UNSET && trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), blacklistDurationMs); }
@Override public void onChunkLoadCompleted(Chunk chunk) { if (chunk instanceof InitializationChunk) { InitializationChunk initializationChunk = (InitializationChunk) chunk; int trackIndex = trackSelection.indexOf(initializationChunk.trackFormat); RepresentationHolder representationHolder = representationHolders[trackIndex]; // The null check avoids overwriting an index obtained from the manifest with one obtained // from the stream. If the manifest defines an index then the stream shouldn't, but in cases // where it does we should ignore it. if (representationHolder.segmentIndex == null) { SeekMap seekMap = representationHolder.extractorWrapper.getSeekMap(); if (seekMap != null) { representationHolders[trackIndex] = representationHolder.copyWithNewSegmentIndex( new DashWrappingSegmentIndex( (ChunkIndex) seekMap, representationHolder.representation.presentationTimeOffsetUs)); } } } if (playerTrackEmsgHandler != null) { playerTrackEmsgHandler.onChunkLoadCompleted(chunk); } }
@Override public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { // Segments are aligned across representations, so any segment index will do. for (RepresentationHolder representationHolder : representationHolders) { if (representationHolder.segmentIndex != null) { long segmentNum = representationHolder.getSegmentNum(positionUs); long firstSyncUs = representationHolder.getSegmentStartTimeUs(segmentNum); long secondSyncUs = firstSyncUs < positionUs && segmentNum < representationHolder.getSegmentCount() - 1 ? representationHolder.getSegmentStartTimeUs(segmentNum + 1) : firstSyncUs; return Util.resolveSeekPositionUs(positionUs, seekParameters, firstSyncUs, secondSyncUs); } } // We don't have a segment index to adjust the seek position with yet. return positionUs; }
/* package */ RepresentationHolder( long periodDurationUs, int trackType, Representation representation, boolean enableEventMessageTrack, boolean enableCea608Track, TrackOutput playerEmsgTrackOutput) { this( periodDurationUs, representation, createExtractorWrapper( trackType, representation, enableEventMessageTrack, enableCea608Track, playerEmsgTrackOutput), /* segmentNumShift= */ 0, representation.getIndex()); }
public long getLastAvailableSegmentNum( DashManifest manifest, int periodIndex, long nowUnixTimeUs) { int availableSegmentCount = getSegmentCount(); if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) { // The index is itself unbounded. We need to use the current time to calculate the range of // available segments. long liveEdgeTimeUs = nowUnixTimeUs - C.msToUs(manifest.availabilityStartTimeMs); long periodStartUs = C.msToUs(manifest.getPeriod(periodIndex).startMs); long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs; // getSegmentNum(liveEdgeTimeInPeriodUs) will not be completed yet, so subtract one to get // the index of the last completed segment. return getSegmentNum(liveEdgeTimeInPeriodUs) - 1; } return getFirstSegmentNum() + availableSegmentCount - 1; }
public long getFirstAvailableSegmentNum( DashManifest manifest, int periodIndex, long nowUnixTimeUs) { if (getSegmentCount() == DashSegmentIndex.INDEX_UNBOUNDED && manifest.timeShiftBufferDepthMs != C.TIME_UNSET) { // The index is itself unbounded. We need to use the current time to calculate the range of // available segments. long liveEdgeTimeUs = nowUnixTimeUs - C.msToUs(manifest.availabilityStartTimeMs); long periodStartUs = C.msToUs(manifest.getPeriod(periodIndex).startMs); long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs; long bufferDepthUs = C.msToUs(manifest.timeShiftBufferDepthMs); return Math.max( getFirstSegmentNum(), getSegmentNum(liveEdgeTimeInPeriodUs - bufferDepthUs)); } return getFirstSegmentNum(); }
private void updateLiveEdgeTimeUs( RepresentationHolder representationHolder, long lastAvailableSegmentNum) { liveEdgeTimeUs = manifest.dynamic ? representationHolder.getSegmentEndTimeUs(lastAvailableSegmentNum) : C.TIME_UNSET; }
@CheckResult /* package */ RepresentationHolder copyWithNewSegmentIndex(DashSegmentIndex segmentIndex) { return new RepresentationHolder( periodDurationUs, representation, extractorWrapper, segmentNumShift, segmentIndex); }
private long getSegmentNum( RepresentationHolder representationHolder, @Nullable MediaChunk previousChunk, long loadPositionUs, long firstAvailableSegmentNum, long lastAvailableSegmentNum) { return previousChunk != null ? previousChunk.getNextChunkIndex() : Util.constrainValue( representationHolder.getSegmentNum(loadPositionUs), firstAvailableSegmentNum, lastAvailableSegmentNum); }
@Override public long getChunkEndTimeUs() { checkInBounds(); return representationHolder.getSegmentEndTimeUs(getCurrentIndex()); } }