纯 websocket 无法直接实现低延迟对讲,必须与 webaudio 深度协同并绕开浏览器默认音频处理链,端到端延迟需控制在 80–150ms;需直采原始 pcm、精简 websocket 二进制帧结构、精确调度 webaudio 播放,并接受 ios 不支持 audioworklet 等现实限制。

纯 WebSocket 无法直接实现低延迟对讲,必须与 WebAudio 深度协同,并绕开浏览器默认音频处理链。核心目标是端到端延迟压到 80–150ms,这在网页环境中是可行的,但需放弃“拿来即用”的思路,重点控制采集、传输、播放三个环节的确定性。
音频采集:跳过 MediaRecorder,直采原始 PCM
MediaRecorder 会引入不可控编码延迟(Chrome 中常超 200ms),且输出封装格式(如 WebM)增加解析负担。应走 AudioContext 底层通路:
- 调用 navigator.mediaDevices.getUserMedia({audio: true}) 获取麦克风流
- 创建离屏 AudioContext({latencyHint: 'interactive'}),创建后立即 suspend(),仅在需要时 resume()
- 用 createMediaStreamSource() 接入音频流,再挂载自定义 AudioWorklet 处理器
- 在 Worklet 中按固定帧长(推荐 128 或 256 样本 @48kHz)读取 Float32Array,转为小端序 Int16Array,再包装成 Uint8Array 供发送
WebSocket 传输:精简二进制结构,拒绝 JSON 和 Base64
WebSocket 不是媒体传输协议,但可高效中转压缩后的原始音频帧——前提是结构极简、无冗余:
- 设置 socket.binaryType = 'arraybuffer',全程发送 ArrayBuffer,禁用字符串或 Base64
- 自定义帧头(共 5 字节):2 字节类型 + 2 字节采样率 ID + 1 字节声道数,后续紧接 interleaved 的 int16 PCM 数据
- 服务端不做解码,只做转发;不校验、不重组、不缓冲,保持帧级透传
- 客户端发送频率与采集帧率严格对齐(如每 2.67ms 发一帧),避免因 JS 调度抖动导致节奏失真
音频播放:WebAudio 精确调度,避开高延迟默认路径
默认 AudioContext 播放延迟可能高达 500ms。必须显式干预播放链与时序:
- 播放端也使用 latencyHint: 'interactive' 创建 AudioContext
- 不用 MediaStreamAudioDestinationNode,改用 AudioBufferSourceNode + 预分配 AudioBuffer(例如 48kHz/2ch/128samples = 512 字节)
- 收到帧后,立即填入 AudioBuffer 并用 context.currentTime 精确调度播放起始时间
- 加入 2–3 帧的轻量抖动缓冲(基于时间戳滑动窗口),防网络丢包导致卡顿,但不引入额外延迟
必须接受的现实限制
这不是 VoIP 客户端,而是受限于浏览器沙箱的 Web 实现:
- iOS Safari 至今不支持 AudioWorklet(iOS 17 仍如此),只能退回到废弃的 ScriptProcessorNode,延迟略高且兼容性差
- 无内置 AEC(回声消除)、NS(降噪)能力,需依赖 getUserMedia 的基础约束(echoCancellation: true)或前端 JS 算法补足
- 不解决 NAT 穿透问题,若需跨公网通信,仍要搭配 WebRTC 的 STUN/TURN 或专用 SFU 服务
- 移动端后台运行时 AudioContext 可能被系统 suspend,需监听 visibilitychange 做恢复逻辑
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











