From 98f4fb85c210b2704a66e00c8477d4a41a234e9c Mon Sep 17 00:00:00 2001 From: eguven Date: Mon, 27 Feb 2017 07:36:11 -0800 Subject: [PATCH] Move utility methods to DashUtil class ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148647040 --- .../exoplayer2/drm/OfflineLicenseHelper.java | 69 +------- .../exoplayer2/source/dash/DashUtil.java | 166 ++++++++++++++++++ 2 files changed, 169 insertions(+), 66 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java b/library/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java index b3729c2377..6a7f905a51 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.drm; import android.media.MediaDrm; -import android.net.Uri; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; @@ -27,24 +26,14 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.EventListener; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.Mode; import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; -import com.google.android.exoplayer2.extractor.Extractor; -import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; -import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; -import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; -import com.google.android.exoplayer2.source.chunk.InitializationChunk; +import com.google.android.exoplayer2.source.dash.DashUtil; import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; -import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.Period; -import com.google.android.exoplayer2.source.dash.manifest.RangedUri; import com.google.android.exoplayer2.source.dash.manifest.Representation; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSourceInputStream; -import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.Factory; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MimeTypes; import java.io.IOException; import java.util.HashMap; @@ -58,28 +47,6 @@ public final class OfflineLicenseHelper { private final DefaultDrmSessionManager drmSessionManager; private final HandlerThread handlerThread; - /** - * Helper method to download a DASH manifest. - * - * @param dataSource The {@link HttpDataSource} from which the manifest should be read. - * @param manifestUriString The URI of the manifest to be read. - * @return An instance of {@link DashManifest}. - * @throws IOException If an error occurs reading data from the stream. - * @see DashManifestParser - */ - public static DashManifest downloadManifest(HttpDataSource dataSource, String manifestUriString) - throws IOException { - DataSourceInputStream inputStream = new DataSourceInputStream( - dataSource, new DataSpec(Uri.parse(manifestUriString))); - try { - inputStream.open(); - DashManifestParser parser = new DashManifestParser(); - return parser.parse(dataSource.getUri(), inputStream); - } finally { - inputStream.close(); - } - } - /** * Instantiates a new instance which uses Widevine CDM. Call {@link #releaseResources()} when * you're done with the helper instance. @@ -174,7 +141,7 @@ public final class OfflineLicenseHelper { */ public byte[] download(HttpDataSource dataSource, String manifestUriString) throws IOException, InterruptedException, DrmSessionException { - return download(dataSource, downloadManifest(dataSource, manifestUriString)); + return download(dataSource, DashUtil.loadManifest(dataSource, manifestUriString)); } /** @@ -210,14 +177,8 @@ public final class OfflineLicenseHelper { Representation representation = adaptationSet.representations.get(0); DrmInitData drmInitData = representation.format.drmInitData; if (drmInitData == null) { - ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format, + Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation, adaptationSet.type); - InitializationChunk initializationChunk = loadInitializationChunk(dataSource, representation, - extractorWrapper); - if (initializationChunk == null) { - return null; - } - Format sampleFormat = extractorWrapper.getSampleFormat(); if (sampleFormat != null) { drmInitData = sampleFormat.drmInitData; } @@ -291,28 +252,4 @@ public final class OfflineLicenseHelper { return session; } - private static InitializationChunk loadInitializationChunk(DataSource dataSource, - Representation representation, ChunkExtractorWrapper extractorWrapper) - throws IOException, InterruptedException { - RangedUri rangedUri = representation.getInitializationUri(); - if (rangedUri == null) { - return null; - } - DataSpec dataSpec = new DataSpec(rangedUri.resolveUri(representation.baseUrl), rangedUri.start, - rangedUri.length, representation.getCacheKey()); - InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec, - representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */, - extractorWrapper); - initializationChunk.load(); - return initializationChunk; - } - - private static ChunkExtractorWrapper newWrappedExtractor(Format format, int trackType) { - final String mimeType = format.containerMimeType; - final boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM) - || mimeType.startsWith(MimeTypes.AUDIO_WEBM); - final Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor(); - return new ChunkExtractorWrapper(extractor, format, trackType); - } - } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java new file mode 100644 index 0000000000..bc8d67816f --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashUtil.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source.dash; + +import android.net.Uri; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ChunkIndex; +import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; +import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; +import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; +import com.google.android.exoplayer2.source.chunk.InitializationChunk; +import com.google.android.exoplayer2.source.dash.manifest.DashManifest; +import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; +import com.google.android.exoplayer2.source.dash.manifest.RangedUri; +import com.google.android.exoplayer2.source.dash.manifest.Representation; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSourceInputStream; +import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.util.MimeTypes; +import java.io.IOException; + +/** + * Utility methods for DASH streams. + */ +public final class DashUtil { + + /** + * Loads a DASH manifest. + * + * @param dataSource The {@link HttpDataSource} from which the manifest should be read. + * @param manifestUriString The URI of the manifest to be read. + * @return An instance of {@link DashManifest}. + * @throws IOException If an error occurs reading data from the stream. + * @see DashManifestParser + */ + public static DashManifest loadManifest(DataSource dataSource, String manifestUriString) + throws IOException { + DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, + new DataSpec(Uri.parse(manifestUriString), DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH)); + try { + inputStream.open(); + DashManifestParser parser = new DashManifestParser(); + return parser.parse(dataSource.getUri(), inputStream); + } finally { + inputStream.close(); + } + } + + /** + * 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. + * @param type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*} + * constants. + * @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, + int type) throws IOException, InterruptedException { + ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, + type, false); + return extractorWrapper == null ? null : extractorWrapper.getSampleFormat(); + } + + /** + * 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. + * @param type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*} + * constants. + * @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, + int type) throws IOException, InterruptedException { + ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, + type, true); + return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); + } + + /** + * 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 type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*} + * constants. + * @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, int type, boolean loadIndex) + throws IOException, InterruptedException { + RangedUri initializationUri = representation.getInitializationUri(); + if (initializationUri == null) { + return null; + } + ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format, type); + 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, int trackType) { + 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, trackType); + } + + private DashUtil() {} + +}