【问题标题】:Stream Audio via WebSocket - Web Audio通过 WebSocket 流式传输音频 - 网络音频
【发布时间】:2020-11-01 23:55:59
【问题描述】:

我非常接近通过 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)
            }
        }

【问题讨论】:

  • 如果您尝试进行语音聊天,您肯定会想要切换到 WebRTC。否则,延迟会太高,并且播放会一直落后,因为您不会在网络丢失任何音频时丢失任何音频。
  • 你还能通过 WebRTC 根据一些房间名称或代码创建一组个人吗?

标签: audio websocket socket.io audio-streaming web-audio-api


【解决方案1】:

您当前的解决方案不起作用的原因是 MediaRecorder 不需要发出可以自行编码的块。停止MediaRecorder 后,需要将所有块拼接在一起才能获得有效文件。此外,Web Audio API 只能使用其decodeAudioData() 方法解码完整文件。

正如上面的 cmets 中所说,WebRTC 是专门为此用例制作的 API。如果您想拥有单独的房间,您可以确保您的信令流程仅连接属于同一房间的客户端。

如果您想避免使用 WebRTC,您可以尝试我编写的一个库,它添加了对 MediaRecorder 的 WAVE 支持。该库称为extendable-media-recorder。当被要求发出块时,这些块本身也不是有效的 WAVE 文件,但手动解码部分 WAVE 文件比解码压缩文件要容易得多。尽管包含头的前 44 个字节只是原始 PCM 数据。

您也可以反其道而行之,保留原生 MediaRecorder,并将其与接收端的自定义解码器结合使用。如果您将MediaRecorder 配置为对Opus 文件进行编码,opus-stream-decoder 应该能够解码这些块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多