
Compartir:
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.
Traducción en tiempo real con Video API de Vonage
Tiempo de lectura: 8 minutos
Introducción
En el panorama en constante evolución de la comunicación digital, la traducción en tiempo real de las videollamadas se ha convertido en un componente vital para derribar las barreras idiomáticas y fomentar la colaboración global. Con la Video API de Vonage, los desarrolladores tienen el poder de integrar a la perfección esta capacidad transformadora en sus aplicaciones.
Si estás creando una plataforma de conferencias transfronterizas, una aplicación de aprendizaje de idiomas o mejorando la atención al cliente a escala global, nuestra funcionalidad de traducción en tiempo real acelerará tu desarrollo. En esta entrada de blog, aprenderás a crear experiencias de Video inclusivas y accesibles para una audiencia mundial. ¡Vamos allá!
Demostración de traducción en tiempo real
Antes de empezar, echemos un vistazo a lo que vamos a construir. Echa un vistazo a este Video de muestra:
Ahora bien, si quieres ver una demostración de una aplicación más compleja con 2 usuarios que hablan en idiomas diferentes, consulta este otro vídeo:
Requisitos previos
Una cuenta de Video API de Vonage. Haz clic en Regístrate para crear una si aún no la tienes.
Node.js instalado API de Experience Composer habilitado en su Account. Puede hacerlo en el portal de cuentas
Una Account de Google Cloud Platform y un proyecto con Texto a voz, traducir y voz a texto API habilitadas.
Esta publicación del blog asume que estás familiarizado con la Video API de Vonage.
El compromiso de medios de su sitio web debe ser alto para que la política de reproducción automática de Google no bloquee la reproducción automática de audio. Más información aquí. Para realizar pruebas, puede utilizar interruptores para desarrolladores
Diagrama de arquitectura: Qué construiremos
El siguiente diagrama muestra la arquitectura de lo que vamos a construir. Para empezar, podemos utilizar la Conector de Audio API para obtener los datos de audio en bruto (PCM 16 Khz/16bit) de nuestra sesión de Video enviados a nuestro servidor a través de una conexión WebSocket segura. A continuación, nuestro servidor establecerá una conexión con Google Cloud para canalizar los datos de audio sin procesar, transcribirlos, traducirlos y obtener los datos de audio traducidos.
Real-Time Translation Architectual Diagram
Una vez traducidos los subtítulos y los datos de audio, podemos aprovechar la API de Experience Composer para inyectar el audio en la sesión de Video. La API Experience Composer te permite publicar el contenido de un sitio web como una nueva transmisión en una sesión de la Video API de Vonage.
Por lo tanto, podemos exponer una página web sencilla que reciba el audio de nuestro servidor y lo reproduzca utilizando la API de audio web. Dado que el contenido de esta página web se está publicando en una sesión gracias a la potencia de Experience Composer, todos los usuarios de la llamada podrán escuchar las traducciones. Esta lógica se describe en la siguiente captura de pantalla:
Vonage API Real-Time Translation Logic
Quizás pienses, ¿por qué no enviar directamente el audio a cada usuario a través de una conexión WebSocket y reproducirlo localmente? Bueno, la respuesta corta es que TCP no es la opción preferida para enviar datos de audio en tiempo real, y en condiciones de red no ideales en el lado del cliente, oiríamos el audio traducido con bastante latencia. Si utilizamos la API de Experience Composer, no tendríamos que depender de las redes de cada usuario. Por lo tanto, puede pensar en Experience Composer en este caso como un usuario cuyo trabajo consiste en recibir el audio traducido desde nuestro servidor y reproducirlo en la llamada.
Cómo construir nuestro middleware WebSocket
El servidor es el middleware responsable de recibir el audio de la sesión de Video a través de WebSocket desde la API Audio Connect y de enviarlo a Google para el reconocimiento de voz, la traducción y el procesamiento de texto a voz.
Expondremos un punto final de la API que nos permita iniciar la instancia de la API del conector de audio para recibir datos de audio. El punto final start streaming llamará a la función startStreamer con la función streamId del usuario y también pasaremos la URL del websocket donde queremos recibir los datos de audio. Esta API se llamará desde el lado del cliente una vez que el usuario comience a publicar en la sesión. También instanciaremos una instancia de 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;
}
};
Para recibir datos de audio en nuestro servidor necesitamos configurar un websocket para que Vonage pueda enviarnos los datos de audio sin procesar. Inicializaremos una conexión de cliente de streaming con Google por usuario.
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;
});
});
Puede ver la implementación del createExperienceComposer en este archivo. Para autenticar las peticiones con la API, necesitamos crear un token JWT como se explica en la documentación de autenticación. He utilizado el paquete jsonwebtoken de npm pero puedes usar cualquier otro paquete. `CreateRender` y `deleteRender` son funciones destinadas a iniciar y detener una instancia de compositor de experiencia.
Para crear la instancia del compositor de experiencias, necesitamos pasar algunos parámetros en el cuerpo JSON según la documentación. Algunos de los parámetros son obligatorios, como url, sessionId, token y projectId. Preste especial atención al parámetro URL. Piense que es la URL que visitará el "usuario invisible" que representa Experience Composer. A continuación, se cargará el código Javascript y Experience Composer publicará un nuevo flujo en la sessionId.
Habrás notado que hay un objeto extra properties que contiene un parámetro name parámetro. Este nombre será útil cuando escuchemos eventos streamCreated eventos en el lado del cliente. Vamos a volver a eso en un poco, pero todo lo que necesita saber por ahora es que este es el nombre de la corriente que vamos a publicar en la sesión.
app.get('/ec', (req, res) => {
const options = {
root: path.join(__dirname),
};
res.sendFile('public/ec.html', options);
});
Cómo integrarse con las API de Google Cloud Text
Puedes echar un vistazo al código de ejemplo terminado para el conector de Google en este repositorio. Sin embargo, vamos a hablar brevemente sobre ello. La función principal que inicia la conexión con Google se llama startRecognised. Necesitamos definir el objeto config, con el idioma de origen, el modelo y la codificación. Puedes consultar el código de ejemplo para conocer los parámetros elegidos.
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;
}
Una vez que Google haya transcrito el discurso, podemos pasarlo a través de la API de traducción y el texto traducido lo convertimos en datos de audio utilizando la API de texto a voz. Esta es la definición del método translate que se utiliza para traducir el texto al idioma de destino
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;
}
Por último, una vez que tenemos el texto traducido llamamos al método playTTS para obtener los datos de audio del texto traducido. El audio no se reproducirá todavía, sólo estamos convirtiendo el texto traducido en datos de audio. El objeto de petición toma varios parámetros como la codificación y la voz. Puedes personalizarlo consultando Google text-to-speech docs
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);
}
}
} Conclusión
En resumen, la traducción de vídeo en tiempo real no sólo rompe las barreras lingüísticas, sino que abre una nueva dimensión en las posibilidades de comunicación global. ¿Qué cosas nuevas vas a construir? ¿Qué nuevas funciones te entusiasmarían? Háznoslo saber en nuestror Vonage Community Slack o envíanos un mensaje a X, antes conocido como Twitter.
Descargo de responsabilidad
La lógica de la aplicación, el ensamblaje de componentes y los flujos de llamadas a la API que se describen en este artículo y en el repositorio de Github correspondiente son un subconjunto de reivindicaciones que forman parte de una patente pendiente de Vonage. Puedes usar este código fuente de referencia y la lógica de la aplicación para tus propias implementaciones comerciales mediante la API de Video de Vonage y cualquier proveedor de servicios de IA para capacidades como reconocimiento de voz, traducción, conversión de texto a voz...