From 35256062d94129e142596a9ea134347e9c27c221 Mon Sep 17 00:00:00 2001 From: lrusso Date: Mon, 28 Aug 2023 14:55:34 -0300 Subject: [PATCH 1/6] fixing PIP mode on iOS --- ios/Video/Features/RCTPictureInPicture.swift | 19 ++++++------- ios/Video/Features/RCTPlayerOperations.swift | 6 ++-- ios/Video/RCTVideo.swift | 29 ++++++++++++++------ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/ios/Video/Features/RCTPictureInPicture.swift b/ios/Video/Features/RCTPictureInPicture.swift index 4525df0b..da6a9d8c 100644 --- a/ios/Video/Features/RCTPictureInPicture.swift +++ b/ios/Video/Features/RCTPictureInPicture.swift @@ -4,15 +4,15 @@ import MediaAccessibility import React import Foundation -#if TARGET_OS_IOS +#if os(iOS) class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { - private var _onPictureInPictureStatusChanged: RCTDirectEventBlock? - private var _onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? + private var _onPictureInPictureStatusChanged: (() -> Void)? = nil + private var _onRestoreUserInterfaceForPictureInPictureStop: (() -> Void)? = nil private var _restoreUserInterfaceForPIPStopCompletionHandler:((Bool) -> Void)? = nil private var _pipController:AVPictureInPictureController? private var _isActive:Bool = false - init(_ onPictureInPictureStatusChanged: @escaping RCTDirectEventBlock, _ onRestoreUserInterfaceForPictureInPictureStop: @escaping RCTDirectEventBlock) { + init(_ onPictureInPictureStatusChanged: (() -> Void)? = nil, _ onRestoreUserInterfaceForPictureInPictureStop: (() -> Void)? = nil) { _onPictureInPictureStatusChanged = onPictureInPictureStatusChanged _onRestoreUserInterfaceForPictureInPictureStop = onRestoreUserInterfaceForPictureInPictureStop } @@ -20,22 +20,20 @@ class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } - _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: true)]) + _onPictureInPictureStatusChanged() } func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } - _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: false)]) + _onPictureInPictureStatusChanged() } func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { - - assert(_restoreUserInterfaceForPIPStopCompletionHandler == nil, "restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited.") - + guard let _onRestoreUserInterfaceForPictureInPictureStop = _onRestoreUserInterfaceForPictureInPictureStop else { return } - _onRestoreUserInterfaceForPictureInPictureStop([:]) + _onRestoreUserInterfaceForPictureInPictureStop() _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler } @@ -47,7 +45,6 @@ class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { } func setupPipController(_ playerLayer: AVPlayerLayer?) { - guard playerLayer != nil && AVPictureInPictureController.isPictureInPictureSupported() && _isActive else { return } // Create new controller passing reference to the AVPlayerLayer _pipController = AVPictureInPictureController(playerLayer:playerLayer!) _pipController?.delegate = self diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index 58bd35c2..f304edc8 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -112,15 +112,13 @@ enum RCTPlayerOperations { } } } else { // default. invalid type or "system" - #if os(tvOS) - // Do noting. Fix for tvOS native audio menu language selector - #else + #if os(iOS) player?.currentItem?.selectMediaOptionAutomatically(in: group) return #endif } - #if os(tvOS) + #if os(iOS) // Do noting. Fix for tvOS native audio menu language selector #else // If a match isn't found, option will be nil and text tracks will be disabled diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 055a1915..43eec0e7 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -81,8 +81,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH private let _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler() #endif -#if TARGET_OS_IOS - private let _pip:RCTPictureInPicture = RCTPictureInPicture(self.onPictureInPictureStatusChanged, self.onRestoreUserInterfaceForPictureInPictureStop) +#if os(iOS) + private var _pip:RCTPictureInPicture? = nil #endif // Events @@ -109,6 +109,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? @objc var onGetLicense: RCTDirectEventBlock? @objc var onReceiveAdEvent: RCTDirectEventBlock? + + @objc func _onPictureInPictureStatusChanged() { + onPictureInPictureStatusChanged?([ "isActive": NSNumber(value: true)]) + } + + @objc func _onRestoreUserInterfaceForPictureInPictureStop() { + onPictureInPictureStatusChanged?([ "isActive": NSNumber(value: false)]) + } init(eventDispatcher:RCTEventDispatcher!) { super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) @@ -118,6 +126,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _eventDispatcher = eventDispatcher +#if os(iOS) + _pip = RCTPictureInPicture(self._onPictureInPictureStatusChanged, self._onRestoreUserInterfaceForPictureInPictureStop) +#endif + NotificationCenter.default.addObserver( self, selector: #selector(applicationWillResignActive(notification:)), @@ -410,15 +422,15 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc func setPictureInPicture(_ pictureInPicture:Bool) { -#if TARGET_OS_IOS - _pip.setPictureInPicture(pictureInPicture) +#if os(iOS) + _pip?.setPictureInPicture(pictureInPicture) #endif } @objc func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { -#if TARGET_OS_IOS - _pip.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) +#if os(iOS) + _pip?.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) #endif } @@ -737,6 +749,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH viewController.view.frame = self.bounds viewController.player = player + viewController.allowsPictureInPicturePlayback = true return viewController } @@ -755,8 +768,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH self.layer.addSublayer(_playerLayer) } self.layer.needsDisplayOnBoundsChange = true -#if TARGET_OS_IOS - _pip.setupPipController(_playerLayer) +#if os(iOS) + _pip?.setupPipController(_playerLayer) #endif } } From a0450e026c76c004125b27a0329248e2b2b6257c Mon Sep 17 00:00:00 2001 From: lrusso Date: Wed, 30 Aug 2023 01:43:22 -0300 Subject: [PATCH 2/6] activating the audio session --- ios/Video/RCTVideo.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 43eec0e7..406a0c66 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -127,6 +127,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _eventDispatcher = eventDispatcher #if os(iOS) + let audioSession = AVAudioSession.sharedInstance() + do { + try audioSession.setCategory(.playback) + try audioSession.setActive(true, options: []) + } catch { + } _pip = RCTPictureInPicture(self._onPictureInPictureStatusChanged, self._onRestoreUserInterfaceForPictureInPictureStop) #endif From 7c8244bd07bec3e0f0ce4e62d6c0c0e24f59920c Mon Sep 17 00:00:00 2001 From: lrusso Date: Wed, 30 Aug 2023 01:45:06 -0300 Subject: [PATCH 3/6] activating the audio session --- ios/Video/RCTVideo.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 406a0c66..9a3073d8 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -127,12 +127,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _eventDispatcher = eventDispatcher #if os(iOS) - let audioSession = AVAudioSession.sharedInstance() - do { - try audioSession.setCategory(.playback) - try audioSession.setActive(true, options: []) - } catch { - } _pip = RCTPictureInPicture(self._onPictureInPictureStatusChanged, self._onRestoreUserInterfaceForPictureInPictureStop) #endif @@ -429,6 +423,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc func setPictureInPicture(_ pictureInPicture:Bool) { #if os(iOS) + let audioSession = AVAudioSession.sharedInstance() + do { + try audioSession.setCategory(.playback) + try audioSession.setActive(true, options: []) + } catch { + } _pip?.setPictureInPicture(pictureInPicture) #endif } From 03cba605ea335c81713ca91de505fc30925203c7 Mon Sep 17 00:00:00 2001 From: lrusso Date: Wed, 30 Aug 2023 22:22:31 -0300 Subject: [PATCH 4/6] preventing the iOS pip to be closed automatically when sending to the background --- ios/Video/RCTVideo.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 9a3073d8..fcad3189 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -186,7 +186,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } @objc func applicationDidEnterBackground(notification:NSNotification!) { - if _playInBackground { + if !_playInBackground { // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html _playerLayer?.player = nil _playerViewController?.player = nil From 14f12df9f8b3690edb61308f1816a2f94f13e764 Mon Sep 17 00:00:00 2001 From: lrusso Date: Mon, 4 Sep 2023 17:33:21 -0300 Subject: [PATCH 5/6] rollback logic --- ios/Video/Features/RCTPlayerOperations.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index f304edc8..b10d0b2b 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -112,7 +112,9 @@ enum RCTPlayerOperations { } } } else { // default. invalid type or "system" - #if os(iOS) + #if os(tvOS) + // Do noting. Fix for tvOS native audio menu language selector + #else player?.currentItem?.selectMediaOptionAutomatically(in: group) return #endif From 63f0344742dd81f4337e18605193c5f96640015a Mon Sep 17 00:00:00 2001 From: lrusso Date: Mon, 4 Sep 2023 17:34:46 -0300 Subject: [PATCH 6/6] rollback logic --- ios/Video/Features/RCTPlayerOperations.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index b10d0b2b..58bd35c2 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -120,7 +120,7 @@ enum RCTPlayerOperations { #endif } - #if os(iOS) + #if os(tvOS) // Do noting. Fix for tvOS native audio menu language selector #else // If a match isn't found, option will be nil and text tracks will be disabled