I've got some trouble with screen recording using ReplayKit
So, the strangeness begins in the startCapture's handler closure
Every time it starts to capture and sampleType is .video, the self.videoWriter?.startSession is called, and instantaneously videoWriter.status becomes failure (3)
This behaviour is not throwing any exceptions until I finish recording:
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)"
Here is my code:
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)
}
}
}
I'm not familiar with iOS development and I think that I'm missing something obvious.
Things I've tried:
- Clean XCode caches
- Run app on both IPhone and Simulator
- Check
PHPhotoLibrary.authorizationStatus()andAVCaptureDevice.authorizationStatusbefore recording - Experiment with audio and video recording settings
Here is my modifications to 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/>
The solution was pretty simple: I just passed wrong variable to
self.videoWriterInputProper initialization should be something like this: