Потоковое аудио через WebSocket - Веб-аудио - PullRequest
0 голосов
/ 12 июля 2020

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

Я использую сервер socket.io для передачи этой информации.

Звук передается хорошо. Используется этот код:

let hasHeader = false 
export function emitAudioStream(mic, sock, room) {
    console.log('beginning record')
    const recorder = new MediaRecorder(mic)
    recorder.ondataavailable = (evt) => {
        // fetch the header
        if (!hasHeader) {
            console.log('header:', evt.data)
            sock.emit('header:audio', evt.data)
            hasHeader = true
        }
        // console.log(evt.data.size)
        sock.emit('stream:audio', ({room, streamData: evt.data}))
    }
    recorder.start()
    console.log(`Recording begin. (State: "${recorder.state}")`)

    setInterval(() => {
        recorder.requestData()
    }, 1e3/60)
}

Есть комнаты «участников» - подключенных физических лиц. Сервер обрабатывает запросы следующим образом:

    sock.on('header:audio', (packet) => {
        console.log(`setting audio header for ${sock.id}`)
        sock.__audioHeader = packet
    })

    sock.on('stream:audio', ({room, streamData}) => {
        const participants = rooms[room]
        if (!participants) {
            console.log(`not found ${room} room`)
            return
        } 
        // create a getParticipants to handle not found
        // add flag to include current socket
        participants.forEach(participant => {
            // if (participant.id === sock.id) return 
            participant.emit('stream:audio:packet', {header: sock.__audioHeader, streamData})
        })
    })

Снова на клиенте, где я пытаюсь играть (где все это не удается), это выглядит так. Скорее всего, я неправильно истолковал документы Web Audio. Может ли кто-нибудь указать мне правильное направление / объяснить, почему это неправильный подход?

sck.on('stream:audio:packet', ({header, streamData}) => {
  playSound(streamData)
})

function playSound(buffer) {
  const context = new AudioContext()
  var source = context.createBufferSource()
  source.buffer = buffer
  source.connect(context.destination)
  source.start(0)
}

Другая попытка декодирования, которую я использовал:

        sck.on('stream:audio:packet',async  ({header, streamData}) => {
            if (streamData === 'data:') return
            const b64ToBuffer = (data) => fetch(data).then(r => r.blob())
            const buff = await b64ToBuffer(streamData)


            playSound(await buff.arrayBuffer())
        })

        let context = new AudioContext()


        
        async function playSound(buffer) {
            try {
                const buff = await context.decodeAudioData(buffer)
                let source = context.createBufferSource()
                source.connect(context.destination)
                console.log(buff)
                source.buffer = buff
                source.start(0)
            } catch (err) {
                console.warn('error decoding data:', err)
            }
        }

1 Ответ

2 голосов
/ 13 июля 2020

Причина, по которой ваше текущее решение не работает, заключается в том, что MediaRecorder не требуется для генерации фрагментов, которые могут быть закодированы сами по себе. Все фрагменты необходимо сшить вместе после остановки MediaRecorder, чтобы получить действительный файл. Кроме того, API веб-аудио может декодировать только полные файлы с помощью своего метода decodeAudioData().

Как сказано в комментариях выше, WebRT C - это API, специально созданный для этого случая использования. Если вы хотите иметь отдельные комнаты, вы можете убедиться, что ваш процесс сигнализации соединяет только клиентов, которые принадлежат одной комнате.

Если вы хотите избежать WebRT C, вы можете попробовать библиотеку, которую я написал, которая добавляет Поддержка WAVE для MediaRecorder. Библиотека называется расширяемый медиа-рекордер . Когда их просят выдать фрагменты, эти фрагменты также не являются допустимыми файлами WAVE сами по себе, но декодирование частичных файлов WAVE вручную намного проще, чем декодирование сжатых файлов. Несмотря на то, что самые первые 44 байта составляют заголовок, это просто необработанные данные PCM.

Вы также можете сделать обратное и сохранить собственный MediaRecorder и объединить его с настраиваемым декодером на принимающей стороне. Если вы настроили MediaRecorder для кодирования файлов Opus, opus-stream-decoder должен иметь возможность декодировать фрагменты.

...