如何在Swift应用中播放Socket.io传来的实时INT32 PCM音频数据?
在Swift应用中播放Socket.io获取的实时INT32 PCM音频数据
核心实现思路
要播放这类实时PCM音频,需借助AVAudioEngine和AVAudioPlayerNode搭建音频播放管线,将Socket.io接收的原始字节数据转换为音频引擎可识别的缓冲区,再调度播放。以下是具体步骤和代码示例:
1. 配置音频播放引擎
首先初始化匹配参数的音频格式,搭建AVAudioEngine播放管线:
- 音频参数:4000采样率、2声道、32位整数PCM(交错格式)
- 核心组件:
AVAudioEngine(音频引擎)、AVAudioPlayerNode(负责调度播放缓冲区)
import AVFoundation class AudioStreamPlayer: NSObject { private let audioEngine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() private let targetFormat: AVAudioFormat override init() { // 初始化目标音频格式 guard let format = AVAudioFormat(commonFormat: .pcmFormatInt32, sampleRate: 4000, channels: 2, interleaved: true) else { fatalError("无法创建指定的音频格式") } self.targetFormat = format super.init() setupAudioPipeline() } private func setupAudioPipeline() { // 将播放器节点挂载到音频引擎 audioEngine.attach(playerNode) // 连接播放器节点到混音器(输出到扬声器) audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: targetFormat) do { try audioEngine.start() } catch { print("音频引擎启动失败: \(error.localizedDescription)") } } }
2. 处理Socket.io接收的音频数据
每次从Socket.io收到256字节的INT32 PCM数据后,完成以下操作:
- 数据合法性校验(确保长度为256字节)
- 将
Data转换为AVAudioPCMBuffer - 调度缓冲区到播放器节点播放
在AudioStreamPlayer中添加数据处理方法:
extension AudioStreamPlayer { func handleReceivedPCMData(_ rawData: Data) { // 校验数据长度:256字节对应32个双声道采样帧(每帧8字节=4字节/声道×2声道) guard rawData.count == 256 else { print("数据长度不符合预期,跳过本次数据") return } // 创建对应容量的音频缓冲区 guard let audioBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: 32) else { print("无法创建音频缓冲区") return } audioBuffer.frameLength = 32 // 将原始字节数据复制到缓冲区 let totalBytes = Int(targetFormat.streamDescription.pointee.mBytesPerFrame * audioBuffer.frameLength) rawData.copyBytes(to: audioBuffer.int32ChannelData![0], count: totalBytes) // 调度缓冲区播放(interruptsAtLoop确保实时流的连续性) playerNode.scheduleBuffer(audioBuffer, at: nil, options: .interruptsAtLoop) } }
3. 集成Socket.io接收逻辑
假设使用Socket.IO-Client-Swift库接收数据,在业务代码中完成连接和数据转发:
// 初始化音频播放器 let audioPlayer = AudioStreamPlayer() // 配置Socket.io连接 let socket = SocketIOClient(socketURL: URL(string: "你的Socket服务器地址")!, config: [.compress]) // 监听音频数据事件 socket.on("audioData") { dataArray, _ in guard let pcmData = dataArray.first as? Data else { return } // 在后台线程处理数据,避免阻塞UI DispatchQueue.global(qos: .userInitiated).async { audioPlayer.handleReceivedPCMData(pcmData) } } // 启动连接 socket.connect()
关键注意事项
- 权限配置:iOS 10+需在
Info.plist中添加NSAudioUsageDescription键,填写音频使用说明(如"需要播放实时音频流"),否则会崩溃。 - 字节序兼容性:确保Socket.io传输的INT32数据为小端字节序(iOS默认使用小端,若服务器为大端需额外转换)。
- 实时性优化:若出现播放卡顿,可调整缓冲区调度策略,或通过
scheduleBuffer的completionHandler控制数据流入节奏,避免缓冲区堆积。
内容的提问来源于stack exchange,提问作者seem7teen
相关产品推荐
相关产品推荐

