使用WebSocket将音频从客户端流传输到服务器再传输到客户端

时间:2020-07-26 17:13:13

标签: javascript node.js websocket getusermedia web-mediarecorder

我试图从客户端Web浏览器捕获麦克风音频,使用WebSocket将捕获的音频实时流传输到Node.js服务器,然后再次将音频流传输回另一个Web浏览器客户端。

到目前为止,在客户端,我使用JavaScript打开了WebSocket连接

const webSocket = new WebSocket('ws://127.0.0.1:8080');
webSocket.binaryType = 'blob';

在连接到服务器时,我捕获了来自用户麦克风的音频流,并捕获了每1秒钟可用的每个数据块,并将其通过WebSocket发送到服务器

webSocket.onopen = event => {
console.log('info: connected to server');

navigator.mediaDevices
  .getUserMedia({ audio: true, video: false })
  .then(stream => {
    const mediaRecorder = new MediaRecorder(stream, {
      mimeType: 'audio/webm',
    });

    mediaRecorder.addEventListener('dataavailable', event => {
      if (event.data.size > 0) {
        webSocket.send(event.data);
      }
    });

    mediaRecorder.start(1000);
  });
};

现在,在服务器端,使用ws模块,我收到每个blob并将其发送给另一个客户端

wss.on('connection', ws => {
  console.log('info: client connected');

  ws.on('message', message => {
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === webSocket.OPEN) {
        client.send(message);
      }
    });
  });
});

回到客户端,我尝试使用audio标签和引用audioEl播放音频

  webSocket.onmessage = event => {
    audioEl.src = window.URL.createObjectURL(event.data);
    audioEl.play();
  };

现在,我知道这仅适用于第一个数据块(并且确实有效),因为audioEl.play();是异步的。在这种情况下,我试图每秒钟更改audio元素的Blob URL,通过WebSocket接收到一个新的Blob。

经过一个星期的研究,我发现了有关如何将服务器流式传输到客户端音频,开始录制音频,停止录制然后将整个块作为Blob发送的解决方案。

我还尝试发送AudioBuffer,但不知道如何处理它以播放音频。

const context = new AudioContext();
    const source = context.createMediaStreamSource(stream);
    const processor = context.createScriptProcessor(1024, 1, 1);

    source.connect(processor);
    processor.connect(context.destination);

    processor.onaudioprocess = function(e) {
      webSocket.send(e.inputBuffer);
    }

我要实现的目标是,用户对着他/她的麦克风讲话,音频流先流向服务器,然后又流向另一个用户,并同时播放。

如果我每秒发送一个Blob的方法是正确的,我如何使代码能够连续播放音频?也许我需要创建一些我不知道的缓冲区。或者,如果该方法完全不正确,请指导我选择正确的方法。

使用WebRTC技术进行对等通信对我来说不是一种选择,因为我不希望增加STUN或TURN服务器的开销。

1 个答案:

答案 0 :(得分:1)

MediaRecorder将数据块传递到dataavailable事件处理程序。为了使这些块有用,必须按顺序播放它们。它们是通常在.webm format(也称为Matroska format)中的媒体文件块。他们并不孤单。 (第一个除外)。

因此,如果您通过websocket负载将它们传递给另一个浏览器,则它们实际上无法单独播放。

您可以尝试在接收的浏览器上解析webm文件,并尝试从websocket的消息事件中播放该文件。有npm package called ebml可以帮助您解决此问题。如果您寻求该解决方案,请寻找“如何在浏览器中解码Opus音频”。我已经在视频中这样做了。开发和调试在xxx脖子上是一个痛苦。 (我之所以这样做,是因为某些用户需要使用Redmond中学科学项目(即Microsoft Internet Explorer)来渲染低延迟视频。我本可以为所有这些用户购买新计算机,但开发这些计算机的成本却很高)

奇怪的是,但事实是,WebRTC通信堆栈打包音频的方式与MediaRecorder的方式完全不同。

(值得的是,有一家名为xirsys.com的供应商,它提供STUN / TURN服务器。它们有大量的免费开发层和少量工作。这值得考虑。我在开发阶段。)