First iteration implementing video caching
This commit is contained in:
parent
d5e012b749
commit
c926d8ba6c
@ -4,10 +4,12 @@
|
|||||||
#import "UIView+FindUIViewController.h"
|
#import "UIView+FindUIViewController.h"
|
||||||
#import "RCTVideoPlayerViewController.h"
|
#import "RCTVideoPlayerViewController.h"
|
||||||
#import "RCTVideoPlayerViewControllerDelegate.h"
|
#import "RCTVideoPlayerViewControllerDelegate.h"
|
||||||
|
#import "RCTVideoCache.h"
|
||||||
|
#import "DVURLAsset.h"
|
||||||
|
|
||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
|
|
||||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate>
|
||||||
|
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#import <React/RCTConvert.h>
|
#import <React/RCTConvert.h>
|
||||||
|
#import "RCTVideoCache.h"
|
||||||
#import "RCTVideo.h"
|
#import "RCTVideo.h"
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
#import <React/RCTEventDispatcher.h>
|
#import <React/RCTEventDispatcher.h>
|
||||||
@ -47,6 +48,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
NSString * _resizeMode;
|
NSString * _resizeMode;
|
||||||
BOOL _fullscreenPlayerPresented;
|
BOOL _fullscreenPlayerPresented;
|
||||||
UIViewController * _presentingViewController;
|
UIViewController * _presentingViewController;
|
||||||
|
RCTVideoCache * _videoCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||||
@ -68,6 +70,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
_playInBackground = false;
|
_playInBackground = false;
|
||||||
_playWhenInactive = false;
|
_playWhenInactive = false;
|
||||||
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
||||||
|
_videoCache = [RCTVideoCache sharedInstance];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(applicationWillResignActive:)
|
selector:@selector(applicationWillResignActive:)
|
||||||
@ -268,7 +271,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
|
|
||||||
- (void)setCache:(BOOL *)cache
|
- (void)setCache:(BOOL *)cache
|
||||||
{
|
{
|
||||||
// @TODO: Implement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSrc:(NSDictionary *)source
|
- (void)setSrc:(NSDictionary *)source
|
||||||
@ -336,10 +339,16 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]];
|
[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]];
|
||||||
|
|
||||||
if (isNetwork) {
|
if (isNetwork) {
|
||||||
// @TODO: Check if item is cached an if so use the cached asset
|
[_videoCache getItemForUri:uri withCallback:^(AVAsset * _Nullable asset) {
|
||||||
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
|
if (asset) {
|
||||||
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetHTTPCookiesKey : cookies}];
|
handler([AVPlayerItem playerItemWithAsset: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;
|
return;
|
||||||
}
|
}
|
||||||
else if (isAsset) {
|
else if (isAsset) {
|
||||||
@ -751,6 +760,18 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
_playerLayer = nil;
|
_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
|
#pragma mark - RCTVideoPlayerViewControllerDelegate
|
||||||
|
|
||||||
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController
|
- (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…
x
Reference in New Issue
Block a user