From 285e9ca8817ff7de4f6ac2d4cf51f944ef5237c9 Mon Sep 17 00:00:00 2001 From: virgil Date: Fri, 15 Apr 2022 17:29:56 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=F0=9F=90=9B=20support=20ios=20camer?= =?UTF-8?q?aroll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Video.js | 2 +- ios/Video/RCTVideo.m | 64 ++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Video.js b/Video.js index b492d48b25..6af4fc3a60 100644 --- a/Video.js +++ b/Video.js @@ -275,7 +275,7 @@ export default class Video extends Component { } const isNetwork = !!(uri && uri.match(/^https?:/)); - const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/)); + const isAsset = !!(uri && uri.match(/^(assets-library|ph|ipod-library|file|content|ms-appx|ms-appdata):/)); let nativeResizeMode; const RCTVideoInstance = this.getViewManagerConfig('RCTVideo'); diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index a757c08a50..f2255007c8 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -5,6 +5,7 @@ #import #include #include +#import static NSString *const statusKeyPath = @"status"; static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; @@ -492,22 +493,22 @@ - (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nu handler([AVPlayerItem playerItemWithAsset:mixComposition]); } -- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler -{ +- (void)loadAssetFromSource:(NSDictionary *)source withCallback:(void(^)(AVURLAsset *asset, NSMutableDictionary *assetOptions)) handler { bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; bool shouldCache = [RCTConvert BOOL:[source objectForKey:@"shouldCache"]]; + NSString *uri = [source objectForKey:@"uri"]; NSString *type = [source objectForKey:@"type"]; - AVURLAsset *asset; + if (!uri || [uri isEqualToString:@""]) { DebugLog(@"Could not find video URL in source '%@'", source); return; } NSURL *url = isNetwork || isAsset - ? [NSURL URLWithString:uri] - : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; + ? [NSURL URLWithString:uri] + : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; if (isNetwork) { @@ -529,26 +530,43 @@ - (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlaye return; } #endif - - asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; + handler([AVURLAsset URLAssetWithURL:url options:assetOptions], assetOptions); } else if (isAsset) { - asset = [AVURLAsset URLAssetWithURL:url options:nil]; + if ([uri hasPrefix:@"ph://"]) { + NSString *assetId = [uri substringFromIndex:@"ph://".length]; + PHAsset *asset = [[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil] firstObject]; + PHVideoRequestOptions *options = [PHVideoRequestOptions new]; + options.networkAccessAllowed = YES; + + [[PHCachingImageManager new] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { + handler((AVURLAsset *)asset, assetOptions); + }]; + } else { + handler([AVURLAsset URLAssetWithURL:url options:nil], assetOptions); + } } else { - asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; - } - // Reset _loadingRequest - if (_loadingRequest != nil) { - [_loadingRequest finishLoading]; + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; + handler(asset, assetOptions); } - _requestingCertificate = NO; - _requestingCertificateErrored = NO; - // End Reset _loadingRequest - if (self->_drm != nil) { - dispatch_queue_t queue = dispatch_queue_create("assetQueue", nil); - [asset.resourceLoader setDelegate:self queue:queue]; - } - - [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; +} + +- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler +{ + [self loadAssetFromSource:source withCallback:^(AVURLAsset *asset, NSMutableDictionary *assetOptions) { + // Reset _loadingRequest + if (self->_loadingRequest != nil) { + [self->_loadingRequest finishLoading]; + } + self->_requestingCertificate = NO; + self->_requestingCertificateErrored = NO; + // End Reset _loadingRequest + if (self->_drm != nil) { + dispatch_queue_t queue = dispatch_queue_create("assetQueue", nil); + [asset.resourceLoader setDelegate:self queue:queue]; + } + + [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; + }]; } #if __has_include() @@ -2028,4 +2046,4 @@ - (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPict } #endif -@end +@end \ No newline at end of file From 329df1d91abda9fd882c1ff2c1b7aa69a67bdb54 Mon Sep 17 00:00:00 2001 From: virgil Date: Fri, 15 Apr 2022 17:36:23 +0800 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc7f594380..6dcc12f668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Add Google's maven repository to avoid build error [#2552] (https://github.com/react-native-video/react-native-video/pull/2552) +- Fix crash when the source is a cameraroll [#2639] (https://github.com/react-native-video/react-native-video/pull/2639) + ### Version 5.2.0 - Fix for tvOS native audio menu language selector From fcea0ffcea051b284135514d11e91ff8269bacec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=9B=A6?= Date: Wed, 27 Jul 2022 21:13:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20support=20camera?= =?UTF-8?q?=20video=20for=20swift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Video/Features/RCTVideoUtils.swift | 15 ++++++++++++++- ios/Video/RCTVideo.swift | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index d8ec8677f6..46cce42a8d 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -1,5 +1,6 @@ import AVFoundation import Promises +import Photos /*! * Collection of pure functions @@ -264,8 +265,20 @@ enum RCTVideoUtils { } } + static func preparePHAsset(uri: String) -> Promise { + return Promise(on: .global()) { fulfill, reject in + let assetId = String(uri[uri.index(uri.startIndex, offsetBy: "ph://".count)...]) + let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject + let options = PHVideoRequestOptions() + options.isNetworkAccessAllowed = true + PHCachingImageManager().requestAVAsset(forVideo: phAsset!, options: options) { data, _, _ in + fulfill(data) + } + } + } + static func prepareAsset(source:VideoSource) -> (asset:AVURLAsset?, assetOptions:NSMutableDictionary?)? { - guard source.uri != nil && source.uri != "" else { return nil } + guard let sourceUri = source.uri, sourceUri != "" else { return nil } var asset:AVURLAsset! let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? "" let url = source.isNetwork || source.isAsset diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 59973a36e0..a339aff4cf 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -227,10 +227,20 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH RCTVideoUtils.delay() .then{ [weak self] in guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)} - guard let source = self._source, - let assetResult = RCTVideoUtils.prepareAsset(source: source), - let asset = assetResult.asset, - let assetOptions = assetResult.assetOptions else { + guard let source = self._source else { + DebugLog("The source not exist") + throw NSError(domain: "", code: 0, userInfo: nil) + } + if let uri = source.uri, uri.starts(with: "ph://") { + return Promise { + RCTVideoUtils.preparePHAsset(uri: uri).then { asset in + return self.playerItemPrepareText(asset:asset, assetOptions:nil) + } + } + } + guard let assetResult = RCTVideoUtils.prepareAsset(source: source), + let asset = assetResult.asset, + let assetOptions = assetResult.assetOptions else { DebugLog("Could not find video URL in source '\(self._source)'") throw NSError(domain: "", code: 0, userInfo: nil) } From cee650c84cb37f31b2596ae1181ea36f0c80bb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=9B=A6?= Date: Wed, 27 Jul 2022 21:34:08 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20exception=20ca?= =?UTF-8?q?tch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Video/Features/RCTVideoUtils.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 46cce42a8d..50df8e3f14 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -268,10 +268,13 @@ enum RCTVideoUtils { static func preparePHAsset(uri: String) -> Promise { return Promise(on: .global()) { fulfill, reject in let assetId = String(uri[uri.index(uri.startIndex, offsetBy: "ph://".count)...]) - let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject + guard let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject else { + reject(NSError(domain: "", code: 0, userInfo: nil)) + return + } let options = PHVideoRequestOptions() options.isNetworkAccessAllowed = true - PHCachingImageManager().requestAVAsset(forVideo: phAsset!, options: options) { data, _, _ in + PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: options) { data, _, _ in fulfill(data) } } From 1631e5de5230d1d18b12389c137537e6b602e425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=9B=A6?= Date: Tue, 2 Aug 2022 15:21:43 +0800 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20change=20chang?= =?UTF-8?q?elog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adfb0da5e5..805868d589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix being unable to disable sideloaded texttracks in the AVPlayer [#2679](https://github.com/react-native-video/react-native-video/pull/2679) - Fixed crash when iOS seek method called reject on the promise [#2743](https://github.com/react-native-video/react-native-video/pull/2743) - Fix maxBitRate property being ignored on Android [#2670](https://github.com/react-native-video/react-native-video/pull/2670) +- Fix crash when the source is a cameraroll [#2639] (https://github.com/react-native-video/react-native-video/pull/2639) ### Version 6.0.0-alpha.1 @@ -55,8 +56,6 @@ - Fixed `onReadyForDisplay` not being called [#2721](https://github.com/react-native-video/react-native-video/pull/2721) - Fix type of `_eventDispatcher` on iOS target to match `bridge.eventDispatcher()` [#2720](https://github.com/react-native-video/react-native-video/pull/2720) -- Fix crash when the source is a cameraroll [#2639] (https://github.com/react-native-video/react-native-video/pull/2639) - ### Version 5.2.0 - Fix for tvOS native audio menu language selector