【问题标题】:Capturing video and saving it via AVAssetWriter捕获视频并通过 AVAssetWriter 保存
【发布时间】:2019-05-30 10:50:15
【问题描述】:

我想将AVAssetWriter 输出保存到相机胶卷,我目前正在将其保存到文档目录。

我尝试使用 UISaveVideoAtPathToSavedPhotosAlbum(_:_:_:_:) 。我目前正在使用AVAssetWriter 写信给.documentsdirectotry。但是,当我尝试编写时,它会默默地失败。要写,我会打电话给startRecording(),我会打电话给stopRecording()来完成写作。

let captureSession = AVCaptureSession()
var videoOutput = AVCaptureVideoDataOutput()
var assetWriter: AVAssetWriter!
var assetWriterInput: AVAssetWriterInput!
var isCameraSetup = false
var hasStartedWritingCurrentVideo  = false
var isWriting = false
let queue = DispatchQueue(label: "com.name.camera-queue") 


// Camera preview setup code

//Setting up Asset Writer to save videos
public func setUpAssetWriter() {
    do {
        let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
        let outputURL = URL(fileURLWithPath: documentsPath!).appendingPathComponent("test.m4v")
        assetWriter = try! AVAssetWriter(outputURL: outputURL, fileType: .mp4)
        assetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoOutput.recommendedVideoSettingsForAssetWriter(writingTo: .mp4))
        assetWriterInput.expectsMediaDataInRealTime = true

        if assetWriter.canAdd(assetWriterInput) {
            assetWriter.add(assetWriterInput)
        } else {
            print("no input added")
        }
        assetWriter.startWriting()
        } catch let error {
        debugPrint(error.localizedDescription)
    }
}
public func tearDownAssetWriter() {
    assetWriter = nil
    assetWriterInput = nil
}
public func startWriting(){
    if isWriting{ return }
        setUpAssetWriter()
        hasStartedWritingCurrentVideo = false
        isWriting = true
}
public func finishWriting(_ completion: @escaping (URL) -> Void) {
    if isWriting == false { return }
    isWriting = false
    assetWriterInput.markAsFinished()
    let url = self.assetWriter.outputURL
    assetWriter.finishWriting {
        completion(url)
        self.tearDownAssetWriter()
    }
    hasStartedWritingCurrentVideo = false
}

// MARK: Starts the capture session
public func start() {
    if !captureSession.isRunning {
        captureSession.startRunning()
    }
}

public func stop() {
    if captureSession.isRunning {
        captureSession.stopRunning()
    }
}
// MARK: Records after camera is set up
public func startRecording() {
    startWriting()
    isWriting = true
}

public func stopRecording() {
    assetWriterInput.markAsFinished()
    assetWriter.finishWriting {
        [weak self] in return
    }
}
}

extension VideoCapture: AVCaptureVideoDataOutputSampleBufferDelegate {
    public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // Because lowering the capture device's FPS looks ugly in the preview,
        // we capture at full speed but only call the delegate at its desired
        // framerate.
        let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
        let deltaTime = timestamp - lastTimestamp
        if deltaTime >= CMTimeMake(value: 1, timescale: Int32(fps)) {
            lastTimestamp = timestamp
            let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
            delegate?.videoCapture(self, didCaptureVideoFrame: imageBuffer, timestamp: timestamp)
        }
    // Asset Writer
        guard let assetWriter = assetWriter, let assetWriterInput = assetWriterInput else { return }
        if isWriting == false{ return }
        if self.assetWriter.status == .failed {
            setUpAssetWriter()
            hasStartedWritingCurrentVideo = false
        }
        if hasStartedWritingCurrentVideo == false && output === videoOutput { return }
        if hasStartedWritingCurrentVideo == false {
            hasStartedWritingCurrentVideo = true
            let sourceTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
            assetWriter.startSession(atSourceTime: sourceTime)
        }
        if output === videoOutput && assetWriterInput.isReadyForMoreMediaData{
            if isWriting == false{return}
            assetWriterInput.append(sampleBuffer)
        }
    }
}

当前实现设置了相机和预览,但没有任何内容保存到输出中。 它应该保存到 .documentDirectory,但是,它没有保存。我想把它保存到相机胶卷,但我不确定我应该在哪里打电话给UISaveVideoAtPathToSavedPhotosAlbum(_:_:_:_:)。 问题可能出在我的扩展委托中。

提前感谢您的帮助。

【问题讨论】:

    标签: ios avfoundation avassetwriter avcapture avcaptureoutput


    【解决方案1】:

    我不熟悉 UISaveVideoAtPathToSavedPhotosAlbum。但是浏览stackoverflow和git,很多人用PHPhotoLibrary,我也是。不管url,下面的代码把视频添加到photoLibrary。

    https://developer.apple.com/documentation/photokit/phassetchangerequest/1624057-creationrequestforassetfromvideo

    1) Info.plist 通过 + 按钮添加新的键值对。选择“Privary - Photo Library Usage Description”作为键。设置诸如“将视频保存在照片库中”之类的值

    2) 代码

    fileWriter.finishWriting(completionHandler: {
        let status = PHPhotoLibrary.authorizationStatus()
    
        //no access granted yet
        if status == .notDetermined || status == .denied{
            PHPhotoLibrary.requestAuthorization({auth in
                if auth == .authorized{
                    saveInPhotoLibrary(url)
                }else{
                    print("user denied access to photo Library")
                }
            })
    
        //access granted by user already
        }else{
            saveInPhotoLibrary(url)
        }
    })
    
    private func saveInPhotoLibrary(_ url:URL){
        PHPhotoLibrary.shared().performChanges({
    
            //add video to PhotoLibrary here
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
        }) { completed, error in
            if completed {
                print("save complete! path : " + url.absoluteString)
            }else{
                print("save failed")
            }
        }
    }
    

    希望这会有所帮助。 GW

    【讨论】:

      猜你喜欢
      • 2019-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多