I'm attempting to integrate a PiP (Picture-in-Picture) controller alongside the camera preview within a view controller. However, the camera preview is only visible in the view to which it's first assigned(The other one just goes black). Is it possible to use the same AVCaptureVideoPreviewLayer
instance for both views? Or what needs to change here?
class ViewController: UIViewController {
let captureSession = AVCaptureSession()
let captureSessionQueue = DispatchQueue(label: "Capture Session Queue")
var pipVideoCallViewController: AVPictureInPictureVideoCallViewController!
var pipController: AVPictureInPictureController!
override func viewDidLoad() {
super.viewDidLoad()
let previewView = PreviewView(captureSession)
let previewLayer = previewView.previewLayer
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
previewLayer.connection?.videoOrientation = .landscapeLeft
view.layer.addSublayer(previewLayer)
pipVideoCallViewController = .init(previewView,
preferredContentSize: CGSize(width: 1080, height: 1920))
let pipContentSource = AVPictureInPictureController.ContentSource(
activeVideoCallSourceView: view,
contentViewController: pipVideoCallViewController)
pipController = AVPictureInPictureController(contentSource: pipContentSource)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = true
startSession()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !pipController.isPictureInPictureActive {
pipController.startPictureInPicture()
}
}
private func startSession() {
captureSessionQueue.async { [unowned self] in
let device = AVCaptureDevice.default(for: .video)!
captureSession.addInput(try! AVCaptureDeviceInput(device: device))
captureSession.sessionPreset = .hd1920x1080
captureSession.isMultitaskingCameraAccessEnabled = captureSession.isMultitaskingCameraAccessSupported
captureSession.startRunning()
}
}
}
extension ViewController: AVPictureInPictureControllerDelegate {
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
print(error.localizedDescription)
}
}
class PreviewView: UIView {
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
init(_ session: AVCaptureSession) {
super.init(frame: .zero)
previewLayer.session = session
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension AVPictureInPictureVideoCallViewController {
convenience init(_ previewView: PreviewView, preferredContentSize: CGSize) {
// Initialize.
self.init()
// Set the preferredContentSize.
self.preferredContentSize = preferredContentSize
// Configure the PreviewView.
previewView.translatesAutoresizingMaskIntoConstraints = false
previewView.frame = self.view.frame
self.view.addSubview(previewView)
NSLayoutConstraint.activate([
previewView.topAnchor.constraint(equalTo: self.view.topAnchor),
previewView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
previewView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
previewView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
}
AVCaptureVideoPreviewLayer can only be added to a single view, create a single AVCaptureVideoPreviewLayer add it as a sublayer to both views
viewDidLoad add the AV to both
Update the Controller