From 86d655c3d1c8a81f6b841d7379ba624903440b32 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Wed, 8 Aug 2018 15:37:18 -0700 Subject: [PATCH] Refactor to move caching code into a single block --- ios/Video/RCTVideo.h | 3 +- ios/Video/RCTVideo.m | 132 +++++++++++++++++++++++++------------------ 2 files changed, 80 insertions(+), 55 deletions(-) diff --git a/ios/Video/RCTVideo.h b/ios/Video/RCTVideo.h index 0028c3f7..b2296c5d 100644 --- a/ios/Video/RCTVideo.h +++ b/ios/Video/RCTVideo.h @@ -7,7 +7,8 @@ #if __has_include() #import -#import "DVURLAsset.h" +#import +#import #endif @class RCTEventDispatcher; diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index 7c98707b..68c04e83 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -15,6 +15,12 @@ static NSString *const timedMetadata = @"timedMetadata"; static int const RCTVideoUnset = -1; +#ifdef DEBUG + #define DebugLog(...) NSLog(__VA_ARGS__) +#else + #define DebugLog(...) (void)0 +#endif + @implementation RCTVideo { AVPlayer *_player; @@ -25,6 +31,7 @@ static int const RCTVideoUnset = -1; BOOL _playerLayerObserverSet; AVPlayerViewController *_playerViewController; NSURL *_videoURL; + AVURLAsset *_testAsset; /* Required to publish events */ RCTEventDispatcher *_eventDispatcher; @@ -312,7 +319,7 @@ static int const RCTVideoUnset = -1; [self removePlayerTimeObserver]; [self removePlayerItemObservers]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 0), dispatch_get_main_queue(), ^{ // perform on next run loop, otherwise other passed react-props may not be set [self playerItemForSource:source withCallback:^(AVPlayerItem * playerItem) { @@ -337,7 +344,7 @@ static int const RCTVideoUnset = -1; [self addPlayerTimeObserver]; //Perform on next run loop, otherwise onVideoLoadStart is nil - if(self.onVideoLoadStart) { + if (self.onVideoLoadStart) { id uri = [source objectForKey:@"uri"]; id type = [source objectForKey:@"type"]; self.onVideoLoadStart(@{@"src": @{ @@ -362,7 +369,7 @@ static int const RCTVideoUnset = -1; NSString* relativeFilePath = [filepath lastPathComponent]; // the file may be multiple levels below the documents directory NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"]; - if (fileComponents.count>1) { + if (fileComponents.count > 1) { relativeFilePath = [fileComponents objectAtIndex:1]; } @@ -373,12 +380,13 @@ static int const RCTVideoUnset = -1; return nil; } -- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSMutableDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler +- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler { if (!_textTracks) { handler([AVPlayerItem playerItemWithAsset:asset]); return; } + // sideload text tracks AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; @@ -430,53 +438,29 @@ static int const RCTVideoUnset = -1; NSString *uri = [source objectForKey:@"uri"]; NSString *type = [source objectForKey:@"type"]; - NSURL *url = (isNetwork || isAsset) ? - [NSURL URLWithString:uri] : - [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; + NSURL *url = isNetwork || isAsset + ? [NSURL URLWithString:uri] + : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; if (isNetwork) { -#if __has_include() - [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) { - switch (videoCacheStatus) { - case RCTVideoCacheStatusMissingFileExtension: { -#ifdef DEBUG - NSLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md.", uri); -#endif - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; - [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; - return; - } - case RCTVideoCacheStatusUnsupportedFileExtension: { -#ifdef DEBUG - NSLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md.", uri); -#endif - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; - [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; - return; - } - default: - if (cachedAsset) { - [self playerItemPrepareText:cachedAsset assetOptions:assetOptions withCallback:handler]; - return; - } - } -#endif + /* Per #1091, this is not a public API. + * We need to either get approval from Apple to use this or use a different approach. + NSDictionary *headers = [source objectForKey:@"requestHeaders"]; + if ([headers count] > 0) { + [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; + } + */ NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; #if __has_include() - DVURLAsset *asset = [[DVURLAsset alloc] initWithURL:url options:assetOptions networkTimeout: 10000]; - asset.loaderDelegate = self; + [self playerItemForSourceUsingCache:uri assetOptions:assetOptions withCallback:handler]; #else AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; -#endif [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; -#if __has_include() - }]; #endif return; - } - else if (isAsset) { + } else if (isAsset) { AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; return; @@ -486,6 +470,60 @@ static int const RCTVideoUnset = -1; [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; } +#if __has_include() + +- (void)playerItemForSourceUsingCache:(NSString *)uri assetOptions:(NSDictionary *)options withCallback:(void(^)(AVPlayerItem *))handler { + NSURL *url = [NSURL URLWithString:uri]; + [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) { + switch (videoCacheStatus) { + case RCTVideoCacheStatusMissingFileExtension: { + DebugLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; + [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; + return; + } + case RCTVideoCacheStatusUnsupportedFileExtension: { + DebugLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; + [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; + return; + } + default: + if (cachedAsset) { + DebugLog(@"Playing back uri '%@' from cache", uri); + [self playerItemPrepareText:cachedAsset assetOptions:options withCallback:handler]; + return; + } + } + + /* + DVURLAsset *asset = [[DVURLAsset alloc] initWithURL:url options:options networkTimeout:10000]; + asset.loaderDelegate = self; + */ + + DVAssetLoaderDelegate *resourceLoaderDelegate = [[DVAssetLoaderDelegate alloc] initWithURL:url]; + resourceLoaderDelegate.delegate = self; + NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; + components.scheme = [DVAssetLoaderDelegate scheme]; + AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[components URL] options:options]; + [asset.resourceLoader setDelegate:resourceLoaderDelegate queue:dispatch_get_main_queue()]; + + [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; + }]; +} + +#pragma mark - DVAssetLoaderDelegate + +- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate + didLoadData:(NSData *)data + forURL:(NSURL *)url { + [_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) { + DebugLog(@"Cache data stored successfully 🎉"); + }]; +} + +#endif + - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == _playerItem) { @@ -1153,20 +1191,6 @@ static int const RCTVideoUnset = -1; _playerLayer = nil; } -#if __has_include() -#pragma mark - DVAssetLoaderDelegate -- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate - didLoadData:(NSData *)data - forURL:(NSURL *)url { - [_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) { -#ifdef DEBUG - NSLog(@"data stored succesfully 🎉"); -#endif - }]; -} - -#endif - #pragma mark - RCTVideoPlayerViewControllerDelegate - (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController