流入 <audio> 元素

Streaming into <audio> element

我想从发送未知总长度的声音数据包的网络套接字播放音频。播放应在第一个包裹到达后立即开始,不应被新包裹打断。

到目前为止我做了什么:

ws.onmessage = e => {
  const soundDataBase64 = JSON.parse(e.data);
  const bytes = window.atob(soundDataBase64);
  const arrayBuffer = new window.ArrayBuffer(bytes.length);
  const bufferView = new window.Uint8Array(arrayBuffer);
  for (let i = 0; i < bytes.length; i++) {
    bufferView[i] = bytes.charCodeAt(i);
  }
  const blob = new Blob([arrayBuffer], {"type": "audio/mp3"});
  const objectURL = window.URL.createObjectURL(blob);
  const audio = document.createElement("audio");
  audio.src = objectURL;
  audio.controls = "controls";
  document.body.appendChild(audio);
};

但是,据我所知,无法扩展 ArrayBufferUint8Array 的大小。我必须创建一个新的 blob,object URL 并将其分配给 audio 元素。但我想,这会中断音频播放。

<audio>, there is a hint to MediaStream 的 MDN 页面上,看起来很有希望。但是,我不太确定如何将数据写入媒体流以及如何将媒体流连接到音频元素。

目前是否可以使用 JS 编写类似管道的东西,我可以在一端输入数据,然后将其流式传输给消费者?在JS中如何实现无缝推流(最好不要大量的微管理代码)?

正如@Kaiido 在评论中指出的那样,我可以使用 MediaSource 对象。将 MediaSource 对象连接到 DOM 中的 <audio> 元素后,我可以将 SourceBuffer 添加到打开的 MediaSource 对象,然后附加 ArrayBuffers 到 SourceBuffer.

示例:

const ws = new window.WebSocket(url);
ws.onmessage = _ => {
  console.log("Media source not ready yet... discard this package");
};

const mediaSource = new window.MediaSource();
const audio = document.createElement("audio");
audio.src = window.URL.createObjectURL(mediaSource);
audio.controls = true;
document.body.appendChild(audio);

mediaSource.onsourceopen = _ => {
  const sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg"); // mpeg appears to not work in Firefox, unfortunately :(
  ws.onmessage = e => {
    const soundDataBase64 = JSON.parse(e.data);
    const bytes = window.atob(soundDataBase64);
    const arrayBuffer = new window.ArrayBuffer(bytes.length);
    const bufferView = new window.Uint8Array(arrayBuffer);
    for (let i = 0; i < bytes.length; i++) {
      bufferView[i] = bytes.charCodeAt(i);
    }
    sourceBuffer.appendBuffer(arrayBuffer);
  };
};

我在 Google Chrome 94 中测试成功。不幸的是,在 Firefox 92 中,MIME 类型 audio/mpeg 似乎不起作用。在那里,我收到错误 Uncaught DOMException: MediaSource.addSourceBuffer: Type not supported in MediaSource 和警告 Cannot play media. No decoders for requested formats: audio/mpeg.