
Diffusion d'appels vers un navigateur avec les WebSockets Voice
Temps de lecture : 6 minutes
Nous avons récemment annoncé la prise en charge de prise en charge de WebSocket dans notre nouvelle Voice API. Les cas d'utilisation initiaux concernent la communication de serveur à serveur entre l'API Voice de Vonage et les plateformes d'IA vocale telles que IBM Watson ou Amazon Alexa. Cependant, j'aimerais vous montrer une petite démo que nous avons réalisée pour notre stand à AWS ReInvent et qui montre une autre utilisation. Elle montre la diffusion en continu de l'audio d'une conférence téléphonique vers un navigateur web et sa lecture à l'aide de l'API Web Audio API.
Pourquoi voudriez-vous faire cela ? Tout d'abord, c'est une bonne façon de montrer la nouvelle fonctionnalité WebSocket, mais en commençant à la développer, j'ai réalisé qu'elle était parfaite pour les cas d'utilisation où l'on souhaite qu'un grand nombre de personnes soient en mode "écoute seulement" lors d'un appel. Prenons l'exemple des conférences téléphoniques typiques des grandes entreprises. Si plus de 5 à 10 personnes parlent, c'est le chaos. Dans ces appels à grande échelle, la plupart des participants ne sont que des auditeurs passifs qui (espérons-le) restent muets. Mais cette méthode est plutôt inefficace et coûteuse. Elle n'est pas non plus très évolutive. Ces participants réguliers ne sont pas vraiment impliqués dans une conférence téléphonique ; leur participation s'apparente davantage à l'écoute d'une station de radio. Alors pourquoi ne pas leur proposer une technologie plus proche de la radiodiffusion ?
La nouvelle Vonage WebSocket Voice API de Vonage est une excellente solution pour ce cas d'utilisation. Je vais vous expliquer comment créer une application qui diffuse le son d'un appel à un certain nombre de navigateurs connectés.
Les détails techniques
Je vais vous présenter les détails techniques de ce fonctionnement et, je l'espère, vous faire comprendre les capacités de notre WebSocket Voice. Je suis très enthousiaste à propos de cette fonctionnalité car elle ouvre tout un monde de possibilités pour l'intégration de la Voice dans les applications Web.
Voici un schéma de l'ensemble :
architecture
La conférence téléphonique
Nous avons deux "domaines" : une conférence téléphonique typique hébergée sur l'API Vonage Voice, et une conférence téléphonique classique hébergée sur l'API Vonage Voice. Voice APIde Vonage, dans laquelle les interlocuteurs se connectent. Le code est assez simple, puisqu'il suffit de créer une nouvelle application Vonage et de faire pointer l'API answer_url vers un NCCO qui crée la conférence. Nous servons cette application à partir du serveur de l'application web.
Le NCCO se présente comme suit :
[
{
"action": "talk",
"text": "Connecting to Audio Socket Conf"
},
{
"action": "conversation",
"name": "audiosocket",
"eventUrl" : ["http://example.com/event"]
}
]Ainsi, lorsque les utilisateurs appellent un numéro lié à l'application, ils sont placés dans une conférence très basique. En outre, nous envoyons des événements au serveur de l'application web concernant l'état de la conférence téléphonique. Vous pouvez, bien sûr, ajouter des fonctions plus avancées à votre appel, comme la modération, un code PIN, etc.
Un participant WebSocket
Voici maintenant la partie (légèrement) compliquée. Lorsque vous interagissez avec l'API WebSocket de Vonage, votre application n'est pas un client WebSocket (par exemple, un navigateur). Votre application est un serveur WebSocket. Votre serveur d'application doit donc faire une demande à l'API REST de API REST de Vonage de Vonage pour indiquer à la plateforme vocale de faire de votre application un participant à la conférence en établissant une connexion websocket sortante vers votre serveur d'application web. Pour ce faire, nous faisons pointer le answer_url de l'appel sortant vers le même NCCO que celui utilisé pour les appels téléphoniques.
La demande d'appel sortant vers le websocket se présente comme suit :
POST /v1/calls
Host: api.nexmo.com
Authorization: Bearer [YOUR_JWT_TOKEN]
{ "to": [{
"type": "websocket",
"uri": "ws://example.com/socket",
"content-type": "audio/l16;rate=16000",
"headers": {
"app": "audiosocket"
}
}],
"from": {
"type": "phone",
"number": "442037831800"
},
"answer_url": ["http://example.com/ncco"]
}Ce diagramme de séquence montre les flux :
sequence diagram
Pour vous assurer qu'une seule connexion websocket est établie entre Vonage et le serveur d'application, vous devez suivre dans votre application l'état de cet appel et son identifiant (callid). Je vérifie le nombre de connexions client établies. Lorsqu'il est égal à zéro, je ferme à nouveau l'appel websocket via l'API REST. Seule la première connexion client initie la connexion de Vonage.
Traitement des données WebSocket entrantes
Une fois que la connexion websocket est établie entre Vonage et le serveur d'application, nous devons comprendre ce qu'elle envoie. Lors de la connexion initiale, l'API Voice envoie un " message " textuel unique contenant des données JSON. Il s'agit principalement de la description du format audio ainsi que de toutes les valeurs supplémentaires que vous transmettez depuis votre application via le NCCO lorsque la connexion a été créée (dans ce cas, app : audiosocket).
{
"app": "audiosocket",
"content-type": "audio/l16;rate=16000"
}Après le message texte initial, Vonage enverra des messages binaires contenant chacun 20 ms d'audio RAW. (Remarque : l'audio RAW n'est pas tout à fait la même chose qu'un fichier .wav.) Cela signifie que dans votre code, vous devrez déterminer si le message reçu est textuel ou binaire et le traiter en conséquence.
Envoi de données audio au navigateur
Pour lire ce fichier audio dans un navigateur à l'aide de WebAudionous devons transformer l'audio RAW en fichier .wav. Cela signifie qu'il faut ajouter un petit en-tête de 44 octets au fichier. Cependant, effectuer cette opération pour chaque image de 20 ms représenterait une surcharge considérable et, compte tenu de notre cas d'utilisation, nous pouvons tolérer une petite latence pour les auditeurs. Pour éviter cela, nous pouvons mettre en mémoire tampon 10 des messages de Vonage, les concaténer ensemble et coller l'en-tête de 44 octets sur le dessus. Nous obtiendrons ainsi un fichier .wav de 200 ms.
Nous pouvons ensuite diffuser ces fichiers .wav aux clients en parcourant une liste de websockets clients connectés et en envoyant à chacun le fichier sous la forme d'un message binaire.
Lecture de l'audio dans un navigateur
Sur le client web, nous devons créer du JavaScript pour nous connecter au serveur websocket et traiter les messages audio reçus. Comme le format audio que nous recevons de Vonage est 16bit 16Khz et que le format natif de la plupart des navigateurs est 32bit 44.1Khz, nous ne pouvons pas lire un flux constant avec WebAudio. Nous devons demander au navigateur de transcoder l'audio au taux de lecture approprié. La source de tampon WebAudio bufferSource fait cela très bien et ajoute très peu de latence, mais elle ne peut fonctionner qu'avec des fichiers discrets et une nouvelle instance doit être créée pour chaque fichier. Par conséquent, lorsqu'un nouveau fichier audio arrive sur le websocket, nous devons le passer à une fonction qui créera un nouveau fichier bufferSource et le lira sur l'interface principale audioContext.
L'autre point à prendre en compte est le timing. Si l'utilisation d'échantillons moins nombreux mais plus longs (200 ms contre 20 ms) permet de réduire la gigue, les messages n'arriveront toujours pas à l'intervalle exact. Par conséquent, si nous les jouons simplement l'un après l'autre, il y aura des problèmes. Heureusement, WebAudio dispose d'une interface de synchronisation très précise qui peut nous aider. En prenant le temps du premier échantillon comme T0 puis en comptant le nombre de messages reçus et en le multipliant par 0.2nous pouvons programmer chaque échantillon pour qu'il démarre au bon moment et réassembler le flux de manière à ce qu'il n'y ait pratiquement pas de problèmes.
Ces parties du code client sont détaillées ci-dessous avec des commentaires :
var startTime; // Make startTime a global var
ws.onmessage = function(event) {
// On the first message set the startTime to the currentTime from the audio context
if (count ==0){
startTime = audioContext.currentTime;
}
audioContext.decodeAudioData(event.data, function(data) {
count ++; // Keep a count of how many messages have been received
var playTime = startTime + (count *0.2) //Play each at file 200ms
playSound(data, playTime); //call the function to play the sample at the appropriate time
});
};
function playSound(buffer, playTime) {
var source = audioContext.createBufferSource(); //Create a new BufferSource fr the
source.buffer = buffer; // Put the sample content into the buffer
source.start(playTime); // Set the starting time of the sample to the scheduled play time
source.connect(analyserNode); //Connect the source to the visualiser
source.connect(audioContext.destination); // Also Connect the source to the audio output
}Bien sûr, il est toujours possible qu'un fichier arrive trop tard par rapport à l'heure de début prévue. Cependant, WebAudio est intelligent et ajuste la lecture pour qu'elle commence au bon endroit, comme si le fichier avait été présent dès le début. Ainsi, si un 200ms est censé être lu à T 1200ms mais qu'il n'est pas invoqué avant 1300msWebAudio sautera 100ms dans l'échantillon pour commencer la lecture. Cela signifie qu'il peut arriver qu'un petit début d'échantillon soit manqué, mais c'est tout à fait acceptable pour un son de type appel téléphonique. Cela ne fonctionnera peut-être pas aussi bien pour de la musique de haute qualité.
Obtenir le code
Et voilà : un flux audio unidirectionnel à faible latence de votre conférence téléphonique est lu directement dans un navigateur.
Consultez le code sur le site GitHub de la communauté Vonage. Organisation GitHub de la communauté Vonage et apprenez-en plus sur l Vonage WebSocket Voice API dans la documentation..