web-dev-qa-db-fra.com

Chat vocal entre Node.js et le navigateur (flux audio, VoIP)

J'ai déjà discuté entre deux serveurs node.js (voir: tvoip ), ce qui fonctionne assez bien, mais j'aimerais maintenant le faire entre un serveur node.js et un navigateur. Comment cela pourrait-il être fait?
De node.js à node.js, j’utilisais simplement des flux PCM bruts sur une connexion TCP.
Pour le navigateur, cela ne va probablement pas être aussi simple, non? Je veux dire que le navigateur n'offre pas vraiment une API TCP. Il propose une API WebSocket, mais gère-t-il les flux? Devrais-je convertir les flux et si oui dans quel format et comment? Quel protocole devrais-je utiliser? Existe-t-il déjà des bibliothèques utiles pour accomplir cela? Est-ce que socket.io-stream est une bibliothèque viable pour envoyer ce type de flux?

D'après ce que j'ai compris, les flux audio sont au format PCM sur le navigateur. Donc, il devrait être compatible avec les flux que j'ai obtenus dans Node.js. Cette hypothèse est-elle correcte?

J'ai réussi à diriger l'entrée du micro du navigateur vers la sortie du haut-parleur du navigateur comme ceci:

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

Comme vous pouvez le voir, j'ai trouvé deux solutions. Je vais essayer de baser le noeud <-> du chat vocal du navigateur sur le second.

Pour Node.js, j’ai proposé ce code pour diriger une entrée de micro node.js vers une sortie de haut-parleur 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')

Trouver la bonne device pour le micro et le haut-parleur peut être un peu délicat si vous n'êtes pas familier avec ALSA sous Linux. Il est expliqué ici au cas où vous n'êtes pas sûr. Je ne suis pas sûr de savoir comment cela fonctionne sous Windows et Mac OS avec SoX. 

J'ai ensuite proposé une petite application de test pour connecter les deux idées à l'aide de socket.io-stream (une bibliothèque socket.io qui permet d'envoyer des flux sur un socket). Et évidemment, c'est là que je suis coincé.

En gros, j'essaye ceci du côté de 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()
    })
})

et ceci du côté du navigateur:

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

Le code complet peut être consulté à l’adresse: https://github.com/T-vK/node-browser-audio-stream-test
(Le README.md contient des instructions sur la façon de le configurer, si vous voulez le tester.) Le code correspondant se trouve dans server.js (la fonction setupStream () contient les informations intéressantes. code.) et client.html .

Comme vous pouvez le constater, j'essaie d'envoyer le flux duplex sur la connexion et de canaliser les entrées microphone vers le flux duplex, puis d'acheminer le flux duplex vers le haut-parleur à chaque extrémité (comme je l'ai fait dans tvoip ). Cela ne fonctionne pas, cependant. 

Modifier:

Je ne sais pas si je comprends bien, mais le "flux" que je reçois de getUserMedia () est un MediaStream et ce flux multimédia peut avoir MediaStreamTrack s (audio, vidéo ou les deux). Je suis mon cas ce serait évidemment juste une piste (audio). Mais une MediaStreamTrack ne semble pas être un stream tel que je le connais depuis Node.js, ce qui signifie qu’il ne peut pas être simplement canalisé. Alors peut-être faudrait-il le convertir en un. J'ai trouvé cette bibliothèque intéressante appelée microphone-stream qui prétend pouvoir le faire. Mais il ne semble pas être disponible comme une simple bibliothèque de navigateur. Cela semble nécessiter d'encapsuler votre projet entier avec browserify. Ce qui semble très exagéré. J'aimerais rester simple.

15
Forivin

Il existe une norme pour l'utilisation de la VoIP avec les navigateurs qui est prise en charge par tous les principaux navigateurs: WebRTC . Bien qu'il s'agisse d'une terrible bête de complexité, il est immédiatement pris en charge par tous les navigateurs principaux, ce qui cache sa complexité. Je ne suis pas un développeur javascript, mais je suppose fortement qu'il existe un support en or dans le monde de JS. cet article de blog .

Si vous ne voulez pas de la solution complète de surdose, je voudrais revenir à RTP en tant que protocole de diffusion en continu, qui est en quelque sorte un standard en VoIP et en Opus pour le codage. Les deux sont des technologies bien établies et forment en quelque sorte la paire par défaut de streaming VoIP, RTP est léger, et Opus est efficace pour compresser tout en obtenant une haute qualité audio. Ils devraient être bien supportés dans les environnements Browser et node.js.

Attention: si vous décidez d'envoyer du PCM simple, définissez avec précision tous les paramètres - longueur de trame (8, 16, 32 bits), un signe Signé/non signé, un entier/float et spécialement endianness!

3
Michael Beer

Vous ne devez pas utiliser directement les flux PCM bruts pour connecter le navigateur et l'application nodejs ensemble. Cela peut devenir très inutile en un rien de temps.

d’autre part, ce qui fonctionne dans le nœud peut ou non fonctionner dans le navigateur (allez vérifier votre dépôt pour voir ce que vous essayez de faire et vérifier si je peux vérifier quelque chose là-bas) "

Une autre solution consisterait à utiliser un serveur - tel que icecast -, qui rendrait tout le matériel d’arrière-plan/données très utile.

Ensuite, vous interagissez via un navigateur Web avec une balise HTML.

Vérifiez ceci -> () J'avais un lien vers un thread associé dans le débordement de pile, mais j'ai perdu IT Lol ()

Vous trouvez cela utile, Cordialement.

2
Ian Franco Pozo

Une des bibliothèques dans laquelle vous devez utiliser son socket.io et l’un des meilleurs tutoriels est son ici . vous pouvez l'apprendre et, après avoir créé l'application de chat dans node.js, démarrez l'application de chat vocal

0
yasin

SFMediaStream peut vous aider à diffuser des données audio de microphone à partir du navigateur, et vous pouvez les diffuser avec socket.io. Les données audio sont encodées en opus selon le navigateur.

Il a également une fonction permettant de donner un filtre/effet audio pour le streamer, et vous pouvez également utiliser la bibliothèque pour créer un lecteur vidéo/audio.

Peut-être serez-vous intéressé après avoir vérifié cet exemple de base

0
StefansArya