From 21e635aaa240057e54428df7d8b43acf8daa7e6c Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Fri, 6 Jul 2018 15:28:11 +0200 Subject: [PATCH 01/21] Add ExoPlayer cache --- android-exoplayer/build.gradle | 4 +- .../exoplayer/CacheDownloadService.java | 67 +++++++ .../brentvatne/exoplayer/ExoPlayerCache.java | 24 +++ .../exoplayer/ReactExoplayerView.java | 163 ++++++++++++------ 4 files changed, 201 insertions(+), 57 deletions(-) create mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java create mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle index a957dd6212..8a9052cb26 100644 --- a/android-exoplayer/build.gradle +++ b/android-exoplayer/build.gradle @@ -23,8 +23,8 @@ android { dependencies { //noinspection GradleDynamicVersion provided "com.facebook.react:react-native:${_reactNativeVersion}" - compile 'com.google.android.exoplayer:exoplayer:2.7.3' - compile('com.google.android.exoplayer:extension-okhttp:2.7.3') { + compile 'com.google.android.exoplayer:exoplayer:2.8.2' + compile('com.google.android.exoplayer:extension-okhttp:2.8.2') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } compile 'com.squareup.okhttp3:okhttp:3.9.1' diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java new file mode 100644 index 0000000000..7973b0cef9 --- /dev/null +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java @@ -0,0 +1,67 @@ +/* + * 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.brentvatne.exoplayer; + +import android.app.Notification; + +import com.brentvatne.react.R; +import com.google.android.exoplayer2.offline.DownloadManager; +import com.google.android.exoplayer2.offline.DownloadManager.TaskState; +import com.google.android.exoplayer2.offline.DownloadService; +import com.google.android.exoplayer2.scheduler.PlatformScheduler; +import com.google.android.exoplayer2.ui.DownloadNotificationUtil; +import com.google.android.exoplayer2.util.NotificationUtil; +import com.google.android.exoplayer2.util.Util; + +/** A service for downloading media. */ +public class CacheDownloadService extends DownloadService { + + private static final String CHANNEL_ID = "download_channel"; + private static final int JOB_ID = 1; + private static final int FOREGROUND_NOTIFICATION_ID = 1; +// private DownloadManager downloadManager; + + public CacheDownloadService() { + super( + FOREGROUND_NOTIFICATION_ID, + DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, + CHANNEL_ID, + R.string.exo_download_notification_channel_name); + +// this.downloadManager = downloadManager; + } + + @Override + protected DownloadManager getDownloadManager() { + return null; + } + + @Override + protected PlatformScheduler getScheduler() { + return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null; + } + + @Override + protected Notification getForegroundNotification(TaskState[] taskStates) { + return DownloadNotificationUtil.buildProgressNotification( + /* context= */ this, + R.drawable.exo_controls_play, + CHANNEL_ID, + /* contentIntent= */ null, + /* message= */ null, + taskStates); + } +} diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java new file mode 100644 index 0000000000..22f47a802e --- /dev/null +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -0,0 +1,24 @@ +package com.brentvatne.exoplayer; + +import android.content.Context; + +import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.SimpleCache; + +import java.io.File; + +public class ExoPlayerCache { + private static SimpleCache instance = null; + + protected ExoPlayerCache() { + // Exists only to defeat instantiation. + } + + public static SimpleCache getInstance(Context context) { + if(instance == null) { + instance = new SimpleCache(new File(context.getCacheDir().toString() + "/video-cache"), new NoOpCacheEvictor()); + } + return instance; + } + +} diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 614da57ec7..3535ac50d4 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -34,11 +34,17 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataRenderer; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloadManager; +import com.google.android.exoplayer2.offline.DownloadService; +import com.google.android.exoplayer2.offline.Downloader; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; +import com.google.android.exoplayer2.offline.ProgressiveDownloader; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; @@ -47,9 +53,12 @@ import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; +import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; import com.google.android.exoplayer2.source.hls.HlsMediaSource; +import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; +import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; @@ -58,9 +67,17 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.upstream.cache.Cache; +import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; +import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.util.Util; +import java.io.File; +import java.io.IOException; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; @@ -82,6 +99,14 @@ class ReactExoplayerView extends FrameLayout implements private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final CookieManager DEFAULT_COOKIE_MANAGER; private static final int SHOW_PROGRESS = 1; +// private static final String DOWNLOAD_ACTION_FILE = "actions"; +// private static final DownloadAction.Deserializer[] DOWNLOAD_DESERIALIZERS = +// new DownloadAction.Deserializer[] { +// DashDownloadAction.DESERIALIZER, +// HlsDownloadAction.DESERIALIZER, +// SsDownloadAction.DESERIALIZER, +// ProgressiveDownloadAction.DESERIALIZER +// }; static { DEFAULT_COOKIE_MANAGER = new CookieManager(); @@ -93,10 +118,13 @@ class ReactExoplayerView extends FrameLayout implements private Handler mainHandler; private ExoPlayerView exoPlayerView; + private SimpleCache downloadCache; +// private DownloadManager downloadManager; private DataSource.Factory mediaDataSourceFactory; private SimpleExoPlayer player; private MappingTrackSelector trackSelector; private boolean playerNeedsSource; +// protected String userAgent; private int resumeWindow; private long resumePosition; @@ -154,6 +182,7 @@ public ReactExoplayerView(ThemedReactContext context) { audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); themedReactContext.addLifecycleEventListener(this); audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); +// userAgent = Util.getUserAgent(context, "ReactExoPlayer"); initializePlayer(); } @@ -168,6 +197,19 @@ public void setId(int id) { private void createViews() { clearResumePosition(); mediaDataSourceFactory = buildDataSourceFactory(true); + downloadCache = ExoPlayerCache.getInstance(getContext()); + +// DownloaderConstructorHelper downloaderConstructorHelper = +// new DownloaderConstructorHelper( +// downloadCache, buildHttpDataSourceFactory(/* listener= */ null)); +// downloadManager = +// new DownloadManager( +// downloaderConstructorHelper, +// 4, +// DownloadManager.DEFAULT_MIN_RETRY_COUNT, +// new File(getContext().getCacheDir(), DOWNLOAD_ACTION_FILE), +// DOWNLOAD_DESERIALIZERS); + mainHandler = new Handler(); if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER); @@ -182,6 +224,12 @@ private void createViews() { addView(exoPlayerView, 0, layoutParams); } +// /** Returns a {@link HttpDataSource.Factory}. */ +// public HttpDataSource.Factory buildHttpDataSourceFactory( +// TransferListener listener) { +// return new DefaultHttpDataSourceFactory(userAgent, listener); +// } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -261,6 +309,9 @@ private void initializePlayer() { player.prepare(mediaSource, !haveResumePosition, false); playerNeedsSource = false; + DownloadAction downloadAction = new ProgressiveDownloadAction(srcUri, false, null, null); + DownloadService.startWithAction(getContext(), CacheDownloadService.class, downloadAction, true); + eventEmitter.loadStart(); loadVideoStarted = true; } @@ -279,8 +330,10 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension) { case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null); case C.TYPE_OTHER: - return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), - mainHandler, null); + return new ExtractorMediaSource.Factory(new CacheDataSourceFactory(downloadCache, mediaDataSourceFactory)) + .createMediaSource(uri); +// return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), +// mainHandler, null); default: { throw new IllegalStateException("Unsupported type: " + type); } @@ -723,57 +776,57 @@ public void setRepeatModifier(boolean repeat) { } public void setSelectedTextTrack(String type, Dynamic value) { - textTrackType = type; - textTrackValue = value; - - int index = getTextTrackRendererIndex(); - if (index == C.INDEX_UNSET) { - return; - } - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - if (info == null) { - return; - } - - TrackGroupArray groups = info.getTrackGroups(index); - int trackIndex = C.INDEX_UNSET; - if (TextUtils.isEmpty(type)) { - // Do nothing - } else if (type.equals("disabled")) { - trackSelector.setSelectionOverride(index, groups, null); - return; - } else if (type.equals("language")) { - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - if (format.language != null && format.language.equals(value.asString())) { - trackIndex = i; - break; - } - } - } else if (type.equals("title")) { - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - if (format.id != null && format.id.equals(value.asString())) { - trackIndex = i; - break; - } - } - } else if (type.equals("index")) { - trackIndex = value.asInt(); - } else { // default. invalid type or "system" - trackSelector.clearSelectionOverrides(index); - return; - } - - if (trackIndex == C.INDEX_UNSET) { - trackSelector.clearSelectionOverrides(trackIndex); - return; - } - - MappingTrackSelector.SelectionOverride override - = new MappingTrackSelector.SelectionOverride( - new FixedTrackSelection.Factory(), trackIndex, 0); - trackSelector.setSelectionOverride(index, groups, override); +// textTrackType = type; +// textTrackValue = value; +// +// int index = getTextTrackRendererIndex(); +// if (index == C.INDEX_UNSET) { +// return; +// } +// MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); +// if (info == null) { +// return; +// } +// +// TrackGroupArray groups = info.getTrackGroups(index); +// int trackIndex = C.INDEX_UNSET; +// if (TextUtils.isEmpty(type)) { +// // Do nothing +// } else if (type.equals("disabled")) { +// trackSelector.setSelectionOverride(index, groups, null); +// return; +// } else if (type.equals("language")) { +// for (int i = 0; i < groups.length; ++i) { +// Format format = groups.get(i).getFormat(0); +// if (format.language != null && format.language.equals(value.asString())) { +// trackIndex = i; +// break; +// } +// } +// } else if (type.equals("title")) { +// for (int i = 0; i < groups.length; ++i) { +// Format format = groups.get(i).getFormat(0); +// if (format.id != null && format.id.equals(value.asString())) { +// trackIndex = i; +// break; +// } +// } +// } else if (type.equals("index")) { +// trackIndex = value.asInt(); +// } else { // default. invalid type or "system" +// trackSelector.clearSelectionOverrides(index); +// return; +// } +// +// if (trackIndex == C.INDEX_UNSET) { +// trackSelector.clearSelectionOverrides(trackIndex); +// return; +// } +// +// MappingTrackSelector.SelectionOverride override +// = new MappingTrackSelector.SelectionOverride( +// new FixedTrackSelection.Factory(), trackIndex, 0); +// trackSelector.setSelectionOverride(index, groups, override); } public void setPausedModifier(boolean paused) { From b695bc7f76f5b34665e1822f168f7e082d089b7b Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Tue, 10 Jul 2018 17:39:48 +0200 Subject: [PATCH 02/21] Create preloadVideo method --- .../brentvatne/exoplayer/ExoPlayerCache.java | 33 +++++++++++++++++-- .../brentvatne/react/ReactVideoPackage.java | 8 ++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 22f47a802e..84732c1fdd 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -1,17 +1,44 @@ package com.brentvatne.exoplayer; import android.content.Context; +import android.net.Uri; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloadManager; +import com.google.android.exoplayer2.offline.DownloadService; +import com.google.android.exoplayer2.offline.Downloader; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; +import com.google.android.exoplayer2.offline.ProgressiveDownloader; + +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; import java.io.File; -public class ExoPlayerCache { +public class ExoPlayerCache extends ReactContextBaseJavaModule { + private static SimpleCache instance = null; + + + public ExoPlayerCache(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public String getName() { + return "ExoPlayerCache"; + } - protected ExoPlayerCache() { - // Exists only to defeat instantiation. + @ReactMethod + public void preloadVideo(String url) { + DownloadAction downloadAction = new ProgressiveDownloadAction(Uri.parse(url), false, null, null); + DownloadService.startWithAction(getReactApplicationContext(), CacheDownloadService.class, downloadAction, true); } public static SimpleCache getInstance(Context context) { diff --git a/android-exoplayer/src/main/java/com/brentvatne/react/ReactVideoPackage.java b/android-exoplayer/src/main/java/com/brentvatne/react/ReactVideoPackage.java index 286f972c8c..67906d5eac 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/react/ReactVideoPackage.java +++ b/android-exoplayer/src/main/java/com/brentvatne/react/ReactVideoPackage.java @@ -1,12 +1,14 @@ package com.brentvatne.react; import com.brentvatne.exoplayer.ReactExoplayerViewManager; +import com.brentvatne.exoplayer.ExoPlayerCache; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -14,7 +16,11 @@ public class ReactVideoPackage implements ReactPackage { @Override public List createNativeModules(ReactApplicationContext reactContext) { - return Collections.emptyList(); + List modules = new ArrayList<>(); + + modules.add(new ExoPlayerCache(reactContext)); + + return modules; } // Deprecated RN 0.47 From 1d8bca94d218fef5f325e1eefc1b804056411dbb Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Tue, 10 Jul 2018 17:41:32 +0200 Subject: [PATCH 03/21] Use CacheUtil to preload video --- Video.js | 95 +++++++++++++------ .../brentvatne/exoplayer/ExoPlayerCache.java | 49 ++++++++-- .../exoplayer/ReactExoplayerView.java | 31 ------ 3 files changed, 109 insertions(+), 66 deletions(-) diff --git a/Video.js b/Video.js index 7a0d7a3996..55353e8dd8 100644 --- a/Video.js +++ b/Video.js @@ -1,6 +1,16 @@ -import React, {Component} from 'react'; +import React, { + Component +} from 'react'; import PropTypes from 'prop-types'; -import {StyleSheet, requireNativeComponent, NativeModules, View, ViewPropTypes, Image, Platform} from 'react-native'; +import { + StyleSheet, + requireNativeComponent, + NativeModules, + View, + ViewPropTypes, + Image, + Platform +} from 'react-native'; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import TextTrackType from './TextTrackType'; import VideoResizeMode from './VideoResizeMode.js'; @@ -11,7 +21,14 @@ const styles = StyleSheet.create({ }, }); -export { TextTrackType }; +const { + ExoPlayerCache +} = NativeModules; + +export { + TextTrackType, + ExoPlayerCache, +}; export default class Video extends Component { @@ -26,17 +43,17 @@ export default class Video extends Component { setNativeProps(nativeProps) { this._root.setNativeProps(nativeProps); } - + toTypeString(x) { switch (typeof x) { case "object": - return x instanceof Date - ? x.toISOString() - : JSON.stringify(x); // object, null + return x instanceof Date ? + x.toISOString() : + JSON.stringify(x); // object, null case "undefined": return ""; default: // boolean, number, string - return x.toString(); + return x.toString(); } } @@ -59,16 +76,22 @@ export default class Video extends Component { } }); } else { - this.setNativeProps({ seek: time }); + this.setNativeProps({ + seek: time + }); } }; presentFullscreenPlayer = () => { - this.setNativeProps({ fullscreen: true }); + this.setNativeProps({ + fullscreen: true + }); }; dismissFullscreenPlayer = () => { - this.setNativeProps({ fullscreen: false }); + this.setNativeProps({ + fullscreen: false + }); }; _assignRoot = (component) => { @@ -101,7 +124,9 @@ export default class Video extends Component { _onSeek = (event) => { if (this.state.showPoster && !this.props.audioOnly) { - this.setState({showPoster: false}); + this.setState({ + showPoster: false + }); } if (this.props.onSeek) { @@ -165,7 +190,9 @@ export default class Video extends Component { _onPlaybackRateChange = (event) => { if (this.state.showPoster && event.nativeEvent.playbackRate !== 0 && !this.props.audioOnly) { - this.setState({showPoster: false}); + this.setState({ + showPoster: false + }); } if (this.props.onPlaybackRateChange) { @@ -257,24 +284,34 @@ export default class Video extends Component { resizeMode: this.props.posterResizeMode || 'contain' }; - return ( - - - - + return ( < + View style = { + nativeProps.style + } > + < + RCTVideo ref = { + this._assignRoot + } { ...nativeProps + } + /> < + Image style = { + posterStyle + } + source = { + { + uri: this.props.poster + } + } + /> < + /View> ); } - return ( - ); } @@ -380,4 +417,4 @@ const RCTVideo = requireNativeComponent('RCTVideo', Video, { seek: true, fullscreen: true, }, -}); +}); \ No newline at end of file diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 84732c1fdd..da97cc97bd 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -2,9 +2,14 @@ import android.content.Context; import android.net.Uri; +import android.util.Log; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; +import com.google.android.exoplayer2.upstream.cache.CacheUtil; +import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters; +import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.offline.DownloadAction; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadService; @@ -12,6 +17,8 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; import com.google.android.exoplayer2.offline.ProgressiveDownloader; +import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.MediaSource; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; @@ -31,14 +38,44 @@ public ExoPlayerCache(ReactApplicationContext reactContext) { } @Override - public String getName() { - return "ExoPlayerCache"; - } + public String getName() { + return "ExoPlayerCache"; + } @ReactMethod - public void preloadVideo(String url) { - DownloadAction downloadAction = new ProgressiveDownloadAction(Uri.parse(url), false, null, null); - DownloadService.startWithAction(getReactApplicationContext(), CacheDownloadService.class, downloadAction, true); + public void preloadVideo(final String url) { + Log.d(getName(), "preloadVideo"); + + Thread cacheThread = new Thread(new Runnable() { + @Override + public void run() { + Log.d(getName(), "Caching..."); + Log.d(getName(), url); + final Uri uri = Uri.parse(url); + final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); + final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); + + try { + CacheUtil.cache( + dataSpec, + downloadCache, + new CacheDataSourceFactory(downloadCache, DataSourceUtil.getDefaultDataSourceFactory( + getReactApplicationContext(), + null, + null + )).createDataSource(), + null, + null + ); + + Log.d(getName(), "Cache succeeded"); + } catch (Exception e) { + Log.d(getName(), "Cache error"); + e.printStackTrace(); + } + } + }, "cache_thread"); + cacheThread.start(); } public static SimpleCache getInstance(Context context) { diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 3535ac50d4..7fc9f29897 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -99,14 +99,6 @@ class ReactExoplayerView extends FrameLayout implements private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final CookieManager DEFAULT_COOKIE_MANAGER; private static final int SHOW_PROGRESS = 1; -// private static final String DOWNLOAD_ACTION_FILE = "actions"; -// private static final DownloadAction.Deserializer[] DOWNLOAD_DESERIALIZERS = -// new DownloadAction.Deserializer[] { -// DashDownloadAction.DESERIALIZER, -// HlsDownloadAction.DESERIALIZER, -// SsDownloadAction.DESERIALIZER, -// ProgressiveDownloadAction.DESERIALIZER -// }; static { DEFAULT_COOKIE_MANAGER = new CookieManager(); @@ -119,12 +111,10 @@ class ReactExoplayerView extends FrameLayout implements private ExoPlayerView exoPlayerView; private SimpleCache downloadCache; -// private DownloadManager downloadManager; private DataSource.Factory mediaDataSourceFactory; private SimpleExoPlayer player; private MappingTrackSelector trackSelector; private boolean playerNeedsSource; -// protected String userAgent; private int resumeWindow; private long resumePosition; @@ -182,7 +172,6 @@ public ReactExoplayerView(ThemedReactContext context) { audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); themedReactContext.addLifecycleEventListener(this); audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); -// userAgent = Util.getUserAgent(context, "ReactExoPlayer"); initializePlayer(); } @@ -199,17 +188,6 @@ private void createViews() { mediaDataSourceFactory = buildDataSourceFactory(true); downloadCache = ExoPlayerCache.getInstance(getContext()); -// DownloaderConstructorHelper downloaderConstructorHelper = -// new DownloaderConstructorHelper( -// downloadCache, buildHttpDataSourceFactory(/* listener= */ null)); -// downloadManager = -// new DownloadManager( -// downloaderConstructorHelper, -// 4, -// DownloadManager.DEFAULT_MIN_RETRY_COUNT, -// new File(getContext().getCacheDir(), DOWNLOAD_ACTION_FILE), -// DOWNLOAD_DESERIALIZERS); - mainHandler = new Handler(); if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER); @@ -224,12 +202,6 @@ private void createViews() { addView(exoPlayerView, 0, layoutParams); } -// /** Returns a {@link HttpDataSource.Factory}. */ -// public HttpDataSource.Factory buildHttpDataSourceFactory( -// TransferListener listener) { -// return new DefaultHttpDataSourceFactory(userAgent, listener); -// } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -309,9 +281,6 @@ private void initializePlayer() { player.prepare(mediaSource, !haveResumePosition, false); playerNeedsSource = false; - DownloadAction downloadAction = new ProgressiveDownloadAction(srcUri, false, null, null); - DownloadService.startWithAction(getContext(), CacheDownloadService.class, downloadAction, true); - eventEmitter.loadStart(); loadVideoStarted = true; } From 3b7a47daf42691f33a804e3fc8d76831765b27f4 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Fri, 13 Jul 2018 12:07:47 +0200 Subject: [PATCH 04/21] Delete CacheDownloadService --- .../exoplayer/CacheDownloadService.java | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java deleted file mode 100644 index 7973b0cef9..0000000000 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/CacheDownloadService.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.brentvatne.exoplayer; - -import android.app.Notification; - -import com.brentvatne.react.R; -import com.google.android.exoplayer2.offline.DownloadManager; -import com.google.android.exoplayer2.offline.DownloadManager.TaskState; -import com.google.android.exoplayer2.offline.DownloadService; -import com.google.android.exoplayer2.scheduler.PlatformScheduler; -import com.google.android.exoplayer2.ui.DownloadNotificationUtil; -import com.google.android.exoplayer2.util.NotificationUtil; -import com.google.android.exoplayer2.util.Util; - -/** A service for downloading media. */ -public class CacheDownloadService extends DownloadService { - - private static final String CHANNEL_ID = "download_channel"; - private static final int JOB_ID = 1; - private static final int FOREGROUND_NOTIFICATION_ID = 1; -// private DownloadManager downloadManager; - - public CacheDownloadService() { - super( - FOREGROUND_NOTIFICATION_ID, - DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, - CHANNEL_ID, - R.string.exo_download_notification_channel_name); - -// this.downloadManager = downloadManager; - } - - @Override - protected DownloadManager getDownloadManager() { - return null; - } - - @Override - protected PlatformScheduler getScheduler() { - return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null; - } - - @Override - protected Notification getForegroundNotification(TaskState[] taskStates) { - return DownloadNotificationUtil.buildProgressNotification( - /* context= */ this, - R.drawable.exo_controls_play, - CHANNEL_ID, - /* contentIntent= */ null, - /* message= */ null, - taskStates); - } -} From 713cffb7d0cd123c67a1a50eaae004c75b48e7fc Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Fri, 13 Jul 2018 12:12:28 +0200 Subject: [PATCH 05/21] Cleanup imports --- .../com/brentvatne/exoplayer/ExoPlayerCache.java | 10 ---------- .../brentvatne/exoplayer/ReactExoplayerView.java | 16 ---------------- 2 files changed, 26 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index da97cc97bd..cd85d105a0 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -7,18 +7,8 @@ import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.CacheUtil; -import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.offline.DownloadAction; -import com.google.android.exoplayer2.offline.DownloadManager; -import com.google.android.exoplayer2.offline.DownloadService; -import com.google.android.exoplayer2.offline.Downloader; -import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; -import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; -import com.google.android.exoplayer2.offline.ProgressiveDownloader; -import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.MediaSource; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7fc9f29897..f9fd917c0b 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -38,13 +38,6 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataRenderer; -import com.google.android.exoplayer2.offline.DownloadAction; -import com.google.android.exoplayer2.offline.DownloadManager; -import com.google.android.exoplayer2.offline.DownloadService; -import com.google.android.exoplayer2.offline.Downloader; -import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; -import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; -import com.google.android.exoplayer2.offline.ProgressiveDownloader; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; @@ -53,12 +46,9 @@ import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; -import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; @@ -67,17 +57,11 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; -import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.util.Util; -import java.io.File; -import java.io.IOException; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; From dc78bfb8f6d33a2c8ecaa7d75cf5ef96dc735676 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Fri, 20 Jul 2018 14:17:36 +0200 Subject: [PATCH 06/21] Save video cache to //video/ --- .../src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index cd85d105a0..2e5927a8f0 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -70,7 +70,7 @@ public void run() { public static SimpleCache getInstance(Context context) { if(instance == null) { - instance = new SimpleCache(new File(context.getCacheDir().toString() + "/video-cache"), new NoOpCacheEvictor()); + instance = new SimpleCache(new File(context.getCacheDir().toString() + "/video"), new NoOpCacheEvictor()); } return instance; } From 7662158e46dc59bfb1c2709eb27641ca76335a5a Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Mon, 23 Jul 2018 11:04:21 +0200 Subject: [PATCH 07/21] Expose exportVideo on Android --- .../brentvatne/exoplayer/ExoPlayerCache.java | 80 +++++++++++++++++-- .../exoplayer/ReactExoplayerView.java | 2 - 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 2e5927a8f0..32a3ec56ac 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -5,18 +5,25 @@ import android.util.Log; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.CacheUtil; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSourceInputStream; import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; +import java.io.IOException; import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; public class ExoPlayerCache extends ReactContextBaseJavaModule { @@ -49,11 +56,7 @@ public void run() { CacheUtil.cache( dataSpec, downloadCache, - new CacheDataSourceFactory(downloadCache, DataSourceUtil.getDefaultDataSourceFactory( - getReactApplicationContext(), - null, - null - )).createDataSource(), + createDataSource(downloadCache), null, null ); @@ -68,11 +71,76 @@ public void run() { cacheThread.start(); } + @ReactMethod + public void exportVideo(final String url, final Promise promise) { + Log.d(getName(), "exportVideo"); + + Thread exportThread = new Thread(new Runnable() { + @Override + public void run() { + Log.d(getName(), "Exporting..."); + Log.d(getName(), url); + final Uri uri = Uri.parse(url); + final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); + final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); + CacheUtil.CachingCounters counters = new CacheUtil.CachingCounters(); + + try { + CacheUtil.getCached( + dataSpec, + downloadCache, + counters + ); + + // TODO check counters for when download is not complete + + DataSourceInputStream inputStream = new DataSourceInputStream(createDataSource(downloadCache), dataSpec); + + File targetFile = new File(ExoPlayerCache.getCacheDir(getReactApplicationContext()) + "/" + uri.getLastPathSegment()); + OutputStream outStream = new FileOutputStream(targetFile); + + byte[] buffer = new byte[8 * 1024]; + int bytesRead; + try { + while ((bytesRead = inputStream.read(buffer)) != -1) { + outStream.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + Log.d(getName(), "Read error"); + e.printStackTrace(); + } + + Log.d(getName(), "Export succeeded"); + Log.d(getName(), targetFile.getPath()); + + promise.resolve(targetFile.getPath()); + } catch (Exception e) { + Log.d(getName(), "Export error"); + e.printStackTrace(); + promise.reject(e); + } + } + }, "export_thread"); + exportThread.start(); + } + public static SimpleCache getInstance(Context context) { if(instance == null) { - instance = new SimpleCache(new File(context.getCacheDir().toString() + "/video"), new NoOpCacheEvictor()); + instance = new SimpleCache(new File(ExoPlayerCache.getCacheDir(context)), new NoOpCacheEvictor()); } return instance; } + private static String getCacheDir(Context context) { + return context.getCacheDir().toString() + "/video"; + } + + private DataSource createDataSource(Cache cache) { + return new CacheDataSourceFactory(cache, DataSourceUtil.getDefaultDataSourceFactory( + getReactApplicationContext(), + null, + null + )).createDataSource(); + } + } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index f9fd917c0b..65acc96caf 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -285,8 +285,6 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension) { case C.TYPE_OTHER: return new ExtractorMediaSource.Factory(new CacheDataSourceFactory(downloadCache, mediaDataSourceFactory)) .createMediaSource(uri); -// return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), -// mainHandler, null); default: { throw new IllegalStateException("Unsupported type: " + type); } From 06c3ce88056ffb324d0e012f3c93b0fc58e0de1d Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Mon, 23 Jul 2018 11:29:03 +0200 Subject: [PATCH 08/21] Log amount of cached bytes --- .../java/com/brentvatne/exoplayer/ExoPlayerCache.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 32a3ec56ac..be78282d33 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -93,6 +93,7 @@ public void run() { ); // TODO check counters for when download is not complete + Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (start)"); DataSourceInputStream inputStream = new DataSourceInputStream(createDataSource(downloadCache), dataSpec); @@ -110,6 +111,14 @@ public void run() { e.printStackTrace(); } + CacheUtil.getCached( + dataSpec, + downloadCache, + counters + ); + + Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (end)"); + Log.d(getName(), "Export succeeded"); Log.d(getName(), targetFile.getPath()); From fdfae8013a36429d8fd39a956f9e43bb6073fc47 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Mon, 23 Jul 2018 12:37:35 +0200 Subject: [PATCH 09/21] Add some notes --- .../main/java/com/brentvatne/exoplayer/ExoPlayerCache.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index be78282d33..75b828b3ca 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -81,7 +81,7 @@ public void run() { Log.d(getName(), "Exporting..."); Log.d(getName(), url); final Uri uri = Uri.parse(url); - final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); + final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); // TODO won't work for video's over 100 MB final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); CacheUtil.CachingCounters counters = new CacheUtil.CachingCounters(); @@ -92,7 +92,7 @@ public void run() { counters ); - // TODO check counters for when download is not complete + // TODO check counters for when download is not complete // Download can complete during writing Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (start)"); DataSourceInputStream inputStream = new DataSourceInputStream(createDataSource(downloadCache), dataSpec); @@ -107,6 +107,7 @@ public void run() { outStream.write(buffer, 0, bytesRead); } } catch (IOException e) { + // TODO this exception should not be thrown Log.d(getName(), "Read error"); e.printStackTrace(); } @@ -117,6 +118,7 @@ public void run() { counters ); + // TODO are we sure the complete video is downloaded? Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (end)"); Log.d(getName(), "Export succeeded"); From 9a7f4af8749f61502095e6f9ffcc5960f544c737 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Wed, 25 Jul 2018 15:33:17 +0200 Subject: [PATCH 10/21] Remove preloading methods --- .../brentvatne/exoplayer/ExoPlayerCache.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 75b828b3ca..29ae58ea88 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -39,38 +39,6 @@ public String getName() { return "ExoPlayerCache"; } - @ReactMethod - public void preloadVideo(final String url) { - Log.d(getName(), "preloadVideo"); - - Thread cacheThread = new Thread(new Runnable() { - @Override - public void run() { - Log.d(getName(), "Caching..."); - Log.d(getName(), url); - final Uri uri = Uri.parse(url); - final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); - final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); - - try { - CacheUtil.cache( - dataSpec, - downloadCache, - createDataSource(downloadCache), - null, - null - ); - - Log.d(getName(), "Cache succeeded"); - } catch (Exception e) { - Log.d(getName(), "Cache error"); - e.printStackTrace(); - } - } - }, "cache_thread"); - cacheThread.start(); - } - @ReactMethod public void exportVideo(final String url, final Promise promise) { Log.d(getName(), "exportVideo"); From 952909ddf87c0ab549ce8fdf2b51b4c2a51795b2 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Thu, 26 Jul 2018 11:29:56 +0200 Subject: [PATCH 11/21] Remove autoformat --- Video.js | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/Video.js b/Video.js index 55353e8dd8..4282ad0d64 100644 --- a/Video.js +++ b/Video.js @@ -47,9 +47,9 @@ export default class Video extends Component { toTypeString(x) { switch (typeof x) { case "object": - return x instanceof Date ? - x.toISOString() : - JSON.stringify(x); // object, null + return x instanceof Date + ? x.toISOString() + : JSON.stringify(x); // object, null case "undefined": return ""; default: // boolean, number, string @@ -284,34 +284,24 @@ export default class Video extends Component { resizeMode: this.props.posterResizeMode || 'contain' }; - return ( < - View style = { - nativeProps.style - } > - < - RCTVideo ref = { - this._assignRoot - } { ...nativeProps - } - /> < - Image style = { - posterStyle - } - source = { - { - uri: this.props.poster - } - } - /> < - /View> + return ( + + + + ); } - return ( < - RCTVideo ref = { - this._assignRoot - } { ...nativeProps - } + return ( + ); } @@ -417,4 +407,4 @@ const RCTVideo = requireNativeComponent('RCTVideo', Video, { seek: true, fullscreen: true, }, -}); \ No newline at end of file +}); From 941ecc026183564a804068bcd219ce19457ac20a Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Wed, 1 Aug 2018 08:54:31 +0200 Subject: [PATCH 12/21] Revert exoplayer upgrade --- android-exoplayer/build.gradle | 4 +- .../exoplayer/ReactExoplayerView.java | 102 +++++++++--------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle index 8a9052cb26..a957dd6212 100644 --- a/android-exoplayer/build.gradle +++ b/android-exoplayer/build.gradle @@ -23,8 +23,8 @@ android { dependencies { //noinspection GradleDynamicVersion provided "com.facebook.react:react-native:${_reactNativeVersion}" - compile 'com.google.android.exoplayer:exoplayer:2.8.2' - compile('com.google.android.exoplayer:extension-okhttp:2.8.2') { + compile 'com.google.android.exoplayer:exoplayer:2.7.3' + compile('com.google.android.exoplayer:extension-okhttp:2.7.3') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } compile 'com.squareup.okhttp3:okhttp:3.9.1' diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 65acc96caf..591124ccc3 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -727,57 +727,57 @@ public void setRepeatModifier(boolean repeat) { } public void setSelectedTextTrack(String type, Dynamic value) { -// textTrackType = type; -// textTrackValue = value; -// -// int index = getTextTrackRendererIndex(); -// if (index == C.INDEX_UNSET) { -// return; -// } -// MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); -// if (info == null) { -// return; -// } -// -// TrackGroupArray groups = info.getTrackGroups(index); -// int trackIndex = C.INDEX_UNSET; -// if (TextUtils.isEmpty(type)) { -// // Do nothing -// } else if (type.equals("disabled")) { -// trackSelector.setSelectionOverride(index, groups, null); -// return; -// } else if (type.equals("language")) { -// for (int i = 0; i < groups.length; ++i) { -// Format format = groups.get(i).getFormat(0); -// if (format.language != null && format.language.equals(value.asString())) { -// trackIndex = i; -// break; -// } -// } -// } else if (type.equals("title")) { -// for (int i = 0; i < groups.length; ++i) { -// Format format = groups.get(i).getFormat(0); -// if (format.id != null && format.id.equals(value.asString())) { -// trackIndex = i; -// break; -// } -// } -// } else if (type.equals("index")) { -// trackIndex = value.asInt(); -// } else { // default. invalid type or "system" -// trackSelector.clearSelectionOverrides(index); -// return; -// } -// -// if (trackIndex == C.INDEX_UNSET) { -// trackSelector.clearSelectionOverrides(trackIndex); -// return; -// } -// -// MappingTrackSelector.SelectionOverride override -// = new MappingTrackSelector.SelectionOverride( -// new FixedTrackSelection.Factory(), trackIndex, 0); -// trackSelector.setSelectionOverride(index, groups, override); + textTrackType = type; + textTrackValue = value; + + int index = getTextTrackRendererIndex(); + if (index == C.INDEX_UNSET) { + return; + } + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + if (info == null) { + return; + } + + TrackGroupArray groups = info.getTrackGroups(index); + int trackIndex = C.INDEX_UNSET; + if (TextUtils.isEmpty(type)) { + // Do nothing + } else if (type.equals("disabled")) { + trackSelector.setSelectionOverride(index, groups, null); + return; + } else if (type.equals("language")) { + for (int i = 0; i < groups.length; ++i) { + Format format = groups.get(i).getFormat(0); + if (format.language != null && format.language.equals(value.asString())) { + trackIndex = i; + break; + } + } + } else if (type.equals("title")) { + for (int i = 0; i < groups.length; ++i) { + Format format = groups.get(i).getFormat(0); + if (format.id != null && format.id.equals(value.asString())) { + trackIndex = i; + break; + } + } + } else if (type.equals("index")) { + trackIndex = value.asInt(); + } else { // default. invalid type or "system" + trackSelector.clearSelectionOverrides(index); + return; + } + + if (trackIndex == C.INDEX_UNSET) { + trackSelector.clearSelectionOverrides(trackIndex); + return; + } + + MappingTrackSelector.SelectionOverride override + = new MappingTrackSelector.SelectionOverride( + new FixedTrackSelection.Factory(), trackIndex, 0); + trackSelector.setSelectionOverride(index, groups, override); } public void setPausedModifier(boolean paused) { From 74dd52072f5253c97ae40b0b0bb16fe384eee183 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Wed, 15 Jan 2020 15:12:30 +0100 Subject: [PATCH 13/21] Replace CachingCounters by CacheKeyFactory --- .../com/brentvatne/exoplayer/ExoPlayerCache.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 29ae58ea88..34bbb38eb4 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -9,6 +9,7 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.CacheUtil; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; +import com.google.android.exoplayer2.upstream.cache.CacheKeyFactory; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSourceInputStream; @@ -51,18 +52,15 @@ public void run() { final Uri uri = Uri.parse(url); final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); // TODO won't work for video's over 100 MB final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); - CacheUtil.CachingCounters counters = new CacheUtil.CachingCounters(); + CacheKeyFactory cacheKeyFactory = new CacheKeyFactory(); try { CacheUtil.getCached( dataSpec, downloadCache, - counters + cacheKeyFactory ); - // TODO check counters for when download is not complete // Download can complete during writing - Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (start)"); - DataSourceInputStream inputStream = new DataSourceInputStream(createDataSource(downloadCache), dataSpec); File targetFile = new File(ExoPlayerCache.getCacheDir(getReactApplicationContext()) + "/" + uri.getLastPathSegment()); @@ -83,12 +81,9 @@ public void run() { CacheUtil.getCached( dataSpec, downloadCache, - counters + cacheKeyFactory ); - // TODO are we sure the complete video is downloaded? - Log.d(getName(), "Cached " + counters.totalCachedBytes() + " bytes (end)"); - Log.d(getName(), "Export succeeded"); Log.d(getName(), targetFile.getPath()); From cf94740c31717558724003b3bb458d136dd36812 Mon Sep 17 00:00:00 2001 From: Ralf Nieuwenhuizen Date: Wed, 15 Jan 2020 15:24:42 +0100 Subject: [PATCH 14/21] Implement cacheKeyFactory correctly --- .../main/java/com/brentvatne/exoplayer/ExoPlayerCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 34bbb38eb4..5eb82e6c14 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -29,7 +29,7 @@ public class ExoPlayerCache extends ReactContextBaseJavaModule { private static SimpleCache instance = null; - + private static final String CACHE_KEY_PREFIX = "exoPlayerCacheKeyPrefix"; public ExoPlayerCache(ReactApplicationContext reactContext) { super(reactContext); @@ -52,7 +52,7 @@ public void run() { final Uri uri = Uri.parse(url); final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); // TODO won't work for video's over 100 MB final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); - CacheKeyFactory cacheKeyFactory = new CacheKeyFactory(); + CacheKeyFactory cacheKeyFactory = ds -> CACHE_KEY_PREFIX + "." + CacheUtil.generateKey(ds.uri);; try { CacheUtil.getCached( From 65a30a719e96982c23c68887acefb7923da6da5f Mon Sep 17 00:00:00 2001 From: jochem725 Date: Wed, 15 Jul 2020 10:34:20 +0200 Subject: [PATCH 15/21] Retry on video codec error --- .../exoplayer/ReactExoplayerView.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index fd85401c69..7d41eacce7 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -111,6 +111,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean isPaused; private boolean isBuffering; private float rate = 1f; + private int codecRetries = 0; private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; @@ -658,6 +659,28 @@ else if (e.type == ExoPlaybackException.TYPE_SOURCE) { ex = e.getSourceException(); errorString = getResources().getString(R.string.unrecognized_media_format); } + + // Assumption: If we have a codec error it could be that it is still busy because another component has not yet unmounted. + // We retry this max 3 times with some delay and see if the error decreases. + int MAX_CODEC_RETRIES = 3; + if (e.type == ExoPlaybackException.TYPE_RENDERER) { + if (this.codecRetries < MAX_CODEC_RETRIES) { + this.codecRetries += 1; + + // Release the current player. + releasePlayer(); + + // Retry init after some delay. + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + initializePlayer(); + } + }, 500); + return; + } + } + if (errorString != null) { eventEmitter.error(errorString, ex); } From ff7c1fff5d7aa1b98488072751b927e7d420269b Mon Sep 17 00:00:00 2001 From: Jasper Meijaard Date: Thu, 22 Oct 2020 14:46:24 +0200 Subject: [PATCH 16/21] Reject promise on every exception in video export --- .../brentvatne/exoplayer/ExoPlayerCache.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 5eb82e6c14..ed5b418e07 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -4,6 +4,8 @@ import android.net.Uri; import android.util.Log; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.SimpleCache; @@ -41,7 +43,7 @@ public String getName() { } @ReactMethod - public void exportVideo(final String url, final Promise promise) { + public void exportVideo(final String url, boolean improvedErrorHandling, final Promise promise) { Log.d(getName(), "exportVideo"); Thread exportThread = new Thread(new Runnable() { @@ -53,10 +55,10 @@ public void run() { final DataSpec dataSpec = new DataSpec(uri, 0, 100 * 1024 * 1024, null); // TODO won't work for video's over 100 MB final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext()); CacheKeyFactory cacheKeyFactory = ds -> CACHE_KEY_PREFIX + "." + CacheUtil.generateKey(ds.uri);; - + try { CacheUtil.getCached( - dataSpec, + dataSpec, downloadCache, cacheKeyFactory ); @@ -65,7 +67,7 @@ public void run() { File targetFile = new File(ExoPlayerCache.getCacheDir(getReactApplicationContext()) + "/" + uri.getLastPathSegment()); OutputStream outStream = new FileOutputStream(targetFile); - + byte[] buffer = new byte[8 * 1024]; int bytesRead; try { @@ -76,10 +78,14 @@ public void run() { // TODO this exception should not be thrown Log.d(getName(), "Read error"); e.printStackTrace(); + + if (improvedErrorHandling) { + throw e; + } } CacheUtil.getCached( - dataSpec, + dataSpec, downloadCache, cacheKeyFactory ); @@ -91,6 +97,13 @@ public void run() { } catch (Exception e) { Log.d(getName(), "Export error"); e.printStackTrace(); + + if (improvedErrorHandling) { + String className = e.getClass().getSimpleName(); + promise.reject(className, className + ": " + e.getMessage()); + return; + } + promise.reject(e); } } From 557e29516fccf992fc4579336e8be13c291ca19a Mon Sep 17 00:00:00 2001 From: Jasper Meijaard Date: Thu, 22 Oct 2020 16:14:00 +0200 Subject: [PATCH 17/21] Remove unused import --- .../src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index ed5b418e07..68847b68aa 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -5,7 +5,6 @@ import android.util.Log; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.SimpleCache; From 77de90d13671c3c0354daa6a85aae59a279e37a9 Mon Sep 17 00:00:00 2001 From: Bryan van Wijk Date: Mon, 9 Nov 2020 18:49:06 +0100 Subject: [PATCH 18/21] Remove test parameter --- .../com/brentvatne/exoplayer/ExoPlayerCache.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index 68847b68aa..bf7c2a3749 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -42,7 +42,7 @@ public String getName() { } @ReactMethod - public void exportVideo(final String url, boolean improvedErrorHandling, final Promise promise) { + public void exportVideo(final String url, final Promise promise) { Log.d(getName(), "exportVideo"); Thread exportThread = new Thread(new Runnable() { @@ -78,9 +78,7 @@ public void run() { Log.d(getName(), "Read error"); e.printStackTrace(); - if (improvedErrorHandling) { - throw e; - } + throw e; } CacheUtil.getCached( @@ -97,11 +95,9 @@ public void run() { Log.d(getName(), "Export error"); e.printStackTrace(); - if (improvedErrorHandling) { - String className = e.getClass().getSimpleName(); - promise.reject(className, className + ": " + e.getMessage()); - return; - } + String className = e.getClass().getSimpleName(); + promise.reject(className, className + ": " + e.getMessage()); + return; promise.reject(e); } From 9a03f8cf36a524725afb9db10798f6827a9d7764 Mon Sep 17 00:00:00 2001 From: Bryan van Wijk Date: Tue, 10 Nov 2020 11:41:32 +0100 Subject: [PATCH 19/21] Remove unreachable case --- .../src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index bf7c2a3749..fa2c1ae227 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -98,8 +98,6 @@ public void run() { String className = e.getClass().getSimpleName(); promise.reject(className, className + ": " + e.getMessage()); return; - - promise.reject(e); } } }, "export_thread"); From ce495d629981f06eebe07575d808e8a57d1ec625 Mon Sep 17 00:00:00 2001 From: Jasper Meijaard Date: Mon, 14 Jun 2021 11:51:47 +0200 Subject: [PATCH 20/21] Return arguments from exportVideo method (#30) * Return bytes read and written in export video method * Cleanup unused vars --- .../java/com/brentvatne/exoplayer/ExoPlayerCache.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index fa2c1ae227..a0f34cead6 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -4,6 +4,8 @@ import android.net.Uri; import android.util.Log; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.Cache; @@ -87,10 +89,17 @@ public void run() { cacheKeyFactory ); + if (!targetFile.exists()) { + throw new Exception("Target file not present after writing bytes"); + } + Log.d(getName(), "Export succeeded"); Log.d(getName(), targetFile.getPath()); - promise.resolve(targetFile.getPath()); + WritableMap result = Arguments.createMap(); + result.putString("path", targetFile.getPath()); + + promise.resolve(result); } catch (Exception e) { Log.d(getName(), "Export error"); e.printStackTrace(); From 977261c13af2a282f7ad012b3a390d29b2f9e9cf Mon Sep 17 00:00:00 2001 From: Jasper Meijaard Date: Wed, 23 Jun 2021 11:20:44 +0200 Subject: [PATCH 21/21] Make exo player cache size and directory configurable (#31) * Store exo player cache in subdirectory * Let exo player fully manage cache size * Restrict cache size to 380MB * Make cache evictor configurable * Make sub cache directory configurable * Use correct type --- .../brentvatne/exoplayer/ExoPlayerCache.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java index a0f34cead6..cc318a1ba6 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerCache.java @@ -7,8 +7,9 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; +import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.Cache; +import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.CacheUtil; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; @@ -17,10 +18,8 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSourceInputStream; -import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; @@ -33,6 +32,8 @@ public class ExoPlayerCache extends ReactContextBaseJavaModule { private static SimpleCache instance = null; private static final String CACHE_KEY_PREFIX = "exoPlayerCacheKeyPrefix"; + private static int maxCacheSizeBytes = -1; // Default no maximum size + private static String cacheSubDirectory = ""; public ExoPlayerCache(ReactApplicationContext reactContext) { super(reactContext); @@ -43,6 +44,18 @@ public String getName() { return "ExoPlayerCache"; } + @ReactMethod + public void setMaxCacheSize(final int bytes, final Promise promise) { + maxCacheSizeBytes = bytes; + promise.resolve(maxCacheSizeBytes); + } + + @ReactMethod + public void setCacheSubDirectory(final String directory, final Promise promise) { + cacheSubDirectory = directory; + promise.resolve(cacheSubDirectory); + } + @ReactMethod public void exportVideo(final String url, final Promise promise) { Log.d(getName(), "exportVideo"); @@ -115,7 +128,12 @@ public void run() { public static SimpleCache getInstance(Context context) { if(instance == null) { - instance = new SimpleCache(new File(ExoPlayerCache.getCacheDir(context)), new NoOpCacheEvictor()); + instance = new SimpleCache( + new File(ExoPlayerCache.getCacheDir(context) + cacheSubDirectory), + maxCacheSizeBytes == -1 + ? new NoOpCacheEvictor() + : new LeastRecentlyUsedCacheEvictor(maxCacheSizeBytes) + ); } return instance; }