Merge pull request #3167 from BeApp/feature/ios-fix

#3166 Memory leaks
#3085 onFullscreen call backs are never fired
#3040 [Bug] Fullscreen broken on iOS due to #3017
This commit is contained in:
Olivier Bouillet 2023-07-10 21:58:22 +02:00 committed by GitHub
commit 9914faf4d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 26 deletions

View File

@ -4,7 +4,7 @@ import GoogleInteractiveMediaAds
class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate { class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
private var _video:RCTVideo private weak var _video: RCTVideo?
/* Entry point for the SDK. Used to make ad requests. */ /* Entry point for the SDK. Used to make ad requests. */
private var adsLoader: IMAAdsLoader! private var adsLoader: IMAAdsLoader!
@ -23,6 +23,7 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
} }
func requestAds() { func requestAds() {
guard let _video = _video else {return}
// Create ad display container for ad rendering. // Create ad display container for ad rendering.
let adDisplayContainer = IMAAdDisplayContainer(adContainer: _video, viewController: _video.reactViewController()) let adDisplayContainer = IMAAdDisplayContainer(adContainer: _video, viewController: _video.reactViewController())
@ -54,6 +55,7 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
// MARK: - IMAAdsLoaderDelegate // MARK: - IMAAdsLoaderDelegate
func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) { func adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) {
guard let _video = _video else {return}
// Grab the instance of the IMAAdsManager and set yourself as the delegate. // Grab the instance of the IMAAdsManager and set yourself as the delegate.
adsManager = adsLoadedData.adsManager adsManager = adsLoadedData.adsManager
adsManager?.delegate = self adsManager?.delegate = self
@ -71,12 +73,13 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
print("Error loading ads: " + adErrorData.adError.message!) print("Error loading ads: " + adErrorData.adError.message!)
} }
_video.setPaused(false) _video?.setPaused(false)
} }
// MARK: - IMAAdsManagerDelegate // MARK: - IMAAdsManagerDelegate
func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) { func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
guard let _video = _video else {return}
// Mute ad if the main player is muted // Mute ad if the main player is muted
if (_video.isMuted()) { if (_video.isMuted()) {
adsManager.volume = 0; adsManager.volume = 0;
@ -102,19 +105,19 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
} }
// Fall back to playing content // Fall back to playing content
_video.setPaused(false) _video?.setPaused(false)
} }
func adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager) { func adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager) {
// Pause the content for the SDK to play ads. // Pause the content for the SDK to play ads.
_video.setPaused(true) _video?.setPaused(true)
_video.setAdPlaying(true) _video?.setAdPlaying(true)
} }
func adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) { func adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) {
// Resume the content since the SDK is done playing ads (at least for now). // Resume the content since the SDK is done playing ads (at least for now).
_video.setAdPlaying(false) _video?.setAdPlaying(false)
_video.setPaused(false) _video?.setPaused(false)
} }
// MARK: - Helpers // MARK: - Helpers

View File

@ -25,7 +25,7 @@ protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc {
class RCTPlayerObserver: NSObject { class RCTPlayerObserver: NSObject {
var _handlers: RCTPlayerObserverHandler! weak var _handlers: RCTPlayerObserverHandler?
var player:AVPlayer? { var player:AVPlayer? {
willSet { willSet {
@ -84,11 +84,13 @@ class RCTPlayerObserver: NSObject {
private var _playerViewControllerOverlayFrameObserver:NSKeyValueObservation? private var _playerViewControllerOverlayFrameObserver:NSKeyValueObservation?
deinit { deinit {
NotificationCenter.default.removeObserver(_handlers) if let _handlers = _handlers {
NotificationCenter.default.removeObserver(_handlers)
}
} }
func addPlayerObservers() { func addPlayerObservers() {
guard let player = player else { guard let player = player, let _handlers = _handlers else {
return return
} }
@ -102,7 +104,7 @@ class RCTPlayerObserver: NSObject {
} }
func addPlayerItemObservers() { func addPlayerItemObservers() {
guard let playerItem = playerItem else { return } guard let playerItem = playerItem, let _handlers = _handlers else { return }
_playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: _handlers.handlePlayerItemStatusChange) _playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: _handlers.handlePlayerItemStatusChange)
_playerPlaybackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .old], changeHandler: _handlers.handlePlaybackBufferKeyEmpty) _playerPlaybackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .old], changeHandler: _handlers.handlePlaybackBufferKeyEmpty)
@ -118,7 +120,7 @@ class RCTPlayerObserver: NSObject {
} }
func addPlayerViewControllerObservers() { func addPlayerViewControllerObservers() {
guard let playerViewController = playerViewController else { return } guard let playerViewController = playerViewController, let _handlers = _handlers else { return }
_playerViewControllerReadyForDisplayObserver = playerViewController.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) _playerViewControllerReadyForDisplayObserver = playerViewController.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay)
@ -131,6 +133,7 @@ class RCTPlayerObserver: NSObject {
} }
func addPlayerLayerObserver() { func addPlayerLayerObserver() {
guard let _handlers = _handlers else {return}
_playerLayerReadyForDisplayObserver = playerLayer?.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) _playerLayerReadyForDisplayObserver = playerLayer?.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay)
} }
@ -139,6 +142,7 @@ class RCTPlayerObserver: NSObject {
} }
func addPlayerTimeObserver() { func addPlayerTimeObserver() {
guard let _handlers = _handlers else {return}
removePlayerTimeObserver() removePlayerTimeObserver()
let progressUpdateIntervalMS:Float64 = _progressUpdateInterval / 1000 let progressUpdateIntervalMS:Float64 = _progressUpdateInterval / 1000
// @see endScrubbing in AVPlayerDemoPlaybackViewController.m // @see endScrubbing in AVPlayerDemoPlaybackViewController.m
@ -174,6 +178,7 @@ class RCTPlayerObserver: NSObject {
} }
func attachPlayerEventListeners() { func attachPlayerEventListeners() {
guard let _handlers = _handlers else {return}
NotificationCenter.default.removeObserver(_handlers, NotificationCenter.default.removeObserver(_handlers,
name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, name:NSNotification.Name.AVPlayerItemDidPlayToEndTime,
@ -202,6 +207,8 @@ class RCTPlayerObserver: NSObject {
func clearPlayer() { func clearPlayer() {
player = nil player = nil
playerItem = nil playerItem = nil
NotificationCenter.default.removeObserver(_handlers) if let _handlers = _handlers {
NotificationCenter.default.removeObserver(_handlers)
}
} }
} }

View File

@ -59,6 +59,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
private var _fullscreenAutorotate:Bool = true private var _fullscreenAutorotate:Bool = true
private var _fullscreenOrientation:String! = "all" private var _fullscreenOrientation:String! = "all"
private var _fullscreenPlayerPresented:Bool = false private var _fullscreenPlayerPresented:Bool = false
private var _fullscreenUncontrolPlayerPresented:Bool = false // to call events switching full screen mode from player controls
private var _filterName:String! private var _filterName:String!
private var _filterEnabled:Bool = false private var _filterEnabled:Bool = false
private var _presentingViewController:UIViewController? private var _presentingViewController:UIViewController?
@ -246,7 +247,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
// MARK: - Player and source // MARK: - Player and source
@objc @objc
func setSrc(_ source:NSDictionary!) { func setSrc(_ source:NSDictionary!) {
DispatchQueue.global(qos: .default).async { DispatchQueue.global(qos: .default).async { [weak self] in
guard let self = self else {return}
self._source = VideoSource(source) self._source = VideoSource(source)
if (self._source?.uri == nil || self._source?.uri == "") { if (self._source?.uri == nil || self._source?.uri == "") {
self._player?.replaceCurrentItem(with: nil) self._player?.replaceCurrentItem(with: nil)
@ -662,7 +664,13 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
self.onVideoFullscreenPlayerWillPresent?(["target": reactTag as Any]) self.onVideoFullscreenPlayerWillPresent?(["target": reactTag as Any])
if let playerViewController = _playerViewController { if let playerViewController = _playerViewController {
viewController.present(playerViewController, animated:true, completion:{ if(_controls) {
// prevents crash https://github.com/react-native-video/react-native-video/issues/3040
self._playerViewController?.removeFromParent()
}
viewController.present(playerViewController, animated:true, completion:{ [weak self] in
guard let self = self else {return}
self._playerViewController?.showsPlaybackControls = self._controls self._playerViewController?.showsPlaybackControls = self._controls
self._fullscreenPlayerPresented = fullscreen self._fullscreenPlayerPresented = fullscreen
self._playerViewController?.autorotate = self._fullscreenAutorotate self._playerViewController?.autorotate = self._fullscreenAutorotate
@ -674,8 +682,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
} else if !fullscreen && _fullscreenPlayerPresented, let _playerViewController = _playerViewController { } else if !fullscreen && _fullscreenPlayerPresented, let _playerViewController = _playerViewController {
self.videoPlayerViewControllerWillDismiss(playerViewController: _playerViewController) self.videoPlayerViewControllerWillDismiss(playerViewController: _playerViewController)
_presentingViewController?.dismiss(animated: true, completion:{ _presentingViewController?.dismiss(animated: true, completion:{[weak self] in
self.videoPlayerViewControllerDidDismiss(playerViewController: _playerViewController) self?.videoPlayerViewControllerDidDismiss(playerViewController: _playerViewController)
}) })
} }
} }
@ -1103,12 +1111,27 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
let oldRect = change.oldValue let oldRect = change.oldValue
let newRect = change.newValue let newRect = change.newValue
if !oldRect!.equalTo(newRect!) { if !oldRect!.equalTo(newRect!) {
// https://github.com/react-native-video/react-native-video/issues/3085#issuecomment-1557293391
if newRect!.equalTo(UIScreen.main.bounds) { if newRect!.equalTo(UIScreen.main.bounds) {
RCTLog("in fullscreen") RCTLog("in fullscreen")
if (!_fullscreenUncontrolPlayerPresented) {
_fullscreenUncontrolPlayerPresented = true;
self.reactViewController().view.frame = UIScreen.main.bounds self.onVideoFullscreenPlayerWillPresent?(["target": self.reactTag as Any])
self.reactViewController().view.setNeedsLayout() self.onVideoFullscreenPlayerDidPresent?(["target": self.reactTag as Any])
} else {NSLog("not fullscreen")} }
} else {
NSLog("not fullscreen")
if (_fullscreenUncontrolPlayerPresented) {
_fullscreenUncontrolPlayerPresented = false;
self.onVideoFullscreenPlayerWillDismiss?(["target": self.reactTag as Any])
self.onVideoFullscreenPlayerDidDismiss?(["target": self.reactTag as Any])
}
}
self.reactViewController().view.frame = UIScreen.main.bounds
self.reactViewController().view.setNeedsLayout()
} }
} }

View File

@ -2,7 +2,7 @@ import AVKit
class RCTVideoPlayerViewController: AVPlayerViewController { class RCTVideoPlayerViewController: AVPlayerViewController {
var rctDelegate:RCTVideoPlayerViewControllerDelegate! weak var rctDelegate: RCTVideoPlayerViewControllerDelegate?
// Optional paramters // Optional paramters
var preferredOrientation:String? var preferredOrientation:String?
@ -19,11 +19,9 @@ class RCTVideoPlayerViewController: AVPlayerViewController {
override func viewDidDisappear(_ animated: Bool) { override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated) super.viewDidDisappear(animated)
if rctDelegate != nil { rctDelegate?.videoPlayerViewControllerWillDismiss(playerViewController: self)
rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self) rctDelegate?.videoPlayerViewControllerDidDismiss(playerViewController: self)
rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self)
}
} }
#if !TARGET_OS_TV #if !TARGET_OS_TV