Merge pull request #3221 from lrusso/master

Fixing the PIP mode on iOS
This commit is contained in:
Olivier Bouillet 2023-09-04 23:42:01 +02:00 committed by GitHub
commit 30dac90959
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 20 deletions

View File

@ -4,15 +4,15 @@ import MediaAccessibility
import React import React
import Foundation import Foundation
#if TARGET_OS_IOS #if os(iOS)
class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate {
private var _onPictureInPictureStatusChanged: RCTDirectEventBlock? private var _onPictureInPictureStatusChanged: (() -> Void)? = nil
private var _onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? private var _onRestoreUserInterfaceForPictureInPictureStop: (() -> Void)? = nil
private var _restoreUserInterfaceForPIPStopCompletionHandler:((Bool) -> Void)? = nil private var _restoreUserInterfaceForPIPStopCompletionHandler:((Bool) -> Void)? = nil
private var _pipController:AVPictureInPictureController? private var _pipController:AVPictureInPictureController?
private var _isActive:Bool = false private var _isActive:Bool = false
init(_ onPictureInPictureStatusChanged: @escaping RCTDirectEventBlock, _ onRestoreUserInterfaceForPictureInPictureStop: @escaping RCTDirectEventBlock) { init(_ onPictureInPictureStatusChanged: (() -> Void)? = nil, _ onRestoreUserInterfaceForPictureInPictureStop: (() -> Void)? = nil) {
_onPictureInPictureStatusChanged = onPictureInPictureStatusChanged _onPictureInPictureStatusChanged = onPictureInPictureStatusChanged
_onRestoreUserInterfaceForPictureInPictureStop = onRestoreUserInterfaceForPictureInPictureStop _onRestoreUserInterfaceForPictureInPictureStop = onRestoreUserInterfaceForPictureInPictureStop
} }
@ -20,22 +20,20 @@ class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate {
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return }
_onPictureInPictureStatusChanged([ "isActive": NSNumber(value: true)]) _onPictureInPictureStatusChanged()
} }
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return }
_onPictureInPictureStatusChanged([ "isActive": NSNumber(value: false)]) _onPictureInPictureStatusChanged()
} }
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { 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 } guard let _onRestoreUserInterfaceForPictureInPictureStop = _onRestoreUserInterfaceForPictureInPictureStop else { return }
_onRestoreUserInterfaceForPictureInPictureStop([:]) _onRestoreUserInterfaceForPictureInPictureStop()
_restoreUserInterfaceForPIPStopCompletionHandler = completionHandler _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler
} }
@ -47,7 +45,6 @@ class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate {
} }
func setupPipController(_ playerLayer: AVPlayerLayer?) { func setupPipController(_ playerLayer: AVPlayerLayer?) {
guard playerLayer != nil && AVPictureInPictureController.isPictureInPictureSupported() && _isActive else { return }
// Create new controller passing reference to the AVPlayerLayer // Create new controller passing reference to the AVPlayerLayer
_pipController = AVPictureInPictureController(playerLayer:playerLayer!) _pipController = AVPictureInPictureController(playerLayer:playerLayer!)
_pipController?.delegate = self _pipController?.delegate = self

View File

@ -82,8 +82,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
private let _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler() private let _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler()
#endif #endif
#if TARGET_OS_IOS #if os(iOS)
private let _pip:RCTPictureInPicture = RCTPictureInPicture(self.onPictureInPictureStatusChanged, self.onRestoreUserInterfaceForPictureInPictureStop) private var _pip:RCTPictureInPicture? = nil
#endif #endif
// Events // Events
@ -110,6 +110,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
@objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? @objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock?
@objc var onGetLicense: RCTDirectEventBlock? @objc var onGetLicense: RCTDirectEventBlock?
@objc var onReceiveAdEvent: RCTDirectEventBlock? @objc var onReceiveAdEvent: RCTDirectEventBlock?
@objc func _onPictureInPictureStatusChanged() {
onPictureInPictureStatusChanged?([ "isActive": NSNumber(value: true)])
}
@objc func _onRestoreUserInterfaceForPictureInPictureStop() {
onPictureInPictureStatusChanged?([ "isActive": NSNumber(value: false)])
}
init(eventDispatcher:RCTEventDispatcher!) { init(eventDispatcher:RCTEventDispatcher!) {
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
@ -119,6 +127,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_eventDispatcher = eventDispatcher _eventDispatcher = eventDispatcher
#if os(iOS)
_pip = RCTPictureInPicture(self._onPictureInPictureStatusChanged, self._onRestoreUserInterfaceForPictureInPictureStop)
#endif
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(applicationWillResignActive(notification:)), selector: #selector(applicationWillResignActive(notification:)),
@ -175,7 +187,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
@objc func applicationDidEnterBackground(notification:NSNotification!) { @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 // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html
_playerLayer?.player = nil _playerLayer?.player = nil
_playerViewController?.player = nil _playerViewController?.player = nil
@ -411,15 +423,21 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
@objc @objc
func setPictureInPicture(_ pictureInPicture:Bool) { func setPictureInPicture(_ pictureInPicture:Bool) {
#if TARGET_OS_IOS #if os(iOS)
_pip.setPictureInPicture(pictureInPicture) let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playback)
try audioSession.setActive(true, options: [])
} catch {
}
_pip?.setPictureInPicture(pictureInPicture)
#endif #endif
} }
@objc @objc
func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) {
#if TARGET_OS_IOS #if os(iOS)
_pip.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) _pip?.setRestoreUserInterfaceForPIPStopCompletionHandler(restore)
#endif #endif
} }
@ -753,6 +771,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
viewController.view.frame = self.bounds viewController.view.frame = self.bounds
viewController.player = player viewController.player = player
viewController.allowsPictureInPicturePlayback = true
return viewController return viewController
} }
@ -771,8 +790,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
self.layer.addSublayer(_playerLayer) self.layer.addSublayer(_playerLayer)
} }
self.layer.needsDisplayOnBoundsChange = true self.layer.needsDisplayOnBoundsChange = true
#if TARGET_OS_IOS #if os(iOS)
_pip.setupPipController(_playerLayer) _pip?.setupPipController(_playerLayer)
#endif #endif
} }
} }