【问题标题】:Voice chat between Node.js and browser (audio streams, VoIP)Node.js 和浏览器之间的语音聊天(音频流、VoIP)
【发布时间】:2018-11-09 11:30:56
【问题描述】:

我之前在两个 node.js 服务器之间进行过语音聊天(请参阅:tvoip),效果很好,但现在我想在 node.js 服务器和浏览器之间进行。这是怎么做到的?
从 node.js 到 node.js,我只是通过 TCP 连接使用原始 PCM 流。
对于浏览器来说,这可能不会那么容易,对吧?我的意思是浏览器并没有真正提供 TCP API。它确实提供了 WebSocket API,但它是否处理流?我是否必须将流转换为什么格式以及如何转换?我应该使用什么协议?是否有任何有用的库来实现这一点? socket.io-stream 是发送这类流的可行库吗?

据我了解,音频流在浏览器上采用 PCM 格式。所以它应该与我在 Node.js 中获得的流兼容。这个假设正确吗?

我已经设法将浏览器麦克风输入通过管道传输到浏览器扬声器输出,如下所示:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>

<!-- alternative method that also works
<audio></audio>
<script>
navigator.mediaDevices.getUserMedia({ audio: true }).then(function(stream) {
    const audio = document.querySelector('audio')
    audio.srcObject = stream
    audio.onloadedmetadata = function(e) {
        audio.play()
    }
}).catch(console.error)
</script>
-->
<script>
    navigator.mediaDevices.getUserMedia({audio: true}).then(stream => {
        const aCtx = new AudioContext()
        const analyser = aCtx.createAnalyser()
        const microphone = aCtx.createMediaStreamSource(stream)
        microphone.connect(analyser)
        analyser.connect(aCtx.destination)
    }).catch(err => {
        console.error("Error getting audio stream from getUserMedia")
    })
</script>

</body>
</html>

如您所见,我找到了两个解决方案。我将尝试将节点浏览器语音聊天基于第二个。

对于 Node.js,我想出了这段代码,将 node.js 麦克风输入通过管道传输到 node.js 扬声器输出:

const mic = require('mic')
const Speaker = require('speaker')

const micInstance = mic({ // arecord -D hw:0,0 -f S16_LE -r 44100 -c 2
    device: 'hw:2,0',           //   -D hw:0,0
    encoding: 'signed-integer', //             -f S
    bitwidth: '16',             //                 16
    endian: 'little',           //                   _LE
    rate: '44100',              //                       -r 44100
    channels: '1',              //                                -c 2
    debug: true
})
const micInputStream = micInstance.getAudioStream()

const speakerInstance = new Speaker({ // | aplay -D plughw:CARD=0,DEV=0
    channels: 1,
    bitDepth: 16,
    sampleRate: 44100,
    signed: true,
    device: 'plughw:2,0' //'plughw:NVidia,7'
})
speakerInstance.on('open', ()=>{
    console.log("Speaker received stuff")
})

// Pipe the readable microphone stream to the writable speaker stream:
micInputStream.pipe(speakerInstance)

micInputStream.on('data', data => {
    //console.log("Recieved Input Stream: " + data.length)
})
micInputStream.on('error', err => {
    cosole.log("Error in Input Stream: " + err)
})
micInstance.start()

console.log('Started')

如果您不熟悉 Linux 下的 ALSA,为麦克风和扬声器找到合适的 device 可能会有点棘手。 It is explained here 如果您不确定。我不确定它在带有 SoX 的 Windows 和 Mac OS 上如何工作。

然后我想出了一个小型测试应用程序,使用 socket.io-stream(一个允许通过套接字发送流的 socket.io 库)连接这两个想法。很明显,这就是我被困的地方。

基本上,我在 node.js 端试试这个:

const mic = require('mic')
const Speaker = require('speaker')
const SocketIO = require('socket.io')
const ss = require('socket.io-stream')

...

io.on('connection', socket => {
    let micInstance = mic(micConfig)
    let micInputStream = micInstance.getAudioStream()
    let speakerInstance = new Speaker(speakerConfig)

    ...

    ss(socket).on('client-connect', (stream, data) => { // stream: duplex stream
        stream.pipe(speakerInstance) //speakerInstance: writable stream
        micInputStream.pipe(stream) //micInputStream: readable stream
        micInstance.start()
    })
})

这在浏览器端:

const socket = io()
navigator.mediaDevices.getUserMedia({audio:true}).then(clientMicStream => { // Get microphone input
    // Create a duplex stream using the socket.io-stream library's ss.createStream() method and emit it it to the server
    const stream = ss.createStream() //stream: duplex stream
    ss(socket).emit('client-connect', stream)

    // Send microphone input to the server by piping it into the stream
    clientMicStream.pipe(stream) //clientMicStream: readable stream
    // Play audio received from the server through the stream
    const aCtx = new AudioContext()
    const analyser = aCtx.createAnalyser()
    const microphone = aCtx.createMediaStreamSource(stream)
    microphone.connect(analyser)
    analyser.connect(aCtx.destination)
}).catch(e => {
    console.error('Error capturing audio.')
    alert('Error capturing audio.')
})

完整的代码可以查看:https://github.com/T-vK/node-browser-audio-stream-test
README.md 包含有关如何设置它的说明,如果您想对其进行测试。)相关代码在server.js(setupStream() 函数包含有趣的代码。)和client.html

如您所见,我正在尝试通过连接发送双工流,并将麦克风输入通过管道传输到双工流中,并将双工流通过管道传输到每一端的扬声器(就像我在 tvoip 中所做的那样)。但是,它在 atm 中不起作用。

编辑:

我不确定我是否正确,但我从getUserMedia() 获得的“流”是MediaStream,并且该媒体流可以包含MediaStreamTracks(音频、视频或两者)。我是我的情况,它显然只是一首曲目(音频)。但是MediaStreamTrack 似乎不是stream,因为我从Node.js 知道它,这意味着它不能只是通过管道传输。所以也许它必须被转换成一个。我发现了一个名为microphone-stream 的有趣库,它声称能够做到这一点。但它似乎不能作为一个简单的浏览器库使用。似乎需要用 browserify 包装你的整个项目。这似乎非常矫枉过正。我想保持简单。

【问题讨论】:

    标签: javascript node.js audio browser voip


    【解决方案1】:

    您必须使用的库之一是socket.io,最好的教程之一是here。你可以学习它,在 node.js 中制作聊天应用后,然后启动语音聊天应用

    【讨论】:

    • 我已经知道如何使用它,但我不确定通过它发送 PCM 流是否是个好主意。
    【解决方案2】:

    您不应直接使用原始 PCM 流将浏览器和 nodejs 应用程序连接在一起。它很快就会变得非常浪费。

    另一方面,在节点中有效的方法,在浏览器中可能有效也可能无效(检查您的仓库以查看您正在尝试做什么,并检查我是否可以在那里检查某些内容)“

    另一种解决方案是使用服务器(如 icecast),这将使所有后端/数据的细节变得非常重要。

    然后你通过网络浏览器只用一个 html 标签进行交互。

    检查这个 -> () 我有一个指向堆栈溢出相关线程的链接,但我丢失了 IT Lol()

    您觉得这很有用, 问候。

    【讨论】:

    • 我还没有寻找有效的解决方案。我首先需要让它工作。而且由于 PCM 是默认/原生格式,我现在想坚持下去。压缩/编码和解压缩/解码流是另一个问题,超出了这个问题的范围。设置像 icecast 这样的第三方软件并不是一个真正的选择。我想用 Node.js 来做,当然它必须双向工作,而不仅仅是一个。
    【解决方案3】:

    存在一个使用所有主流浏览器都支持的浏览器进行 VoIP 的标准:WebRTC。尽管它是一个可怕的复杂性野兽,但它被所有隐藏其复杂性的市长浏览器开箱即用地支持。我不是 javascript 开发人员,但我高度假设在 JS 世界中存在对它的黄金支持,例如this blogpost.

    如果您不想要全功能的矫枉过正的解决方案,我会退回到 RTP 作为流协议,这是 VoIP 和 Opus 中用于编码的一种标准。 两者都是成熟的技术,形成了一种默认的 VoIP 流媒体对,RTP 是轻量级的,并且 Opus 在保持高音频质量的同时压缩效率很高。它们应该在 Browser 和 node.js 环境中都得到很好的支持。

    注意:如果您决定发送纯 PCM,请精确定义所有参数 - 帧长度(8、16、32 位)、a 有符号/无符号,整数/浮点数,特别是 endianness

    【讨论】:

      【解决方案4】:

      SFMediaStream 可以帮助您从浏览器流式传输麦克风音频数据,您可以使用socket.io 进行广播。音频数据编码为opus,具体取决于浏览器。

      它还具有为流媒体提供音频过滤器/效果的功能,您还可以使用该库构建视频/音频播放器。

      查看basic example之后你可能会感兴趣

      【讨论】:

        猜你喜欢
        • 2015-02-12
        • 2014-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-22
        • 2010-12-04
        相关资源
        最近更新 更多