【问题标题】:RPScreenRecorder.shared().startCapture won't write / keeps failingRPScreenRecorder.shared().startCapture 不会写入/一直失败
【发布时间】:2020-04-24 02:07:54
【问题描述】:

尝试使用 ReplayKit 录制和保存音频/视频时,我不断收到错误消息。我正在使用

Xcode: Version 11.2.1
Swift 5
iOS 13
iPhone 7+ physical device

当我设置文件路径时,我已经在使用URL(fileURLWithPath: )。文件扩展名和AVFileType 都是.mp4。我检查该文件是否已存在于FileManager 中,如果存在,我将其删除:do { try FileManager.default.removeItem(at: videoURL) }。我试图将路径本身更改为“Library/Caches/”,就像@florianSAP answer 一样,但没有成功。

这里有 3 个错误:

// 1. from recording
if !self.assetWriter.startWriting() {
    print("Can't write")
    return
}

// 2. from recording
if self.assetWriter.status == AVAssetWriter.Status.failed {
    print("StartCapture Error Occurred, Status = \(self.assetWriter.status.rawValue), \(self.assetWriter.error?.localizedDescription) \(self.assetWriter.error?.debugDescription)")
    return
}

// 3. this one is when trying to save the url in the PHAssetChangeRequest.creationRequestForAssetFromVideo completionHandler
if let error = error {
    print("PHAssetChangeRequest Video Error: \(error.localizedDescription)")
    return
}

// 4. this isn't an error but inside the switch rpSampleBufferType { } statement "not a video sample" kept printing out

错误信息是:

StartCapture 发生错误,状态 = 3,无法执行该操作 已完成可选(错误域=AVFoundationErrorDomain 代码=-11800 “操作无法完成” UserInfo={NSLocalizedFailureReason=发生未知错误 (-17508), NSLocalizedDescription=操作无法完成, NSUnderlyingError=0x2833a93b0 {错误域=NSOSStatusErrorDomain 代码=-17508 "(null)"}})

PHAssetChangeRequest 视频错误:操作无法完成。 (PHPhotosErrorDomain 错误-1。)

我会在哪里出错?

开始录制

let recorder = RPScreenRecorder.shared()
var assetWriter: AVAssetWriter!
var videoURL: URL!
var videoInput: AVAssetWriterInput!
var audioMicInput: AVAssetWriterInput!

guard let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { return }

videoURL = URL(fileURLWithPath: documentsPath.appending(UUID().uuidString + ".mp4"))

guard let videoURL = videoURL else { return }

do {
    try FileManager.default.removeItem(at: videoURL)
} catch {}

do {
    try assetWriter = AVAssetWriter(outputURL: videoURL, fileType: .mp4) // AVAssetWriter(url: videoURL, fileType: .mp4) didn't make a difference
} catch {}

let videoSettings: [String : Any] = [
    AVVideoCodecKey: AVVideoCodecType.h264,
    AVVideoWidthKey: view.bounds.width,
    AVVideoHeightKey: view.bounds.height
]

videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
videoInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(videoInput) {
    assetWriter.add(videoInput)
}

let audioSettings: [String:Any] = [AVFormatIDKey : kAudioFormatMPEG4AAC,
    AVNumberOfChannelsKey : 2,
    AVSampleRateKey : 44100.0,
    AVEncoderBitRateKey: 192000
]

audioMicInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
audioMicInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(audioMicInput) {
    assetWriter.add(audioMicInput)
}

guard recorder.isAvailable else { return }

recorder.startCapture(handler: { (cmSampleBuffer, rpSampleBufferType, err) in

    if let err = err { return }

    // I tried to check if this was ready and added the below code to it but it made no difference
    // if CMSampleBufferDataIsReady(cmSampleBuffer) { ... the code below was put in here ... }

    DispatchQueue.main.async {

        switch rpSampleBufferType {
           case .video:

                if self.assetWriter.status == AVAssetWriter.Status.unknown {

                    if !self.assetWriter.startWriting() {
                        print("Can't write")
                        return
                    }

                    print("Starting writing")
                    self.assetWriter.startWriting()
                    self.assetWriter.startSession(atSourceTime:  CMSampleBufferGetPresentationTimeStamp(cmSampleBuffer))
                }

                if self.assetWriter.status == AVAssetWriter.Status.failed {
                    print("StartCapture Error Occurred, Status = \(self.assetWriter.status.rawValue), \(self.assetWriter.error?.localizedDescription) \(self.assetWriter.error?.debugDescription)")
                    return
                }

                if self.assetWriter.status == AVAssetWriter.Status.writing {
                    if self.videoInput.isReadyForMoreMediaData {
                        if self.videoInput.append(cmSampleBuffer) == false {
                            print("problem writing video")
                        }
                    }
                }

            case .audioMic:
                if self.audioMicInput.isReadyForMoreMediaData {
                    print("audioMic data added")
                    self.audioMicInput.append(cmSampleBuffer)
                }

            default:
                print("not a video sample")
            }
        }
    }

}, completionHandler: { (error) in

    if let error = error { return }
})

停止录制:

recorder.stopCapture { (error) in

    if let error = error { return }

    guard let videoInput = self.videoInput else { return }
    guard let audioMicInput = self.audioMicInput else { return }
    guard let assetWriter = self.assetWriter else { return }
    guard let videoURL = videoURL else { return }

    videoInput.markAsFinished()
    audioMicInput.markAsFinished()
    assetWriter.finishWriting(completionHandler: {

        PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoUrl)
            }) { (saved, error) in

                if let error = error {
                    print("PHAssetChangeRequest Video Error: \(error.localizedDescription)")
                    return
                }

                if saved {
                    // ... show success message
                }
            }
    })
}

永远不会被调用的 RPScreenRecorder 委托:

func screenRecorder(_ screenRecorder: RPScreenRecorder, didStopRecordingWith previewViewController: RPPreviewViewController?, error: Error?) {
    if let error = error {
        print(error.localizedDescription)
    }
}

【问题讨论】:

    标签: ios swift avassetwriter phasset replaykit


    【解决方案1】:

    我可以通过做两件事来解决这个问题:

    1- 我做的第一件事是将videoURL 的文件路径从:

    // Old Way that was causing some sort of path error
    videoURL = URL(fileURLWithPath: documentsPath.appending(UUID().uuidString + ".mp4"))
    
    // This is what the Old Path looked like. Look at the series of numbers beginning with 506... directly after Documents
    ///var/mobile/Containers/Data/Application/AAEF38A2-7AF1-4A32-A612-296B1584A764/Documents506D36BA-0C27-466A-A0BA-C197481F471A.mp4
    

    // New Way that got the path to work
    let dirPath = "\(documentsPath)/Videos_\(UUID().uuidString).mp4"
    videoURL = URL(fileURLWithPath: dirPath)
    
    // This is what the new path looks like. After Documents there is now a forward slash, the word Videos with an underscore, and then the series of numbers beginning with 506...
    ///var/mobile/Containers/Data/Application/AAEF38A2-7AF1-4A32-A612-296B1584A764/Documents/Videos_506D36BA-0C27-466A-A0BA-C197481F471A.mp4
    

    2 - 我做的第二件事是更改 recorder.startCapture(handler: { (cmSampleBuffer, rpSampleBufferType, err) 中的代码:

    recorder.startCapture(handler: { (cmSampleBuffer, rpSampleBufferType, err) in
    
        if let err = err { return }
    
        if CMSampleBufferDataIsReady(cmSampleBuffer) {
    
            DispatchQueue.main.async {
    
                switch rpSampleBufferType {
                case .video:
    
                    print("writing sample....")
    
                    if self.assetWriter?.status == AVAssetWriter.Status.unknown {
    
                        print("Started writing")
                        self.assetWriter?.startWriting()
                        self.assetWriter?.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(cmSampleBuffer))
                    }
    
                    if self.assetWriter.status == AVAssetWriter.Status.failed {
                        print("StartCapture Error Occurred, Status = \(self.assetWriter.status.rawValue), \(self.assetWriter.error!.localizedDescription) \(self.assetWriter.error.debugDescription)")
                         return
                    }
    
                    if self.assetWriter.status == AVAssetWriter.Status.writing {
                        if self.videoInput.isReadyForMoreMediaData {
                            print("Writing a sample")
                            if self.videoInput.append(cmSampleBuffer) == false {
                                 print("problem writing video")
                            }
                         }
                     }
    
                case .audioMic:
                    if self.audioMicInput.isReadyForMoreMediaData {
                        print("audioMic data added")
                        self.audioMicInput.append(cmSampleBuffer)
                    }
    
                default:
                    print("not a video sample")
                }
            }
    }, completionHandler: { (error) in
    
        if let error = error { return }
    })
    

    这与我遇到的实际问题无关,但如果音频不同步,那么您必须将此代码添加到viewDidLoad。我是从comments section here 那里得到的。

    do {
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .videoRecording, options: [.defaultToSpeaker])
        try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
    } catch {
        #if DEBUG
        print("Setting category to AVAudioSessionCategoryPlayback failed.")
        #endif
    }
    

    如果您需要查找错误代码的含义,可以查看这里https://www.osstatus.com。它帮助我找到 11800 解决这个问题,但不是 17508

    【讨论】:

    • 不,我还没有下载它。为什么它在 iOS 14 中不起作用?
    • 是的,我的兄弟遇到了问题
    • 在哪一行,有什么问题
    猜你喜欢
    • 2015-10-15
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    • 2014-02-08
    • 2021-10-18
    相关资源
    最近更新 更多