Swift使用AVCaptureSession录制视频二次点击崩溃问题求助
解决AVCaptureSession第二次录制崩溃的问题
问题根源分析
从你给出的报错信息An AVCaptureOutput instance may not be added to more than one session和代码来看,核心问题在于每次页面出现(viewWillAppear)都会执行session.addOutput(movieOutput),但movieOutput是类的成员变量,当它已经被添加到session中时,重复调用addOutput就会触发这个异常。
具体拆解你的代码流程:
- 第一次进入页面:
viewWillAppear配置session并添加movieOutput,录制正常;点击停止后你调用了session.removeOutput(movieOutput)。 - 第二次进入页面(比如返回后再次进入):
viewWillAppear再次执行session.addOutput(movieOutput)——如果此时movieOutput还在session中(比如你没点击停止就离开页面),就会直接抛出异常。另外,session作为成员变量被重复配置输入输出,也会带来额外的状态混乱。
修复方案
我帮你调整代码结构,让session和output的管理更严谨:
1. 把session配置移到viewDidLoad,避免重复初始化
将session的输入输出配置、预览层设置逻辑移到viewDidLoad,只执行一次:
class CaptureViewViewController: BaseViewController,AVCaptureFileOutputRecordingDelegate{ let session: AVCaptureSession = AVCaptureSession() var movieOutput = AVCaptureMovieFileOutput() var previewLayer: AVCaptureVideoPreviewLayer? // 保存预览层引用,避免重复添加 override func viewDidLoad() { super.viewDidLoad() setupCaptureSession() } private func setupCaptureSession() { session.beginConfiguration() session.sessionPreset = AVCaptureSession.Preset.high // 添加音频输入 if let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio){ do { let audioInput = try AVCaptureDeviceInput(device: audioDevice) if session.canAddInput(audioInput) { session.addInput(audioInput) } } catch { print("Unable to add audio device to the recording.") } } // 添加视频输入 if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front) { do { let videoInput = try AVCaptureDeviceInput(device: device) if session.canAddInput(videoInput) { session.addInput(videoInput) } } catch { print(error.localizedDescription) } // 配置预览层 previewLayer = AVCaptureVideoPreviewLayer(session: session) self.preViewImage.layer.addSublayer(previewLayer!) previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill previewLayer?.frame = self.preViewImage.layer.bounds } // 检查并添加视频输出 if session.canAddOutput(movieOutput) { movieOutput.movieFragmentInterval = CMTime.invalid session.addOutput(movieOutput) } session.commitConfiguration() }
2. 简化页面生命周期逻辑
只在页面出现时启动session,消失时停止session,避免重复操作:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 避免重复启动session if !session.isRunning { session.startRunning() } // 更新预览层frame,适配页面布局变化 previewLayer?.frame = self.preViewImage.layer.bounds } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 页面消失时强制停止录制,重置状态 if videoRecordState == .start { movieOutput.stopRecording() recordVideoButton.setTitle("Start Recording", for: .normal) videoRecordState = .stop } // 停止session if session.isRunning { session.stopRunning() } }
3. 优化录制按钮逻辑
移除不必要的session.removeOutput(movieOutput),复用同一个output实例:
@IBAction func handleMoviewRecord(_ sender: Any) { if videoRecordState == .stop { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let fileUrl = paths[0].appendingPathComponent("output.mov") try? FileManager.default.removeItem(at: fileUrl) // 保险检查:确保output在session中 if !session.outputs.contains(movieOutput) { if session.canAddOutput(movieOutput) { session.addOutput(movieOutput) } } movieOutput.startRecording(to: fileUrl, recordingDelegate: self) recordVideoButton.setTitle("Stop Recording", for: .normal) recordVideoButton.backgroundColor = UIColor.red videoRecordState = .start }else { movieOutput.stopRecording() recordVideoButton.setTitle("Start Recording", for: .normal) videoRecordState = .stop // 无需移除output,后续录制还要复用 } }
额外注意点
- 每次添加输入输出前,务必用
canAddInput/canAddOutput做检查,避免触发异常。 - 保存
previewLayer的引用,在页面布局变化时更新frame,保证预览画面正常显示。 - 页面消失时强制重置录制状态,避免回到页面后出现状态混乱。
内容的提问来源于stack exchange,提问作者Manjula Nang
相关产品推荐
相关产品推荐

