Socket.IO+Ionic4实时音频流服务ALSA崩溃问题修复咨询
修复实时音频流服务ALSA崩溃问题
从你的报错信息和代码来看,核心问题是音频播放资源泄漏——服务端重复创建播放实例却未清理旧资源,导致ALSA/PulseAudio的设备连接被耗尽,最终无法打开音频设备。同时你的播放逻辑也没有正确处理实时传入的音频帧,而是在重复播放初始缓冲区内容。下面是具体的修复方案:
1. 问题根源分析
- 原服务端代码在
counter == 0时创建了一个无限循环的setTimeout,每次循环都会新建play实例,旧实例未被销毁,持续占用音频设备资源,直到设备无法处理新连接。 - 客户端发送的是
PCM_16BIT格式的Int16Array数据,服务端直接用Float32Array接收,未做格式转换,会导致音频数据失真,也可能触发播放异常。 - 没有在客户端断开时清理播放资源,残留的资源会持续占用设备。
2. 修复后的服务端代码
let app = require('express')(); let http = require('http').Server(app); let io = require('socket.io')(http); let audioContext = require('audio-context')({ sampleRate: 8000, latencyHint: 'balanced' }); let buff = require('audio-buffer'); // 匹配客户端的缓冲区配置 const bufferSize = 8192; var audioBuffer = new buff(audioContext, { length: bufferSize, sampleRate: 8000, numberOfChannels: 1 }); // 复用播放源,避免重复创建资源 let playbackSource = null; // 将客户端的Int16格式PCM转换为Web Audio需要的Float32格式 function int16ToFloat32(inputArray) { let outputArray = new Float32Array(inputArray.length); for (let i = 0; i < inputArray.length; i++) { // Int16范围是-32768到32767,转换为Float32的-1到1区间 outputArray[i] = inputArray[i] / 32768; } return outputArray; } io.on('connection', (socket) => { console.log('客户端已连接'); socket.on('message', (data) => { console.log(data); }); socket.on('audioinput', (data) => { // 转换音频数据格式 const float32Data = int16ToFloat32(new Int16Array(data)); // 更新缓冲区内容 audioBuffer.copyToChannel(float32Data, 0, 0); // 停止旧的播放源(如果存在) if (playbackSource) { playbackSource.stop(); } // 创建新的播放源并播放当前缓冲区 playbackSource = audioContext.createBufferSource(); playbackSource.buffer = audioBuffer; playbackSource.connect(audioContext.destination); playbackSource.start(); // 播放结束后释放资源 playbackSource.onended = () => { playbackSource = null; }; }); socket.on('disconnect', () => { console.log('客户端已断开'); // 客户端断开时清理播放资源 if (playbackSource) { playbackSource.stop(); playbackSource = null; } }); }); var port = process.env.PORT || 6500; http.listen(port, () => { console.log('服务启动,监听端口 ' + port); });
3. 关键修改说明
- 格式转换:新增
int16ToFloat32函数,将客户端发送的PCM_16BIT数据转换为Web Audio API要求的Float32格式,避免数据错误。 - 资源复用与清理:不再创建无限循环的定时器,每次收到音频数据时停止旧播放源,创建新播放源并播放当前缓冲区,播放结束后主动释放资源。
- 断开清理:客户端断开连接时,主动停止播放源并释放资源,避免残留资源占用音频设备。
4. 额外优化建议
- 调整缓冲区大小:8192的缓冲区在8000采样率下时长约1秒,可适当减小到4096或2048,降低延迟的同时减少单次播放的资源占用。
- 实现音频队列:如果需要更流畅的播放,可以构建音频帧队列,当队列有足够帧时连续播放,避免频繁创建播放源。
- 检查音频服务状态:确保服务器上的PulseAudio服务正常运行,可通过
pulseaudio --start启动服务,或切换到ALSA直接输出。 - 添加错误捕获:给
audioContext和播放源添加错误监听,避免未处理的错误导致进程崩溃。
内容的提问来源于stack exchange,提问作者Азат Алиуллин
相关产品推荐
相关产品推荐

