From 708cad6b28ed66c02826fb1d3ded8974591ecf7e Mon Sep 17 00:00:00 2001 From: eguven Date: Thu, 25 Apr 2019 16:52:35 +0100 Subject: [PATCH] Add DownloadHelper.createMediaSource utility method PiperOrigin-RevId: 245243488 --- .../exoplayer2/demo/DownloadTracker.java | 9 +- .../exoplayer2/demo/PlayerActivity.java | 29 ++-- library/core/proguard-rules.txt | 9 +- .../exoplayer2/offline/DownloadHelper.java | 126 +++++++++++------- 4 files changed, 98 insertions(+), 75 deletions(-) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java index f372a47df6..a913a9b891 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java @@ -30,15 +30,12 @@ import com.google.android.exoplayer2.offline.DownloadIndex; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadRequest; import com.google.android.exoplayer2.offline.DownloadService; -import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; /** Tracks media that has been downloaded. */ @@ -86,11 +83,9 @@ public class DownloadTracker { } @SuppressWarnings("unchecked") - public List getOfflineStreamKeys(Uri uri) { + public DownloadRequest getDownloadRequest(Uri uri) { Download download = downloads.get(uri); - return download != null && download.state != Download.STATE_FAILED - ? download.request.streamKeys - : Collections.emptyList(); + return download != null && download.state != Download.STATE_FAILED ? download.request : null; } public void toggleDownload( diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index acb24adebe..35307eb5d8 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -45,7 +45,8 @@ import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; -import com.google.android.exoplayer2.offline.StreamKey; +import com.google.android.exoplayer2.offline.DownloadHelper; +import com.google.android.exoplayer2.offline.DownloadRequest; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.MediaSource; @@ -75,7 +76,6 @@ import java.lang.reflect.Constructor; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; -import java.util.List; import java.util.UUID; /** An activity that plays media using {@link SimpleExoPlayer}. */ @@ -457,33 +457,26 @@ public class PlayerActivity extends AppCompatActivity } private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) { + DownloadRequest downloadRequest = + ((DemoApplication) getApplication()).getDownloadTracker().getDownloadRequest(uri); + if (downloadRequest != null) { + return DownloadHelper.createMediaSource(downloadRequest, dataSourceFactory); + } @ContentType int type = Util.inferContentType(uri, overrideExtension); - List offlineStreamKeys = getOfflineStreamKeys(uri); switch (type) { case C.TYPE_DASH: - return new DashMediaSource.Factory(dataSourceFactory) - .setStreamKeys(offlineStreamKeys) - .createMediaSource(uri); + return new DashMediaSource.Factory(dataSourceFactory).createMediaSource(uri); case C.TYPE_SS: - return new SsMediaSource.Factory(dataSourceFactory) - .setStreamKeys(offlineStreamKeys) - .createMediaSource(uri); + return new SsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); case C.TYPE_HLS: - return new HlsMediaSource.Factory(dataSourceFactory) - .setStreamKeys(offlineStreamKeys) - .createMediaSource(uri); + return new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); case C.TYPE_OTHER: return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri); - default: { + default: throw new IllegalStateException("Unsupported type: " + type); - } } } - private List getOfflineStreamKeys(Uri uri) { - return ((DemoApplication) getApplication()).getDownloadTracker().getOfflineStreamKeys(uri); - } - private DefaultDrmSessionManager buildDrmSessionManagerV18( UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, boolean multiSession) throws UnsupportedDrmException { diff --git a/library/core/proguard-rules.txt b/library/core/proguard-rules.txt index 07ba438182..8c11810506 100644 --- a/library/core/proguard-rules.txt +++ b/library/core/proguard-rules.txt @@ -46,18 +46,21 @@ # Constructors accessed via reflection in DownloadHelper -dontnote com.google.android.exoplayer2.source.dash.DashMediaSource$Factory --keepclassmembers class com.google.android.exoplayer2.source.dash.DashMediaSource$Factory { +-keepclasseswithmembers class com.google.android.exoplayer2.source.dash.DashMediaSource$Factory { (com.google.android.exoplayer2.upstream.DataSource$Factory); + ** setStreamKeys(java.util.List); com.google.android.exoplayer2.source.dash.DashMediaSource createMediaSource(android.net.Uri); } -dontnote com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory --keepclassmembers class com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory { +-keepclasseswithmembers class com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory { (com.google.android.exoplayer2.upstream.DataSource$Factory); + ** setStreamKeys(java.util.List); com.google.android.exoplayer2.source.hls.HlsMediaSource createMediaSource(android.net.Uri); } -dontnote com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory --keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory { +-keepclasseswithmembers class com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory { (com.google.android.exoplayer2.upstream.DataSource$Factory); + ** setStreamKeys(java.util.List); com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource createMediaSource(android.net.Uri); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java index c9b0451f41..2742e85a5f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java @@ -20,7 +20,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import androidx.annotation.Nullable; -import android.util.Pair; import android.util.SparseIntArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -32,6 +31,7 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.BaseTrackSelection; @@ -44,6 +44,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSource.Factory; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; @@ -106,30 +107,13 @@ public final class DownloadHelper { void onPrepareError(DownloadHelper helper, IOException e); } - @Nullable private static final Constructor DASH_FACTORY_CONSTRUCTOR; - @Nullable private static final Constructor HLS_FACTORY_CONSTRUCTOR; - @Nullable private static final Constructor SS_FACTORY_CONSTRUCTOR; - @Nullable private static final Method DASH_FACTORY_CREATE_METHOD; - @Nullable private static final Method HLS_FACTORY_CREATE_METHOD; - @Nullable private static final Method SS_FACTORY_CREATE_METHOD; - - static { - Pair<@NullableType Constructor, @NullableType Method> dashFactoryMethods = - getMediaSourceFactoryMethods( - "com.google.android.exoplayer2.source.dash.DashMediaSource$Factory"); - DASH_FACTORY_CONSTRUCTOR = dashFactoryMethods.first; - DASH_FACTORY_CREATE_METHOD = dashFactoryMethods.second; - Pair<@NullableType Constructor, @NullableType Method> hlsFactoryMethods = - getMediaSourceFactoryMethods( - "com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory"); - HLS_FACTORY_CONSTRUCTOR = hlsFactoryMethods.first; - HLS_FACTORY_CREATE_METHOD = hlsFactoryMethods.second; - Pair<@NullableType Constructor, @NullableType Method> ssFactoryMethods = - getMediaSourceFactoryMethods( - "com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory"); - SS_FACTORY_CONSTRUCTOR = ssFactoryMethods.first; - SS_FACTORY_CREATE_METHOD = ssFactoryMethods.second; - } + private static final MediaSourceFactory DASH_FACTORY = + getMediaSourceFactory("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory"); + private static final MediaSourceFactory SS_FACTORY = + getMediaSourceFactory( + "com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory"); + private static final MediaSourceFactory HLS_FACTORY = + getMediaSourceFactory("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory"); /** * Creates a {@link DownloadHelper} for progressive streams. @@ -202,8 +186,7 @@ public final class DownloadHelper { DownloadRequest.TYPE_DASH, uri, /* cacheKey= */ null, - createMediaSource( - uri, dataSourceFactory, DASH_FACTORY_CONSTRUCTOR, DASH_FACTORY_CREATE_METHOD), + DASH_FACTORY.createMediaSource(uri, dataSourceFactory, /* streamKeys= */ null), trackSelectorParameters, Util.getRendererCapabilities(renderersFactory, drmSessionManager)); } @@ -252,8 +235,7 @@ public final class DownloadHelper { DownloadRequest.TYPE_HLS, uri, /* cacheKey= */ null, - createMediaSource( - uri, dataSourceFactory, HLS_FACTORY_CONSTRUCTOR, HLS_FACTORY_CREATE_METHOD), + HLS_FACTORY.createMediaSource(uri, dataSourceFactory, /* streamKeys= */ null), trackSelectorParameters, Util.getRendererCapabilities(renderersFactory, drmSessionManager)); } @@ -302,11 +284,42 @@ public final class DownloadHelper { DownloadRequest.TYPE_SS, uri, /* cacheKey= */ null, - createMediaSource(uri, dataSourceFactory, SS_FACTORY_CONSTRUCTOR, SS_FACTORY_CREATE_METHOD), + SS_FACTORY.createMediaSource(uri, dataSourceFactory, /* streamKeys= */ null), trackSelectorParameters, Util.getRendererCapabilities(renderersFactory, drmSessionManager)); } + /** + * Utility method to create a MediaSource which only contains the tracks defined in {@code + * downloadRequest}. + * + * @param downloadRequest A {@link DownloadRequest}. + * @param dataSourceFactory A factory for {@link DataSource}s to read the media. + * @return A MediaSource which only contains the tracks defined in {@code downloadRequest}. + */ + public static MediaSource createMediaSource( + DownloadRequest downloadRequest, DataSource.Factory dataSourceFactory) { + MediaSourceFactory factory; + switch (downloadRequest.type) { + case DownloadRequest.TYPE_DASH: + factory = DASH_FACTORY; + break; + case DownloadRequest.TYPE_SS: + factory = SS_FACTORY; + break; + case DownloadRequest.TYPE_HLS: + factory = HLS_FACTORY; + break; + case DownloadRequest.TYPE_PROGRESSIVE: + return new ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(downloadRequest.uri); + default: + throw new IllegalStateException("Unsupported type: " + downloadRequest.type); + } + return factory.createMediaSource( + downloadRequest.uri, dataSourceFactory, downloadRequest.streamKeys); + } + private final String downloadType; private final Uri uri; @Nullable private final String cacheKey; @@ -728,35 +741,54 @@ public final class DownloadHelper { } } - private static Pair<@NullableType Constructor, @NullableType Method> - getMediaSourceFactoryMethods(String className) { + private static MediaSourceFactory getMediaSourceFactory(String className) { Constructor constructor = null; + Method setStreamKeysMethod = null; Method createMethod = null; try { // LINT.IfChange Class factoryClazz = Class.forName(className); - constructor = factoryClazz.getConstructor(DataSource.Factory.class); + constructor = factoryClazz.getConstructor(Factory.class); + setStreamKeysMethod = factoryClazz.getMethod("setStreamKeys", List.class); createMethod = factoryClazz.getMethod("createMediaSource", Uri.class); // LINT.ThenChange(../../../../../../../../proguard-rules.txt) - } catch (Exception e) { + } catch (ClassNotFoundException e) { // Expected if the app was built without the respective module. + } catch (NoSuchMethodException | SecurityException e) { + // Something is wrong with the library or the proguard configuration. + throw new IllegalStateException(e); } - return Pair.create(constructor, createMethod); + return new MediaSourceFactory(constructor, setStreamKeysMethod, createMethod); } - private static MediaSource createMediaSource( - Uri uri, - DataSource.Factory dataSourceFactory, - @Nullable Constructor factoryConstructor, - @Nullable Method createMediaSourceMethod) { - if (factoryConstructor == null || createMediaSourceMethod == null) { - throw new IllegalStateException("Module missing to create media source."); + private static final class MediaSourceFactory { + @Nullable private final Constructor constructor; + @Nullable private final Method setStreamKeysMethod; + @Nullable private final Method createMethod; + + public MediaSourceFactory( + @Nullable Constructor constructor, + @Nullable Method setStreamKeysMethod, + @Nullable Method createMethod) { + this.constructor = constructor; + this.setStreamKeysMethod = setStreamKeysMethod; + this.createMethod = createMethod; } - try { - Object factory = factoryConstructor.newInstance(dataSourceFactory); - return (MediaSource) Assertions.checkNotNull(createMediaSourceMethod.invoke(factory, uri)); - } catch (Exception e) { - throw new IllegalStateException("Failed to instantiate media source.", e); + + private MediaSource createMediaSource( + Uri uri, Factory dataSourceFactory, @Nullable List streamKeys) { + if (constructor == null || setStreamKeysMethod == null || createMethod == null) { + throw new IllegalStateException("Module missing to create media source."); + } + try { + Object factory = constructor.newInstance(dataSourceFactory); + if (streamKeys != null) { + setStreamKeysMethod.invoke(factory, streamKeys); + } + return (MediaSource) Assertions.checkNotNull(createMethod.invoke(factory, uri)); + } catch (Exception e) { + throw new IllegalStateException("Failed to instantiate media source.", e); + } } }