https://d226lax1qjow5r.cloudfront.net/blog/blogposts/video-ai-configurable-audio-processing-or-video-applications/videoai_audio-processing-module.png

Video + AI: procesamiento de audio configurable para Video Applications

Publicado el September 26, 2023

Tiempo de lectura: 6 minutos

A principios de este año, escribí sobre una aplicación Node que había creado para transcribir el audio de una videollamada utilizando la función Audio Connector de la Video API de Vonage. El objetivo principal de la aplicación y de la entrada de blog que la acompañaba era demostrar un caso de uso básico para el conector de audio. Cuando se piensa en el procesamiento de audio, la transcripción es probablemente una de las primeras cosas que vienen a la mente, por lo que tenía sentido centrarse en este caso de uso como una introducción a la función. Sin embargo, hay mucho más que puedes hacer con el procesamiento de audio, como ya habrás visto en otros artículos de esta serie, así que pensé que podría ser interesante incorporar algunas funciones más a esa aplicación para demostrar otros casos de uso. Puedes ver la aplicación actualizada en GitHub.

En este artículo voy a esbozar brevemente las características añadidas antes de proporcionar una explicación de alto nivel de algunas de las implementaciones en código. El artículo no va a cubrir la arquitectura general o la implementación de la iteración inicial de la aplicación, por lo que es posible que desee leer ese artículo primero. También puedes examinar el código (tal como estaba en ese momento de la historia) en nuestro Vonage Community GitHub org.

Antes de describir las nuevas funciones de la aplicación, recordemos brevemente qué es Audio Connector. Audio Connector es una función de la API de Video de Vonage que te permite enviar uno o más flujos de audio sin procesar (ya sea en forma individual o mezclados) desde una sesión de Video de Vonage, a través de tu propio servidor WebSocket, a servicios externos para su posterior procesamiento.

A diagram of the process flow when using Audio Connector. Video streams are sent to the Vonage Video Media router and the extracted audio streams sent by the Media Router to a pre-defined WebSocket serverAudio Connector process flow diagram

Para usarlo, debes realizar una llamada REST API a la Video API de Vonage, detallando la transmisión o transmisiones para las que deseas el audio, así como la dirección WebSocket a la que enviarlas. Básicamente, la función del conector de audio es extraer datos de audio de una llamada de video y enviar esos datos a un WebSocket. Lo que hagas después con esos datos de audio depende completamente de ti.

Puede obtener más información sobre el conector de audio en la documentación de la Video API.

En la primera iteración de la aplicación, utilizamos el Symbl.ai JavaScript SDK para enviar los datos de audio a la de Symbl de Symbl para la transcripción. Veamos ahora algunas de las nuevas funciones de procesamiento de audio en la iteración actual de la aplicación.

¿Qué hay de nuevo?

Además de la transcripción, la aplicación de demostración ofrece ahora una serie de datos específicos basados en el audio de una videollamada.

  • Preguntas: Las preguntas se extraen del audio cuando un participante en la videollamada dice algo como parte de la conversación que se enmarca como una pregunta, por ejemplo, "¿cuándo está el presupuesto para este proyecto?"

  • Acciones: Similares a las preguntas, se extraen de una conversación cuando algo se enmarca como una tarea específica a completar, por ejemplo "Completaré la entrada del blog para el final del sprint actual."

  • Temas: Los temas se extraen de una conversación cuando se detecta una palabra o frase que se determina que es una palabra clave importante.

Flujo de aplicaciones

Las nuevas funciones se han implementado de forma que se puedan configurar las partes del audio que el usuario final desea procesar.

El flujo de la aplicación comienza con un formulario que contiene una serie de casillas de verificación, cada una de las cuales corresponde a una opción de procesamiento. El usuario selecciona las opciones que desea y hace clic en el botón "Videollamada con procesamiento de audio".

A form with checkboxes for 'Transcription', 'Action Items', 'Questions', and 'Topics' (with the Transcription and Questions options checked) and a 'Video Call with Audio Processing' buttonApplication Config Options Screen

A continuación, se accede a la pantalla principal de la llamada, donde se puede introducir el nombre e iniciar la llamada. Hay un botón para iniciar el procesamiento de audio y enlaces a páginas que muestran el audio procesado para las opciones seleccionadas.

A web app screen with a video thumbnail. A meeting link (which is a URL for additional participants to join the meeting), buttons for 'Join' and 'Start Processing', and a couple of links for Processed Output: 'Get Transcription' and 'Get Questions'Main call screen

Al hacer clic en el enlace "Obtener preguntas", por ejemplo, el usuario accedería a una página en la que se enumerarían todas las preguntas detectadas durante la videollamada.

A screen showing the rendered text output of questions raised during the video call: "What is the budget for this project?", "What is the timeline for this project?"Questions output screen

Código Explicación

En la iteración inicial de la aplicación, el procesamiento de la transcripción se gestionaba a través de la Symbl.ai's Streaming APImás específicamente, mediante el uso del SDK de JavaScript para realizar una solicitud a la API utilizando su método sendAudio de Symbl.ai. Había dos aspectos principales para implementar esto:

  • Definición de un controlador para el habla detectada por el SDK de Symbl startRealtimeRequest de Symbl SDK.

  • Hacer algo con los datos devueltos (que en este caso significaba almacenarlos en un transcriptions array). Este array estaba disponible a través del objeto app.context de modo que su contenido pudiera representarse posteriormente según fuera necesario.

Las nuevas funciones de procesamiento (preguntas, elementos de acción y temas) también están disponibles a través de la Streaming API de Symbl, pero requieren gestores adicionales. También decidí almacenar los datos de respuesta de los distintos gestores en matrices separadas (aunque también podría haberlos almacenado en la misma matriz y filtrarlos al renderizar).

Otro aspecto de las nuevas funciones es que son configurables. Así, al invocar startRealtimeRequestsólo quería añadir los manejadores que fueran necesarios.

La lógica para gestionar todo esto se extrajo a una clase SymblProcessor y en lugar de definir un transcriptions en app.context dentro del index.js un objeto SymblProcessor y se añade al contexto:

app.context.symblProcessor = new SymblProcessor();

Gran parte de la nueva implementación es manejada por esta SymblProcessor clase.

class SymblProcessor {
	constructor() {
		this.messages = [];
		this.insights = [];
		this.topics = [];
		this.config = {
			transcription: false,
			actionItems: false,
			questions: false,
			topics: false
		};
	}

	setConfig(config) {
		config.forEach(option => this.config[option] = true);
	}

	sethandlers() {
		let handlers = {};
		if (this.config.transcription) { handlers.onMessageResponse = this.onMessageResponseHandler; }
		if (this.config.actionItems || this.config.questions) { handlers.onInsightResponse = this.onInsightResponseHandler; }
		if (this.config.topics) { handlers.onTopicResponse = this.onTopicResponseHandler; }
		return handlers;
	}

	setInsightTypes() {
		let insightTypes = [];
		if (this.config.actionItems) { insightTypes.push('action_item'); }
		if (this.config.questions) { insightTypes.push('question'); }
		return insightTypes;
	}

	getTranscriptions() {
		return this.messages.map(message => ({id: message[0].from.id, name: message[0].from.name, transcription: message[0].payload.content}));
	}

	getActionItems() {
		let actionItems = this.insights.filter(insight => insight[0].type == 'action_item');
		return actionItems.map(item => item[0].payload.content);
	}

	getQuestions() {
		let questions = this.insights.filter(insight => insight[0].type == 'question');
		return questions.map(question => question[0].payload.content);
	}

	getTopics() {
		return this.topics.map(topic => topic[0].phrases);
	}

	onMessageResponseHandler = (data) => {
		this.messages.push(data);
	}

	onInsightResponseHandler = (data) => {
		this.insights.push(data);
	}

	onTopicResponseHandler = (data) => {
		this.topics.push(data);
	}
}

La clase define un constructor con matrices separadas para messages (transcripciones), insights (que contiene tanto preguntas como elementos de acción), y topicsasí como un valor por defecto config.

A continuación, dispone de tres métodos (setConfig, sethandlersy setInsightTypes) para actualizar el objeto config objeto (en función de las opciones elegidas por el usuario) y luego utilizar esa actualización config actualizado para determinar handlers y insightTypes para la symblSdk.startRealtimeRequest llamada.

Los métodos restantes son las definiciones de los propios manejadores (que esencialmente sólo empujan los datos devueltos a la matriz apropiada) y los métodos para recuperar datos de las matrices y filtrarlos y/o mapearlos según sea necesario para que luego se muestren en las vistas pertinentes.

Hay algunos cambios adicionales en la aplicación, como actualizaciones del postSymblCall controlador, postSymblProcessing controlador, rutas adicionales, manejadores de ruta y vistas, y algunos de reestructuración / cambio de nombre de archivos, funciones, etc. No voy a entrar en detalles sobre estos cambios, ya que son principalmente sólo para facilitar la extracción de gran parte de la lógica de procesamiento a la SymblProcessor clase.

Conector de audio: Flujos individuales frente a combinados

Como nota final, sólo quería destacar un aspecto del uso de la función Conector de audio, que podría determinar cómo implementas una aplicación que la utilice.

La dirección /connect punto final de la Video API de Vonage, que se utiliza para iniciar una conexión WebSocket del conector de audio, te da la opción de pasar una matriz streams como parte del cuerpo de la solicitud, que contiene los ID de los flujos cuyo audio debe enviarse al WebSocket.

{
  "sessionId": "Vonage Video API session ID",
  "token": "A valid Vonage Video API token",
  "websocket": {
    "uri": "wss://service.com/ws-endpoint",
    "streams": [
      "streamId-1",
      "streamId-2"
    ],
    "headers": {
      "headerKey": "headerValue"
    },
    "audioRate" : 8000
  }
}

Si se omite esta propiedad streams se envía el audio combinado de todos los flujos de la sesión.

Dado que la implementación inicial de esta aplicación se centraba en la transcripción, y yo quería que partes específicas del audio transcrito se atribuyeran a hablantes individuales identificados dentro de la videollamada, la aplicación se implementó realizando múltiples peticiones a este punto final, cada una especificando un único flujo, y definiendo un socket web diferente para cada flujo.

Sin embargo, si los requisitos de su aplicación no requieren que atribuya el audio transcrito a hablantes individuales identificados (digamos, por ejemplo, que desea recopilar las preguntas planteadas en la llamada, pero no necesita saber quién ha formulado la pregunta), entonces probablemente tendría más sentido combinar el audio y enviarlo a un único punto final de socket web.

Próximos pasos

Esperamos que este artículo te haya dado algunas ideas sobre el tipo de funciones que puedes crear con Audio Connector y que te haya inspirado para crear algo increíble.

Si tienes algún comentario o pregunta, o simplemente quieres compartir un proyecto que hayas construido con Audio Connector, no dudes en ponerte en contacto con nosotros en nuestro Slack para desarrolladores de Vonage.

Compartir:

https://a.storyblok.com/f/270183/373x376/e8d3211236/karl-lingiah.png
Karl LingiahDefensor del desarrollador Ruby

Karl es un defensor de los desarrolladores para Vonage, centrado en el mantenimiento de nuestros SDK de servidor Ruby y la mejora de la experiencia de los desarrolladores para nuestra comunidad. Le encanta aprender, hacer cosas, compartir conocimientos y, en general, todo lo relacionado con la tecnología web.