如何使用AVFoundation录制长视频(>1分钟)?

4

当我使用以下视图控制器录制视频时:

class AVCameraViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    initializeMotionManager()
    sessionQueue.async {
        let movieFileOutput = AVCaptureMovieFileOutput()
        if self.session.canAddOutput(movieFileOutput) {
            self.session.beginConfiguration()
            self.session.addOutput(movieFileOutput)
            self.session.sessionPreset = .high
            if let connection = movieFileOutput.connection(with: .video) {
                if connection.isVideoStabilizationSupported {
                    connection.preferredVideoStabilizationMode = .auto
                }
            }
            self.session.commitConfiguration()
            movieFileOutput.maxRecordedDuration = CMTime(seconds: 120, preferredTimescale: 60)

            self.movieFileOutput = movieFileOutput

            DispatchQueue.main.async {
                self.recordButton.isEnabled = true
                }
            }
        }
    }

    func fileOutput(_ output: AVCaptureFileOutput,
                didFinishRecordingTo outputFileURL: URL,
                from connections: [AVCaptureConnection],
                error: Error?) {
    // Note: Since we use a unique file path for each recording, a new recording won't overwrite a recording mid-

save.
        UIApplication.shared.isIdleTimerDisabled = false
        func cleanup() {
            let path = outputFileURL.path
            if FileManager.default.fileExists(atPath: path) {
                do {
                    try FileManager.default.removeItem(atPath: path)
                } catch {
                    print("Could not remove file at url: \(outputFileURL)")
                }
            }

            if let currentBackgroundRecordingID = backgroundRecordingID {
                backgroundRecordingID = UIBackgroundTaskIdentifier.invalid

                if currentBackgroundRecordingID != UIBackgroundTaskIdentifier.invalid {
                    UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID)
                }
            }
        }

        var success = true

        if error != nil {
            print("Movie file finishing error: \(String(describing: error))")
            success = (((error! as NSError).userInfo[AVErrorRecordingSuccessfullyFinishedKey] as AnyObject).boolValue)!
        }

        if success {
            // Check authorization status.
            UIView.animate(withDuration: 0.5){
                self.overlay.alpha = 0.9
                self.navigationController?.navigationBar.isTranslucent = false
            }
            footageURL = outputFileURL
            performSegue(withIdentifier: "TrimFootage", sender: nil)
        } else {
            cleanup()
        }

        // Enable the Camera and Record buttons to let the user switch camera and start another recording.
        DispatchQueue.main.async {
            // Only enable the ability to change camera if the device has more than one camera.
            self.recordButton.isEnabled = true
//            self.recordButton.setImage(#imageLiteral(resourceName: "CaptureVideo"), for: [])
        }
    }
}

正如您所看到的,我正在将maxRecordedDuration设置为2分钟。 当成功录制时,它最终会转移到另一个视图控制器。

问题在于现在它仅录制一分钟然后停止录制并进行转换。 我不确定是否没有正确设置maxRecordedDuration或者我必须做其他事情。


嘿,也许这篇或者这篇帖子可以帮到你。 - LoVo
@LoVo 我开始使用 CMTime(seconds: 120, preferredTimescale: 1),根据那些帖子,这应该是120秒。我的相机行为没有改变 - 它仍然在1分钟停止。因此,我不确定是否与 CMTime 本身有关,而是与我将其分配给相机的方式有关? - BigBoy1337
我怀疑后台任务可能会在1分钟后终止它。首先尝试在前台运行,看看是否可以正常工作。 - ugur
3个回答

1

我刚刚使用了这段代码:(大部分来自this的被接受答案。感谢@gwinyai。你应该给他点赞。我已经点了;)

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

    @IBOutlet weak var camPreview: UIView!

    let cameraButton = UIView()

    let captureSession = AVCaptureSession()

    let movieOutput = AVCaptureMovieFileOutput()

    var previewLayer: AVCaptureVideoPreviewLayer!

    var activeInput: AVCaptureDeviceInput!

    var outputURL: URL!


    override func viewDidLoad() {
        super.viewDidLoad()
        movieOutput.maxRecordedDuration = CMTime(seconds: 120, preferredTimescale: 600)
        if setupSession() {
            setupPreview()
            startSession()
        }

        cameraButton.isUserInteractionEnabled = true

        let cameraButtonRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.startCapture))

        cameraButton.addGestureRecognizer(cameraButtonRecognizer)

        cameraButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)

        cameraButton.backgroundColor = UIColor.red

        camPreview.addSubview(cameraButton)

    }

    func setupPreview() {
        // Configure previewLayer
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = camPreview.bounds
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        camPreview.layer.addSublayer(previewLayer)
    }

    //MARK:- Setup Camera

    func setupSession() -> Bool {

        captureSession.sessionPreset = AVCaptureSession.Preset.high

        // Setup Camera
        let camera = AVCaptureDevice.default(for: AVMediaType.video)!

        do {

            let input = try AVCaptureDeviceInput(device: camera)
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
                activeInput = input
            }
        } catch {
            print("Error setting device video input: \(error)")
            return false
        }

        // Setup Microphone
        let microphone = AVCaptureDevice.default(for: AVMediaType.audio)!

        do {
            let micInput = try AVCaptureDeviceInput(device: microphone)
            if captureSession.canAddInput(micInput) {
                captureSession.addInput(micInput)
            }
        } catch {
            print("Error setting device audio input: \(error)")
            return false
        }


        // Movie output
        if captureSession.canAddOutput(movieOutput) {
            captureSession.addOutput(movieOutput)
        }

        return true
    }

    func setupCaptureMode(_ mode: Int) {
        // Video Mode

    }

    //MARK:- Camera Session
    func startSession() {

        if !captureSession.isRunning {
            videoQueue().async {
                self.captureSession.startRunning()
            }
        }
    }

    func stopSession() {
        if captureSession.isRunning {
            videoQueue().async {
                self.captureSession.stopRunning()
            }
        }
    }

    func videoQueue() -> DispatchQueue {
        return DispatchQueue.main
    }

    func 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
    }

    @objc func startCapture() {
        if movieOutput.isRecording == false {
            startRecording()
            print("-------- startRecording --------")
        } else {
            stopRecording()
            print("-------- stopRecording --------")

        }
    }

    func tempURL() -> URL? {
        let directory = NSTemporaryDirectory() as NSString

        if directory != "" {
            let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
            return URL(fileURLWithPath: path)
        }

        return nil
    }


    func startRecording() {

        if movieOutput.isRecording == false {
            let connection = movieOutput.connection(with: AVMediaType.video)

            if (connection?.isVideoOrientationSupported)! {
                connection?.videoOrientation = currentVideoOrientation()
            }

            if (connection?.isVideoStabilizationSupported)! {
                connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
            }

            let device = activeInput.device

            if (device.isSmoothAutoFocusSupported) {

                do {
                    try device.lockForConfiguration()
                    device.isSmoothAutoFocusEnabled = false
                    device.unlockForConfiguration()
                } catch {
                    print("Error setting configuration: \(error)")
                }

            }
            outputURL = tempURL()
            movieOutput.startRecording(to: outputURL, recordingDelegate: self)
        }
        else {
            stopRecording()
        }

    }

    func stopRecording() {
        if movieOutput.isRecording == true {
            movieOutput.stopRecording()
        }
    }

    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {

        if let error = error,
            let nserror = error as NSError? {
            switch nserror.code {
            case AVError.Code.maximumDurationReached.rawValue:
                //no error because we want to stop if max duration is reached
                print(output.maxRecordedDuration.seconds, "<<<<<<<<<<<<" )
                recordingEnded()
                return

            default:
                //handle error
                print(nserror.userInfo)
                break
            }
        } else {
            //user manually stopped the video before maxduration was reached
             recordingEnded()
        }
    }

    func recordingEnded() -> Void {
        print("recording ended successfully")
        let videoRecorded = outputURL! as URL
    }
}

在120秒后停止录制。

它有效!您只需要在Storyboard中添加camPreview,并确保您的.plist中已添加Privacy - Microphone Usage DescriptionPrivacy - Camera Usage Description

为什么movieOutput.maxRecordedDuration = CMTime(seconds: 120, preferredTimescale: 600)

苹果公司建议使用视频的时间尺度为600,因为600是常见视频帧速率(24、25和30 FPS)的倍数。如果您需要对音频文件进行样本精确索引,则可能需要将其提高到60,000或更高。

请检查此处


一切都好,但我的委托方法没有被调用。 - Abhishek Thapliyal
哪个委托方法? - LoVo
记录开始和结束,我得到了解决方案,但创建会话需要时间。因此,在会话开始时,我尝试调用开始录制,并且它可以正常工作。 - Abhishek Thapliyal
好的,这不应该花费太长时间。 - LoVo
是的,最多1秒钟。 - Abhishek Thapliyal

0
我假设self.session是AVCaptureSession的一个实例。尝试将maxRecordedDefinition属性定义移动到movieFileOutput定义之后。
let movieFileOutput = AVCaptureMovieFileOutput()
movieFileOutput.maxRecordedDuration = CMTime(seconds: 120, preferredTimescale: 1)

您可能会在设置不正确的情况下过早地提交会话配置。

(另外,使用首选时间刻度为1,表示一整秒 - 但我认为您已经在上面的注释中注意到了这一点。)


0

如果您无法通过maxRecordedDuration解决问题,我建议尝试删除它并设置一个计时器。当录制开始时,它会触发并运行120秒。如果您在此之前按下停止按钮,请使其失效,以便它不会触发。如果计时器运行到结束,只需调用stopRecording函数即可停止录制并触发所需的segue。

这样可以吗?

private var timer: Timer?

private func startRecording() {
    // Code to start recording, you can start timer here once you start recording
    self.timer = Timer.scheduledTimer(withTimeInterval: 120, repeats: false, block: { [weak self] (t) in
        guard let welf = self else {return}
        welf.stopRecording()
    })
}
private func stopRecording() {
    // Code to stop recording and segue further
}

或者您可以使用 UIImagePickerController 和属性 videoMaximumDuration在这里 - LoVo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接