iOS 中的屏幕录制问题
Trouble with screen recording in iOS
我在使用 ReplayKit
进行屏幕录制时遇到了一些问题
所以,奇怪的事情开始于 startCapture
的 handler
闭包
每次开始抓包,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 开发,我认为我遗漏了一些明显的东西。
我尝试过的事情:
- 清理 XCode 缓存
- 运行 IPhone 和模拟器
上的应用程序
- 录制前检查
PHPhotoLibrary.authorizationStatus()
和AVCaptureDevice.authorizationStatus
- 试验音频和视频录制设置
这是我对 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
我在使用 ReplayKit
所以,奇怪的事情开始于 startCapture
的 handler
闭包
每次开始抓包,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 开发,我认为我遗漏了一些明显的东西。
我尝试过的事情:
- 清理 XCode 缓存
- 运行 IPhone 和模拟器 上的应用程序
- 录制前检查
PHPhotoLibrary.authorizationStatus()
和AVCaptureDevice.authorizationStatus
- 试验音频和视频录制设置
这是我对 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