You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:52:38