后端websocket返回音频流前端如何流畅播放?

后端通过socket返回base64音频流,前端需要根据流来实时播放音频,问题是websocket返回速度特别快导致前端音频播放速度过快或卡顿,如何处理?

阅读 482
1 个回答

问题分析

WebSocket 发送速度快于播放速度:

  • WebSocket 是实时推送的,可能会在短时间内传输大量数据。
  • 如果前端不做节流或缓冲处理,音频播放可能会出现卡顿或不稳定。

Base64 解码和播放开销较大:

  • Base64 编码的数据比原始音频大约多 33%,解码也会增加 CPU 开销。

解决方案

1. 使用缓冲队列控制播放节奏

前端维护一个音频帧缓冲队列,WebSocket 接收到数据后先放入队列,由播放器定时从队列中取出数据播放。

const audioQueue = [];
let isPlaying = false;

socket.onmessage = async (event) => {
  const base64Data = event.data;
  audioQueue.push(base64Data);
  if (!isPlaying) playFromQueue();
};

async function playFromQueue() {
  if (audioQueue.length === 0) {
    isPlaying = false;
    return;
  }

  isPlaying = true;
  const base64 = audioQueue.shift();
  const audioBuffer = await decodeBase64ToAudioBuffer(base64);
  playAudioBuffer(audioBuffer);

  // 等待当前音频播放完成后再播放下一个
  setTimeout(() => {
    playFromQueue();
  }, audioBuffer.duration * 1000);
}

2. 使用 AudioContext 进行解码和播放

const audioContext = new AudioContext();

async function decodeBase64ToAudioBuffer(base64) {
  const binary = atob(base64);
  const len = binary.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return await audioContext.decodeAudioData(bytes.buffer);
}

function playAudioBuffer(buffer) {
  const source = audioContext.createBufferSource();
  source.buffer = buffer;
  source.connect(audioContext.destination);
  source.start();
}

3. 节流机制(可选)

如果后端推送速度过快,可以在前端设置最大队列长度,超过后丢弃旧数据或暂停接收:

const MAX_QUEUE_LENGTH = 50;
if (audioQueue.length > MAX_QUEUE_LENGTH) {
  audioQueue.shift(); // 丢弃最早的帧
}

进阶优化补充

  • 使用 Web Audio API 的 MediaSource(适合更复杂的流式音频播放)。
  • 后端改为推送原始 PCM 或 Opus 格式(避免 Base64 编码开销)。
  • 使用 WebAssembly 解码器(提升性能,适用于高频率音频流)。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题