https://s3.amazonaws.com/a.storyblok.com/f/270183/51781/f33a65b97c/real-time-translation.png

Traduction en temps réel avec Video API de Vonage

Temps de lecture : 7 minutes

Introduction

Dans le paysage en constante évolution de la communication numérique, la traduction en temps réel des appels Video est devenue un élément essentiel pour faire tomber les barrières linguistiques et favoriser la collaboration mondiale. Avec l'API Video de Vonage, les développeurs ont la possibilité d'intégrer de manière transparente cette capacité de transformation dans leurs Applications.

Que vous construisiez une plateforme de conférence transfrontalière, une application d'apprentissage des langues ou que vous amélioriez le support client à l'échelle mondiale, notre fonctionnalité de traduction en temps réel accélérera votre développement. Dans cet article de blog, vous apprendrez à créer des expériences vidéo inclusives et accessibles pour un public mondial. Plongeons dans l'aventure !

Démonstration de traduction en temps réel

Avant de commencer, voyons ce que nous allons construire. Jetez un coup d'œil à cet exemple de Video :

Si vous voulez voir une démonstration d'une application plus complexe avec deux utilisateurs parlant des langues différentes, regardez cette autre vidéo :

Conditions préalables

  1. Un compte Video API de Vonage. Cliquez sur S'inscrire pour en créer un si vous n'en avez pas déjà un.

  2. Node.js installé API Experience Composer Experience Composer activée dans votre Account. Vous pouvez le faire dans le portail Account

  3. Un compte Google Cloud Platform et un projet avec les éléments suivants Synthèse vocale, traduire et Speech-to-text API activées.

  4. Cet article de blog suppose que vous connaissez l'API Video de Vonage.

  5. L'engagement médiatique de votre site web doit être élevé pour que la politique de lecture automatique de Google ne bloque pas l'audio en lecture automatique. Pour en savoir plus ici. À des fins de test, vous pouvez utiliser les commutateurs pour développeurs

Diagramme d'architecture : Ce que nous allons construire

Le diagramme suivant montre l'architecture de ce que nous allons construire. Pour commencer, nous pouvons utiliser l Connecteur audio API pour obtenir les données audio brutes (PCM 16 Khz/16bit) de notre session Video envoyées à notre serveur via une connexion WebSocket sécurisée. Notre serveur établira ensuite une connexion avec Google Cloud pour acheminer les données audio brutes, les transcrire, les traduire et obtenir les données audio traduites.

Real-Time Translation Architectual DiagramReal-Time Translation Architectual Diagram

Une fois les sous-titres et les données audio traduits, nous pouvons utiliser l'API Experience Composer API pour injecter l'audio dans la session Video. L'API Experience Composer vous permet de publier le contenu d'un site Web en tant que nouveau flux dans une session API Video de Vonage.

Par conséquent, nous pouvons exposer une simple page web qui reçoit l'audio de notre serveur et le joue à l'aide de l'API Web Audio API. Étant donné que le contenu de ce site web est publié dans une session grâce à la puissance d'Experience Composer, tous les utilisateurs participant à l'appel pourront entendre les traductions. Cette logique est décrite dans la capture d'écran suivante :

Vonage API Real-Time Translation LogicVonage API Real-Time Translation Logic

Pourquoi ne pas envoyer directement l'audio à chaque utilisateur via une connexion WebSocket et l'écouter localement ? En bref, la réponse est que TCP n'est pas l'option préférée pour envoyer des données audio en temps réel, et dans des conditions de réseau non idéales du côté du client, nous entendrions l'audio traduit avec une latence assez importante. Si nous utilisons l'API Experience Composer, nous n'aurons pas à nous fier aux réseaux des utilisateurs individuels. Par conséquent, vous pouvez considérer Experience Composer dans ce cas comme un utilisateur dont le travail consiste à recevoir l'audio traduit de notre serveur et à le jouer dans l'appel.

Comment construire notre intergiciel WebSocket

Le serveur est l'intergiciel chargé de recevoir l'audio de la session Video via WebSocket à partir de l'API Audio Connect et de l'envoyer à Google pour la reconnaissance vocale, la traduction et le traitement de la synthèse vocale.

Nous exposerons un point de terminaison API qui nous permettra de démarrer l'instance API du connecteur audio afin de recevoir des données audio. Le point de terminaison start streaming appellera la fonction startStreamer à l'aide de la fonction streamId de l'utilisateur et nous transmettons également l'URL du websocket où nous voulons recevoir les données audio. Cette API sera appelée du côté client une fois que l'utilisateur commencera à publier dans la session. Nous allons également instancier une instance d'Experience Composer :

app.post('/startStreaming', async (req, res) => {
    try {
        console.log('someone wants to stream');
        const {
            streamId,
            sessionId
        } = req.body;
        console.log(streamId, sessionId);
        const response = await startStreamer(streamId, sessionId);
        const render = await createExperienceComposer(process.env.sessionId);
        if (render && response) {
            return res.send(response);
        }
        res.send(response);
    } catch (e) {
        console.log(e);
    }
});
const startStreamer = async (streamId, sessionId) => {
    try {
        const {
            token
        } = await opentok.generateToken(sessionId, 'publisher');


        const data = JSON.stringify({
            sessionId: sessionId,
            token: token,
            websocket: {
                uri: `${process.env.websocket_url}/socket`,
                streams: [streamId],
                headers: {
                    from: streamId,
                },
            },
        });
        const config = {
            method: 'post',
            url: `https://api.opentok.com/v2/project/${process.env.VIDEO_API_API_KEY}/connect`,
            headers: {
                'X-OPENTOK-AUTH': await generateRestToken(),
                'Content-Type': 'application/json',
            },
            data: data,
        };
        const response = await axios(config);
        console.log(response.data);
        return response.data;
    } catch (e) {
        console.log(e);
        return e;
    }
};

Pour recevoir les données audio sur notre serveur, nous devons configurer une socket web afin que Vonage puisse nous envoyer les données audio brutes. Nous allons initialiser une connexion client de streaming avec Google par utilisateur.

app.ws('/socket', async (ws, req) => {
    console.log('someone connected');
    console.log(`starting connection for user with streamId ${streamId}`)
    const uuid = req?.query?.uuid
    let sc = new StreamingClient(uuid, 'es', 'en-US');
    await sc.init();
    const aWss = expressWs.getWss().clients;


    sc.setAudioChunkAvailableCallback(function(chunk) {
        console.log('audio available. Sending audio to the clients connected to the wss');


        const aWss = expressWs.getWss('/socket').clients;
        if (ws.readyState === 1) {
            aWss.forEach(function(client) {
                client.send(chunk);
            });
        }
    });


    sc.setTranscriptionAvailableCallback(function(data) {
        console.log(‘Sending original and translated captions to the connected clients’);
        const aWss = expressWs.getWss('/socket').clients;
        aWss.forEach(function(client) {
            client.send(JSON.stringify(data));
        });
    });
    sc.startRecognizer();
    ws.on('message', (msg) => {
        try {
            if (typeof msg === 'string') {
                let config = JSON.parse(msg);
                console.log(config);
                sc.setId(config.from);
            } else {
                console.log(‘Sending audio data to Google
                    for Speech to text recognition’);
                sc.sendMessage(msg);
            }
        } catch (err) {
            console.log('[' + uuid + '] ' + err);
            ws.removeAllListeners('message');
            ws.close();
        }
    });


    ws.on('close', () => {
        console.log('[' + uuid + '] Websocket closed');
        sc.closeConversation();
        sc = null;
    });
});

Vous pouvez voir la mise en œuvre du createExperienceComposer dans ce fichier. Pour authentifier les requêtes avec l'API, nous devons créer un jeton JWT comme expliqué dans la documentation sur l'authentification. J'ai utilisé le paquetage jsonwebtoken de npm mais vous pouvez utiliser n'importe quel autre paquet. `CreateRender` et `deleteRender` sont des fonctions destinées à démarrer et arrêter une instance de compositeur d'expérience.

Pour créer l'instance du compositeur d'expérience, nous devons passer quelques paramètres dans le corps JSON, conformément à la documentation. Certains paramètres sont obligatoires, tels que url, sessionId, token et projectId. Accordez une attention particulière au paramètre URL. Considérez-le comme l'URL que l'"utilisateur invisible" que représente Experience Composer visitera. Le code Javascript sera ensuite chargé et l'Experience Composer publiera un nouveau flux dans le sessionId.

Vous avez peut-être remarqué qu'il y a un objet supplémentaire properties qui contient un name paramètre. Ce nom s'avérera utile lorsque nous écouterons les événements streamCreated côté client. Nous y reviendrons dans un instant, mais tout ce que vous devez savoir pour l'instant, c'est qu'il s'agit du nom du flux que nous allons publier dans la session.

app.get('/ec', (req, res) => {
    const options = {
        root: path.join(__dirname),
    };
    res.sendFile('public/ec.html', options);
});

Comment intégrer les API de Google Cloud Text ?

Vous pouvez jeter un coup d'œil à l'exemple de code terminé pour le connecteur Google dans ce dépôt. Cependant, parlons-en brièvement. La fonction principale qui démarre la connexion avec Google est appelée startRecognised. Nous devons définir l'objet config, avec la langue source, le modèle et l'encodage. Vous pouvez vous référer à l'exemple de code pour les paramètres choisis.

startRecognizer() {
    const config = {
        encoding: encoding,
        sampleRateHertz: sampleRateHertz,
        model: model,
        enableAutomaticPunctuation: true,
        languageCode: this.language,
    };

    const request = {
        config: config,
        interimResults: true,
    };

    this.recognizeStream = this.speechClient.streamingRecognize(request);
    this.recognizeStream.on('data', async (data) => {
        let originalText = data.results\[0].alternatives\[0].transcript;
        console.log('\[' + this.id + ']\[User]' + originalText);
        let translatedText = await this.translate(originalText);
        if (this.transcriptAvailableCallback) {
            this.transcriptAvailableCallback({
                original: originalText,
                translated: translatedText,
                id: this.id,
            });
        }
        this.getAudioData(translatedText);
    });

    this.recognizeStream.on('error', (err) => {
        console.error(err);
        this.tryEndAudioStream();
        if (this.errorCallback) {
            this.errorCallback(err);
        }
    });

    this.recognizeStream.on('finish', (res) => {});
    console.log('\[' + this.id + '] Stream Created');
    this.waitingRestart = false;
    return this.recognizeStream;
}

Une fois le discours transcrit par Google, nous pouvons le transmettre à l'API Translate API et le texte traduit est converti en données audio à l'aide de l'API API de conversion du texte en parole. Voici la définition de la méthode translate qui est utilisée pour traduire le texte dans la langue cible

async translate(text) {
    let output = '';
    try {
        let [translations] = await this.translationClient.translate(text, this.outputLanguage);
        translations = Array.isArray(translations) ? translations : [translations];
        translations.forEach((translation, i) => {
            output += `${translation}`;
        });
    } catch (err) {
        console.log(err);
    }
    console.log(output);
    return output;
}

Enfin, une fois le texte traduit, nous appelons la méthode playTTS pour obtenir des données audio à partir du texte traduit. L'audio ne sera pas encore joué, nous ne faisons que convertir le texte traduit en données audio. L'objet de requête prend plusieurs paramètres comme l'encodage et la Voice. Vous pouvez le personnaliser en consultant La documentation de Google sur la synthèse vocale

async getAudioData(text, isSessionEnding = false) {
    try {
        this.sessionEnding = isSessionEnding;
        const request = {
            input: {
                text: text
            },
            voice: {
                languageCode: this.outputLanguage,
                ssmlGender: 'NEUTRAL'
            },
            audioConfig: {
                audioEncoding: encoding,
                sampleRateHertz: sampleRateHertz,
            },
        };
        const [response] = await this.ttsClient.synthesizeSpeech(request);
        if (this.audioChunkAvailableCallback) {
            this.audioChunkAvailableCallback(response.audioContent);
        }
    } catch (err) {
        if (this.errorCallback) {
            this.errorCallback(err);
        }
    }
}

Conclusion

En résumé, l'infusion de la traduction vidéo en temps réel ne se contente pas d'abattre les barrières linguistiques ; elle ouvre une toute nouvelle dimension dans les possibilités de communication mondiale. Quelles nouvelles choses allez-vous construire ? Quelles sont les nouvelles fonctionnalités qui vous enthousiasmeraient ? Faites-le nous savoir sur notreCommunauté Vonage Slack ou envoyez-nous un message sur X, anciennement connu sous le nom de Twitter.

Clause de non-responsabilité

La logique d'application, l'assemblage des composants, les flux d'appels API, décrits dans cet article et dans le dépôt Github correspondant, sont un sous-ensemble de revendications faisant partie d'un brevet Vonage en cours. Vous pouvez utiliser ce code source de référence et cette logique d'application pour vos propres déploiements commerciaux en utilisant l'API Video de Vonage et tout fournisseur de services d'IA pour des capacités telles que la reconnaissance vocale, la traduction, la synthèse vocale.

Partager:

https://a.storyblok.com/f/270183/384x384/6007824739/javier-molina-sanz.png
Javier Molina Sanz

Javier studied Industrial Engineering back in Madrid where he's from. He is now one of our Solution Engineers, so if you get into trouble using our APIs he may be the one that gives you a hand. Out of work he loves playing football and travelling as much as he can.