@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); }
@Override public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { if (queue.isEmpty()) { return 0; } int queueSize = queue.size(); long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs; if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) { return queueSize; } int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime()); Format idealFormat = getFormat(idealSelectedIndex); // Discard from the first SD chunk beyond minDurationToRetainAfterDiscardUs whose resolution and // bitrate are both lower than the ideal track. for (int i = 0; i < queueSize; i++) { MediaChunk chunk = queue.get(i); long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs; if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs && chunk.trackFormat.bitrate < idealFormat.bitrate && chunk.trackFormat.height < idealFormat.height && chunk.trackFormat.height < 720 && chunk.trackFormat.width < 1280) { return i; } } return queueSize; }
@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); }
@Override public void getNextChunk(MediaChunk previous, long playbackPositionUs, long loadPositionUs, ChunkHolder out) { long bufferedDurationUs = loadPositionUs - playbackPositionUs; trackSelection.updateSelectedTrack(playbackPositionUs, bufferedDurationUs, C.TIME_UNSET); int chunkIndex = previous == null ? dataSet.getChunkIndexByPosition(playbackPositionUs) : previous.getNextChunkIndex(); if (chunkIndex >= dataSet.getChunkCount()) { out.endOfStream = true; } else { Format selectedFormat = trackSelection.getSelectedFormat(); long startTimeUs = dataSet.getStartTime(chunkIndex); long endTimeUs = startTimeUs + dataSet.getChunkDuration(chunkIndex); int trackGroupIndex = trackSelection.getIndexInTrackGroup(trackSelection.getSelectedIndex()); String uri = dataSet.getUri(trackGroupIndex); Segment fakeDataChunk = dataSet.getData(uri).getSegments().get(chunkIndex); DataSpec dataSpec = new DataSpec(Uri.parse(uri), fakeDataChunk.byteOffset, fakeDataChunk.length, null); int trackType = MimeTypes.getTrackType(selectedFormat.sampleMimeType); out.chunk = new SingleSampleMediaChunk(dataSource, dataSpec, selectedFormat, trackSelection.getSelectionReason(), trackSelection.getSelectionData(), startTimeUs, endTimeUs, chunkIndex, trackType, selectedFormat); } }
@Override public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { if (queue.isEmpty()) { return 0; } int queueSize = queue.size(); long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs; if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) { return queueSize; } int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime()); Format idealFormat = getFormat(idealSelectedIndex); // If the chunks contain video, discard from the first SD chunk beyond // minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal // track. for (int i = 0; i < queueSize; i++) { MediaChunk chunk = queue.get(i); Format format = chunk.trackFormat; long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs; if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs && format.bitrate < idealFormat.bitrate && format.height != Format.NO_VALUE && format.height < 720 && format.width != Format.NO_VALUE && format.width < 1280 && format.height < idealFormat.height) { return i; } } return queueSize; }
@Override public int getPreferredQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { if (fatalError != null || trackSelection.length() < 2) { return queue.size(); } return trackSelection.evaluateQueueSize(playbackPositionUs, queue); }
private static MediaChunk newMediaChunk(Format format, DataSource dataSource, Uri uri, String cacheKey, int chunkIndex, long chunkStartTimeUs, long chunkEndTimeUs, int trackSelectionReason, Object trackSelectionData, ChunkExtractorWrapper extractorWrapper) { DataSpec dataSpec = new DataSpec(uri, 0, C.LENGTH_UNSET, cacheKey); // In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk. // To convert them the absolute timestamps, we need to set sampleOffsetUs to chunkStartTimeUs. long sampleOffsetUs = chunkStartTimeUs; return new ContainerMediaChunk(dataSource, dataSpec, format, trackSelectionReason, trackSelectionData, chunkStartTimeUs, chunkEndTimeUs, chunkIndex, 1, sampleOffsetUs, extractorWrapper); }
private static MediaChunk newMediaChunk(Format format, DataSource dataSource, Uri uri, String cacheKey, int chunkIndex, long chunkStartTimeUs, long chunkEndTimeUs, int trackSelectionReason, Object trackSelectionData, ChunkExtractorWrapper extractorWrapper) { DataSpec dataSpec = new DataSpec(uri, 0, C.LENGTH_UNSET, cacheKey); // In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk. // To convert them the absolute timestamps, we need to set sampleOffsetUs to chunkStartTimeUs. long sampleOffsetUs = chunkStartTimeUs; return new ContainerMediaChunk(dataSource, dataSpec, format, trackSelectionReason, trackSelectionData, chunkStartTimeUs, chunkEndTimeUs, chunkIndex, sampleOffsetUs, extractorWrapper, format); }
@Override public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { return queue.size(); }
@Override public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { Assert.assertTrue(isEnabled); return 0; }
@Override public int getPreferredQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) { return trackSelection.evaluateQueueSize(playbackPositionUs, queue); }
/** * May be called periodically by sources that load media in discrete {@link MediaChunk}s and * support discarding of buffered chunks in order to re-buffer using a different selected track. * Returns the number of chunks that should be retained in the queue. * <p> * To avoid excessive re-buffering, implementations should normally return the size of the queue. * An example of a case where a smaller value may be returned is if network conditions have * improved dramatically, allowing chunks to be discarded and re-buffered in a track of * significantly higher quality. Discarding chunks may allow faster switching to a higher quality * track in this case. * * @param playbackPositionUs The current playback position in microseconds. * @param queue The queue of buffered {@link MediaChunk}s. Must not be modified. * @return The number of chunks to retain in the queue. */ int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue);
/** * May be called periodically by sources that load media in discrete {@link MediaChunk}s and * support discarding of buffered chunks in order to re-buffer using a different selected track. * Returns the number of chunks that should be retained in the queue. * <p> * To avoid excessive re-buffering, implementations should normally return the size of the queue. * An example of a case where a smaller value may be returned is if network conditions have * improved dramatically, allowing chunks to be discarded and re-buffered in a track of * significantly higher quality. Discarding chunks may allow faster switching to a higher quality * track in this case. This method may only be called when the selection is enabled. * * @param playbackPositionUs The current playback position in microseconds. If playback of the * period to which this track selection belongs has not yet started, the value will be the * starting position in the period minus the duration of any media in previous periods still * to be played. * @param queue The queue of buffered {@link MediaChunk}s. Must not be modified. * @return The number of chunks to retain in the queue. */ int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue);