Skip to content
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

Manual cache #9

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion android-exoplayer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {

dependencies {
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
implementation('com.google.android.exoplayer:exoplayer:2.10.5') {
api('com.google.android.exoplayer:exoplayer:2.10.5') {
exclude group: 'com.android.support'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.brentvatne.exoplayer;

import android.content.Context;
import android.media.MediaDataSource;
import android.net.Uri;
import android.util.Log;
import android.util.Pair;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
Expand All @@ -26,6 +32,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class ExoPlayerCache extends ReactContextBaseJavaModule {

Expand All @@ -50,42 +57,40 @@ public void exportVideo(final String url, final Promise promise) {
public void run() {
Log.d(getName(), "Exporting...");
Log.d(getName(), url);

final Uri uri = Uri.parse(url);

final SimpleCache downloadCache = VideoCache.getInstance().getSimpleCache();
final DataSource dataSource = DataSourceUtil.getDefaultDataSourceFactory(getReactApplicationContext(),
null, null).createDataSource();

final DataSpec dataSpec = new DataSpec(uri, 0, C.LENGTH_UNSET, null);
final SimpleCache downloadCache = ExoPlayerCache.getInstance(getReactApplicationContext());
CacheKeyFactory cacheKeyFactory = ds -> CACHE_KEY_PREFIX + "." + CacheUtil.generateKey(ds.uri);;

try {
CacheUtil.getCached(
dataSpec,
downloadCache,
cacheKeyFactory
);
File targetFile = new File(getReactApplicationContext().getCacheDir().toString() + "/export", uri.getLastPathSegment());

// Create export dir if not exists.
targetFile.getParentFile().mkdirs();

DataSourceInputStream inputStream = new DataSourceInputStream(createDataSource(downloadCache), dataSpec);

File targetFile = new File(ExoPlayerCache.getCacheDir(getReactApplicationContext()) + "/" + uri.getLastPathSegment());
// https://github.com/google/ExoPlayer/issues/5569
try {
OutputStream outStream = new FileOutputStream(targetFile);

byte[] buffer = new byte[8 * 1024];
int bytesRead;
dataSource.open(dataSpec);

try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
// TODO Add onProgress() callback here
byte[] data = new byte[1024];
int position = 0;
int bytesRead;
while ((bytesRead = dataSource.read(data, position, data.length)) != C.RESULT_END_OF_INPUT) {
outStream.write(data, 0, bytesRead);
position += bytesRead;
}
} catch (IOException e) {
// TODO this exception should not be thrown
Log.d(getName(), "Read error");
Log.d(getName(), "Write error");
e.printStackTrace();
} finally {
dataSource.close();
}

CacheUtil.getCached(
dataSpec,
downloadCache,
cacheKeyFactory
);

Log.d(getName(), "Export succeeded");
Log.d(getName(), targetFile.getPath());

Expand All @@ -100,18 +105,8 @@ public void run() {
exportThread.start();
}

public static SimpleCache getInstance(Context context) {
if(instance == null) {
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) {
private CacheDataSource createDataSource(Cache cache) {
return new CacheDataSourceFactory(cache, DataSourceUtil.getDefaultDataSourceFactory(
getReactApplicationContext(),
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
Expand All @@ -62,7 +61,6 @@
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;
Expand Down Expand Up @@ -197,7 +195,7 @@ public void setId(int id) {
private void createViews() {
clearResumePosition();
mediaDataSourceFactory = buildDataSourceFactory(true);
downloadCache = ExoPlayerCache.getInstance(getContext());
downloadCache = VideoCache.getInstance().getSimpleCache();

if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.brentvatne.exoplayer;

import com.google.android.exoplayer2.upstream.cache.SimpleCache;

public class VideoCache {

private static volatile VideoCache instance;
private static SimpleCache cache;

private VideoCache() {
if (instance != null) {
throw new RuntimeException("Use getInstance()");
}
}

public void setSimpleCache(SimpleCache cache) {
this.cache = cache;
}

public SimpleCache getSimpleCache() {
if (this.cache == null) {
throw new RuntimeException("Tried to access video cache but no cache is set");
}

return this.cache;
}

public static VideoCache getInstance() {
if (instance == null) {
synchronized (VideoCache.class) {
if (instance == null) instance = new VideoCache();
}
}

return instance;
}
}
1 change: 1 addition & 0 deletions ios/RCTVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ - (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate
didLoadData:(NSData *)data
forURL:(NSURL *)url {
DebugLog(@"dvAssetLoaderDelegate: url '%@'", [url absoluteString]);

[_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) {
DebugLog(@"Cache data stored successfully 🎉");
}];
Expand Down
3 changes: 1 addition & 2 deletions ios/RCTVideoCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ typedef NS_ENUM(NSUInteger, RCTVideoCacheStatus) {
}

@property(nonatomic, strong) SPTPersistentCache * _Nullable videoCache;
@property(nonatomic, strong) NSString * cachePath;
@property(nonatomic, strong) NSString * cacheIdentifier;
@property(nonatomic, strong) NSString * temporaryCachePath;

+ (RCTVideoCache *)sharedInstance;
Expand All @@ -34,5 +32,6 @@ typedef NS_ENUM(NSUInteger, RCTVideoCacheStatus) {
- (AVURLAsset *)getItemFromTemporaryStorage:(NSString *)key;
- (BOOL)saveDataToTemporaryStorage:(NSData *)data key:(NSString *)key;
- (void) createTemporaryPath;
- (void) setVideoCache:(SPTPersistentCache *)videoCache;

@end
23 changes: 3 additions & 20 deletions ios/RCTVideoCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
@implementation RCTVideoCache

@synthesize videoCache;
@synthesize cachePath;
@synthesize cacheIdentifier;
@synthesize temporaryCachePath;

+ (RCTVideoCache *)sharedInstance {
Expand All @@ -18,24 +16,7 @@ + (RCTVideoCache *)sharedInstance {

- (id)init {
if (self = [super init]) {
self.cacheIdentifier = @"rct.video.cache";
self.temporaryCachePath = [NSTemporaryDirectory() stringByAppendingPathComponent:self.cacheIdentifier];
self.cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:self.cacheIdentifier];
SPTPersistentCacheOptions *options = [SPTPersistentCacheOptions new];
options.cachePath = self.cachePath;
options.cacheIdentifier = self.cacheIdentifier;
options.defaultExpirationPeriod = 60 * 60 * 24 * 30;
options.garbageCollectionInterval = (NSUInteger)(1.5 * SPTPersistentCacheDefaultGCIntervalSec);
options.sizeConstraintBytes = 1024 * 1024 * 100;
options.useDirectorySeparation = NO;
Comment on lines -25 to -30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did all this config go?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript 😄

#ifdef DEBUG
options.debugOutput = ^(NSString *string) {
NSLog(@"Video Cache: %@", string);
};
#endif
[self createTemporaryPath];
self.videoCache = [[SPTPersistentCache alloc] initWithOptions:options];
[self.videoCache scheduleGarbageCollector];
self.temporaryCachePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"cache"];
}
return self;
}
Expand All @@ -60,6 +41,8 @@ - (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BO
handler(NO);
return;
}


[self saveDataToTemporaryStorage:data key:key];
[self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) {
if (response.error) {
Expand Down