forked from mirrors/linphone-iphone
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:
parent
0ed4f948ca
commit
f8562a45de
2 changed files with 124 additions and 1 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue