iOS 中的屏幕录制问题

Trouble with screen recording in iOS

我在使用 ReplayKit

进行屏幕录制时遇到了一些问题

所以,奇怪的事情开始于 startCapturehandler 闭包

每次开始抓包,sampleType.video时,调用self.videoWriter?.startSession,瞬间videoWriter.status变为failure (3)

在我完成录制之前,此行为不会抛出任何异常:

Optional("Cannot Encode")

Optional("Optional(Error Domain=AVFoundationErrorDomain Code=-11834 "Cannot Encode" UserInfo={AVErrorMediaSubTypeKey=(\n 778924083\n), NSLocalizedFailureReason=The encoder required for this media cannot be found., AVErrorMediaTypeKey=soun, NSLocalizedDescription=Cannot Encode })") Optional("<AVAssetWriter: 0x283ef0920, outputURL = file:///var/mobile/Containers/Data/Application/A85826BA-DCBE-428B-AB9E-84D77F234FF6/Documents/Replays/capture92949FA1-F65D-48A0-8140-207BC892C204.mp4, outputFileType = public.mpeg-4>")

Error Domain=PHPhotosErrorDomain Code=3302 "(null)"

这是我的代码:

let settings = [
    AVFormatIDKey: Int(AudioFormat.kAudioFormatMPEGLayer3.rawValue),
    AVNumberOfChannelsKey: 2,
    AVSampleRateKey: 44100,
    AVEncoderBitRateKey: 128000
]
let audioInput = AVAssetWriterInput(
    mediaType: .audio,
    outputSettings: settings
)

audioInput.expectsMediaDataInRealTime = true
videoWriter.add(audioInput)

self.videoWriterInput = audioInput
self.videoWriter = videoWriter
let videoSettings: [String: Any] = [
   AVVideoCodecKey: AVVideoCodecType.h264,
   AVVideoWidthKey: passingSize.width,
   AVVideoHeightKey: passingSize.height
]

self.videoWriterInput = AVAssetWriterInput(
    mediaType: AVMediaType.video,
    outputSettings: videoSettings
)

self.appAudioWriterInput = AVAssetWriterInput(
    mediaType: .audio,
    outputSettings: settings
)
self.videoWriter.add(appAudioWriterInput!)


self.recorder.startCapture(
    handler: { (sampleBuffer, sampleType, passedError) in 
       switch sampleType {
       case .video:
           self.handleSampleBuffer(sampleBuffer: sampleBuffer)
       case .audioApp:
           self.add(sample: sampleBuffer, to: appAudioWriterInput)
       default:
           break
       }
   },
   completionHandler: { error in
       if let error = error {
          print(">>> Error!")
          errorHandler(error: error)
       }
    }
)

private func handleSampleBuffer(sampleBuffer: CMSampleBuffer) {
    if videoWriter?.status == AVAssetWriter.Status.unknown {
        serialQueue.async {
            if !self.isRecording {
                self.videoWriter?.startWriting()
                self.videoWriter?.startSession(
                    atSourceTime: .zero
                )
                self.isRecording = true
            }
        }
    } else if videoWriter?.status == AVAssetWriter.Status.writing &&
        videoWriterInput?.isReadyForMoreMediaData == true {
            serialQueue.async {
                self.videoWriterInput?.append(sampleBuffer)
            }
    }
}

我不熟悉 iOS 开发,我认为我遗漏了一些明显的东西。

我尝试过的事情:

  1. 清理 XCode 缓存
  2. 运行 IPhone 和模拟器
  3. 上的应用程序
  4. 录制前检查PHPhotoLibrary.authorizationStatus()AVCaptureDevice.authorizationStatus
  5. 试验音频和视频录制设置

这是我对 Info.plist

的修改
<key>NSPhotoLibraryAddUsageDescription</key>
<string>The app requires access to Photos to save media to it.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>The app requires access to Photos to interact with it.</string>
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>

解决方案非常简单:我只是将错误的变量传递给 self.videoWriterInput

正确的初始化应该是这样的:

let screenSize: CGRect = UIScreen.main.bounds
        
let videoSettings: [String: Any] = [
    AVVideoCodecKey: AVVideoCodecType.h264,
    AVVideoWidthKey: screenSize.width,
    AVVideoHeightKey: screenSize.height,
]

let newVideoWriterInput = AVAssetWriterInput(
    mediaType: AVMediaType.video,
    outputSettings: videoSettings
)

        
newVideoWriterInput.expectsMediaDataInRealTime = true
self.videoWriter?.add(newVideoWriterInput)
self.videoWriterInput = newVideoWriterInput // THE ERROR WAS HERE
        
let settings: [String : Any] = [
    AVNumberOfChannelsKey : 2,
    AVFormatIDKey : kAudioFormatMPEG4AAC,
    AVSampleRateKey: 44100,
    AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]

let audioInput = AVAssetWriterInput(
    mediaType: .audio,
    outputSettings: settings
)

audioInput.expectsMediaDataInRealTime = true
self.videoWriter?.add(audioInput)
self.appAudioWriterInput = audioInput