我以前在两个node.js服务器之间做过语音聊天(参见:tvoip),它工作得很好,但现在我想在node.js服务器和浏览器之间做这件事。如何做到这一点呢?
从node.js到node.js,我只是在TCP连接上使用原始的PCM流。
对于浏览器来说,这可能不会那么容易,对吧?我的意思是,浏览器并没有真正提供TCP API。它确实提供了一个WebSocket应用程序接口,但是它能处理流吗?我是否必须将流转换为什么格式以及如何转换?我应该使用什么协议?已经有什么有用的库来完成这个任务了吗?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下的device
,那么为麦克风和扬声器找到合适的ALSA可能有点棘手。It is explained here,以防您不确定。我不确定它在装有SoX的视窗和Mac上是如何工作的。
然后,我想出了一个使用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中所做的那样)。但它不能在自动取款机上工作。
编辑:
我不确定我是否正确,但是我从getUserMedia()得到的“流”是一个MediaStream,这个媒体流可以有MediaStreamTracks (音频、视频或两者都有)。以我为例,它显然只有一首曲目(音频)。但我从Node.js中了解到,MediaStreamTrack
似乎不是stream,这意味着它不能只是通过管道传输。因此,可能必须将其转换为一个。我发现了一个名为microphone-stream的有趣的库,它声称能够做到这一点。但它似乎并不是一个简单的浏览器库。这似乎需要用browserify来包装整个项目。这看起来太过分了。我想让它保持简单。
https://stackoverflow.com/questions/50607578
复制相似问题