add picture in picture for single video calls when the view goes in background (waiting apple permission to enable video capture in picture in picture video calls)

This commit is contained in:
Quentin Monnier 2023-09-05 13:22:59 +02:00 committed by QuentinArguillere
parent 0ed4f948ca
commit f8562a45de
2 changed files with 124 additions and 1 deletions

View file

@ -193,7 +193,13 @@ extension ProviderDelegate: CXProviderDelegate {
if (UIApplication.shared.applicationState != .active) {
CallManager.instance().backgroundContextCall = call
CallManager.instance().backgroundContextCameraIsEnabled = call?.params?.videoEnabled == true || call?.callLog?.wasConference() == true
call?.cameraEnabled = false // Disable camera while app is not on foreground
if #available(iOS 16.0, *) {
if (call?.cameraEnabled == true) {
call?.cameraEnabled = AVCaptureSession().isMultitaskingCameraAccessSupported
}
} else {
call?.cameraEnabled = false // Disable camera while app is not on foreground
}
}
CallManager.instance().callkitAudioSessionActivated = false
CallManager.instance().lc?.configureAudioSession()

View file

@ -20,6 +20,7 @@
import UIKit
import linphonesw
import AVKit
@objc class SingleCallView: AbstractCallView, UICompositeViewDelegate {
@ -28,6 +29,9 @@ import linphonesw
var callPausedByLocalView : PausedCallOrConferenceView? = nil
var currentCallView : ActiveCallView? = nil
private var pipController: AVPictureInPictureController!
private var pipRemoteVideoView = SampleBufferVideoCallView()
static let compositeDescription = UICompositeViewDescription(SingleCallView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: nil, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
@ -98,6 +102,11 @@ import linphonesw
view.onClick {
ControlsViewModel.shared.audioRoutesSelected.value = false
}
// picture in picture init
if #available(iOS 15.0, *) {
DispatchQueue.main.async { self.configurationPiPViewController() }
}
}
@ -111,4 +120,112 @@ import linphonesw
self.currentCallView?.layoutRotatableElements()
}
// Picture in picture on video call
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (CallsViewModel.shared.currentCallData.value??.call.state == .StreamsRunning && pipController.isPictureInPicturePossible) {
pipController.startPictureInPicture()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if pipController.isPictureInPictureActive {
pipController.stopPictureInPicture()
}
}
}
// Picture in picture on video call
@available(iOS 15.0, *)
extension SingleCallView : AVPictureInPictureControllerDelegate {
func configurationPiPViewController() {
let pipVideoCallController = PictureInPictureVideoCallViewController()
pipRemoteVideoView = pipVideoCallController.pipRemoteVideoView
let pipContentSource = AVPictureInPictureController.ContentSource(
activeVideoCallSourceView: currentCallView!.remoteVideo,
contentViewController: pipVideoCallController)
pipController = AVPictureInPictureController(contentSource: pipContentSource)
pipController.delegate = self
ControlsViewModel.shared.isVideoEnabled.readCurrentAndObserve{ (video) in
pipVideoCallController.matchVideoDimension()
self.pipController.canStartPictureInPictureAutomaticallyFromInline = video == true
}
CallsViewModel.shared.currentCallData.observe(onChange: { callData in
if (callData??.call.state != .StreamsRunning && self.pipController.isPictureInPictureActive) {
self.pipController.stopPictureInPicture()
}
})
}
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
Core.get().nativeVideoWindow = pipRemoteVideoView
}
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
Core.get().nativeVideoWindow = currentCallView?.remoteVideo
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
Core.get().nativeVideoWindow = currentCallView?.remoteVideo
Log.e("Start Picture in Picture video call error : \(error)")
DispatchQueue.main.async { self.configurationPiPViewController() }
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
if (CallsViewModel.shared.currentCallData.value??.call.state == .StreamsRunning && PhoneMainView.instance().currentView != self.compositeViewDescription()) {
PhoneMainView.instance().changeCurrentView(self.compositeViewDescription())
Core.get().nativeVideoWindow = pipRemoteVideoView // let the video on the pip view during the stop animation
}
pictureInPictureController.contentSource?.activeVideoCallContentViewController.view.layer.cornerRadius = ActiveCallView.center_view_corner_radius
completionHandler(true)
}
}
@available(iOS 15.0, *)
class PictureInPictureVideoCallViewController : AVPictureInPictureVideoCallViewController {
var pipRemoteVideoView = SampleBufferVideoCallView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.clipsToBounds = true
view.addSubview(pipRemoteVideoView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
view.layer.cornerRadius = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
matchVideoDimension()
}
func matchVideoDimension() {
let videoDefinition = CallsViewModel.shared.currentCallData.value??.call.currentParams?.receivedVideoDefinition
if (videoDefinition != nil) {
self.preferredContentSize = CGSize(width: Double(videoDefinition!.width), height: Double(videoDefinition!.height))
pipRemoteVideoView.frame = view.bounds
}
}
}
class SampleBufferVideoCallView: UIView {
override class var layerClass: AnyClass {
AVSampleBufferDisplayLayer.self
}
var sampleBufferDisplayLayer: AVSampleBufferDisplayLayer {
layer as! AVSampleBufferDisplayLayer
}
}