AVAudioPlayerNode播放音频出现失真问题的技术求助
AVAudioPlayerNode 音频透传失真问题
我将AVAudioPlayerNode挂载到AVAudioEngine上,通过scheduleBuffer()方法为playerNode提供采样缓冲区,但输出音频出现失真和杂音,虽仍可听清,但未实现预期的直接“透传”效果。
初始相关代码如下:
let myBufferFormat = AVAudioFormat(standardFormatWithSampleRate: 48000, channels: 2) // Configure player node let playerNode = AVAudioPlayerNode() audioEngine.attach(playerNode) audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: myBufferFormat) // Provide audio buffers to playerNode for await buffer in mySource.streamAudio() { await playerNode.scheduleBuffer(buffer) }
上述代码中,mySource.streamAudio()从ScreenCaptureKit的SCStreamDelegate实时获取音频,音频缓冲区以CMSampleBuffer形式到达,转换为AVAudioPCMBuffer后通过AsyncStream传递给音频引擎,已验证转换后的缓冲区有效。
推测可能是缓冲区到达速度不足?一份约25000帧的图表显示inputNode会周期性插入“零”帧片段,这似乎是导致失真的原因。
编辑补充:即使移除流水线中的AsyncStream,在ScreenCaptureKit回调中直接处理缓冲区,失真问题仍存在。以下是可直接运行的完整示例(核心部分为didOutputSampleBuffer):
class Recorder: NSObject, SCStreamOutput { private let audioEngine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() private var stream: SCStream? private let queue = DispatchQueue(label: "sampleQueue", qos: .userInitiated) func setupEngine() { let format = AVAudioFormat(standardFormatWithSampleRate: 48000, channels: 2) audioEngine.attach(playerNode) // playerNode --> mainMixerNode --> outputNode --> speakers audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: format) audioEngine.prepare() try? audioEngine.start() playerNode.play() } func startCapture() async { // Capture audio from Safari let availableContent = try! await SCShareableContent.excludingDesktopWindows(true, onScreenWindowsOnly: false) let display = availableContent.displays.first! let app = availableContent.applications.first(where: {$0.applicationName == "Safari"})! let filter = SCContentFilter(display: display, including: [app], exceptingWindows: []) let config = SCStreamConfiguration() config.capturesAudio = true config.sampleRate = 48000 config.channelCount = 2 stream = SCStream(filter: filter, configuration: config, delegate: nil) try! stream!.addStreamOutput(self, type: .audio, sampleHandlerQueue: queue) try! stream!.addStreamOutput(self, type: .screen, sampleHandlerQueue: queue) // To prevent warnings try! await stream!.startCapture() } func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) { switch type { case .audio: let pcmBuffer = createPCMBuffer(from: sampleBuffer)! playerNode.scheduleBuffer(pcmBuffer, completionHandler: nil) default: break // Ignore video frames } } func createPCMBuffer(from sampleBuffer: CMSampleBuffer) -> AVAudioPCMBuffer? { var ablPointer: UnsafePointer<AudioBufferList>? try? sampleBuffer.withAudioBufferList { audioBufferList, blockBuffer in ablPointer = audioBufferList.unsafePointer } guard let audioBufferList = ablPointer, let absd = sampleBuffer.formatDescription?.audioStreamBasicDescription, let format = AVAudioFormat(standardFormatWithSampleRate: absd.mSampleRate, channels: absd.mChannelsPerFrame) else { return nil } return AVAudioPCMBuffer(pcmFormat: format, bufferListNoCopy: audioBufferList) } } let recorder = Recorder() recorder.setupEngine() Task { await recorder.startCapture() }
内容的提问来源于stack exchange,提问作者Hundley
相关产品推荐
相关产品推荐

