First iteration implementing video caching
This commit is contained in:
parent
d5e012b749
commit
c926d8ba6c
@ -4,10 +4,12 @@
|
||||
#import "UIView+FindUIViewController.h"
|
||||
#import "RCTVideoPlayerViewController.h"
|
||||
#import "RCTVideoPlayerViewControllerDelegate.h"
|
||||
#import "RCTVideoCache.h"
|
||||
#import "DVURLAsset.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate>
|
||||
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#import <React/RCTConvert.h>
|
||||
#import "RCTVideoCache.h"
|
||||
#import "RCTVideo.h"
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
@ -47,6 +48,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
NSString * _resizeMode;
|
||||
BOOL _fullscreenPlayerPresented;
|
||||
UIViewController * _presentingViewController;
|
||||
RCTVideoCache * _videoCache;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
@ -68,6 +70,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
_playInBackground = false;
|
||||
_playWhenInactive = false;
|
||||
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
||||
_videoCache = [RCTVideoCache sharedInstance];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationWillResignActive:)
|
||||
@ -268,7 +271,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
|
||||
- (void)setCache:(BOOL *)cache
|
||||
{
|
||||
// @TODO: Implement
|
||||
|
||||
}
|
||||
|
||||
- (void)setSrc:(NSDictionary *)source
|
||||
@ -336,10 +339,16 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]];
|
||||
|
||||
if (isNetwork) {
|
||||
// @TODO: Check if item is cached an if so use the cached asset
|
||||
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
|
||||
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetHTTPCookiesKey : cookies}];
|
||||
handler([AVPlayerItem playerItemWithAsset:asset]);
|
||||
[_videoCache getItemForUri:uri withCallback:^(AVAsset * _Nullable asset) {
|
||||
if (asset) {
|
||||
handler([AVPlayerItem playerItemWithAsset:asset]);
|
||||
return;
|
||||
}
|
||||
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
|
||||
DVURLAsset * dvAsset = [[DVURLAsset alloc] initWithURL:url options:@{AVURLAssetHTTPCookiesKey : cookies} networkTimeout: 10000];
|
||||
dvAsset.loaderDelegate = self;
|
||||
handler([AVPlayerItem playerItemWithAsset:dvAsset]);
|
||||
}];
|
||||
return;
|
||||
}
|
||||
else if (isAsset) {
|
||||
@ -751,6 +760,18 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
_playerLayer = nil;
|
||||
}
|
||||
|
||||
#pragma mark - DVAssetLoaderDelegate
|
||||
|
||||
- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate
|
||||
didLoadData:(NSData *)data
|
||||
forURL:(NSURL *)url {
|
||||
NSLog(@"File size is : %.2f MB",(float)data.length/1024.0f/1024.0f);
|
||||
[_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) {
|
||||
|
||||
NSLog(@"data stored succesfully 🎉");
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - RCTVideoPlayerViewControllerDelegate
|
||||
|
||||
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController
|
||||
|
31
ios/RCTVideoCache.h
Normal file
31
ios/RCTVideoCache.h
Normal file
@ -0,0 +1,31 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <SPTPersistentCache/SPTPersistentCache.h>
|
||||
#import <SPTPersistentCache/SPTPersistentCacheOptions.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@class SPTPersistentCache;
|
||||
@class SPTPersistentCacheOptions;
|
||||
|
||||
@interface RCTVideoCache : NSObject
|
||||
{
|
||||
SPTPersistentCache *videoCache;
|
||||
NSString * _Nullable cachePath;
|
||||
NSString * temporaryCachePath;
|
||||
NSString * _Nullable cacheIdentifier;
|
||||
}
|
||||
|
||||
@property(nonatomic, strong) SPTPersistentCache * _Nullable videoCache;
|
||||
@property(nonatomic, strong) NSString * cachePath;
|
||||
@property(nonatomic, strong) NSString * cacheIdentifier;
|
||||
@property(nonatomic, strong) NSString * temporaryCachePath;
|
||||
|
||||
+ (RCTVideoCache *)sharedInstance;
|
||||
- (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler;
|
||||
- (void)getItemForUri:(NSString *)url withCallback:(void(^)(AVAsset * _Nullable)) handler;
|
||||
- (NSURL *)createUniqueTemporaryFileUrl:(NSString * _Nonnull)url withExtension:(NSString * _Nonnull) extension;
|
||||
- (AVURLAsset *)getItemFromTemporaryStorage:(NSString *)key;
|
||||
- (BOOL)saveDataToTemporaryStorage:(NSData *)data key:(NSString *)key;
|
||||
- (void) createTemporaryPath;
|
||||
|
||||
@end
|
133
ios/RCTVideoCache.m
Normal file
133
ios/RCTVideoCache.m
Normal file
@ -0,0 +1,133 @@
|
||||
#import "RCTVideoCache.h"
|
||||
|
||||
@implementation RCTVideoCache
|
||||
|
||||
@synthesize videoCache;
|
||||
@synthesize cachePath;
|
||||
@synthesize cacheIdentifier;
|
||||
@synthesize temporaryCachePath;
|
||||
|
||||
+ (RCTVideoCache *) sharedInstance
|
||||
{
|
||||
static RCTVideoCache *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
return 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;
|
||||
#ifdef DEBUG
|
||||
options.debugOutput = ^(NSString *string) {
|
||||
NSLog(@"Video Cache: %@", string);
|
||||
};
|
||||
#endif
|
||||
[self createTemporaryPath];
|
||||
self.videoCache = [[SPTPersistentCache alloc] initWithOptions:options];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) createTemporaryPath
|
||||
{
|
||||
NSError * error = nil;
|
||||
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:self.temporaryCachePath
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&error];
|
||||
if (!success || error) {
|
||||
NSLog(@"Error! %@", error);
|
||||
} else {
|
||||
NSLog(@"Success!");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler;
|
||||
{
|
||||
NSString * key = [[self generateHashForUrl:uri] stringByAppendingPathExtension: [uri pathExtension]];
|
||||
[self saveDataToTemporaryStorage:data key:key];
|
||||
[self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) {
|
||||
if (response.error) {
|
||||
NSLog(@"An error occured while saving the video into the cache: %@", [response.error localizedDescription]);
|
||||
handler(NO);
|
||||
return;
|
||||
}
|
||||
handler(YES);
|
||||
} onQueue:dispatch_get_main_queue()];
|
||||
return;
|
||||
}
|
||||
|
||||
- (AVURLAsset *)getItemFromTemporaryStorage:(NSString *)key {
|
||||
NSString * temporaryFilePath =[self.temporaryCachePath stringByAppendingPathComponent:key];
|
||||
|
||||
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:temporaryFilePath];
|
||||
if (!fileExists) {
|
||||
NSLog(@"Temporary file does not exist");
|
||||
return nil;
|
||||
}
|
||||
NSLog(@"Temporary file does exist");
|
||||
NSURL * assetUrl = [[NSURL alloc] initFileURLWithPath:temporaryFilePath];
|
||||
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:assetUrl options:nil];
|
||||
return asset;
|
||||
}
|
||||
|
||||
- (BOOL)saveDataToTemporaryStorage:(NSData *)data key:(NSString *)key {
|
||||
NSString * temporaryFilePath = [self.temporaryCachePath stringByAppendingPathComponent:key];
|
||||
NSLog(@"Temporary path %@", temporaryFilePath);
|
||||
[data writeToFile:temporaryFilePath atomically:YES];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)getItemForUri:(NSString *)uri withCallback:(void(^)(AVAsset * _Nullable)) handler {
|
||||
NSString * key = [[self generateHashForUrl:uri] stringByAppendingPathExtension: [uri pathExtension]];
|
||||
|
||||
NSLog(@"LOADING FROM TEMPORARY STORAGE %@", key);
|
||||
AVURLAsset * temporaryAsset = [self getItemFromTemporaryStorage:key];
|
||||
if (temporaryAsset != nil) {
|
||||
NSLog(@"FOUND IN TEMPORARY STORAGE");
|
||||
handler(temporaryAsset);
|
||||
return;
|
||||
}
|
||||
NSLog(@"NOT FOUND IN TEMPORARY STORAGE");
|
||||
NSLog(@"LOAD FROM PERSISTENT STORAGE");
|
||||
|
||||
[self.videoCache loadDataForKey:key withCallback:^(SPTPersistentCacheResponse * _Nonnull response) {
|
||||
if (response.record == nil || response.record.data == nil) {
|
||||
NSLog(@"NOT FOUND IN PERSISTENT STORAGE");
|
||||
handler(nil);
|
||||
return;
|
||||
}
|
||||
NSLog(@"FOUND IN PERSISTENT -> SAVE TO TEMPORARY");
|
||||
[self saveDataToTemporaryStorage:response.record.data key:key];
|
||||
handler([self getItemFromTemporaryStorage:key]);
|
||||
} onQueue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (NSString *) generateHashForUrl:(NSString *)string {
|
||||
const char *cStr = [string UTF8String];
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
|
||||
|
||||
return [NSString stringWithFormat:
|
||||
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
Reference in New Issue
Block a user