-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add caching #99
Comments
This comment has been minimized.
This comment has been minimized.
This might be a good solution: https://github.com/johanneslumpe/react-native-fs |
Another approach would be to write downloaded assets to a temp directory and load them from there when available. I've only worked with the Obj C part of the native code, but this approach would work there (I assume it could work on the Java side as well). Then the relevant cleanup logic would have to be added to the component lifecycle, to make sure Wouldn't it be better to think of this as an implementation detail of the native code and maintain the current API? |
I'd be up for implementing this for iOS to start with. My initial proposal is that caching is disabled by default, but requested on a per-clip basis like so: // Cache video locally (write to temp directory)
<Video source={{uri: "https://foo.com/clip1.mp4", cache: true}} ... />
// Don't cache video
<Video source={{uri: "https://foo.com/clip1.mp4"}} ... /> The The native code would then first look for the asset on disk, loading it from there if found, or otherwise load it remotely as usual. There are at least a couple of decisions that have to be made around this:
Would be great to get some feedback on this. I'll probably start work on this soon at any rate since I need this for an app I'm working on, so the sooner the better. |
Sounds like a good approach!
Not sure how I feel about this, but how about being able to configure the params like this? // Use smart defaults (30MB cache size & purge in 10 minute )
<Video source={{uri: "https://foo.com/clip1.mp4", cache: true } ... />
// Configure cache to 50MB & purge in 1 hour
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 50, expiresIn: 3600 }} ... /> |
@jamesfzhang The thing with that approach is that these parameters should probably be set globally for the app. For example, if one writes something like this: <View>
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 50, expiresIn: 3600 }} ... />
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 40, expiresIn: 4800 }} ... />
</View> Which set of cache settings should be used for caching |
I was under the impression that the configurations would be set per video, as opposed to global. I imagine there are some videos you want to cache and others you don't. But I guess just using global configs would be fine too? Maybe you'd need to add a VideoConfiguration class? |
They could be set per video, but it's not clear which settings should take precedence when there are several Something like this would work (maybe not particularly pretty, but then again it only has to be written once): // require this file instead of require('react-native-video') where Video is to be used
var Video = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 50, expiresIn: 3600, key: 'settings'} };
}
module.exports = Video; Then the cache settings would be applied globally through the app, and only once. This could also be done several times with different settings, if the user of the library is confident that there won't be conflicts (i.e. two // LongClipPlayer.js
var LongVideo = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 50, expiresIn: 3600, key: 'longVideoCache'} };
}
module.exports = LongVideo;
// ShortClipPlayer.js
var ShortVideo = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 10, expiresIn: 1200, key: 'shortVideoCache'} };
}
module.exports = ShortVideo; Then the implementation would put videos created with Thoughts on this? |
The second case (multiple configs) may be overkill to start with, we could just go for a single global configuration for starters. The defaults should be fine for most users too, so I don't think this should add to the complexity of using the library, unless more control is needed. |
A really easy solution is the one that @cancan101 mentioned, using react-native-fs, but yeah as @thsig mentioned there will huge issue with the usage of the disk space if we are just storing everything without an expire limit. Otherwise I agree with @thsig , let's keep it simple first, nah? Here is the code that is doing the thing to cache Image, but it is permanent storage, it is never removed :/ One super easy solution to clean the cache is to make a function which check all cached video of the specific application/folder and which deletes the old videos. Something like this: module.exports.cleanCache = function (cacheKey, time) {
const videos = readCache(cacheKey);
videos.forEach((video) => {
// is my video older than `time`?
if (isMyVideoOld(video, time) {
deleteVideo(video);
}
});
} The function will be accessible for the developer and we don't have to care if he/she is going to use it. |
Maybe it would be better to separate the caching implementation from What about something like this? <Video
source={{uri: "https://foo.com/clip1.mp4"}}
cacheName='StevesCache' /> When receiving new sources, the native code would publish a notification to the channel named The cache module implementation would then publish a message to the I'm suggesting an array assuming that multiple-video playback will be added to the library at some point (whether my branch is merged or not, this is probably on the roadmap anyway), so that would pre-empt having to change the API later, possibly after other libraries start depending on it. Then Thoughts, @brentvatne? We might need functionality like this soon, so I'd probably be up for writing the initial iOS implementation. I'm also very open to alternate implementation ideas. |
I have a feeling there may be a better way – proxying requests to server. One could set himself as
Based on <Video
source={{uri: "stevesCache://foo.com/clip1.mp4"}}
/>
...
<Video
source={{uri: "globalCache://foo.com/clip1.mp4"}}
/> Maybe we could use Customizing cache capacity would require calls to some kind of |
Hi, all: I want to know when to publish this function? It waste a lot of flow now. |
Any updates here? @thsig, definitely think you should take over the repo :) |
good point @getnashty! @thsig I have added you as a collaborator on this repo. let me know your npm username and I can give you npm publish access, brentvatne at gmail dot com |
This hasn't been touched for a while, but since iOS natively supports offline caching now it's worth reviving: https://developer.apple.com/reference/avfoundation/avassetdownloadurlsession/1650938-makeassetdownloadtask Video explaining the process. Starts at about 16:20: https://developer.apple.com/videos/play/wwdc2016/504/ Looks like this could all be handled behind the scenes in this package, and all the user has to decide is if the video should be cached and provide it the key name + download location that would be needed to look the task up again.
Sidenote: A sibling package to this that only does the downloading could be helpful as well. Would be used to load videos even without a react-native-video on the screen. (app is in background, preload video, etc..) |
@duhseekoh That's fantastic, thanks for posting that! I should pay more attention to updates. I've been working on a native iOS app with Swift (which I started a few years ago), and now I'm going rewrite it in React Native. I have been wanting to cache downloaded videos for a very long time, but it was going to be a ton of work. But it looks like AVAssetDownloadTask changes everything, and video caching should be pretty easy to implement now. (Although I'm nervous about Java and Windows.) I'm going to need this for my app, so I would like to contribute caching for iOS. I might be able to figure out Android, too. Worst case would be that caching only works on iOS and is just ignored on other platforms.
@thsig - I'm not sure about that, I think most developers would love to pass In the case where you have several Video instances playing the same video, then the last one would have precedence, and we could just show a yellow box warning during development. |
@duhseekoh From the looks of it AVAssetDownloadTask requires HLS, right? So you can't use it for any arbitrary video. That seems different than what this issue is about. |
This comment has been minimized.
This comment has been minimized.
if we use react-native-fs approach,, does it mean the video has to be downloaded fully, before react-native-video can access it? Please correct me if I'm wrong,, but what I want to happen is while the video is playing (and at the same time downloading) in the backgroud, the video file is cache somewhere. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
2 similar comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@DavitVosk I am not sure how others have solved it (looks like my way is similar to @rnowm), but the way I solved it on my project is I started writing my own downloader based off of this project: https://github.com/kfiroo/react-native-cached-image. Once you make that downloader work, you could load the local file path as your source uri. Something like: const mediaUrl = MEDIA.cached ? `file://${MEDIA.path}` : MEDIA.url
<Video source={ uri: mediaUrl } /> |
@hkdahal thanks for your answer. Can you please share your created downloader file that we can use it? Thanks |
@hkdahal Also can you please explain how that work? You use https://github.com/kfiroo/react-native-cached-image package with kfiroo/react-native-cached-image@31ad016 PR, but how react-native-video sees that the videos are cached? How they interact with each other? |
This comment has been minimized.
This comment has been minimized.
@DavitVosk I will create a gist or share a separate project that does the interaction between these packages later this weekend. I will mention you over there. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Thanks for making these changes @n1ru4l! I will add feedback on the PR. This is a pretty impactful change and as @n1ru4l has mentioned, we need testing and feedback to make sure this is ready to go. If you want to use this feature, please start testing it and letting us know how well it works. Once I have a couple thumbs up, I will do a review and get it merged. |
As caching is not yet availalbe out of the box for react-native-video on both Android and IOS, we have been using:
And when Video Player component is mounted, we check if there is already a file on device storage with matching name and if so, we just load video from storage instead of downloading it once more. |
hello, is there any way to download the video with rn-fetch-blob, and play the video while downloading?It's so long for waiting the downloading task complete. |
@webbin No, there is not. For android video caching I am currently using this: n1ru4l@0e1c7c2 (uses a HTTP proxy to cache the file). The idea came from this article: https://instagram-engineering.com/improving-video-playback-on-android-2f6c6a0058d I use this is my private fork and hope I will have the time to someday do a proper implementation for |
This comment has been minimized.
This comment has been minimized.
@gazedash I am still using it with patch-package on
patch-package
--- a/node_modules/react-native-video/android-exoplayer/build.gradle
+++ b/node_modules/react-native-video/android-exoplayer/build.gradle
@@ -31,5 +31,5 @@ dependencies {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
-
+ implementation 'com.danikula:videocache:2.7.1'
}
--- a/node_modules/react-native-video/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
+++ b/node_modules/react-native-video/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
@@ -2,6 +2,7 @@ package com.brentvatne.exoplayer;
import android.content.Context;
import android.content.ContextWrapper;
+import android.net.Uri;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.CookieJarContainer;
@@ -17,6 +18,7 @@ import com.google.android.exoplayer2.util.Util;
import okhttp3.Cookie;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
+import com.danikula.videocache.HttpProxyCacheServer;
import java.util.Map;
@@ -27,6 +29,7 @@ public class DataSourceUtil {
private static DataSource.Factory rawDataSourceFactory = null;
private static DataSource.Factory defaultDataSourceFactory = null;
+ private static HttpProxyCacheServer proxy = null;
private static String userAgent = null;
public static void setUserAgent(String userAgent) {
@@ -84,4 +87,14 @@ public class DataSourceUtil {
return okHttpDataSourceFactory;
}
+
+ public static Uri getCacheUri(Uri uri, Context context) {
+ if (proxy == null) {
+ proxy = new HttpProxyCacheServer.Builder(context)
+ .maxCacheSize(1024 * 1024 * 512)
+ .maxCacheFilesCount(20)
+ .build();
+ }
+ return Uri.parse(proxy.getProxyUrl(uri.toString()));
+ }
}
--- a/node_modules/react-native-video/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/node_modules/react-native-video/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -366,13 +366,7 @@ class ReactExoplayerView extends FrameLayout implements
}
private boolean requestAudioFocus() {
- if (disableFocus) {
- return true;
- }
- int result = audioManager.requestAudioFocus(this,
- AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
- return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ return true;
}
private void setPlayWhenReady(boolean playWhenReady) {
@@ -759,7 +753,7 @@ class ReactExoplayerView extends FrameLayout implements
boolean isOriginalSourceNull = srcUri == null;
boolean isSourceEqual = uri.equals(srcUri);
- this.srcUri = uri;
+ this.srcUri = DataSourceUtil.getCacheUri(uri, themedReactContext);
this.extension = extension;
this.requestHeaders = headers;
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER, this.requestHeaders);
|
This comment has been minimized.
This comment has been minimized.
Hi there! What about caching video files when a url ends like https://.../file.mp4?access_token=xxx ? |
@igor-lemon feel free to send a pull request that allows registering a cache key normalizer. |
Any update? |
@vargajacint We just created a fork to add basic caching feature to the You can find it here: https://github.com/jowapp/react-native-video (@n1ru4l if you think it's worth it, we can send a PR) tl;dr: just reference our repo in your [Note: We are Jow, a wonderful startup based in Paris, offering delighful recipes and helping do your grocery shopping according to what you like to eat. And we're also hiring talented people, especially back & front developers!)] |
Hi @antoinemaillard . Thanks for providing this repo! Does this work for HLS?
I see that it does support HLS but when I try to console log the cache stats with:
It shows an empty object. Yet, with an mp4, I see all of the stats. Also, does this handle deleting the cached video as well? Thanks :) |
@antoinemaillard Thank you for your fork enabling caching on Android! |
looking at the code there is support for those types of media but for some reason the cache does not work
given a url like |
Can you tell me how you did this ?? |
How do you cache a video that's downloaded over the network?
The text was updated successfully, but these errors were encountered: