diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 54296c23..223be5ae 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,10 +10,12 @@ assignees: '' # Bug ## Platform diff --git a/API.md b/API.md index eb1f2eb0..aa9de5d6 100644 --- a/API.md +++ b/API.md @@ -437,7 +437,7 @@ Determines if the player needs to throw an error when connection is lost or not Platforms: Android ### DRM -To setup DRM please follow [this guide](./DRM.md) +To setup DRM please follow [this guide](./docs/DRM.md) Platforms: Android, iOS diff --git a/CHANGELOG.md b/CHANGELOG.md index a0778647..1968e293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,19 @@ ### Version 6.0.0-alpha.2 +- Fix Android #2690 ensure onEnd is not sent twice [#2690](https://github.com/react-native-video/react-native-video/issues/2690) - Fix Exoplayer progress not reported when paused [#2664](https://github.com/react-native-video/react-native-video/pull/2664) - Call playbackRateChange onPlay and onPause [#1493](https://github.com/react-native-video/react-native-video/pull/1493) - 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 - Remove Android MediaPlayer support [#2724](https://github.com/react-native-video/react-native-video/pull/2724) + **WARNING**: when switching from older version to V6, you need to remove all refrerences of android-exoplayer. This android-exoplayer folder has been renamed to android. Exoplayer is now the only player implementation supported. + - Replace Image.propTypes with ImagePropTypes. [#2718](https://github.com/react-native-video/react-native-video/pull/2718) - Fix iOS build caused by type mismatch [#2720](https://github.com/react-native-video/react-native-video/pull/2720) - ERROR TypeError: undefined is not an object (evaluating '_reactNative.Image.propTypes.resizeMode') [#2714](https://github.com/react-native-video/react-native-video/pull/2714) diff --git a/Video.js b/Video.js index f3d96278..46796785 100644 --- a/Video.js +++ b/Video.js @@ -284,7 +284,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/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7e77c4e9..9a334132 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -403,6 +403,15 @@ class ReactExoplayerView extends FrameLayout implements eventListener = new Player.Listener() { @Override public void onPlaybackStateChanged(int playbackState) { + View playButton = playerControlView.findViewById(R.id.exo_play); + View pauseButton = playerControlView.findViewById(R.id.exo_pause); + if (playButton != null && playButton.getVisibility() == GONE) { + playButton.setVisibility(INVISIBLE); + } + if (pauseButton != null && pauseButton.getVisibility() == GONE) { + pauseButton.setVisibility(INVISIBLE); + } + reLayout(playPauseControlContainer); //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done player.removeListener(eventListener); @@ -815,7 +824,10 @@ class ReactExoplayerView extends FrameLayout implements player.setPlayWhenReady(true); } } else { - player.setPlayWhenReady(false); + // ensure playback is not ENDED, else it will trigger another ended event + if (player.getPlaybackState() != Player.STATE_ENDED) { + player.setPlayWhenReady(false); + } } } @@ -1017,9 +1029,15 @@ class ReactExoplayerView extends FrameLayout implements private void videoLoaded() { if (loadVideoStarted) { loadVideoStarted = false; - setSelectedAudioTrack(audioTrackType, audioTrackValue); - setSelectedVideoTrack(videoTrackType, videoTrackValue); - setSelectedTextTrack(textTrackType, textTrackValue); + if (audioTrackType != null) { + setSelectedAudioTrack(audioTrackType, audioTrackValue); + } + if (videoTrackType != null) { + setSelectedVideoTrack(videoTrackType, videoTrackValue); + } + if (textTrackType != null) { + setSelectedTextTrack(textTrackType, textTrackValue); + } Format videoFormat = player.getVideoFormat(); int width = videoFormat != null ? videoFormat.width : 0; int height = videoFormat != null ? videoFormat.height : 0; diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index d8ec8677..50df8e3f 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,23 @@ 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)...]) + 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 + 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 59973a36..a339aff4 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) } diff --git a/ios/Video/RCTVideoPlayerViewController.swift b/ios/Video/RCTVideoPlayerViewController.swift index 95c926af..e398e62f 100644 --- a/ios/Video/RCTVideoPlayerViewController.swift +++ b/ios/Video/RCTVideoPlayerViewController.swift @@ -19,8 +19,11 @@ class RCTVideoPlayerViewController: AVPlayerViewController { override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) - rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self) - rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self) + + if rctDelegate != nil { + rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self) + rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self) + } } #if !TARGET_OS_TV