代码之家  ›  专栏  ›  技术社区  ›  Lance Samaria

RPScreenRecorder.shared().startCapture无法写入/一直失败

  •  0
  • Lance Samaria  · 技术社区  · 5 年前

    当尝试使用ReplayKit录制和保存音频/视频时,我总是遇到错误。我在用

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

    当我设置我已经使用的filePath时 URL(fileURLWithPath: ) .文件扩展名和 AVFileType 两者都是 .mp4 。我检查文件是否已存在于 FileManager 如果是这样,我将其删除: do { try FileManager.default.removeItem(at: videoURL) } 。我试图将路径本身更改为“库/缓存/”,如 @florianSAP answer 这不起作用。

    这里有三个错误:

    // 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,无法执行操作 complete可选(错误域=AVFoundationError域代码=-11800 “操作无法完成” UserInfo={NSLocalizedFailure原因=发生未知错误(-17508), NSLocalizedDescription=操作无法完成, NSUnderlyingError=0x2833a93b0{错误域=NSOSS状态错误域 代码=-17508“(空)”}})

    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)
        }
    }
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Lance Samaria    5 年前

    我通过做两件事解决了这个问题:

    1-我做的第一件事就是改变 videoURL s文件来源路径:

    // 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 .