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

如何使用socket.io发送MediaStream音频数据实现实时语音通话

实时语音通话功能优化实现方案

现有方案问题分析

  • 第一版方案问题:
    1. 每2秒重复调用getUserMedia申请麦克风权限,重复创建媒体流和录制实例,资源消耗高,还可能触发重复权限校验
    2. 2秒的大切片导致端到端延迟至少2s以上,多段音频拼接时容易出现帧错位、间隙,直接导致播放卡顿
  • 第二版方案问题:
    1. 使用了已废弃的navigator.getUserMediaAPI,兼容性差,是直接报错的主要原因
    2. 提取的是Analyser节点的频域特征数据,不是可播放的原始音频编码数据,传输到对端后无法还原为语音,必然出现播放失败
    3. 用requestAnimationFrame每秒发送60次数据,带宽占用极高,完全没有必要

可行优化方案(基于Socket.IO传输)

发送端实现

核心逻辑:仅申请一次麦克风权限,复用媒体流,设置小切片实时发送

// 全局保存实例,避免重复创建
let mediaRecorder = null;
let audioStream = null;

// 仅初始化一次麦克风
async function initAudioCapture() {
  try {
    // 统一用标准API申请权限,开启音频预处理优化通话质量
    audioStream = await navigator.mediaDevices.getUserMedia({ 
      audio: {
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true
      } 
    });
    // 使用opus编码,适配语音场景,低延迟高压缩率
    mediaRecorder = new MediaRecorder(audioStream, { mimeType: 'audio/webm;codecs=opus' });
    // 每100ms输出一次切片,平衡延迟和带宽
    mediaRecorder.ondataavailable = (e) => {
      if (e.data.size > 0) {
        socket.emit('liveAudioToServer', e.data);
      }
    };
    mediaRecorder.start(100); // 传入timeslice参数,每100ms触发一次dataavailable
  } catch (err) {
    console.log('音频采集初始化失败', err);
  }
}

// 通话开始时调用一次即可
initAudioCapture();

接收端实现

核心逻辑:用MediaSource实时拼接音频片段,流式播放,避免拼接间隙

const audio = document.createElement('audio');
audio.autoplay = true;
const mediaSource = new MediaSource();
audio.src = URL.createObjectURL(mediaSource);
let sourceBuffer = null;
// 缓存待播放的切片队列,避免网络波动导致的播放卡顿
const bufferQueue = [];

mediaSource.addEventListener('sourceopen', () => {
  sourceBuffer = mediaSource.addSourceBuffer('audio/webm;codecs=opus');
  sourceBuffer.mode = 'sequence';
  // 缓存队列有数据就追加到缓冲区
  sourceBuffer.addEventListener('updateend', () => {
    if (bufferQueue.length > 0 && !sourceBuffer.updating) {
      const chunk = bufferQueue.shift();
      sourceBuffer.appendBuffer(chunk);
    }
  });
});

// 接收服务端转发的音频切片
socket.on('liveAudioFromServer', (chunk) => {
  const uint8Chunk = new Uint8Array(chunk);
  if (sourceBuffer && !sourceBuffer.updating) {
    sourceBuffer.appendBuffer(uint8Chunk);
  } else {
    bufferQueue.push(uint8Chunk);
  }
});

额外优化建议

  • 多人通话场景如果对延迟要求更高,优先使用WebRTC方案,可直接实现端到端或经SFU中转的实时音视频传输,不需要自行处理音频切片、编码、丢包补偿等逻辑,端到端延迟可控制在200ms以内
  • 可根据网络抖动情况动态调整切片大小,网络差的时候适当增大切片,避免丢包导致卡顿
  • 服务端仅需按房间ID做广播转发即可,不需要额外处理音频数据

内容的提问来源于stack exchange,提问作者sponge bobo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 06:54:03