通过WebSocket将音频流传输到Web音频播放器

时间:2020-03-20 21:50:44

标签: javascript html audio web-audio-api

我有一个可以正常工作的系统

  1. 在服务器上将音频产生到1秒的WAV文件中
  2. 读取WAV文件并通过websocket发送
  3. Websocket将二进制数据发送到AudioContext.decodeAudioData
  4. 解码后的音频一直缓冲到4个数据包(4秒)
  5. 处理缓冲区并将其发送到AudioBufferSourceNode.start(time),其中time =(clip_count *持续时间)

因此,如果我有4个音频剪辑,则通话看起来像

AudioBufferSourceNode.start(0);
AudioBufferSourceNode.start(1);
AudioBufferSourceNode.start(2);
AudioBufferSourceNode.start(3);

我认为这可以完美安排4秒钟的音频,但是似乎要面对时钟问题,也许是因为我希望音频时钟是完美的。我已经使用了一个增益节点来消除每个声音片段(1秒)之间的咔嗒声,但是我立即或在很长一段时间后开始遇到计时问题。基本上,在最坏的情况下,我的音频会像这样播放

 ----------------------  -----------     -----------     -----------
| 1 second | 1 second |  |   950ms |     |  900ms  |    |   850ms  |
 ----------------------  -----------     -----------     -----------
                       gap          gap              gap

在此图中,“ 1秒”和“ #ms”表示正在播放的音频量。它应始终为1秒。随着音频的发展,它似乎也在发展差距。我猜想,即使我告诉音频上下文将文件精确地播放为0,也可以,但是其他预定的音频片段可能会或可能不会准时播放。

这是正确的,还是我的系统出了点问题?我是否可以安排音频剪辑在确切的正确时间播放以100%的可靠性,还是我需要添加一些计算以计算出播放时间的+/-毫秒?

1 个答案:

答案 0 :(得分:3)

看来完成此任务的目的是 AudioWorkletNode

根据AudioBufferSourceNode documentation

AudioBufferSourceNode接口是一个AudioScheduledSourceNode,它表示存储在AudioBuffer中的由内存中音频数据组成的音频源。对于回放对定时精度要求特别严格的音频,例如对于必须符合特定节奏并且可以保留在内存中而不是从磁盘或网络中播放的声音,它尤其有用。要播放需要精确定时但必须从网络流传输或从磁盘播放的声音,请使用AudioWorkletNode 来实现其播放。

这种情况恰好实现了来自网络的流传输。 不是设计的AudioBufferSourceNode可以通过网络即时更新。

什么可能导致不同步

  1. 根据javascript调度程序的性质,不能保证在准确的时间执行代码。该节点可能同时执行另一项工作,从而导致信息发送延迟
  2. 计时器在发送完所有数据后运行下一个滴答,这可能需要一些时间
  3. 客户端调度程序比服务器端具有更多的限制。通常,浏览器每秒可以执行大约250个计时器(每4ms一次)。
  4. 使用的API不适用于该流程

建议

  1. 始终保留缓冲区。如果由于某种原因,缓冲区中的帧已经播放过,则可以更快地请求新帧。
  2. 动态增加缓冲区。收到两条消息后,就可以开始播放了,但是可以将动态缓冲消息的数量增加到大约15秒。
  3. 建议使用其他工具来进行连接和数据传输。 Nginx将完美地发挥作用。如果客户端连接速度较慢,它将“保留”节点,直到数据传输完毕。
  4. 如果连接丢失了一秒钟(例如在移动网络上),则应该有一些方法可以从适当的帧中恢复状态,更新缓冲区并做到所有这些都不会中断。