it-swarm.com.de

Voice-Chat zwischen Node.js und Browser (Audiostreams, VoIP)

Ich habe vorher Voice-Chats zwischen zwei node.js-Servern durchgeführt (siehe: tvoip ), was ziemlich gut funktioniert, aber jetzt möchte ich es zwischen einem node.js-Server und einem Browser tun. Wie kann das gemacht werden?
Von node.js zu node.js habe ich einfach rohe PCM-Streams über eine TCP - Verbindung verwendet.
Für den Browser wird das wahrscheinlich nicht so einfach sein, oder? Ich meine, der Browser bietet nicht wirklich eine TCP - API. Es bietet eine WebSocket-API, handhabt es jedoch Streams? Müsste ich die Streams konvertieren und wenn ja in welches Format und wie? Welches Protokoll soll ich verwenden? Gibt es hilfreiche Bibliotheken, um dies bereits zu erreichen? Ist socket.io-stream eine brauchbare Bibliothek, um solche Streams zu senden?

Soweit ich weiß, sind die Audioströme im PCM-Format im Browser. Es sollte also mit den Streams kompatibel sein, die ich in Node.js bekam. Ist diese Annahme richtig?

Ich habe es geschafft, den Browser-Mikrofoneingang wie folgt an die Lautsprecherausgabe des Browsers weiterzuleiten:

<!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>

Wie Sie sehen, habe ich zwei Lösungen gefunden. Ich werde versuchen, den Sprach-Chat des Knotens <-> Browser auf den zweiten zu stützen.

Für Node.js habe ich diesen Code entwickelt, um eine mic.-Eingabe von node.js an eine node.js-Lautsprecherausgabe weiterzuleiten:

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')

Die richtige device für Mikrofon und Lautsprecher zu finden, kann etwas schwierig sein, wenn Sie sich mit ALSA unter Linux nicht auskennen. Es wird hier erklärt falls Sie unsicher sind. Ich bin nicht sicher, wie es unter Windows und Mac OS mit SoX funktioniert. 

Ich entwickelte dann eine kleine Testanwendung, um die beiden Ideen mithilfe von socket.io-stream (einer Bibliothek socket.io) zu verbinden, die das Senden von Streams über einen Socket ermöglicht. Und an diesem Punkt bin ich natürlich fest.

Grundsätzlich versuche ich das auf der node.js-Seite:

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()
    })
})

und das auf der Browserseite:

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.')
})

Der gesamte Code kann unter folgender Adresse eingesehen werden: https://github.com/T-vK/node-browser-audio-stream-test
(Die README.md enthält Anweisungen zum Einrichten, wenn Sie es testen möchten.) Der entsprechende Code befindet sich in server.js (Die Funktion setupStream () enthält das Interessante code.) und client.html .

Wie Sie sehen, versuche ich, den Duplex-Stream über die Verbindung zu senden und die Mikrofoneingänge in den Duplex-Stream zu leiten und den Duplex-Stream an den Lautsprecher an jedem Ende zu leiten (wie in tvoip ). Es funktioniert aber nicht atm. 

Bearbeiten:

Ich bin nicht sicher, ob ich das richtig mache, aber der "Stream", den ich von getUserMedia () bekomme, ist ein MediaStream und dieser Media-Stream kann MediaStreamTrack s (Audio, Video) haben oder beides). Ich bin mein Fall, es wäre offensichtlich nur eine Spur (Audio). Eine MediaStreamTrack scheint jedoch kein stream zu sein wie ich es von Node.js kenne, was bedeutet, dass es nicht einfach weitergeleitet werden kann. Vielleicht müsste es in eins umgewandelt werden. Ich habe diese interessante Bibliothek mit dem Namen Mikrofonstrom gefunden, die behauptet, dies tun zu können. Es scheint jedoch nicht als einfache Browser-Bibliothek verfügbar zu sein. Es scheint, dass Sie Ihr gesamtes Projekt mit browserify umschließen müssen. Was sehr übertrieben wirkt. Ich möchte es einfach halten.

15
Forivin

Es gibt einen Standard für VoIP mit Browsern, der von allen gängigen Browsern unterstützt wird: WebRTC . Obwohl es ein schreckliches Ungeheuer von Komplexität ist, wird es von allen gängigen Browsern, die seine Komplexität verdecken, sofort unterstützt. Ich bin kein Javascript-Entwickler, aber ich gehe davon aus, dass es in der JS-Welt Gold-Unterstützung dafür gibt, siehe z. dieser blogpost .

Wenn Sie die Overkill-Lösung mit vollem Funktionsumfang nicht möchten, würde ich auf RTP als Streaming-Protokoll zurückgreifen, das bei VoIP und Opus eine Art Standard für die Kodierung ist. Beide sind etablierte Technologien und bilden die Art des Standardpaares für VoIP-Streaming. RTP ist leicht und Opus ist effizient beim Komprimieren und erzielt gleichzeitig eine hohe Audioqualität. Sie sollten in den Umgebungen Browser und node.js gut unterstützt werden.

Achtung: Wenn Sie sich für das Senden von normalem PCM entscheiden, definieren Sie alle Parameter genau - Rahmenlänge (8, 16, 32 Bit), a Signiert/vorzeichenlos, Ganzzahl/Fließkommazahl und insbesondere endianness!

3
Michael Beer

Sie sollten rohe PCM-Streams nicht direkt verwenden, um Browser und nodejs-App miteinander zu verbinden. Es kann in kürzester Zeit ziemlich verschwenderisch werden.

andererseits funktioniert das, was im Knoten funktioniert, möglicherweise nicht im Browser. (Überprüfen Sie Ihr Repo, um zu sehen, was Sie tun möchten, und prüfen Sie, ob ich dort etwas überprüfen kann.) "

Eine andere Lösung wäre, einen Server - wie Icecast - zu verwenden, der alle Daten im Backend und in den Daten sehr lästig machen wird.

Dann interagieren Sie über einen Webbrowser nur mit einem HTML-Tag.

Überprüfen Sie dies -> () Ich hatte einen Link zu einem verwandten Thread im Stapelüberlauf, aber ich verlor IT Lol ().

Hpe findest du das nützlich, Grüße.

2
Ian Franco Pozo

Eine der Bibliotheken, die Sie mit socket.io bearbeiten müssen, und eines der besten Tutorials ist here . Sie können es lernen und nachdem Sie die Chat-App in node.js erstellt haben, können Sie die Voice-Chat-App starten

0
yasin

Mit SFMediaStream können Sie die Audiodaten des Mikrofons vom Browser streamen und mit socket.io übertragen. Die Audiodaten sind je nach Browser in opus codiert.

Es hat auch eine Funktion, um Audiofilter/-effekte für den Streamer bereitzustellen, und Sie können die Bibliothek auch zum Erstellen eines Video-/Audioplayers verwenden.

Vielleicht interessiert es Sie nach diesem grundlegenden Beispiel

0
StefansArya