Я пытаюсь записать и сохранить видео с помощью AVFoundation Framework как с передней, так и с задней камерой. Я могу начать сеанс, но не могу сохранить видеозапись в каталоге документов. Я проверяю movieOutput.isRecording
, каждый раз выдает false
. Следовательно, метод вывода делегата также не вызывается из-за этого. Даже делегат Start не вызывается при начале записи.
import UIKit
import Foundation
import AVKit
import AVFoundation
class AppVideoRecorder: NSObject {
private var session = AVCaptureSession()
private var movieOutput = AVCaptureMovieFileOutput()
private var camera: AVCaptureDevice?
private var activeInput: AVCaptureDeviceInput?
private var previewLayer = AVCaptureVideoPreviewLayer()
private var renderView: UIView!
var isFrontCamera: Bool = false
init(for view: UIView) {
self.renderView = view
}
deinit {
print("Called")
}
func setupSession() {
self.session.sessionPreset = .high
// Setup Camera
self.camera = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: self.isFrontCamera ? .front : .back
)
if let camera = self.camera {
do {
let input = try AVCaptureDeviceInput(device: camera)
if self.session.canAddInput(input) {
self.session.addInput(input)
self.activeInput = input
}
} catch {
print(error)
}
}
// Setup Microphone
if let microphone = AVCaptureDevice.default(for: .audio) {
do {
let micInput = try AVCaptureDeviceInput(device: microphone)
if self.session.canAddInput(micInput) {
self.session.addInput(micInput)
}
} catch {
print(error)
}
}
// Movie output
if self.session.canAddOutput(self.movieOutput) {
self.session.addOutput(self.movieOutput)
}
}
func setupPreview() {
// Configure previewLayer
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
self.previewLayer.frame = self.renderView.bounds
self.previewLayer.videoGravity = .resizeAspectFill
self.renderView.layer.addSublayer(self.previewLayer)
}
func startSession() {
if self.session.isRunning { return }
DispatchQueue.main.async {
self.session.startRunning()
}
}
func stopSession() {
if self.session.isRunning {
DispatchQueue.main.async {
self.session.stopRunning()
}
}
}
func removeInput() {
guard let input = self.activeInput else { return }
self.session.removeInput(input)
}
func isCameraOn(completion: @escaping (Bool) -> Void) {
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
completion(true)
} else {
AVCaptureDevice.requestAccess(for: .video,
completionHandler: { (granted) in
completion(granted)
})
}
}
func toggleCamera() {
self.session.beginConfiguration()
for input in self.session.inputs {
if let inputObj = input as? AVCaptureDeviceInput {
self.session.removeInput(inputObj)
}
}
self.camera = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: self.isFrontCamera ? .front : .back
)
if let camera = self.camera {
do {
let input = try AVCaptureDeviceInput(device: camera)
if self.session.canAddInput(input) {
self.session.addInput(input)
self.activeInput = input
}
} catch {
print(error)
}
}
self.session.commitConfiguration()
}
}
extension AppVideoRecorder: AVCaptureFileOutputRecordingDelegate {
private var currentVideoOrientation: AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation
switch UIDevice.current.orientation {
case .portrait:
orientation = AVCaptureVideoOrientation.portrait
case .landscapeRight:
orientation = AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
orientation = AVCaptureVideoOrientation.portraitUpsideDown
default:
orientation = AVCaptureVideoOrientation.landscapeRight
}
return orientation
}
func recordVideo() {
if self.movieOutput.isRecording { // FALSE EVERY TIME
self.stopRecording()
} else {
self.startRecording()
}
}
private func startRecording() {
guard let connection = self.movieOutput.connection(with: .video),
let device = self.activeInput?.device else { return }
// handle return error
if connection.isVideoOrientationSupported {
connection.videoOrientation = self.currentVideoOrientation
}
if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = .auto
}
if device.isSmoothAutoFocusSupported {
do {
try device.lockForConfiguration()
device.isSmoothAutoFocusEnabled = false
device.unlockForConfiguration()
} catch {
print("Error setting configuration: \(error)")
}
}
let paths = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)
guard let path = paths.first else { return }
let fileUrl = path.appendingPathComponent("celeb_video.mp4")
try? FileManager.default.removeItem(at: fileUrl)
self.movieOutput.startRecording(to: fileUrl, recordingDelegate: self)
}
private func stopRecording() {
self.movieOutput.stopRecording()
}
func fileOutput(_ output: AVCaptureFileOutput,
didFinishRecordingTo outputFileURL: URL,
from connections: [AVCaptureConnection],
error: Error?) {
print("DELEGATE CALL BACK")
if let error = error {
//do something
print(error)
} else {
//do something
print(outputFileURL.path)
// UISaveVideoAtPathToSavedPhotosAlbum(outputFileURL.path, nil, nil, nil)
}
}
func fileOutput(_ output: AVCaptureFileOutput,
didStartRecordingTo fileURL: URL,
from connections: [AVCaptureConnection]) {
print("didStartRecordingTo CALL BACK:", fileURL.path)
}
}
Вот мой код вызова в контроллере представления. recordingView — это UIView.
private lazy var recorder: AppVideoRecorder = {
return AppVideoRecorder(for: self.recordingView)
}()
@IBAction func recordingAction(_ sender: UIButton) {
sender.isSelected.toggle()
if sender.isSelected {
self.recorder.setupSession()
self.recorder.setupPreview()
self.recorder.startSession()
self.recorder.recordVideo()
} else {
self.recorder.recordVideo()
self.recorder.removeInput()
self.recorder.stopSession()
}
}
@IBAction func swapCameraAction(_ sender: UIButton) {
sender.isSelected.toggle()
self.recorder.isFrontCamera = sender.isSelected
self.recorder.toggleCamera()
}
Пожалуйста, дайте мне знать, что я пропустил.