@Override public void onChunkLoadCompleted(Chunk chunk) { if (chunk instanceof InitializationChunk) { InitializationChunk initializationChunk = (InitializationChunk) chunk; RepresentationHolder representationHolder = representationHolders[trackSelection.indexOf(initializationChunk.trackFormat)]; // 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) { representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap); } } } }
@Override public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { if (!cancelable) { return false; } // 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) { int lastAvailableSegmentNum = representationHolder.getFirstSegmentNum() + segmentCount - 1; if (((MediaChunk) chunk).getNextChunkIndex() > lastAvailableSegmentNum) { missingLastSegment = true; return true; } } } // Blacklist if appropriate. return ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection, trackSelection.indexOf(chunk.trackFormat), e); }
private static Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { RangedUri requestUri; String baseUrl = representationHolder.representation.baseUrl; if (initializationUri != null) { // It's common for initialization and index data to be stored adjacently. Attempt to merge // the two requests together to request both at once. requestUri = initializationUri.attemptMerge(indexUri, baseUrl); if (requestUri == null) { requestUri = initializationUri; } } else { requestUri = indexUri; } DataSpec dataSpec = new DataSpec(requestUri.resolveUri(baseUrl), requestUri.start, requestUri.length, representationHolder.representation.getCacheKey()); return new InitializationChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); }
@Override public void onChunkLoadCompleted(Chunk chunk) { if (chunk instanceof InitializationChunk) { InitializationChunk initializationChunk = (InitializationChunk) chunk; RepresentationHolder representationHolder = representationHolders[trackSelection.indexOf(initializationChunk.trackFormat)]; Format sampleFormat = initializationChunk.getSampleFormat(); if (sampleFormat != null) { representationHolder.setSampleFormat(sampleFormat); } // 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 = initializationChunk.getSeekMap(); if (seekMap != null) { representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap, initializationChunk.dataSpec.uri.toString()); } } } }
@Override public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { if (!cancelable) { return false; } // 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 lastAvailableSegmentNum = representationHolder.getLastSegmentNum(); if (((MediaChunk) chunk).chunkIndex >= lastAvailableSegmentNum) { missingLastSegment = true; return true; } } // Blacklist if appropriate. return ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection, trackSelection.indexOf(chunk.trackFormat), e); }
private Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { RangedUri requestUri; if (initializationUri != null) { // It's common for initialization and index data to be stored adjacently. Attempt to merge // the two requests together to request both at once. requestUri = initializationUri.attemptMerge(indexUri); if (requestUri == null) { requestUri = initializationUri; } } else { requestUri = indexUri; } DataSpec dataSpec = new DataSpec(requestUri.getUri(), requestUri.start, requestUri.length, representationHolder.representation.getCacheKey()); return new InitializationChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); }
private Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, Format sampleFormat, int segmentNum) { Representation representation = representationHolder.representation; long startTimeUs = representationHolder.getSegmentStartTimeUs(segmentNum); long endTimeUs = representationHolder.getSegmentEndTimeUs(segmentNum); RangedUri segmentUri = representationHolder.getSegmentUrl(segmentNum); DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, representation.getCacheKey()); if (representationHolder.extractorWrapper == null) { return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, segmentNum, trackFormat); } else { long sampleOffsetUs = -representation.presentationTimeOffsetUs; return new ContainerMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, segmentNum, sampleOffsetUs, representationHolder.extractorWrapper, sampleFormat); } }
protected static Chunk newInitializationChunk(RepresentationHolder representationHolder, DataSource dataSource, Format trackFormat, int trackSelectionReason, Object trackSelectionData, RangedUri initializationUri, RangedUri indexUri) { RangedUri requestUri; String baseUrl = representationHolder.representation.baseUrl; if (initializationUri != null) { // It's common for initialization and index data to be stored adjacently. Attempt to merge // the two requests together to request both at once. requestUri = initializationUri.attemptMerge(indexUri, baseUrl); if (requestUri == null) { requestUri = initializationUri; } } else { requestUri = indexUri; } DataSpec dataSpec = new DataSpec(requestUri.resolveUri(baseUrl), requestUri.start, requestUri.length, representationHolder.representation.getCacheKey()); return new InitializationChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); }
/** * Called when the {@link HlsSampleStreamWrapper} has finished loading a chunk obtained from this * source. * * @param chunk The chunk whose load has been completed. */ public void onChunkLoadCompleted(Chunk chunk) { if (chunk instanceof EncryptionKeyChunk) { EncryptionKeyChunk encryptionKeyChunk = (EncryptionKeyChunk) chunk; scratchSpace = encryptionKeyChunk.getDataHolder(); setEncryptionData(encryptionKeyChunk.dataSpec.uri, encryptionKeyChunk.iv, encryptionKeyChunk.getResult()); } }
@Override public boolean continueLoading(long positionUs) { if (loadingFinished || loader.isLoading()) { return false; } chunkSource.getNextChunk(mediaChunks.isEmpty() ? null : mediaChunks.getLast(), pendingResetPositionUs != C.TIME_UNSET ? pendingResetPositionUs : positionUs, nextChunkHolder); boolean endOfStream = nextChunkHolder.endOfStream; Chunk loadable = nextChunkHolder.chunk; HlsMasterPlaylist.HlsUrl playlistToLoad = nextChunkHolder.playlist; nextChunkHolder.clear(); if (endOfStream) { loadingFinished = true; return true; } if (loadable == null) { if (playlistToLoad != null) { callback.onPlaylistRefreshRequired(playlistToLoad); } return false; } if (isMediaChunk(loadable)) { pendingResetPositionUs = C.TIME_UNSET; HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable; mediaChunk.init(this); mediaChunks.add(mediaChunk); } long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount); eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs); return true; }
@Override public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) { chunkSource.onChunkLoadCompleted(loadable); eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); if (!prepared) { continueLoading(lastSeekPositionUs); } else { callback.onContinueLoadingRequested(this); } }
@Override public void onLoadCanceled(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { eventDispatcher.loadCanceled(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); if (!released) { int sampleQueueCount = sampleQueues.size(); for (int i = 0; i < sampleQueueCount; i++) { sampleQueues.valueAt(i).reset(groupEnabledStates[i]); } callback.onContinueLoadingRequested(this); } }
@Override public int onLoadError(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { long bytesLoaded = loadable.bytesLoaded(); boolean isMediaChunk = isMediaChunk(loadable); boolean cancelable = !isMediaChunk || bytesLoaded == 0; boolean canceled = false; if (chunkSource.onChunkLoadError(loadable, cancelable, error)) { if (isMediaChunk) { HlsMediaChunk removed = mediaChunks.removeLast(); Assertions.checkState(removed == loadable); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; } } canceled = true; } eventDispatcher.loadError(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, canceled); if (canceled) { if (!prepared) { continueLoading(lastSeekPositionUs); } else { callback.onContinueLoadingRequested(this); } return Loader.DONT_RETRY; } else { return Loader.RETRY; } }
/** * Called when the {@link HlsSampleStreamWrapper} has finished loading a chunk obtained from this * source. * * @param chunk The chunk whose load has been completed. */ public void onChunkLoadCompleted(Chunk chunk) { if (chunk instanceof HlsInitializationChunk) { lastLoadedInitializationChunk = (HlsInitializationChunk) chunk; } else if (chunk instanceof MediaPlaylistChunk) { MediaPlaylistChunk mediaPlaylistChunk = (MediaPlaylistChunk) chunk; scratchSpace = mediaPlaylistChunk.getDataHolder(); setMediaPlaylist(mediaPlaylistChunk.variantIndex, mediaPlaylistChunk.getResult()); } else if (chunk instanceof EncryptionKeyChunk) { EncryptionKeyChunk encryptionKeyChunk = (EncryptionKeyChunk) chunk; scratchSpace = encryptionKeyChunk.getDataHolder(); setEncryptionData(encryptionKeyChunk.dataSpec.uri, encryptionKeyChunk.iv, encryptionKeyChunk.getResult()); } }
@Override public void onLoadCanceled(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { eventDispatcher.loadCanceled(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); if (!released) { resetSampleQueues(); if (enabledTrackCount > 0) { callback.onContinueLoadingRequested(this); } } }
private boolean isMediaChunk(Chunk chunk) { return chunk instanceof HlsMediaChunk; }
@Override public void onChunkLoadCompleted(Chunk chunk) { // Do nothing. }
@Override public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { return cancelable && ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection, trackSelection.indexOf(chunk.trackFormat), e); }
@Override public boolean continueLoading(long positionUs) { if (loader.isLoading()) { return false; } chunkSource.getNextChunk(mediaChunks.isEmpty() ? null : mediaChunks.getLast(), pendingResetPositionUs != C.TIME_UNSET ? pendingResetPositionUs : positionUs, nextChunkHolder); boolean endOfStream = nextChunkHolder.endOfStream; Chunk loadable = nextChunkHolder.chunk; long retryInMs = nextChunkHolder.retryInMs; nextChunkHolder.clear(); if (endOfStream) { loadingFinished = true; return true; } if (loadable == null) { if (retryInMs != C.TIME_UNSET) { Assertions.checkState(chunkSource.isLive()); callback.onContinueLoadingRequiredInMs(this, retryInMs); } return false; } if (isMediaChunk(loadable)) { pendingResetPositionUs = C.TIME_UNSET; HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable; mediaChunk.init(this); mediaChunks.add(mediaChunk); } else if (loadable instanceof HlsInitializationChunk) { ((HlsInitializationChunk) loadable).init(this); } long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount); eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs); return true; }
@Override public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { return false; }
@Override public boolean continueLoading(long positionUs) { if (loadingFinished || loader.isLoading()) { return false; } HlsMediaChunk previousChunk; long loadPositionUs; if (isPendingReset()) { previousChunk = null; loadPositionUs = pendingResetPositionUs; } else { previousChunk = mediaChunks.getLast(); loadPositionUs = previousChunk.endTimeUs; } chunkSource.getNextChunk(previousChunk, positionUs, loadPositionUs, nextChunkHolder); boolean endOfStream = nextChunkHolder.endOfStream; Chunk loadable = nextChunkHolder.chunk; HlsMasterPlaylist.HlsUrl playlistToLoad = nextChunkHolder.playlist; nextChunkHolder.clear(); if (endOfStream) { pendingResetPositionUs = C.TIME_UNSET; loadingFinished = true; return true; } if (loadable == null) { if (playlistToLoad != null) { callback.onPlaylistRefreshRequired(playlistToLoad); } return false; } if (isMediaChunk(loadable)) { pendingResetPositionUs = C.TIME_UNSET; HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable; mediaChunk.init(this); mediaChunks.add(mediaChunk); } long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount); eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs); return true; }