/** * @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); } }
/** * @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); } }
/** * Loads initialization data for the {@code representation} and optionally index data then * returns a {@link ChunkExtractorWrapper} which contains the output. * * @param dataSource The source from which the data should be loaded. * @param representation The representation which initialization chunk belongs to. * @param loadIndex Whether to load index data too. * @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no * initialization or (if requested) index data exists. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource, Representation representation, boolean loadIndex) throws IOException, InterruptedException { RangedUri initializationUri = representation.getInitializationUri(); if (initializationUri == null) { return null; } ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format); RangedUri requestUri; if (loadIndex) { RangedUri indexUri = representation.getIndexUri(); if (indexUri == null) { return 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, representation.baseUrl); if (requestUri == null) { loadInitializationData(dataSource, representation, extractorWrapper, initializationUri); requestUri = indexUri; } } else { requestUri = initializationUri; } loadInitializationData(dataSource, representation, extractorWrapper, requestUri); return extractorWrapper; }
private static void loadInitializationData(DataSource dataSource, Representation representation, ChunkExtractorWrapper extractorWrapper, RangedUri requestUri) throws IOException, InterruptedException { DataSpec dataSpec = new DataSpec(requestUri.resolveUri(representation.baseUrl), requestUri.start, requestUri.length, representation.getCacheKey()); InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec, representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */, extractorWrapper); initializationChunk.load(); }
private static ChunkExtractorWrapper newWrappedExtractor(Format format) { String mimeType = format.containerMimeType; boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM); Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor(); return new ChunkExtractorWrapper(extractor, format); }
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); }
/** * Loads initialization data for the {@code representation} and optionally index data then * returns a {@link ChunkExtractorWrapper} which contains the output. * * @param dataSource The source from which the data should be loaded. * @param trackType The type of the representation. Typically one of the * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @param loadIndex Whether to load index data too. * @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no * initialization or (if requested) index data exists. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource, int trackType, Representation representation, boolean loadIndex) throws IOException, InterruptedException { RangedUri initializationUri = representation.getInitializationUri(); if (initializationUri == null) { return null; } ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(trackType, representation.format); RangedUri requestUri; if (loadIndex) { RangedUri indexUri = representation.getIndexUri(); if (indexUri == null) { return 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, representation.baseUrl); if (requestUri == null) { loadInitializationData(dataSource, representation, extractorWrapper, initializationUri); requestUri = indexUri; } } else { requestUri = initializationUri; } loadInitializationData(dataSource, representation, extractorWrapper, requestUri); return extractorWrapper; }
private static ChunkExtractorWrapper newWrappedExtractor(int trackType, Format format) { String mimeType = format.containerMimeType; boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM); Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor(); return new ChunkExtractorWrapper(extractor, trackType, format); }
/** * Loads initialization data for the {@code representation} and returns the sample {@link * Format}. * * @param dataSource The source from which the data should be loaded. * @param representation The representation which initialization chunk belongs to. * @return the sample {@link Format} of the given representation. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ public static Format loadSampleFormat(DataSource dataSource, Representation representation) throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, false); return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0]; }
/** * Loads initialization and index data for the {@code representation} and returns the {@link * ChunkIndex}. * * @param dataSource The source from which the data should be loaded. * @param representation The representation which initialization chunk belongs to. * @return {@link ChunkIndex} of the given representation. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ public static ChunkIndex loadChunkIndex(DataSource dataSource, Representation representation) throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, true); return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); }
/** * Loads initialization data for the {@code representation} and returns the sample {@link Format}. * * @param dataSource The source from which the data should be loaded. * @param trackType The type of the representation. Typically one of the * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @return the sample {@link Format} of the given representation. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ public static Format loadSampleFormat(DataSource dataSource, int trackType, Representation representation) throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, representation, false); return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0]; }
/** * Loads initialization and index data for the {@code representation} and returns the {@link * ChunkIndex}. * * @param dataSource The source from which the data should be loaded. * @param trackType The type of the representation. Typically one of the * {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * @param representation The representation which initialization chunk belongs to. * @return The {@link ChunkIndex} of the given representation, or null if no initialization or * index data exists. * @throws IOException Thrown when there is an error while loading. * @throws InterruptedException Thrown if the thread was interrupted. */ public static ChunkIndex loadChunkIndex(DataSource dataSource, int trackType, Representation representation) throws IOException, InterruptedException { ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, representation, true); return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); }