private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri, Representation representation, ChunkExtractorWrapper extractor, DataSource dataSource, int trigger) { 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, representation.getCacheKey()); return new InitializationChunk(dataSource, dataSpec, trigger, representation.format, extractor); }
private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri, Representation representation, ChunkExtractorWrapper extractor, DataSource dataSource, int manifestIndex, int trigger) { 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, representation.getCacheKey()); return new InitializationChunk(dataSource, dataSpec, trigger, representation.format, extractor, manifestIndex); }
protected Chunk newMediaChunk( PeriodHolder periodHolder, RepresentationHolder representationHolder, DataSource dataSource, MediaFormat mediaFormat, ExposedTrack enabledTrack, int segmentNum, int trigger) { Representation representation = representationHolder.representation; Format format = representation.format; 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()); long sampleOffsetUs = periodHolder.startTimeUs - representation.presentationTimeOffsetUs; if (mimeTypeIsRawText(format.mimeType)) { return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL, format, startTimeUs, endTimeUs, segmentNum, enabledTrack.trackFormat, null, periodHolder.localIndex); } else { boolean isMediaFormatFinal = (mediaFormat != null); return new ContainerMediaChunk(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, segmentNum, sampleOffsetUs, representationHolder.extractorWrapper, mediaFormat, enabledTrack.adaptiveMaxWidth, enabledTrack.adaptiveMaxHeight, periodHolder.drmInitData, isMediaFormatFinal, periodHolder.localIndex); } }
private static Representation buildSegmentTimelineRepresentation(long timelineDurationMs, long timelineStartTimeMs) { List<SegmentTimelineElement> segmentTimeline = new ArrayList<>(); List<RangedUri> mediaSegments = new ArrayList<>(); long segmentStartTimeMs = timelineStartTimeMs; long byteStart = 0; // Create all but the last segment with LIVE_SEGMENT_DURATION_MS. int segmentCount = (int) Util.ceilDivide(timelineDurationMs, LIVE_SEGMENT_DURATION_MS); for (int i = 0; i < segmentCount - 1; i++) { segmentTimeline.add(new SegmentTimelineElement(segmentStartTimeMs, LIVE_SEGMENT_DURATION_MS)); mediaSegments.add(new RangedUri("", "", byteStart, 500L)); segmentStartTimeMs += LIVE_SEGMENT_DURATION_MS; byteStart += 500; } // The final segment duration is calculated so that the total duration is timelineDurationMs. long finalSegmentDurationMs = (timelineStartTimeMs + timelineDurationMs) - segmentStartTimeMs; segmentTimeline.add(new SegmentTimelineElement(segmentStartTimeMs, finalSegmentDurationMs)); mediaSegments.add(new RangedUri("", "", byteStart, 500L)); segmentStartTimeMs += finalSegmentDurationMs; byteStart += 500; // Construct the list. MultiSegmentBase segmentBase = new SegmentList(null, 1000, 0, 0, 0, segmentTimeline, mediaSegments); return Representation.newInstance(null, 0, REGULAR_VIDEO, segmentBase); }
private static Representation generateSegmentTimelineRepresentation(long segmentStartMs, long periodStartMs, long duration) { List<SegmentTimelineElement> segmentTimeline = new ArrayList<>(); List<RangedUri> mediaSegments = new ArrayList<>(); long segmentStartTimeMs = segmentStartMs; long byteStart = 0; for (int i = 0; i < (duration / LIVE_SEGMENT_DURATION_MS); i++) { segmentTimeline.add(new SegmentTimelineElement(segmentStartTimeMs, LIVE_SEGMENT_DURATION_MS)); mediaSegments.add(new RangedUri("", "", byteStart, 500L)); segmentStartTimeMs += LIVE_SEGMENT_DURATION_MS; byteStart += 500; } int startNumber = (int) ((periodStartMs + segmentStartMs) / LIVE_SEGMENT_DURATION_MS); MultiSegmentBase segmentBase = new SegmentList(null, 1000, 0, TrackRenderer.UNKNOWN_TIME_US, startNumber, TrackRenderer.UNKNOWN_TIME_US, segmentTimeline, mediaSegments); return Representation.newInstance(periodStartMs, TrackRenderer.UNKNOWN_TIME_US, null, 0, REGULAR_VIDEO, segmentBase); }
private Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, int segmentNum, int trigger) { Representation representation = representationHolder.representation; DashSegmentIndex segmentIndex = representationHolder.segmentIndex; long startTimeUs = segmentIndex.getTimeUs(segmentNum); long endTimeUs = startTimeUs + segmentIndex.getDurationUs(segmentNum); int absoluteSegmentNum = segmentNum + representationHolder.segmentNumShift; boolean isLastSegment = !currentManifest.dynamic && segmentNum == segmentIndex.getLastSegmentNum(); RangedUri segmentUri = segmentIndex.getSegmentUrl(segmentNum); DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, representation.getCacheKey()); long sampleOffsetUs = representation.periodStartMs * 1000 - representation.presentationTimeOffsetUs; if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) { if (representationHolder.vttHeaderOffsetUs != sampleOffsetUs) { // Update the VTT header. headerBuilder.setLength(0); headerBuilder.append(C.WEBVTT_EXO_HEADER).append("=") .append(C.WEBVTT_EXO_HEADER_OFFSET).append(sampleOffsetUs) .append("\n"); representationHolder.vttHeader = headerBuilder.toString().getBytes(); representationHolder.vttHeaderOffsetUs = sampleOffsetUs; } return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL, representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, MediaFormat.createTextFormat(MimeTypes.TEXT_VTT), null, representationHolder.vttHeader); } else { return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, sampleOffsetUs, representationHolder.extractorWrapper, representationHolder.format, drmInitData, true); } }
private Chunk newInitializationChunk(RangedUri initializationUri, RangedUri indexUri, Representation representation, Extractor extractor, DataSource dataSource, int trigger) { int expectedExtractorResult = Extractor.RESULT_END_OF_STREAM; long indexAnchor = 0; 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. expectedExtractorResult |= Extractor.RESULT_READ_INIT; requestUri = initializationUri.attemptMerge(indexUri); if (requestUri != null) { expectedExtractorResult |= Extractor.RESULT_READ_INDEX; if (extractor.hasRelativeIndexOffsets()) { indexAnchor = indexUri.start + indexUri.length; } } else { requestUri = initializationUri; } } else { requestUri = indexUri; if (extractor.hasRelativeIndexOffsets()) { indexAnchor = indexUri.start + indexUri.length; } expectedExtractorResult |= Extractor.RESULT_READ_INDEX; } DataSpec dataSpec = new DataSpec(requestUri.getUri(), requestUri.start, requestUri.length, representation.getCacheKey()); return new InitializationLoadable(dataSource, dataSpec, trigger, representation.format, extractor, expectedExtractorResult, indexAnchor); }
private Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, int segmentNum, int trigger) { Representation representation = representationHolder.representation; DashSegmentIndex segmentIndex = representationHolder.segmentIndex; long startTimeUs = segmentIndex.getTimeUs(segmentNum); long endTimeUs = startTimeUs + segmentIndex.getDurationUs(segmentNum); boolean isLastSegment = !currentManifest.dynamic && segmentNum == segmentIndex.getLastSegmentNum(); int nextAbsoluteSegmentNum = isLastSegment ? -1 : (representationHolder.segmentNumShift + segmentNum + 1); RangedUri segmentUri = segmentIndex.getSegmentUrl(segmentNum); DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, representation.getCacheKey()); long presentationTimeOffsetUs = representation.presentationTimeOffsetMs * 1000; if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) { if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) { // Update the VTT header. headerBuilder.setLength(0); headerBuilder.append(WebvttParser.EXO_HEADER).append("=") .append(WebvttParser.OFFSET).append(presentationTimeOffsetUs).append("\n"); representationHolder.vttHeader = headerBuilder.toString().getBytes(); representationHolder.vttHeaderOffsetUs = presentationTimeOffsetUs; } return new SingleSampleMediaChunk(dataSource, dataSpec, representation.format, 0, startTimeUs, endTimeUs, nextAbsoluteSegmentNum, null, representationHolder.vttHeader); } else { return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs, endTimeUs, nextAbsoluteSegmentNum, representationHolder.extractor, psshInfo, false, presentationTimeOffsetUs); } }
private Chunk newMediaChunk(RepresentationHolder representationHolder, DataSource dataSource, int segmentNum, int trigger) { Representation representation = representationHolder.representation; DashSegmentIndex segmentIndex = representationHolder.segmentIndex; long startTimeUs = segmentIndex.getTimeUs(segmentNum); long endTimeUs = startTimeUs + segmentIndex.getDurationUs(segmentNum); boolean isLastSegment = !currentManifest.dynamic && segmentNum == segmentIndex.getLastSegmentNum(); int nextAbsoluteSegmentNum = isLastSegment ? -1 : (representationHolder.segmentNumShift + segmentNum + 1); RangedUri segmentUri = segmentIndex.getSegmentUrl(segmentNum); DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, representation.getCacheKey()); long presentationTimeOffsetUs = representation.presentationTimeOffsetUs; if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) { if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) { // Update the VTT header. headerBuilder.setLength(0); headerBuilder.append(WebvttParser.EXO_HEADER).append("=") .append(WebvttParser.OFFSET).append(presentationTimeOffsetUs).append("\n"); representationHolder.vttHeader = headerBuilder.toString().getBytes(); representationHolder.vttHeaderOffsetUs = presentationTimeOffsetUs; } return new SingleSampleMediaChunk(dataSource, dataSpec, representation.format, 0, startTimeUs, endTimeUs, nextAbsoluteSegmentNum, null, representationHolder.vttHeader); } else { return new ContainerMediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs, endTimeUs, nextAbsoluteSegmentNum, representationHolder.extractor, psshInfo, false, presentationTimeOffsetUs); } }
private Chunk newMediaChunk(Representation representation, DashSegmentIndex segmentIndex, Extractor extractor, DataSource dataSource, int segmentNum, int trigger) { int lastSegmentNum = segmentIndex.getLastSegmentNum(); int nextSegmentNum = segmentNum == lastSegmentNum ? -1 : segmentNum + 1; long startTimeUs = segmentIndex.getTimeUs(segmentNum); long endTimeUs = segmentNum < lastSegmentNum ? segmentIndex.getTimeUs(segmentNum + 1) : startTimeUs + segmentIndex.getDurationUs(segmentNum); RangedUri segmentUri = segmentIndex.getSegmentUrl(segmentNum); DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length, representation.getCacheKey()); return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs, endTimeUs, nextSegmentNum, extractor, false, 0); }
@Override public RangedUri getSegmentUrl(int segmentNum) { return new RangedUri(uri, null, chunkIndex.offsets[segmentNum], chunkIndex.sizes[segmentNum]); }
public RangedUri getSegmentUrl(int segmentNum) { return segmentIndex.getSegmentUrl(segmentNum - segmentNumShift); }
private static Representation buildVodRepresentation(Format format) { RangedUri rangedUri = new RangedUri("https://example.com/1.mp4", null, 0, 100); SingleSegmentBase segmentBase = new SingleSegmentBase(rangedUri, 1, 0, "https://example.com/1.mp4", 0, -1); return Representation.newInstance(null, 0, format, segmentBase); }
@Override public RangedUri getSegmentUrl(int segmentNum) { return new RangedUri(uri, null, indexAnchor + segmentIndex.offsets[segmentNum], segmentIndex.sizes[segmentNum]); }
/** * @param startTimeUs The start time of the segment, in microseconds. * @param durationUs The duration of the segment, in microseconds. * @param uri A {@link RangedUri} defining the location of the segment data. */ public DashSingleSegmentIndex(long startTimeUs, long durationUs, RangedUri uri) { this.startTimeUs = startTimeUs; this.durationUs = durationUs; this.uri = uri; }
@Override public RangedUri getSegmentUrl(int segmentNum) { return uri; }
@Override public final void getChunkOperation(List<? extends MediaChunk> queue, long seekPositionUs, long playbackPositionUs, ChunkOperationHolder out) { evaluation.queueSize = queue.size(); if (evaluation.format == null || !lastChunkWasInitialization) { evaluator.evaluate(queue, playbackPositionUs, formats, evaluation); } Format selectedFormat = evaluation.format; out.queueSize = evaluation.queueSize; if (selectedFormat == null) { out.chunk = null; return; } else if (out.queueSize == queue.size() && out.chunk != null && out.chunk.format.id.equals(selectedFormat.id)) { // We already have a chunk, and the evaluation hasn't changed either the format or the size // of the queue. Leave unchanged. return; } Representation selectedRepresentation = representations.get(selectedFormat.id); Extractor extractor = extractors.get(selectedRepresentation.format.id); RangedUri pendingInitializationUri = null; RangedUri pendingIndexUri = null; if (extractor.getFormat() == null) { pendingInitializationUri = selectedRepresentation.getInitializationUri(); } if (!segmentIndexes.containsKey(selectedRepresentation.format.id)) { pendingIndexUri = selectedRepresentation.getIndexUri(); } if (pendingInitializationUri != null || pendingIndexUri != null) { // We have initialization and/or index requests to make. Chunk initializationChunk = newInitializationChunk(pendingInitializationUri, pendingIndexUri, selectedRepresentation, extractor, dataSource, evaluation.trigger); lastChunkWasInitialization = true; out.chunk = initializationChunk; return; } int nextSegmentNum; DashSegmentIndex segmentIndex = segmentIndexes.get(selectedRepresentation.format.id); if (queue.isEmpty()) { nextSegmentNum = segmentIndex.getSegmentNum(seekPositionUs); } else { nextSegmentNum = queue.get(out.queueSize - 1).nextChunkIndex; } if (nextSegmentNum == -1) { out.chunk = null; return; } Chunk nextMediaChunk = newMediaChunk(selectedRepresentation, segmentIndex, extractor, dataSource, nextSegmentNum, evaluation.trigger); lastChunkWasInitialization = false; out.chunk = nextMediaChunk; }
/** * Returns a {@link RangedUri} defining the location of a segment. * * @param segmentNum The segment number. * @return The {@link RangedUri} defining the location of the data. */ RangedUri getSegmentUrl(int segmentNum);