You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合AVCaptureVideoPreviewLayer使用PiP?启动遇连接错误求助

解决AVCaptureVideoPreviewLayer结合PiP时的4099错误及视图消失问题

核心问题分析

你遇到的NSCocoaErrorDomain Code=4099错误,本质是PiP相关对象被过早释放导致与系统PiP服务(com.apple.pegasus)的连接失效;视图消失则是因为视图层级未正确配置、约束缺失。

修复步骤及代码示例

1. 持有PiP相关对象(关键)

将AVPictureInPictureVideoCallViewController和AVPictureInPictureController声明为类的实例属性,避免方法执行完后被ARC自动释放:

class YourViewController: UIViewController {
    // 持有PiP控制器和通话视图控制器
    private var pipController: AVPictureInPictureController?
    private var pipVideoCallVC: AVPictureInPictureVideoCallViewController?
    
    // 你的视频预览视图和捕获会话
    private let videoView = UIView()
    private var captureSession: AVPictureInPictureController?
    
    // ... 其他业务代码
}

2. 正确配置PiP视图层级与约束

确保视频预览视图从原父视图移除,并在PiP控制器视图中添加完整约束:

@available(iOS 15.0, *)
private func startPictureInPicture() {
    // 先验证捕获会话是否已启动
    guard let captureSession = captureSession, captureSession.isRunning else {
        print("捕获会话未启动")
        return
    }
    
    // 移除视频预览视图的原父视图绑定
    videoView.removeFromSuperview()
    
    // 创建并配置PiP通话视图控制器
    let pipVC = AVPictureInPictureVideoCallViewController()
    pipVC.preferredContentSize = CGSize(width: 160, height: 160)
    
    // 添加视频预览视图并设置约束
    videoView.translatesAutoresizingMaskIntoConstraints = false
    pipVC.view.addSubview(videoView)
    NSLayoutConstraint.activate([
        videoView.topAnchor.constraint(equalTo: pipVC.view.topAnchor),
        videoView.leadingAnchor.constraint(equalTo: pipVC.view.leadingAnchor),
        videoView.trailingAnchor.constraint(equalTo: pipVC.view.trailingAnchor),
        videoView.bottomAnchor.constraint(equalTo: pipVC.view.bottomAnchor)
    ])
    self.pipVideoCallVC = pipVC
    
    // 创建PiP内容源
    let contentSource = AVPictureInPictureController.ContentSource(
        activeVideoCallSourceView: videoView,
        contentViewController: pipVC
    )
    
    // 初始化并配置PiP控制器
    guard let pipController = try? AVPictureInPictureController(contentSource: contentSource) else {
        print("创建PiP控制器失败")
        return
    }
    pipController.canStartPictureInPictureAutomaticallyFromInline = true
    pipController.delegate = self // 可选:设置代理监听PiP状态
    self.pipController = pipController
    
    // 启动PiP
    pipController.startPictureInPicture()
}

3. 验证AVCapturePreviewLayer配置

确保预览层正确添加到视频视图,且跟随视图尺寸变化:

private func setupPreviewLayer() {
    guard let captureSession = captureSession else { return }
    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.videoGravity = .resizeAspectFill
    previewLayer.frame = videoView.bounds
    videoView.layer.addSublayer(previewLayer)
    
    // 监听视图尺寸变化,更新预览层
    videoView.addObserver(self, forKeyPath: "bounds", options: .new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "bounds", let previewLayer = videoView.layer.sublayers?.first as? AVCaptureVideoPreviewLayer {
        previewLayer.frame = videoView.bounds
    }
}

4. 检查Info.plist配置

确保添加必要权限:

  • NSCameraUsageDescription:相机权限描述(必填)
  • UIBackgroundModes:添加voip或audio(后台运行PiP需要)
  • NSMicrophoneUsageDescription:若使用音频则添加

可选:监听PiP状态排查问题

实现AVPictureInPictureControllerDelegate,可以更直观地排查PiP启动及运行状态:

extension YourViewController: AVPictureInPictureControllerDelegate {
    func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
        print("PiP启动成功")
    }
    
    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
        print("PiP启动失败:\(error.localizedDescription)")
    }
}

内容的提问来源于stack exchange,提问作者Matheus Weber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:18:33