Потоковая передача звука от клиента к серверу клиенту с помощью WebSocket

Я пытаюсь записать звук с микрофона из клиентского веб-браузера, транслировать записанный звук в прямом эфире на сервер Node.js с помощью WebSocket, а затем снова передавать звук обратно на другой клиент веб-браузера.

Пока что на стороне клиента я открыл соединение WebSocket в JavaScript.

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, я получаю каждый большой двоичный объект и отправляю его другому клиенту.

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(); является асинхронным. В этом случае я пытаюсь изменить URL-адрес большого двоичного объекта для элемента audio каждую секунду, когда через WebSocket поступает новый большой двоичный объект.

После недельного исследования я нашел решения только в том, как транслировать звук с сервера на клиент, начать запись звука, остановить запись, а затем отправить весь фрагмент в виде капли.

Я также пытался отправить 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);
    }

Я пытаюсь добиться того, чтобы пользователь говорил в свой микрофон, а аудиопотоки в реальном времени передавались на сервер, а затем другому пользователю и воспроизводились одновременно.

Если мой подход к отправке капли каждую секунду верен, как я могу заставить код работать для непрерывного воспроизведения звука? Возможно, мне нужно создать какие-то буферы, о которых я не знаю. Или, если подход полностью неверен, подскажите, пожалуйста, правильный.

Использование технологии WebRTC для одноранговой связи для меня не вариант, потому что я не хочу накладных расходов на STUN или сервер TURN.


person Akshit Mehra    schedule 26.07.2020    source источник
comment
вы говорите, что предпочитаете всегда ретранслировать данные через сервер websocket / TCP, а не иногда через сервер TURN (udp; обычно). Пересмотрите свои варианты.   -  person Philipp Hancke    schedule 26.07.2020


Ответы (1)


MediaRecorder передает фрагменты данных на ваш dataavailable обработчик событий. Чтобы эти фрагменты были полезными, их нужно воспроизводить по порядку. Обычно это фрагменты мультимедийного файла в формате .webm, также известном как Формат Matroska. Они не одиноки. (Кроме первого).

Итак, если вы передадите их другому браузеру через полезную нагрузку веб-сокета, они действительно не смогут воспроизводиться по отдельности.

Вы можете попытаться проанализировать файл webm в принимающем браузере и попытаться воспроизвести его из событий сообщений вашего веб-сокета. В этом может помочь пакет npm под названием ebml. Если вы выберете это решение, посмотрите, как декодировать звук opus в браузере. Я сделал это для видео. Разрабатывать и отлаживать - это головная боль. (Я сделал это только потому, что некоторым пользователям потребовалось использовать научный проект средней школы Редмонда, то есть Microsoft Internet Explorer, для рендеринга видео с малой задержкой. Я мог бы купить всем этим пользователям новые компьютеры за ту же цену, что и разработка этого .)

Странно, но факт, что способ пакетирования аудио в стеке связи WebRTC кардинально отличается от того, как это делает MediaRecorder.

(Как бы то ни было, есть поставщик под названием xirsys.com, который предоставляет серверы STUN / TURN. У них есть щедрый уровень бесплатного пользования для разработки и небольших объемов работ. Это заслуживает внимания. Я добился хороших успехов на этапе разработки, с ними.)

person O. Jones    schedule 27.07.2020