use of androidx.media3.datasource.DataSource in project media by androidx.
the class DefaultDashChunkSource method getNextChunk.
@Override
public void getNextChunk(long playbackPositionUs, long loadPositionUs, List<? extends MediaChunk> queue, ChunkHolder out) {
if (fatalError != null) {
return;
}
long bufferedDurationUs = loadPositionUs - playbackPositionUs;
long presentationPositionUs = Util.msToUs(manifest.availabilityStartTimeMs) + Util.msToUs(manifest.getPeriod(periodIndex).startMs) + loadPositionUs;
if (playerTrackEmsgHandler != null && playerTrackEmsgHandler.maybeRefreshManifestBeforeLoadingNextChunk(presentationPositionUs)) {
return;
}
long nowUnixTimeUs = Util.msToUs(Util.getNowUnixTimeMs(elapsedRealtimeOffsetMs));
long nowPeriodTimeUs = getNowPeriodTimeUs(nowUnixTimeUs);
MediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
for (int i = 0; i < chunkIterators.length; i++) {
RepresentationHolder representationHolder = representationHolders[i];
if (representationHolder.segmentIndex == null) {
chunkIterators[i] = MediaChunkIterator.EMPTY;
} else {
long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs);
long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(nowUnixTimeUs);
long segmentNum = getSegmentNum(representationHolder, previous, loadPositionUs, firstAvailableSegmentNum, lastAvailableSegmentNum);
if (segmentNum < firstAvailableSegmentNum) {
chunkIterators[i] = MediaChunkIterator.EMPTY;
} else {
representationHolder = updateSelectedBaseUrl(/* trackIndex= */
i);
chunkIterators[i] = new RepresentationSegmentIterator(representationHolder, segmentNum, lastAvailableSegmentNum, nowPeriodTimeUs);
}
}
}
long availableLiveDurationUs = getAvailableLiveDurationUs(nowUnixTimeUs, playbackPositionUs);
trackSelection.updateSelectedTrack(playbackPositionUs, bufferedDurationUs, availableLiveDurationUs, queue, chunkIterators);
RepresentationHolder representationHolder = updateSelectedBaseUrl(trackSelection.getSelectedIndex());
if (representationHolder.chunkExtractor != null) {
Representation selectedRepresentation = representationHolder.representation;
@Nullable RangedUri pendingInitializationUri = null;
@Nullable RangedUri pendingIndexUri = null;
if (representationHolder.chunkExtractor.getSampleFormats() == null) {
pendingInitializationUri = selectedRepresentation.getInitializationUri();
}
if (representationHolder.segmentIndex == null) {
pendingIndexUri = selectedRepresentation.getIndexUri();
}
if (pendingInitializationUri != null || pendingIndexUri != null) {
// We have initialization and/or index requests to make.
out.chunk = newInitializationChunk(representationHolder, dataSource, trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), trackSelection.getSelectionData(), pendingInitializationUri, pendingIndexUri);
return;
}
}
long periodDurationUs = representationHolder.periodDurationUs;
boolean periodEnded = periodDurationUs != C.TIME_UNSET;
if (representationHolder.getSegmentCount() == 0) {
// The index doesn't define any segments.
out.endOfStream = periodEnded;
return;
}
long firstAvailableSegmentNum = representationHolder.getFirstAvailableSegmentNum(nowUnixTimeUs);
long lastAvailableSegmentNum = representationHolder.getLastAvailableSegmentNum(nowUnixTimeUs);
long segmentNum = getSegmentNum(representationHolder, previous, loadPositionUs, firstAvailableSegmentNum, lastAvailableSegmentNum);
if (segmentNum < firstAvailableSegmentNum) {
// This is before the first chunk in the current manifest.
fatalError = new BehindLiveWindowException();
return;
}
if (segmentNum > lastAvailableSegmentNum || (missingLastSegment && segmentNum >= lastAvailableSegmentNum)) {
// The segment is beyond the end of the period.
out.endOfStream = periodEnded;
return;
}
if (periodEnded && representationHolder.getSegmentStartTimeUs(segmentNum) >= periodDurationUs) {
// The period duration clips the period to a position before the segment.
out.endOfStream = true;
return;
}
int maxSegmentCount = (int) min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1);
if (periodDurationUs != C.TIME_UNSET) {
while (maxSegmentCount > 1 && representationHolder.getSegmentStartTimeUs(segmentNum + maxSegmentCount - 1) >= periodDurationUs) {
// The period duration clips the period to a position before the last segment in the range
// [segmentNum, segmentNum + maxSegmentCount - 1]. Reduce maxSegmentCount.
maxSegmentCount--;
}
}
long seekTimeUs = queue.isEmpty() ? loadPositionUs : C.TIME_UNSET;
out.chunk = newMediaChunk(representationHolder, dataSource, trackType, trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), trackSelection.getSelectionData(), segmentNum, maxSegmentCount, seekTimeUs, nowPeriodTimeUs);
}
use of androidx.media3.datasource.DataSource in project media by androidx.
the class DefaultDashChunkSource method newMediaChunk.
protected Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, @C.TrackType int trackType, Format trackFormat, @C.SelectionReason int trackSelectionReason, Object trackSelectionData, long firstSegmentNum, int maxSegmentCount, long seekTimeUs, long nowPeriodTimeUs) {
Representation representation = representationHolder.representation;
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
if (representationHolder.chunkExtractor == null) {
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
int flags = representationHolder.isSegmentAvailableAtFullNetworkSpeed(firstSegmentNum, nowPeriodTimeUs) ? 0 : DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DashUtil.buildDataSpec(representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, trackType, trackFormat);
} else {
int segmentCount = 1;
for (int i = 1; i < maxSegmentCount; i++) {
RangedUri nextSegmentUri = representationHolder.getSegmentUrl(firstSegmentNum + i);
@Nullable RangedUri mergedSegmentUri = segmentUri.attemptMerge(nextSegmentUri, representationHolder.selectedBaseUrl.url);
if (mergedSegmentUri == null) {
// Unable to merge segment fetches because the URIs do not merge.
break;
}
segmentUri = mergedSegmentUri;
segmentCount++;
}
long segmentNum = firstSegmentNum + segmentCount - 1;
long endTimeUs = representationHolder.getSegmentEndTimeUs(segmentNum);
long periodDurationUs = representationHolder.periodDurationUs;
long clippedEndTimeUs = periodDurationUs != C.TIME_UNSET && periodDurationUs <= endTimeUs ? periodDurationUs : C.TIME_UNSET;
int flags = representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowPeriodTimeUs) ? 0 : DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DashUtil.buildDataSpec(representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
return new ContainerMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, seekTimeUs, clippedEndTimeUs, firstSegmentNum, segmentCount, sampleOffsetUs, representationHolder.chunkExtractor);
}
}
use of androidx.media3.datasource.DataSource in project media by androidx.
the class HlsMediaChunk method createInstance.
/**
* Creates a new instance.
*
* @param extractorFactory A {@link HlsExtractorFactory} from which the {@link
* HlsMediaChunkExtractor} is obtained.
* @param dataSource The source from which the data should be loaded.
* @param format The chunk format.
* @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
* @param mediaPlaylist The media playlist from which this chunk was obtained.
* @param segmentBaseHolder The segment holder.
* @param playlistUrl The url of the playlist from which this chunk was obtained.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the multivariant playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
* @param timestampAdjusterProvider The provider from which to obtain the {@link
* TimestampAdjuster}.
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
* @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
* otherwise.
* @param shouldSpliceIn Whether samples for this chunk should be spliced into existing samples.
*/
public static HlsMediaChunk createInstance(HlsExtractorFactory extractorFactory, DataSource dataSource, Format format, long startOfPlaylistInPeriodUs, HlsMediaPlaylist mediaPlaylist, HlsChunkSource.SegmentBaseHolder segmentBaseHolder, Uri playlistUrl, @Nullable List<Format> muxedCaptionFormats, @C.SelectionReason int trackSelectionReason, @Nullable Object trackSelectionData, boolean isMasterTimestampSource, TimestampAdjusterProvider timestampAdjusterProvider, @Nullable HlsMediaChunk previousChunk, @Nullable byte[] mediaSegmentKey, @Nullable byte[] initSegmentKey, boolean shouldSpliceIn, PlayerId playerId) {
// Media segment.
HlsMediaPlaylist.SegmentBase mediaSegment = segmentBaseHolder.segmentBase;
DataSpec dataSpec = new DataSpec.Builder().setUri(UriUtil.resolveToUri(mediaPlaylist.baseUri, mediaSegment.url)).setPosition(mediaSegment.byteRangeOffset).setLength(mediaSegment.byteRangeLength).setFlags(segmentBaseHolder.isPreload ? FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED : 0).build();
boolean mediaSegmentEncrypted = mediaSegmentKey != null;
@Nullable byte[] mediaSegmentIv = mediaSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(mediaSegment.encryptionIV)) : null;
DataSource mediaDataSource = buildDataSource(dataSource, mediaSegmentKey, mediaSegmentIv);
// Init segment.
HlsMediaPlaylist.Segment initSegment = mediaSegment.initializationSegment;
DataSpec initDataSpec = null;
boolean initSegmentEncrypted = false;
@Nullable DataSource initDataSource = null;
if (initSegment != null) {
initSegmentEncrypted = initSegmentKey != null;
@Nullable byte[] initSegmentIv = initSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV)) : null;
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
initDataSpec = new DataSpec(initSegmentUri, initSegment.byteRangeOffset, initSegment.byteRangeLength);
initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv);
}
long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + mediaSegment.relativeStartTimeUs;
long segmentEndTimeInPeriodUs = segmentStartTimeInPeriodUs + mediaSegment.durationUs;
int discontinuitySequenceNumber = mediaPlaylist.discontinuitySequence + mediaSegment.relativeDiscontinuitySequence;
@Nullable HlsMediaChunkExtractor previousExtractor = null;
Id3Decoder id3Decoder;
ParsableByteArray scratchId3Data;
if (previousChunk != null) {
boolean isSameInitData = initDataSpec == previousChunk.initDataSpec || (initDataSpec != null && previousChunk.initDataSpec != null && initDataSpec.uri.equals(previousChunk.initDataSpec.uri) && initDataSpec.position == previousChunk.initDataSpec.position);
boolean isFollowingChunk = playlistUrl.equals(previousChunk.playlistUrl) && previousChunk.loadCompleted;
id3Decoder = previousChunk.id3Decoder;
scratchId3Data = previousChunk.scratchId3Data;
previousExtractor = isSameInitData && isFollowingChunk && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null;
} else {
id3Decoder = new Id3Decoder();
scratchId3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
}
return new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, format, mediaSegmentEncrypted, initDataSource, initDataSpec, initSegmentEncrypted, playlistUrl, muxedCaptionFormats, trackSelectionReason, trackSelectionData, segmentStartTimeInPeriodUs, segmentEndTimeInPeriodUs, segmentBaseHolder.mediaSequence, segmentBaseHolder.partIndex, /* isPublished= */
!segmentBaseHolder.isPreload, discontinuitySequenceNumber, mediaSegment.hasGapTag, isMasterTimestampSource, /* timestampAdjuster= */
timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber), mediaSegment.drmInitData, previousExtractor, id3Decoder, scratchId3Data, shouldSpliceIn, playerId);
}
use of androidx.media3.datasource.DataSource in project media by androidx.
the class HlsMediaChunk method prepareExtraction.
@RequiresNonNull("output")
@EnsuresNonNull("extractor")
private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec dataSpec, boolean initializeTimestampAdjuster) throws IOException {
long bytesToRead = dataSource.open(dataSpec);
if (initializeTimestampAdjuster) {
try {
timestampAdjuster.sharedInitializeOrWait(isMasterTimestampSource, startTimeUs);
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
}
DefaultExtractorInput extractorInput = new DefaultExtractorInput(dataSource, dataSpec.position, bytesToRead);
if (extractor == null) {
long id3Timestamp = peekId3PrivTimestamp(extractorInput);
extractorInput.resetPeekPosition();
extractor = previousExtractor != null ? previousExtractor.recreate() : extractorFactory.createExtractor(dataSpec.uri, trackFormat, muxedCaptionFormats, timestampAdjuster, dataSource.getResponseHeaders(), extractorInput, playerId);
if (extractor.isPackedAudioExtractor()) {
output.setSampleOffsetUs(id3Timestamp != C.TIME_UNSET ? timestampAdjuster.adjustTsTimestamp(id3Timestamp) : startTimeUs);
} else {
// In case the container format changes mid-stream to non-packed-audio, we need to reset
// the timestamp offset.
output.setSampleOffsetUs(/* sampleOffsetUs= */
0L);
}
output.onNewExtractor();
extractor.init(output);
}
output.setDrmInitData(drmInitData);
return extractorInput;
}
use of androidx.media3.datasource.DataSource in project media by androidx.
the class DashDownloader method getSegments.
@Override
protected List<Segment> getSegments(DataSource dataSource, DashManifest manifest, boolean removing) throws IOException, InterruptedException {
ArrayList<Segment> segments = new ArrayList<>();
for (int i = 0; i < manifest.getPeriodCount(); i++) {
Period period = manifest.getPeriod(i);
long periodStartUs = Util.msToUs(period.startMs);
long periodDurationUs = manifest.getPeriodDurationUs(i);
List<AdaptationSet> adaptationSets = period.adaptationSets;
for (int j = 0; j < adaptationSets.size(); j++) {
addSegmentsForAdaptationSet(dataSource, adaptationSets.get(j), periodStartUs, periodDurationUs, removing, segments);
}
}
return segments;
}
Aggregations