能否在ScreenCaptureKit录屏时用AVAudioEngine处理音频并停止录屏后恢复原音?
解决ScreenCaptureKit录屏时音频效果处理问题
你的核心需求是录屏时捕获系统音频并添加效果播放,停止录屏后恢复原音。现有代码仅做了buffer传递,缺少AVAudioEngine链路配置、音频会话管理和原音频静音处理,导致无法正常工作。以下是完整解决方案:
1. 初始化音频处理组件
提前创建AVAudioEngine相关实例,搭建好包含效果器的音频链路:
import AVFoundation import ScreenCaptureKit class ScreenRecorder: NSObject, SCStreamOutput { private let audioEngine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() private let pitchEffect = AVAudioUnitTimePitch() private var originalAudioSessionConfig: (category: AVAudioSession.Category, options: AVAudioSession.CategoryOptions)? override init() { super.init() // 配置变调效果(示例:升高200音分,约2个半音) pitchEffect.pitch = 200 // 搭建音频链路:playerNode → 变调效果器 → 引擎主混音器 audioEngine.attach(playerNode) audioEngine.attach(pitchEffect) audioEngine.connect(playerNode, to: pitchEffect, format: nil) audioEngine.connect(pitchEffect, to: audioEngine.mainMixerNode, format: nil) } }
2. 录屏启动前配置音频会话
启动录屏前修改音频会话,避免原音频与处理后的音频重叠,同时确保ScreenCaptureKit能正常捕获音频:
func startRecording() async throws { let audioSession = AVAudioSession.sharedInstance() // 保存原音频会话配置,用于停止时恢复 originalAudioSessionConfig = (audioSession.category, audioSession.categoryOptions) // 设置音频会话为播放+录制模式,静音原系统输出,只播放处理后的音频 try audioSession.setCategory(.playAndRecord, options: [.mixWithOthers, .defaultToSpeaker]) try audioSession.activate() // 启动音频引擎 try audioEngine.start() // 配置SCStream(仅展示音频相关配置) let streamConfig = SCStreamConfiguration() streamConfig.capturesAudio = true // 匹配音频引擎的采样率,避免格式不兼容 streamConfig.audioSampleRate = audioEngine.inputNode.inputFormat(forBus: 0).sampleRate // 后续SCStream初始化、启动逻辑... }
3. 修正音频SampleBuffer处理逻辑
在stream(_:didOutputSampleBuffer:of:)中正确转换并播放音频buffer:
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of outputType: SCStreamOutputType) { guard sampleBuffer.isValid else { return } switch outputType { case .screen: // 屏幕帧处理逻辑... case .audio: guard let pcmBuffer = convertCMSampleBufferToPCMBuffer(sampleBuffer) else { return } // 确保playerNode处于播放状态 if !playerNode.isPlaying { playerNode.play() } playerNode.scheduleBuffer(pcmBuffer, completionHandler: nil) default: break } } // 工具方法:将CMSampleBuffer转换为AVAudioPCMBuffer private func convertCMSampleBufferToPCMBuffer(_ sampleBuffer: CMSampleBuffer) -> AVAudioPCMBuffer? { guard let formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer), let audioFormat = AVAudioFormat(cmAudioFormatDescription: formatDesc), let pcmBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount(CMSampleBufferGetNumSamples(sampleBuffer))) else { return nil } pcmBuffer.frameLength = pcmBuffer.frameCapacity guard CMSampleBufferCopyPCMDataIntoAudioBufferList(sampleBuffer, at: 0, frameCount: Int(pcmBuffer.frameLength), into: pcmBuffer.mutableAudioBufferList) else { return nil } return pcmBuffer }
4. 停止录屏时恢复原音频状态
停止录屏后清理音频组件,恢复系统原音频配置:
func stopRecording() { // 停止音频播放并重置引擎 playerNode.stop() audioEngine.stop() audioEngine.reset() // 恢复原音频会话配置 guard let originalConfig = originalAudioSessionConfig else { return } Task { let audioSession = AVAudioSession.sharedInstance() try? audioSession.setCategory(originalConfig.category, options: originalConfig.options) try? audioSession.deactivate(options: .notifyOthersOnDeactivation) } // 停止SCStream逻辑... }
关键注意点
- 格式匹配:SCStream的音频采样率必须与AVAudioEngine的格式一致,否则会出现播放卡顿或无声问题。
- 音频会话冲突:必须通过
playAndRecord类别配合defaultToSpeaker选项,实现原音频静音、仅播放处理后音频的效果。 - 资源清理:停止录屏时务必重置音频引擎并恢复原会话,避免影响后续系统音频播放。
内容的提问来源于stack exchange,提问作者Babitha shetty k
相关产品推荐
相关产品推荐

