@Override public Object build() { StreamElement[] streamElementArray = new StreamElement[streamElements.size()]; streamElements.toArray(streamElementArray); if (protectionElement != null) { DrmInitData drmInitData = new DrmInitData(new SchemeData(protectionElement.uuid, MimeTypes.VIDEO_MP4, protectionElement.data)); for (StreamElement streamElement : streamElementArray) { for (int i = 0; i < streamElement.formats.length; i++) { streamElement.formats[i] = streamElement.formats[i].copyWithDrmInitData(drmInitData); } } } return new SsManifest(majorVersion, minorVersion, timescale, duration, dvrWindowLength, lookAheadCount, isLive, protectionElement, streamElementArray); }
/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param elementIndex The index of the stream element in the manifest. * @param trackSelection The track selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param trackEncryptionBoxes Track encryption boxes for the stream. */ public DefaultSsChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, SsManifest manifest, int elementIndex, TrackSelection trackSelection, DataSource dataSource, TrackEncryptionBox[] trackEncryptionBoxes) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.elementIndex = elementIndex; this.trackSelection = trackSelection; this.dataSource = dataSource; StreamElement streamElement = manifest.streamElements[elementIndex]; extractorWrappers = new ChunkExtractorWrapper[trackSelection.length()]; for (int i = 0; i < extractorWrappers.length; i++) { int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i); Format format = streamElement.formats[manifestTrackIndex]; int nalUnitLengthFieldLength = streamElement.type == C.TRACK_TYPE_VIDEO ? 4 : 0; Track track = new Track(manifestTrackIndex, streamElement.type, streamElement.timescale, C.TIME_UNSET, manifest.durationUs, format, Track.TRANSFORMATION_NONE, trackEncryptionBoxes, nalUnitLengthFieldLength, null, null); FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, null, track); extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format); } }
@Override public void updateManifest(SsManifest newManifest) { StreamElement currentElement = manifest.streamElements[elementIndex]; int currentElementChunkCount = currentElement.chunkCount; StreamElement newElement = newManifest.streamElements[elementIndex]; if (currentElementChunkCount == 0 || newElement.chunkCount == 0) { // There's no overlap between the old and new elements because at least one is empty. currentManifestChunkOffset += currentElementChunkCount; } else { long currentElementEndTimeUs = currentElement.getStartTimeUs(currentElementChunkCount - 1) + currentElement.getChunkDurationUs(currentElementChunkCount - 1); long newElementStartTimeUs = newElement.getStartTimeUs(0); if (currentElementEndTimeUs <= newElementStartTimeUs) { // There's no overlap between the old and new elements. currentManifestChunkOffset += currentElementChunkCount; } else { // The new element overlaps with the old one. currentManifestChunkOffset += currentElement.getChunkIndex(newElementStartTimeUs); } } manifest = newManifest; }
/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param elementIndex The index of the stream element in the manifest. * @param trackSelection The track selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param trackEncryptionBoxes Track encryption boxes for the stream. */ public DefaultSsChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, SsManifest manifest, int elementIndex, TrackSelection trackSelection, DataSource dataSource, TrackEncryptionBox[] trackEncryptionBoxes) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.elementIndex = elementIndex; this.trackSelection = trackSelection; this.dataSource = dataSource; StreamElement streamElement = manifest.streamElements[elementIndex]; extractorWrappers = new ChunkExtractorWrapper[trackSelection.length()]; for (int i = 0; i < extractorWrappers.length; i++) { int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i); Format format = streamElement.formats[manifestTrackIndex]; int nalUnitLengthFieldLength = streamElement.type == C.TRACK_TYPE_VIDEO ? 4 : 0; Track track = new Track(manifestTrackIndex, streamElement.type, streamElement.timescale, C.TIME_UNSET, manifest.durationUs, format, Track.TRANSFORMATION_NONE, trackEncryptionBoxes, nalUnitLengthFieldLength, null, null); FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, track, null); extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format, false, false); } }
/** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. * @param elementIndex The index of the stream element in the manifest. * @param trackSelection The track selection. * @param dataSource A {@link DataSource} suitable for loading the media data. * @param trackEncryptionBoxes Track encryption boxes for the stream. */ public DefaultSsChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, SsManifest manifest, int elementIndex, TrackSelection trackSelection, DataSource dataSource, TrackEncryptionBox[] trackEncryptionBoxes) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.elementIndex = elementIndex; this.trackSelection = trackSelection; this.dataSource = dataSource; StreamElement streamElement = manifest.streamElements[elementIndex]; extractorWrappers = new ChunkExtractorWrapper[trackSelection.length()]; for (int i = 0; i < extractorWrappers.length; i++) { int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i); Format format = streamElement.formats[manifestTrackIndex]; int nalUnitLengthFieldLength = streamElement.type == C.TRACK_TYPE_VIDEO ? 4 : 0; Track track = new Track(manifestTrackIndex, streamElement.type, streamElement.timescale, C.TIME_UNSET, manifest.durationUs, format, Track.TRANSFORMATION_NONE, trackEncryptionBoxes, nalUnitLengthFieldLength, null, null); FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, null, track, null); extractorWrappers[i] = new ChunkExtractorWrapper(extractor, streamElement.type, format); } }
private static void assertManifestEquals(SsManifest expected, SsManifest actual) { assertEquals(expected.durationUs, actual.durationUs); assertEquals(expected.dvrWindowLengthUs, actual.dvrWindowLengthUs); assertEquals(expected.isLive, actual.isLive); assertEquals(expected.lookAheadCount, actual.lookAheadCount); assertEquals(expected.majorVersion, actual.majorVersion); assertEquals(expected.minorVersion, actual.minorVersion); assertEquals(expected.protectionElement.uuid, actual.protectionElement.uuid); assertEquals(expected.protectionElement, actual.protectionElement); for (int i = 0; i < expected.streamElements.length; i++) { StreamElement expectedStreamElement = expected.streamElements[i]; StreamElement actualStreamElement = actual.streamElements[i]; assertEquals(expectedStreamElement.chunkCount, actualStreamElement.chunkCount); assertEquals(expectedStreamElement.displayHeight, actualStreamElement.displayHeight); assertEquals(expectedStreamElement.displayWidth, actualStreamElement.displayWidth); assertEquals(expectedStreamElement.language, actualStreamElement.language); assertEquals(expectedStreamElement.maxHeight, actualStreamElement.maxHeight); assertEquals(expectedStreamElement.maxWidth, actualStreamElement.maxWidth); assertEquals(expectedStreamElement.name, actualStreamElement.name); assertEquals(expectedStreamElement.subType, actualStreamElement.subType); assertEquals(expectedStreamElement.timescale, actualStreamElement.timescale); assertEquals(expectedStreamElement.type, actualStreamElement.type); MoreAsserts.assertEquals(expectedStreamElement.formats, actualStreamElement.formats); } }
@Override public Object build() { Format[] formatArray = new Format[formats.size()]; formats.toArray(formatArray); return new StreamElement(baseUri, url, type, subType, timescale, name, maxWidth, maxHeight, displayWidth, displayHeight, language, formatArray, startTimes, lastChunkDuration); }
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) { if (!manifest.isLive) { return C.TIME_UNSET; } StreamElement currentElement = manifest.streamElements[elementIndex]; int lastChunkIndex = currentElement.chunkCount - 1; long lastChunkEndTimeUs = currentElement.getStartTimeUs(lastChunkIndex) + currentElement.getChunkDurationUs(lastChunkIndex); return lastChunkEndTimeUs - playbackPositionUs; }
@Override protected List<Segment> getAllSegments(DataSource dataSource, SsManifest manifest, boolean allowIndexLoadErrors) throws InterruptedException, IOException { ArrayList<Segment> segments = new ArrayList<>(); for (int i = 0; i < manifest.streamElements.length; i++) { StreamElement streamElement = manifest.streamElements[i]; for (int j = 0; j < streamElement.formats.length; j++) { segments.addAll(getSegments(dataSource, manifest, new TrackKey[] {new TrackKey(i, j)}, allowIndexLoadErrors)); } } return segments; }
@Override protected List<Segment> getSegments(DataSource dataSource, SsManifest manifest, TrackKey[] keys, boolean allowIndexLoadErrors) throws InterruptedException, IOException { ArrayList<Segment> segments = new ArrayList<>(); for (TrackKey key : keys) { StreamElement streamElement = manifest.streamElements[key.streamElementIndex]; for (int i = 0; i < streamElement.chunkCount; i++) { segments.add(new Segment(streamElement.getStartTimeUs(i), new DataSpec(streamElement.buildRequestUri(key.trackIndex, i)))); } } return segments; }
private static SsManifest newSsManifest(StreamElement... streamElements) { return new SsManifest(1, 2, 1000, 5000, 0, 0, false, DUMMY_PROTECTION_ELEMENT, streamElements); }
private static StreamElement newStreamElement(String name, Format... formats) { return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType", 1000, name, 1024, 768, 1024, 768, null, formats, Collections.<Long>emptyList(), 0); }