【问题标题】:Replaykit asset writer status fails randomlyReplaykit 资产写入器状态随机失败
【发布时间】:2023-03-08 10:35:01
【问题描述】:

用例

我正在使用 iOS 11 Replaykit 框架尝试从屏幕录制帧,以及来自应用程序和麦克风的音频。

问题

随机地,当我调用我的 .append(sampleBuffer) 时,得到 AVAssetWriterStatus.failed 并显示 AssetWriter.Error

Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save" UserInfo={NSLocalizedRecoverySuggestion=Try saving again., NSLocalizedDescription=Cannot Save, NSUnderlyingError=0x1c044c360 {Error Domain=NSOSStatusErrorDomain Code=-12412 "(null)"}}

附带问题:我在应用程序录制时播放重复声音以尝试验证音频是否已录制,但当我开始录制时声音停止,即使我的视频和外部音频麦克风正在工作。

如果您需要更多信息,我也可以将其他代码上传到 GitHub。

想法

由于有时会保存录制文件(我可以导出到照片应用程序并重播视频),我认为这一定是异步问题,我加载的内容无序。如果你看到了请告诉我!

我想我将尝试保存到我自己的 /Documents 文件夹中,而不是直接保存到 /Documents,以防出现奇怪的权限错误。虽然我相信这会导致一致的错误,而不是有时会出错。

我的代码

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

    let directoryContents = try! FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: firstDocumentDirectoryPath), includingPropertiesForKeys: nil, options: [])
    print(directoryContents)

    videoURL = URL(fileURLWithPath: firstDocumentDirectoryPath.appending("/\(arc4random()).mp4"))

    print(videoURL.absoluteString)

    assetWriter = try! AVAssetWriter(url: videoURL, fileType: AVFileType.mp4)

    let compressionProperties:[String:Any] = [...]
    let videoSettings:[String:Any] = [...]
    let audioSettings:[String:Any] = [...]

    videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
    audioMicInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
    audioAppInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)

    guard let assetWriter = assetWriter else { return }
    guard let videoInput = videoInput else { return }
    guard let audioAppInput = audioAppInput else { return }
    guard let audioMicInput = audioMicInput else { return }

    videoInput.mediaTimeScale = 60
    videoInput.expectsMediaDataInRealTime = true
    audioMicInput.expectsMediaDataInRealTime = true
    audioAppInput.expectsMediaDataInRealTime = true

    if assetWriter.canAdd(videoInput) {
        assetWriter.add(videoInput)
    }

    if assetWriter.canAdd(audioAppInput) {
        assetWriter.add(audioAppInput)
    }

    if assetWriter.canAdd(audioMicInput) {
        assetWriter.add(audioMicInput)
    }

    assetWriter.movieTimeScale = 60

    RPScreenRecorder.shared().startCapture(handler: recordingHandler(sampleBuffer:sampleBufferType:error:)) { (error:Error?) in
        if error != nil {
            print("RPScreenRecorder.shared().startCapture: \(error.debugDescription)")
        } else {
            print("start capture complete")
        }
    }
}

func recordingHandler (sampleBuffer:CMSampleBuffer, sampleBufferType:RPSampleBufferType, error:Error?){
    if error != nil {
        print("recordingHandler: \(error.debugDescription)")
    }

    if CMSampleBufferDataIsReady(sampleBuffer) {
        guard let assetWriter = assetWriter else { return }
        guard let videoInput = videoInput else { return }
        guard let audioAppInput = audioAppInput else { return }
        guard let audioMicInput = audioMicInput else { return }

        if assetWriter.status == AVAssetWriterStatus.unknown {
            print("AVAssetWriterStatus.unknown")
            if !assetWriter.startWriting() {
                return
            }
            assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
        }

        if assetWriter.status == AVAssetWriterStatus.failed {
            print("AVAssetWriterStatus.failed")
            print("assetWriter.error: \(assetWriter.error.debugDescription)")
            return
        }

        if sampleBufferType == RPSampleBufferType.video {
            if videoInput.isReadyForMoreMediaData {
                print("=appending video data")
                videoInput.append(sampleBuffer)
            }
        }

        if sampleBufferType == RPSampleBufferType.audioApp {
            if audioAppInput.isReadyForMoreMediaData {
                print("==appending app audio data")
                audioAppInput.append(sampleBuffer)
            }
        }

        if sampleBufferType == RPSampleBufferType.audioMic {
            if audioMicInput.isReadyForMoreMediaData {
                print("===appending mic audio data")
                audioMicInput.append(sampleBuffer)
            }
        }
    }
}

func stopRecording() {
    RPScreenRecorder.shared().stopCapture { (error) in
        guard let assetWriter = self.assetWriter else { return }
        guard let videoInput = self.videoInput else { return }
        guard let audioAppInput = self.audioAppInput else { return }
        guard let audioMicInput = self.audioMicInput else { return }

        if error != nil {
            print("recordingHandler: \(error.debugDescription)")
        } else {
            videoInput.markAsFinished()
            audioMicInput.markAsFinished()
            audioAppInput.markAsFinished()

            assetWriter.finishWriting(completionHandler: {
                print(self.videoURL)
                self.saveToCameraRoll(URL: self.videoURL)
            })
        }
    }
}

【问题讨论】:

  • 您找到适合您的解决方案了吗?
  • 您找到解决方案了吗?我有完全相同的问题。 WLee 的建议对我不起作用。
  • 不幸的是没有:(也许看看是否有人为它提交了雷达或提交了一个新的
  • 感谢您的快速回复。会做。 My issuse is only different in that my video records perfectly every time the user chooses "Record Screen Only" but when "Record Screen & Microphone" is selected it always fails on the first attempt then randomly succeeds on attempts after that.我认为您可能对异步想法有所了解。我在我的应用程序中使用多线程。
  • @stanley 视频录制成功,但音频录制无效。请提出建议。

标签: ios swift avfoundation ios11 replaykit


【解决方案1】:

我让它工作了。我相信这确实是一个异步问题。问题,出于某种原因,您必须确保

assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))

严格按顺序发生。

从此更改您的代码:

if assetWriter.status == AVAssetWriterStatus.unknown {
    print("AVAssetWriterStatus.unknown")
    if !assetWriter.startWriting() {
        return
    }
    assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
}

到这里:

DispatchQueue.main.async { [weak self] in
    if self?.assetWriter.status == AVAssetWriterStatus.unknown {
        print("AVAssetWriterStatus.unknown")
        if !self?.assetWriter.startWriting() {
            return
        }
        self?.assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
    }
}

或者更好的是,CMSampleBufferDataIsReady 内的整个块,即。

if CMSampleBufferDataIsReady(sampleBuffer) {
    DispatchQueue.main.async { [weak self] in
        ...
        ...
    }
}

让我知道它是否有效!

【讨论】:

  • 这解决了开始录制的问题。代码对我来说几乎相同,因此添加了“DispatchQueue.main.async”并开始工作。
  • 当我录制视频时。录音不工作。请提出建议。
  • @NileshParmar 您还需要音频方面的帮助吗?
【解决方案2】:

我有类似的问题。我通过首先检查 videoURL 文件是否已经存在来修复它。如果是这样,请先将其删除,然后错误就会消失。

【讨论】:

  • 由于我将回放视频的结果保存在一个临时目录中,在创建新视频之前,请检查该文件是否已经存在。如果是这样,请将其删除。
  • 由于我将回放视频的结果保存在一个临时目录中,在创建新视频之前,请检查该文件是否已经存在。如果是这样,请将其删除。如果 FileManager().fileExists(atPath: Constants.TempPath) {试试! FileManager().removeItem(atPath: Constants.TempPath)} 另外,添加前请确认是否可以添加视频: ifassetWriter.canAdd(videoInput){assetWriter.add(videoInput)}
猜你喜欢
  • 1970-01-01
  • 2020-02-03
  • 1970-01-01
  • 2019-04-25
  • 2015-08-12
  • 2013-03-15
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多