Node.js和浏览器之间的语音聊天(音频流,VoIP)

For*_*vin 15 javascript browser audio voip node.js

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

据我所知,音频流在浏览器上采用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>
Run Code Online (Sandbox Code Playgroud)

如你所见,我找到了两个解决方案.我将尝试在第二个节点上基于节点< - >浏览器语音聊天.

对于Node.js,我想出了这个代码来将node.js mic输入传递给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')
Run Code Online (Sandbox Code Playgroud)

device如果你不熟悉Linux下的ALSA,找到麦克风和扬声器的权利可能有点棘手.如果你不确定,这里会解释.我不确定它如何在Windows和Mac OS上使用SoX.

然后我想出了一个小的测试应用程序来连接这两个想法使用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()
    })
})
Run Code Online (Sandbox Code Playgroud)

这在浏览器方面:

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.')
})
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:

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

yas*_*sin 0

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