@Override public int onLoadError(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); if (isFatal) { return Loader.DONT_RETRY_FATAL; } boolean shouldRetry = true; if (ChunkedTrackBlacklistUtil.shouldBlacklist(error)) { blacklistUntilMs = SystemClock.elapsedRealtime() + ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS; notifyPlaylistBlacklisting(playlistUrl, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS); shouldRetry = primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl(); } return shouldRetry ? Loader.RETRY : Loader.DONT_RETRY; }
/** * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param callback A callback for the wrapper. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param positionUs The position from which to start loading media. * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param minLoadableRetryCount The minimum number of times that the source should retry a load * before propagating an error. * @param eventDispatcher A dispatcher to notify of events. */ public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource chunkSource, Allocator allocator, long positionUs, Format muxedAudioFormat, int minLoadableRetryCount, EventDispatcher eventDispatcher) { this.trackType = trackType; this.callback = callback; this.chunkSource = chunkSource; this.allocator = allocator; this.muxedAudioFormat = muxedAudioFormat; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; loader = new Loader("Loader:HlsSampleStreamWrapper"); nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); sampleQueues = new SparseArray<>(); mediaChunks = new LinkedList<>(); maybeFinishPrepareRunnable = new Runnable() { @Override public void run() { maybeFinishPrepare(); } }; handler = new Handler(); lastSeekPositionUs = positionUs; pendingResetPositionUs = positionUs; }
public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, MediaSource.Listener sourceListener, Allocator allocator, long positionUs) { this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.sourceListener = sourceListener; this.allocator = allocator; streamWrapperIndices = new IdentityHashMap<>(); timestampAdjusterProvider = new TimestampAdjusterProvider(); manifestParser = new HlsPlaylistParser(); continueLoadingHandler = new Handler(); manifestFetcher = new Loader("Loader:ManifestFetcher"); preparePositionUs = positionUs; continueLoadingRunnable = new Runnable() { @Override public void run() { callback.onContinueLoadingRequested(HlsMediaPeriod.this); } }; }
/** * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param callback A callback for the wrapper. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param positionUs The position from which to start loading media. * @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio, * this is the audio {@link Format} as defined by the playlist. * @param muxedCaptionFormat If HLS master playlist indicates that the stream contains muxed * captions, this is the audio {@link Format} as defined by the playlist. * @param minLoadableRetryCount The minimum number of times that the source should retry a load * before propagating an error. * @param eventDispatcher A dispatcher to notify of events. */ public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource chunkSource, Allocator allocator, long positionUs, Format muxedAudioFormat, Format muxedCaptionFormat, int minLoadableRetryCount, EventDispatcher eventDispatcher) { this.trackType = trackType; this.callback = callback; this.chunkSource = chunkSource; this.allocator = allocator; this.muxedAudioFormat = muxedAudioFormat; this.muxedCaptionFormat = muxedCaptionFormat; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; loader = new Loader("Loader:HlsSampleStreamWrapper"); nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); sampleQueues = new SparseArray<>(); mediaChunks = new LinkedList<>(); maybeFinishPrepareRunnable = new Runnable() { @Override public void run() { maybeFinishPrepare(); } }; handler = new Handler(); lastSeekPositionUs = positionUs; pendingResetPositionUs = positionUs; }
/** * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param chunkSource A {@link ChunkSource} from which chunks to load are obtained. * @param callback An {@link Callback} for the stream. * @param allocator An {@link Allocator} from which allocations can be obtained. * @param positionUs The position from which to start loading media. * @param minLoadableRetryCount The minimum number of times that the source should retry a load * before propagating an error. * @param eventDispatcher A dispatcher to notify of events. */ public ChunkSampleStream(int trackType, T chunkSource, SequenceableLoader.Callback<ChunkSampleStream<T>> callback, Allocator allocator, long positionUs, int minLoadableRetryCount, EventDispatcher eventDispatcher) { this.trackType = trackType; this.chunkSource = chunkSource; this.callback = callback; this.eventDispatcher = eventDispatcher; this.minLoadableRetryCount = minLoadableRetryCount; loader = new Loader("Loader:ChunkSampleStream"); nextChunkHolder = new ChunkHolder(); mediaChunks = new LinkedList<>(); readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks); sampleQueue = new DefaultTrackOutput(allocator); lastSeekPositionUs = positionUs; pendingResetPositionUs = positionUs; }
/** * @param initialPlaylistUri Uri for the initial playlist of the stream. Can refer a media * playlist or a master playlist. * @param dataSourceFactory A factory for {@link DataSource} instances. * @param eventDispatcher A dispatcher to notify of events. * @param minRetryCount The minimum number of times loads must be retried before * {@link #maybeThrowPlaylistRefreshError(HlsUrl)} and * {@link #maybeThrowPrimaryPlaylistRefreshError()} propagate any loading errors. * @param primaryPlaylistListener A callback for the primary playlist change events. */ public HlsPlaylistTracker(Uri initialPlaylistUri, HlsDataSourceFactory dataSourceFactory, EventDispatcher eventDispatcher, int minRetryCount, PrimaryPlaylistListener primaryPlaylistListener, ParsingLoadable.Parser<HlsPlaylist> playlistParser) { this.initialPlaylistUri = initialPlaylistUri; this.dataSourceFactory = dataSourceFactory; this.eventDispatcher = eventDispatcher; this.minRetryCount = minRetryCount; this.primaryPlaylistListener = primaryPlaylistListener; this.playlistParser = playlistParser; listeners = new ArrayList<>(); initialPlaylistLoader = new Loader("HlsPlaylistTracker:MasterPlaylist"); playlistBundles = new IdentityHashMap<>(); playlistRefreshHandler = new Handler(); }
/** * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param callback A callback for the wrapper. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param positionUs The position from which to start loading media. * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param minLoadableRetryCount The minimum number of times that the source should retry a load * before propagating an error. * @param eventDispatcher A dispatcher to notify of events. */ public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource chunkSource, Allocator allocator, long positionUs, Format muxedAudioFormat, int minLoadableRetryCount, EventDispatcher eventDispatcher) { this.trackType = trackType; this.callback = callback; this.chunkSource = chunkSource; this.allocator = allocator; this.muxedAudioFormat = muxedAudioFormat; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; loader = new Loader("Loader:HlsSampleStreamWrapper"); nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); sampleQueueTrackIds = new int[0]; sampleQueues = new SampleQueue[0]; mediaChunks = new LinkedList<>(); maybeFinishPrepareRunnable = new Runnable() { @Override public void run() { maybeFinishPrepare(); } }; handler = new Handler(); lastSeekPositionUs = positionUs; pendingResetPositionUs = positionUs; }
public SingleSampleMediaPeriod( DataSpec dataSpec, DataSource.Factory dataSourceFactory, Format format, long durationUs, int minLoadableRetryCount, EventDispatcher eventDispatcher, boolean treatLoadErrorsAsEndOfStream) { this.dataSpec = dataSpec; this.dataSourceFactory = dataSourceFactory; this.format = format; this.durationUs = durationUs; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream; tracks = new TrackGroupArray(new TrackGroup(format)); sampleStreams = new ArrayList<>(); loader = new Loader("Loader:SingleSampleMediaPeriod"); }
@Override public int onLoadError(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { errorCount++; boolean cancel = treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount; eventDispatcher.loadError( loadable.dataSpec, C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, format, C.SELECTION_REASON_UNKNOWN, /* trackSelectionData= */ null, /* mediaStartTimeUs= */ 0, durationUs, elapsedRealtimeMs, loadDurationMs, loadable.sampleSize, error, /* wasCanceled= */ cancel); if (cancel) { loadingFinished = true; return Loader.DONT_RETRY; } return Loader.RETRY; }
/** * @param initialPlaylistUri Uri for the initial playlist of the stream. Can refer a media * playlist or a master playlist. * @param dataSourceFactory A factory for {@link DataSource} instances. * @param eventDispatcher A dispatcher to notify of events. * @param minRetryCount The minimum number of times the load must be retried before blacklisting a * playlist. * @param primaryPlaylistListener A callback for the primary playlist change events. */ public HlsPlaylistTracker(Uri initialPlaylistUri, HlsDataSourceFactory dataSourceFactory, EventDispatcher eventDispatcher, int minRetryCount, PrimaryPlaylistListener primaryPlaylistListener) { this.initialPlaylistUri = initialPlaylistUri; this.dataSourceFactory = dataSourceFactory; this.eventDispatcher = eventDispatcher; this.minRetryCount = minRetryCount; this.primaryPlaylistListener = primaryPlaylistListener; listeners = new ArrayList<>(); initialPlaylistLoader = new Loader("HlsPlaylistTracker:MasterPlaylist"); playlistParser = new HlsPlaylistParser(); playlistBundles = new IdentityHashMap<>(); playlistRefreshHandler = new Handler(); }
@Override public int onLoadError(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY; }
public MediaPlaylistBundle(HlsUrl playlistUrl, long initialLastSnapshotAccessTimeMs) { this.playlistUrl = playlistUrl; lastSnapshotAccessTimeMs = initialLastSnapshotAccessTimeMs; mediaPlaylistLoader = new Loader("HlsPlaylistTracker:MediaPlaylist"); mediaPlaylistLoadable = new ParsingLoadable<>( dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST), UriUtil.resolveToUri(masterPlaylist.baseUri, playlistUrl.url), C.DATA_TYPE_MANIFEST, playlistParser); }
@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; } }
/** * @param uri The {@link Uri} of the media stream. * @param dataSource The data source to read the media. * @param extractors The extractors to use to read the data source. * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. * @param eventHandler A handler for events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param sourceListener A listener to notify when the timeline has been loaded. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache * indexing. May be null. */ public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors, int minLoadableRetryCount, Handler eventHandler, ExtractorMediaSource.EventListener eventListener, MediaSource.Listener sourceListener, Allocator allocator, String customCacheKey) { this.uri = uri; this.dataSource = dataSource; this.minLoadableRetryCount = minLoadableRetryCount; this.eventHandler = eventHandler; this.eventListener = eventListener; this.sourceListener = sourceListener; this.allocator = allocator; this.customCacheKey = customCacheKey; loader = new Loader("Loader:ExtractorMediaPeriod"); extractorHolder = new ExtractorHolder(extractors, this); loadCondition = new ConditionVariable(); maybeFinishPrepareRunnable = new Runnable() { @Override public void run() { maybeFinishPrepare(); } }; onContinueLoadingRequestedRunnable = new Runnable() { @Override public void run() { if (!released) { callback.onContinueLoadingRequested(ExtractorMediaPeriod.this); } } }; handler = new Handler(); pendingResetPositionUs = C.TIME_UNSET; sampleQueues = new SparseArray<>(); length = C.LENGTH_UNSET; }
@Override public int onLoadError(ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { copyLengthFromLoader(loadable); notifyLoadError(error); if (isLoadableExceptionFatal(error)) { return Loader.DONT_RETRY_FATAL; } int extractedSamplesCount = getExtractedSamplesCount(); boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad; configureRetry(loadable); // May reset the sample queues. extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount(); return madeProgress ? Loader.RETRY_RESET_ERROR_COUNT : Loader.RETRY; }
/** * @param primaryTrackType The type of the primary track. One of the {@link C} * {@code TRACK_TYPE_*} constants. * @param embeddedTrackTypes The types of any embedded tracks, or null. * @param chunkSource A {@link ChunkSource} from which chunks to load are obtained. * @param callback An {@link Callback} for the stream. * @param allocator An {@link Allocator} from which allocations can be obtained. * @param positionUs The position from which to start loading media. * @param minLoadableRetryCount The minimum number of times that the source should retry a load * before propagating an error. * @param eventDispatcher A dispatcher to notify of events. */ public ChunkSampleStream(int primaryTrackType, int[] embeddedTrackTypes, T chunkSource, Callback<ChunkSampleStream<T>> callback, Allocator allocator, long positionUs, int minLoadableRetryCount, EventDispatcher eventDispatcher) { this.primaryTrackType = primaryTrackType; this.embeddedTrackTypes = embeddedTrackTypes; this.chunkSource = chunkSource; this.callback = callback; this.eventDispatcher = eventDispatcher; this.minLoadableRetryCount = minLoadableRetryCount; loader = new Loader("Loader:ChunkSampleStream"); nextChunkHolder = new ChunkHolder(); mediaChunks = new LinkedList<>(); readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks); int embeddedTrackCount = embeddedTrackTypes == null ? 0 : embeddedTrackTypes.length; embeddedSampleQueues = new DefaultTrackOutput[embeddedTrackCount]; embeddedTracksSelected = new boolean[embeddedTrackCount]; int[] trackTypes = new int[1 + embeddedTrackCount]; DefaultTrackOutput[] sampleQueues = new DefaultTrackOutput[1 + embeddedTrackCount]; primarySampleQueue = new DefaultTrackOutput(allocator); trackTypes[0] = primaryTrackType; sampleQueues[0] = primarySampleQueue; for (int i = 0; i < embeddedTrackCount; i++) { DefaultTrackOutput trackOutput = new DefaultTrackOutput(allocator); embeddedSampleQueues[i] = trackOutput; sampleQueues[i + 1] = trackOutput; trackTypes[i + 1] = embeddedTrackTypes[i]; } mediaChunkOutput = new BaseMediaChunkOutput(trackTypes, sampleQueues); pendingResetPositionUs = positionUs; lastSeekPositionUs = positionUs; }
@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 || mediaChunks.size() > 1; boolean canceled = false; if (chunkSource.onChunkLoadError(loadable, cancelable, error)) { canceled = true; if (isMediaChunk) { BaseMediaChunk removed = mediaChunks.removeLast(); Assertions.checkState(removed == loadable); primarySampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex(0)); for (int i = 0; i < embeddedSampleQueues.length; i++) { embeddedSampleQueues[i].discardUpstreamSamples(removed.getFirstSampleIndex(i + 1)); } if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; } } } eventDispatcher.loadError(loadable.dataSpec, loadable.type, primaryTrackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, bytesLoaded, error, canceled); if (canceled) { callback.onContinueLoadingRequested(this); return Loader.DONT_RETRY; } else { return Loader.RETRY; } }
public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, int eventSourceId) { this.uri = uri; this.dataSourceFactory = dataSourceFactory; this.format = format; this.minLoadableRetryCount = minLoadableRetryCount; this.eventHandler = eventHandler; this.eventListener = eventListener; this.eventSourceId = eventSourceId; tracks = new TrackGroupArray(new TrackGroup(format)); sampleStreams = new ArrayList<>(); loader = new Loader("Loader:SingleSampleMediaPeriod"); }
@Override public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { sourceListener = listener; if (sideloadedManifest) { loaderErrorThrower = new LoaderErrorThrower.Dummy(); processManifest(false); } else { dataSource = manifestDataSourceFactory.createDataSource(); loader = new Loader("Loader:DashMediaSource"); loaderErrorThrower = loader; handler = new Handler(); startLoadingManifest(); } }
int onManifestLoadError(ParsingLoadable<DashManifest> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY; }
int onUtcTimestampLoadError(ParsingLoadable<Long> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, true); onUtcTimestampResolutionError(error); return Loader.DONT_RETRY; }
@Override public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { sourceListener = listener; if (manifest != null) { manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy(); processManifest(); } else { manifestDataSource = manifestDataSourceFactory.createDataSource(); manifestLoader = new Loader("Loader:Manifest"); manifestLoaderErrorThrower = manifestLoader; manifestRefreshHandler = new Handler(); startLoadingManifest(); } }
@Override public int onLoadError(ParsingLoadable<SsManifest> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY; }
@Override public int onLoadError(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { boolean isFatal = error instanceof ParserException; eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded(), error, isFatal); return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY; }
/** * @param uri The {@link Uri} of the media stream. * @param dataSource The data source to read the media. * @param extractors The extractors to use to read the data source. * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. * @param eventHandler A handler for events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param sourceListener A listener to notify when the timeline has been loaded. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. */ public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors, int minLoadableRetryCount, Handler eventHandler, ExtractorMediaSource.EventListener eventListener, MediaSource.Listener sourceListener, Allocator allocator) { this.uri = uri; this.dataSource = dataSource; this.minLoadableRetryCount = minLoadableRetryCount; this.eventHandler = eventHandler; this.eventListener = eventListener; this.sourceListener = sourceListener; this.allocator = allocator; loader = new Loader("Loader:ExtractorMediaPeriod"); extractorHolder = new ExtractorHolder(extractors, this); loadCondition = new ConditionVariable(); maybeFinishPrepareRunnable = new Runnable() { @Override public void run() { maybeFinishPrepare(); } }; onContinueLoadingRequestedRunnable = new Runnable() { @Override public void run() { if (!released) { callback.onContinueLoadingRequested(ExtractorMediaPeriod.this); } } }; handler = new Handler(); pendingResetPositionUs = C.TIME_UNSET; sampleQueues = new SparseArray<>(); length = C.LENGTH_UNSET; }
@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 || mediaChunks.size() > 1; boolean canceled = false; if (chunkSource.onChunkLoadError(loadable, cancelable, error)) { canceled = true; if (isMediaChunk) { BaseMediaChunk removed = mediaChunks.removeLast(); Assertions.checkState(removed == loadable); sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex()); if (mediaChunks.isEmpty()) { pendingResetPositionUs = lastSeekPositionUs; } } } eventDispatcher.loadError(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat, loadable.trackSelectionReason, loadable.trackSelectionData, loadable.startTimeUs, loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, bytesLoaded, error, canceled); if (canceled) { callback.onContinueLoadingRequested(this); return Loader.DONT_RETRY; } else { return Loader.RETRY; } }