编辑:这可能只与那些使用 AVAssetWriter 使用样本缓冲区录制视频的人相关——当直接操作和渲染相机的逐帧输出时,这要好几个数量级。
我为此苦苦挣扎了一段时间,因为我正在构建一个复杂的应用程序,它必须在 (1) 播放视频(使用来自其他应用程序的音频)和 (2) 播放和录制视频(使用来自其他应用程序的音频)之间进行委派.在我的上下文中,如果您有一个播放视频的 AVPlayer 对象,以及一个从输入设备录制视频的 AVCaptureSession,您必须在播放视频之前添加以下内容:
do {
// MARK: - AVAudioSession
try AVAudioSession.sharedInstance().setActive(false, options: [])
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
try AVAudioSession.sharedInstance().setActive(true, options: [])
} catch let error {
print("\(#file)/\(#line) - Error setting the AVAudioSession for mixing audio play back: \(error.localizedDescription as Any).")
}
接下来,要录制带有/不带有来自其他应用的音频的视频和/或播放带有音频的视频的 AVPlayer 对象,您必须执行以下操作:
准备音频
// Prepare the audio session to allow simultaneous audio-playback while recording video
do {
// MARK: - AVAudioSession
try AVAudioSession.sharedInstance().setActive(false, options: [.notifyOthersOnDeactivation])
} catch let error {
print("\(#file)/\(#line) - Error deactivating AVAudioSession: \(error.localizedDescription as Any).")
}
do {
// MARK: - AVAudioSession
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.mixWithOthers, .defaultToSpeaker, .allowBluetoothA2DP])
} catch let error {
print("\(#file)/\(#line) - Error setting the AVAudioSession category: \(error.localizedDescription as Any).")
}
do {
// MARK: - AVAudioSession
try AVAudioSession.sharedInstance().setActive(true, options: [])
} catch let error {
print("\(#file)/\(#line) - Error activating the AVAudioSession: \(error.localizedDescription as Any).")
}
使用 AVCaptureSession 设置音频设备输入/输出
// Setup the audio device input
setupAudioDeviceInput { (error: NGError?) in
if error == nil {
print("\(#file)/\(#line) - Successfully added audio-device input.")
} else {
print("\(#file)/\(#line) - Error: \(error?.localizedDescription as Any)")
}
}
// Setup the audio data output
setupAudioDataOutput { (error: NGError?) in
if error == nil {
print("\(#file)/\(#line) - Successfully added audio-data output.")
} else {
print("\(#file)/\(#line) - Error: \(error?.localizedDescription as Any)")
}
}
当这些方法被分解时,它们本质上是:
/// (1) Initializes the AVCaptureDevice for audio, (2) creates the associated AVCaptureDeviceInput for audio, and (3) adds the audio device input to the AVCaptureSession.
/// - Parameter error: An optional NGError object returned if the setup for the audio device input fails.
func setupAudioDeviceInput(completionHandler: @escaping (_ error: NGError?) -> ()) {
// Setup the AVCaptureDevice for audio input
self.audioCaptureDevice = AVCaptureDevice.default(for: .audio)
// Unwrap the AVCaptureDevice for audio input
guard audioCaptureDevice != nil else {
print("\(#file)/\(#line) - Couldn't unwrap the AVCaptureDevice for audio.")
return
}
do {
/// Create the AVCaptureDeviceInput for the AVCaptureDevice for audio
self.audioCaptureDeviceInput = try AVCaptureDeviceInput(device: audioCaptureDevice)
// Add the AVCaptureDeviceInput for the audio
if self.captureSession.canAddInput(self.audioCaptureDeviceInput) {
self.captureSession.addInput(self.audioCaptureDeviceInput)
// Pass the values in the completion handler
completionHandler(nil)
} else {
// MARK: - NGError
let error = NGError(message: "\(#file)/\(#line) - Couldn't add the AVCaptureDeviceInput to the capture session.")
// Pass the values in the completion handler
completionHandler(error)
}
} catch let error {
// MARK: - NGError
let error = NGError(message: "\(#file)/\(#line) - Error setting up audio input for the capture session \(error.localizedDescription as Any)")
// Pass the values in the completion handler
completionHandler(error)
}
}
/// (1) Initializes the AVCaptureAudioDataOutput, (2) sets its AVCaptureAudioDataOutputSampleBufferDelegate and adds it to the AVCaptureSession.
/// - Parameter error: An optional NGError object returned if the setup fails.
func setupAudioDataOutput(completionHandler: @escaping (_ error: NGError?) -> ()) {
// Setup the AVCaptureAudioDataOutput
self.audioDataOutput = AVCaptureAudioDataOutput()
// Determine if the AVCaptureSession can add the audio data output
if self.captureSession.canAddOutput(self.audioDataOutput) {
// Setup the AVCaptureAudioDataOutput's AVCaptureAudioDataOutputSampleBufferDelegate and add it to the AVCaptureSession
self.audioDataOutput.setSampleBufferDelegate(self, queue: self.outputQueue)
self.captureSession.addOutput(self.audioDataOutput)
// Pass the values to the completion handler
completionHandler(nil)
} else {
// MARK: - NGError
let error = NGError(message: "\(#file)/\(#line) - Couldn't add the AVCaptureAudioDataOutput to the AVCaptureSession.")
// Pass the values to the completion handler
completionHandler(error)
}
}
设置 AVAssetWriter
准备好所有这些后,您需要设置和配置 AVAssetWriter 以开始将视频数据写入文件。
移除音频输入和输出
// Remove the audio device input and its audio data output.
if let audioInput = audioCaptureDeviceInput, let audioOutput = audioDataOutput {
captureSession.removeInput(audioInput)
captureSession.removeOutput(audioOutput)
} else {
print("\(#file)/\(#line) - Couldn't remove the AVCaptureDeviceInput for audio and the AVCaptureAudioDataOutput.")
}
确保在处理完视频/音频数据后将 AVAssetWriter 的视频和音频输入标记为已完成。