Conector de vídeo para Node.js
La biblioteca Vonage Video Connector para Node.js te permite participar mediante programación en sesiones de la Video API de Vonage como participante del lado del servidor. Esta biblioteca te permite conectarte a sesiones de vídeo, publicar y suscribirte a transmisiones, y procesar datos de audio y vídeo en tiempo real.
Importante La biblioteca Vonage Video Connector para Node.js está diseñada para aplicaciones del lado del servidor y requiere credenciales y tokens válidos de la Video API de Vonage con los permisos adecuados.
Esta página trata sobre la API de Node.js. Para conocer los Concepts, los formatos multimedia y el comportamiento en tiempo de ejecución comunes a todas las bibliotecas de Video Connector, consulta la Conector de vídeo guía. Si trabajas con Python, consulta Conector de vídeo para Python.
Este tema incluye las siguientes secciones:
- Primeros pasos
- Requisitos
- La API basada en promesas
- Estructuras de datos
- Conectarse a una sesión
- Configuración de la sesión
- Flujos de publicación
- Suscripción a flujos
- Tratamiento de datos de audio
- Tratamiento de datos de vídeo
- Gestión de la memoria intermedia
- Llamadas de retorno de eventos
- Gestión de errores
Primeros pasos
El Vonage Video Connector para Node.js se distribuye como el @vonage/video-connector paquete.
Para instalar la biblioteca, ejecuta:
El paquete incluye binarios nativos ya compilados y no requiere ningún paso de compilación. Las declaraciones de tipos de TypeScript están
incluidas, por lo que no es necesario @types Se necesita el paquete.
Requisitos
Esta biblioteca requiere Node.js 18 o una versión posterior, ejecutándose en plataformas Linux x64 o ARM64. Recomendamos utilizar Debian Bookworm, ya que es la distribución en la que se ha probado más a fondo.
Al cargar la biblioteca en una plataforma no compatible se produce un error en el momento de la importación, ya que no existe el módulo nativo para esa combinación de plataforma y arquitectura.
La API basada en promesas
La biblioteca Node.js presenta las operaciones del ciclo de vida como promesas en lugar de como funciones de devolución de llamada de finalización. Esto define la API en su conjunto, por lo que conviene comprenderlo antes de seguir leyendo el resto de esta página.
Cada uno de connect(), publish(), subscribe(), disconnect(), unpublish()y unsubscribe() devuelve una
promesa:
| Método | Se aprueba lo siguiente: | Se resuelve cuando |
|---|---|---|
connect() |
Session |
Se ha establecido la conexión de la sesión |
publish() |
Publisher |
Se ha creado tu transmisión publicada |
subscribe() |
Subscriber |
La suscripción ya está activada |
disconnect() |
void |
La desconexión se ha completado |
unpublish() |
void |
Se ha completado la retirada de la publicación |
unsubscribe() |
void |
Se ha completado la baja |
Por consiguiente, los eventos del ciclo de vida que se producen una sola vez no tienen una llamada de retorno correspondiente en esta biblioteca: la promesa
es la notificación. No hay onConnected, sin editorial onStreamCreated, y ningún suscriptor
onConnected para registrarse. El resto de eventos son funciones de devolución de llamada que se pasan en el objeto de argumentos.
La gestión de errores sigue la misma estructura. Si se produce un error antes de una operación se liquida, la promesa se rechaza y
tu onError No se ejecuta la función de devolución de llamada; el rechazo constituye la notificación de error. Si se produce un error después de Una vez que
la operación se haya completado, tu onError La función de devolución de llamada se ejecuta con normalidad.
import { VonageVideoClient } from '@vonage/video-connector';
const client = new VonageVideoClient();
try {
const session = await client.connect(applicationId, sessionId, token, {
// Fires only for errors after the connection is established
onError: (session, description, code) => {
console.error(`Session error [${code}]: ${description}`);
},
});
console.log(`Connected to session ${session.sessionId}`);
} catch (error) {
// Failure to connect surfaces here, not in onError
console.error('Failed to connect:', error);
}
Importante
Crea solo uno VonageVideoClient una instancia por proceso. El SDK subyacente admite una única conexión activa, y la creación de varias instancias da lugar a un comportamiento indefinido.
Estructuras de datos
Todos los tipos que se indican a continuación se exportan desde el paquete y pueden importarse como tipos.
Sesión
Representa una sesión de la Video API de Vonage a la que se pueden conectar los clientes:
interface Session {
sessionId: string; // Unique identifier for the session
}
El Session El objeto se pasa a las funciones de devolución de llamada a nivel de sesión para identificar qué sesión ha desencadenado el evento.
Conexión
Representa la conexión de un participante a una sesión:
interface Connection {
connectionId: string; // Unique identifier for the connection
creationTime: number; // Unix timestamp in milliseconds
data: string; // Connection data (encoded in the token)
}
Los datos de conexión pueden utilizarse para almacenar metadatos personalizados sobre los participantes, como los identificadores de usuario o sus funciones.
Ten en cuenta que creationTime es una marca de tiempo numérica de Unix expresada en milisegundos, por lo que se puede pasar directamente a
new Date(connection.creationTime).
Transmisión
Representa una transmisión multimedia (audio/vídeo) publicada por un participante:
interface Stream {
streamId: string; // Unique identifier for the stream
connection: Connection; // The connection that published this stream
}
Editorial
Representa su flujo publicado en la sesión:
interface Publisher {
stream: Stream; // The underlying stream for this publisher
}
Suscriptor
Representa una suscripción al flujo de otro participante:
interface Subscriber {
stream: Stream; // The stream being subscribed to
}
AudioData
Representa los datos de audio que se transmiten o se reciben:
interface AudioData {
data: Buffer; // Raw PCM 16-bit LE samples
sampleRate: SampleRate; // 8000 | 12000 | 16000 | 24000 | 32000 | 44100 | 48000
numberOfChannels: NumberOfChannels; // 1 (mono) or 2 (stereo)
numberOfFrames: number; // One frame = one sample per channel
}
Requisitos de formato de audio:
datadebe ser unBufferque contiene muestras de 16 bits con signo en formato «little-endian»- Frecuencias de muestreo válidas: 8000, 12000, 16000, 24000, 32000, 44100, 48000 Hz
- Canales: 1 (mono) o 2 (estéreo)
data.byteLengthdebe ser como mínimonumberOfFrames * numberOfChannels * 2bytes
VideoFrame
Representa los datos de un fotograma de vídeo que se están transmitiendo o recibiendo:
interface VideoFrame {
data: Buffer; // Raw pixel data
width: number; // Frame width in pixels
height: number; // Frame height in pixels
format?: VideoFrameFormat; // 'YUV420P' | 'RGB24' | 'ARGB32', defaults to 'YUV420P'
}
Requisitos de formato de vídeo:
datadebe ser unBufferde valores sin signo de 8 bitswidthyheightson propiedades planas del marco; no hay ningún objeto de resolución independiente- Resolución máxima: 1920x1080 píxeles (2.073.600 píxeles en total)
- El tamaño de búfer necesario depende del formato:
| Formato | Obligatorio data.byteLength |
|---|---|
YUV420P |
width * height * 3 / 2 |
RGB24 |
width * height * 3 |
ARGB32 |
width * height * 4 |
Resolución de vídeo
Representa las dimensiones de un fotograma de vídeo:
interface VideoResolution {
width: number; // Width in pixels. Must be a positive integer
height: number; // Height in pixels. Must be a positive integer
}
Número total de píxeles (width * height) no debe superar los 1920 × 1080 (2 073 600). Este tipo lo utiliza
SubscriberVideoSettings.preferredResolution; la geometría del editor en SessionVideoPublisherSettings y además
VideoFrame utiliza planos width y height propiedades en su lugar.
Datos de los subtítulos
Representa los datos del texto de los subtítulos recibidos de una transmisión a la que se está suscrito:
interface CaptionsData {
text: string; // The caption text content
isFinal: boolean; // true for final captions, false for interim/partial captions
}
MediaBufferStats
Proporciona estadísticas sobre los búferes de los medios de publicación:
interface BufferStats {
durationMs: number; // Duration of queued media in milliseconds
}
interface MediaBufferStats {
audio: BufferStats | null; // null if no audio publisher is active
video: BufferStats | null; // null if no video publisher is active
}
Las duraciones son números sin formato, expresados en milisegundos.
Tipos de validación
La biblioteca exporta tipos de unión para los valores que valida, por lo que los literales no válidos se detectan en tiempo de compilación:
type SampleRate = 8000 | 12000 | 16000 | 24000 | 32000 | 44100 | 48000;
type NumberOfChannels = 1 | 2;
type LogLevel = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE';
type VideoFrameFormat = 'YUV420P' | 'RGB24' | 'ARGB32';
Estructuras de configuración
Configuración de sesión
Configura el comportamiento a nivel de sesión:
import type { SessionSettings } from '@vonage/video-connector';
const sessionSettings: SessionSettings = {
enableMigration: false, // Enable automatic session migration. Defaults to false
av: avSettings, // Optional audio/video configuration
logging: { level: 'INFO' } // Optional logging configuration
};
SesiónAVSettings
Configura los ajustes de audio y vídeo para la sesión:
import type { SessionAVSettings } from '@vonage/video-connector';
const avSettings: SessionAVSettings = {
audioPublisher: { sampleRate: 48000, channels: 2 },
audioSubscribersMix: { sampleRate: 48000, channels: 1 },
videoPublisher: { width: 1280, height: 720, fps: 30, format: 'YUV420P' }
};
- audioPublisher: Define el formato de los datos de audio que se facilitan a través de
addAudio(). Los datos de audio que envíes deben coincidir con la frecuencia de muestreo y el número de canales de esta configuración. - audioSuscriptoresMix: Define el formato del audio mezclado que recibes de todas las transmisiones a las que estás suscrito a través del nivel de sesión
onAudioDatacallback. La biblioteca se encarga automáticamente de mezclar el audio de varios abonados y de remuestrear/convertir los canales para que coincidan con el formato especificado.
Para obtener orientación sobre cómo elegir estos formatos, consulta El audio para la publicación frente al audio para la suscripción.
SessionAudioSettings
Configura el formato de audio para publicar o recibir datos de audio:
import type { SessionAudioSettings } from '@vonage/video-connector';
const audioSettings: SessionAudioSettings = {
sampleRate: 48000, // Optional, defaults to 48000
channels: 1 // Optional, 1 (mono) or 2 (stereo), defaults to 2
};
Ten en cuenta que la propiedad se llama channels aquí, mientras que AudioData usos numberOfChannels.
SessionVideoPublisherSettings
Configura los ajustes de vídeo para la publicación:
import type { SessionVideoPublisherSettings } from '@vonage/video-connector';
const videoSettings: SessionVideoPublisherSettings = {
width: 1280, // Optional, defaults to 640
height: 720, // Optional, defaults to 480
fps: 30, // Optional, 1-30, defaults to 30
format: 'YUV420P' // Optional, defaults to 'YUV420P'
};
El ancho y la altura son propiedades planas, en lugar de un objeto de resolución anidado.
Configuración de registro
Controla la verbosidad del registro:
import type { LoggingSettings } from '@vonage/video-connector';
const loggingSettings: LoggingSettings = {
level: 'INFO' // Required: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE'
};
Configuración del editor
Configura el flujo publicado:
import type { PublisherSettings } from '@vonage/video-connector';
const publisherSettings: PublisherSettings = {
name: 'My Application', // Required: name for your published stream
hasAudio: true, // Required: whether to publish audio
hasVideo: true, // Required: whether to publish video
enableCaptions: false, // Optional: enable live captions for this stream. Defaults to false
audioSettings: { // Optional
enableStereoMode: true,
enableOpusDtx: false
}
};
Nota: Al menos uno de hasAudio o hasVideo debe ser true. Pasando ambos como false lanza un
ValidationError de forma sincrónica desde publish().
Nota: Establecer enableCaptions: true para que los suscriptores puedan recibir el texto de los subtítulos en directo de esta retransmisión a través del
suscriptor onCaptionText callback. Los subtítulos están desactivados por defecto.
Configuración de audio del editor
Configura los ajustes de audio de tu transmisión publicada:
import type { PublisherAudioSettings } from '@vonage/video-connector';
const audioSettings: PublisherAudioSettings = {
enableStereoMode: true, // Optional: publish in stereo. Defaults to true
enableOpusDtx: false // Optional: enable discontinuous transmission. Defaults to false
};
Transmisión discontinua (DTX) deja de enviar paquetes de audio durante el silencio, ahorrando ancho de banda.
Configuración del suscriptor
Configura el comportamiento de los suscriptores:
import type { SubscriberSettings } from '@vonage/video-connector';
const subscriberSettings: SubscriberSettings = {
subscribeToAudio: true, // Optional, defaults to true
subscribeToVideo: true, // Optional, defaults to true
subscribeToCaptions: false, // Optional, defaults to false
videoSettings: {
preferredResolution: { width: 640, height: 480 },
preferredFrameRate: 15
}
};
Las leyendas deben estar activadas en el lado de la publicación y cada suscriptor debe dar su consentimiento a través de subscribeToCaptions.
SubscriberVideoSettings
Configura las preferencias de vídeo de los abonados:
import type { SubscriberVideoSettings } from '@vonage/video-connector';
const videoSettings: SubscriberVideoSettings = {
preferredResolution: { width: 640, height: 480 }, // Optional
preferredFrameRate: 15 // Optional, 1-30
};
Estos ajustes solicitan un nivel específico de calidad de retransmisión simultánea a la SFU de la Video API de Vonage. Para obtener una explicación de cómo los tiene en cuenta la SFU, consulta Resolución y frecuencia de fotogramas recomendadas para los suscriptores.
Conectarse a una sesión
Conexión básica
Para conectarte a una sesión de la Video API de Vonage, necesitas tu ID de aplicación (clave API si utilizas Tokbox), el ID de sesión y un token válido:
import { VonageVideoClient, type SessionSettings } from '@vonage/video-connector';
const client = new VonageVideoClient();
const sessionSettings: SessionSettings = {
enableMigration: false,
av: {
audioSubscribersMix: { sampleRate: 48000, channels: 1 }
},
logging: { level: 'INFO' }
};
const session = await client.connect(
'your_application_id',
'your_session_id',
'your_token',
{ sessionSettings }
);
console.log(`Connected to session: ${session.sessionId}`);
Conexión con todas las funciones de devolución de llamada
Para una gestión completa de la sesión, registra todas las llamadas de retorno en curso disponibles:
const session = await client.connect(
'your_application_id',
'your_session_id',
'your_token',
{
sessionSettings,
onError: onSessionError,
onDisconnected: onSessionDisconnected,
onConnectionCreated: onConnectionCreated,
onConnectionDropped: onConnectionDropped,
onStreamReceived: onStreamReceived,
onStreamDropped: onStreamDropped,
onAudioData: onAudioData,
onReadyForAudio: onReadyForAudio,
onMediaBufferDrained: onMediaBufferDrained
}
);
No hay onConnected callback: la promesa esperada cumple esa función.
Desconectarse de una sesión
Desconéctese de la sesión cuando haya terminado:
await client.disconnect();
Comprobación del estado de la conexión
if (client.isConnected()) {
console.log('Still connected');
}
Configuración de la sesión
Configuración de audio y vídeo
Configura los ajustes de audio y vídeo de la sesión para controlar el formato de los datos multimedia:
import type { SessionAVSettings } from '@vonage/video-connector';
const avSettings: SessionAVSettings = {
// Format of the audio you supply via addAudio()
audioPublisher: {
sampleRate: 48000, // Valid: 8000, 12000, 16000, 24000, 32000, 44100, 48000
channels: 2 // 1 for mono, 2 for stereo
},
// Format of the mixed audio delivered to your session onAudioData callback
audioSubscribersMix: {
sampleRate: 48000,
channels: 1
},
// Geometry and format of the video you supply via addVideo()
videoPublisher: {
width: 1280,
height: 720,
fps: 30,
format: 'YUV420P' // Valid: YUV420P, RGB24, ARGB32
}
};
await client.connect(applicationId, sessionId, token, {
sessionSettings: { av: avSettings }
});
Los valores no válidos en estos ajustes provocan un TypeError de forma sincrónica desde connect(), antes de cualquier actividad de red.
Configuración del registro
Controla el nivel de detalle de los mensajes de registro en la consola:
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
logging: { level: 'DEBUG' } // Valid: ERROR, WARN, INFO, DEBUG, TRACE
}
});
Migración de sesiones
Activar la migración automática de sesiones en caso de rotación de SFU:
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
enableMigration: true,
av: avSettings,
logging: { level: 'INFO' }
}
});
Flujos de publicación
Configuración del editor
Configura los ajustes del editor antes de empezar a publicar:
import type { PublisherSettings } from '@vonage/video-connector';
// Audio and video publisher
const publisherSettings: PublisherSettings = {
name: 'AI Assistant Bot',
hasAudio: true,
hasVideo: true,
enableCaptions: true,
audioSettings: {
enableStereoMode: true,
enableOpusDtx: false
}
};
// Or audio-only publisher
const audioOnlySettings: PublisherSettings = {
name: 'Audio Bot',
hasAudio: true,
hasVideo: false,
enableCaptions: false
};
Empezar a publicar
Empieza a publicar un flujo en la sesión:
const publisher = await client.publish({
settings: publisherSettings,
onError: onPublisherError,
onStreamDestroyed: onStreamDestroyed
});
console.log(`Publishing stream: ${publisher.stream.streamId}`);
La promesa se resuelve una vez que se ha creado el flujo, por lo que no hay onStreamCreated función de devolución de llamada. La
onStreamDestroyed La función de devolución de llamada solo se activa cuando el flujo se destruye de forma inesperada, como en el caso de una retirada forzada
por parte del servidor; no se activa cuando se llama a unpublish() tú mismo.
Importante
Si vas a publicar audio (hasAudio: true), debes esperar a que el onReadyForAudio función de llamada de retorno antes de llamar a addAudio(). Esta función de devolución de llamada indica que el sistema de audio está inicializado y listo para aceptar datos de audio. La capa nativa descarta de forma silenciosa los fotogramas enviados con anterioridad. Este requisito no se aplica a los casos de publicación exclusivamente de vídeo.
// onReadyForAudio is registered at connect() time, so capture its resolver first
let signalAudioReady: () => void = () => {};
const audioReady = new Promise<void>((resolve) => {
signalAudioReady = resolve;
});
await client.connect(applicationId, sessionId, token, {
sessionSettings,
onReadyForAudio: () => {
console.log('Audio system ready - can now add audio');
signalAudioReady();
}
});
await client.publish({ settings: publisherSettings });
await audioReady;
// Now safe to add audio
client.addAudio(audioData);
El vídeo no tiene una señal de disponibilidad equivalente: los fotogramas se pueden enviar tan pronto como publish() resuelve.
Añadir datos de audio
Envía datos de audio a tu transmisión publicada:
import type { AudioData } from '@vonage/video-connector';
const sampleRate = 48000;
const numberOfChannels = 1;
const numberOfFrames = 960; // 20ms at 48kHz
// PCM16 LE: 2 bytes per sample per channel
const data = Buffer.alloc(numberOfFrames * numberOfChannels * 2);
const audioData: AudioData = {
data,
sampleRate,
numberOfChannels,
numberOfFrames
};
const accepted = client.addAudio(audioData);
addAudio() es síncrona. Devuelve true si se aceptó el fotograma, y false si el controlador no está
inicializado o si la llamada nativa falla. Lanza un TypeError si la trama presenta un error de formato —por ejemplo, si el
búfer es demasiado pequeño para la geometría de la trama declarada—.
Dejar de publicar
Deje de publicar cuando haya terminado:
await client.unpublish();
Comprobación del estado de publicación
if (client.isPublishing()) {
console.log('Still publishing');
}
Suscripción a flujos
Suscribirse a los flujos
Cuando se reciba una nueva transmisión, suscríbete a ella para recibir datos de audio y/o vídeo:
import type { Session, Stream } from '@vonage/video-connector';
async function onStreamReceived(session: Session, stream: Stream): Promise<void> {
console.log(`New stream received: ${stream.streamId}`);
console.log(`From connection: ${stream.connection.connectionId}`);
try {
const subscriber = await client.subscribe(stream, {
settings: {
subscribeToAudio: true,
subscribeToVideo: true,
subscribeToCaptions: true,
videoSettings: {
preferredResolution: { width: 640, height: 480 },
preferredFrameRate: 15
}
},
onError: onSubscriberError,
onDisconnected: onSubscriberDisconnected,
onRenderFrame: onRenderFrame,
onAudioData: onSubscriberAudioData,
onCaptionText: onCaptionText
});
console.log(`Subscribed to stream: ${subscriber.stream.streamId}`);
} catch (error) {
// onStreamReceived is a void callback, so handle failures here rather than
// letting them become an unhandled rejection.
console.error(`Failed to subscribe to ${stream.streamId}:`, error);
}
}
Recepción de medios suscritos
La biblioteca proporciona audio y vídeo a través de diferentes callbacks. Para conocer los motivos que justifican este diseño, consulta Suscripción a flujos en la guía de Video Connector.
Importante
En Buffer dentro de un AudioData o VideoFrame El contenido entregado a una función de devolución de llamada solo es válido mientras dura dicha función. Si necesitas conservar el contenido multimedia más allá de la función de devolución de llamada —para ponerlo en cola o procesarlo de forma asíncrona—, cópialo primero, por ejemplo, con Buffer.from(frame.data).
Datos de vídeo: Los fotogramas de vídeo se entregan individualmente por flujo suscrito a través del onRenderFrame
función de llamada registrada en el momento de la suscripción. Cada invocación incluye la subscriber que identifica a qué flujo
pertenece el fotograma, para que puedas procesar por separado el vídeo de los distintos participantes.
import type { Subscriber, VideoFrame } from '@vonage/video-connector';
function onRenderFrame(subscriber: Subscriber, frame: VideoFrame): void {
const streamId = subscriber.stream.streamId;
console.log(`Video frame from stream ${streamId}: ${frame.width}x${frame.height}`);
// Copy before retaining beyond this callback
const owned = Buffer.from(frame.data);
}
Datos de audio: El audio mezclado se transmite a través del onAudioData registrada durante connect(),
aproximadamente cada 10 ms. La biblioteca mezcla automáticamente el audio de todas las transmisiones a las que se está suscrito en una única
transmisión. En esta llamada de retorno no es posible distinguir el audio de cada uno de los participantes.
import type { Session, AudioData } from '@vonage/video-connector';
function onAudioData(session: Session, audioData: AudioData): void {
console.log(
`Mixed audio from all subscribers: ${audioData.sampleRate}Hz, ` +
`${audioData.numberOfChannels} channel(s)`
);
}
Datos del pie de foto: Esta función está disponible actualmente en versión beta. El texto de los subtítulos se transmite por
cada canal al que se esté suscrito a través de la onCaptionText función de devolución de llamada, con el subscriber identificar el flujo de origen y un
CaptionsData objeto que contiene el texto y si el resultado es definitivo o provisional.
Nota
Para el onCaptionText Para que la función de devolución de llamada reciba los datos de los subtítulos, es necesario que los subtítulos en directo estén habilitados en la configuración de la sesión de la Video API de Vonage subyacente (fuera de esta biblioteca; consulta la Video API de Vonage Subtítulos en directo documentación) y para el flujo específico del emisor que está enviando audio. Establecer subscribeToCaptions: true en la configuración de suscriptor para recibirlas.
import type { Subscriber, CaptionsData } from '@vonage/video-connector';
function onCaptionText(subscriber: Subscriber, captions: CaptionsData): void {
const status = captions.isFinal ? 'final' : 'interim';
console.log(`Caption (${status}) from stream ${subscriber.stream.streamId}: ${captions.text}`);
}
Datos de audio individuales
Esta función está disponible actualmente en versión beta. El audio de cada transmisión se transmite a través del
onAudioData función de devolución de llamada registrada en el momento de la suscripción a través de subscribe() — a diferencia del nivel de sesión
onAudioData, que transmite la mezcla. El audio llega en el formato en el que se recibe de la transmisión, PCM lineal de 16 bits,
y no es posible configurar ni la frecuencia de muestreo ni el número de canales antes de la recepción.
import type { Subscriber, AudioData } from '@vonage/video-connector';
function onSubscriberAudioData(subscriber: Subscriber, audioData: AudioData): void {
console.log(
`Individual audio from stream ${subscriber.stream.streamId}: ` +
`${audioData.sampleRate}Hz, ${audioData.numberOfChannels} channel(s)`
);
}
Darse de baja de streams
Dejar de recibir contenido multimedia de una fuente concreta:
import type { Session, Stream } from '@vonage/video-connector';
function onStreamDropped(session: Session, stream: Stream): void {
console.log(`Stream dropped: ${stream.streamId}`);
client.unsubscribe(stream).catch((error) => {
console.error('Failed to unsubscribe:', error);
});
}
Tratamiento de datos de audio
Formato de audio
Los datos de audio se intercambian como muestras PCM lineal de 16 bits con signo y en formato «little-endian» en un Buffer:
- Tasas de muestreo: 8000, 12000, 16000, 24000, 32000, 44100 o 48000 Hz
- Canales: 1 (mono) o 2 (estéreo)
- Formato: 16 bits con signo, en formato «little-endian», en un
Buffer - Tamaño del marco: Típicamente 20ms trozos (varía según la frecuencia de muestreo)
Procesamiento de datos de audio
Gestiona el audio entrante en la función de devolución de datos de audio de la sesión:
import type { Session, AudioData } from '@vonage/video-connector';
function onAudioData(session: Session, audioData: AudioData): void {
const { sampleRate, numberOfChannels, numberOfFrames, data } = audioData;
console.log(`Received ${numberOfFrames} frames at ${sampleRate}Hz, ${numberOfChannels} channel(s)`);
// Read individual samples as 16-bit signed little-endian
const firstSample = data.readInt16LE(0);
// Copy if the audio is needed after this callback returns
const owned = Buffer.from(data);
}
Creación de datos de audio
Al añadir audio, crea un Buffer de muestras de 16 bits:
const sampleRate = 48000;
const durationMs = 20;
const numberOfChannels = 1;
const numberOfFrames = (sampleRate * durationMs) / 1000; // 960 frames
const data = Buffer.alloc(numberOfFrames * numberOfChannels * 2);
// Example: generate a 440Hz sine wave
for (let i = 0; i < numberOfFrames; i++) {
const sample = Math.round(32767 * 0.5 * Math.sin((2 * Math.PI * 440 * i) / sampleRate));
data.writeInt16LE(sample, i * 2);
}
client.addAudio({ data, sampleRate, numberOfChannels: 1, numberOfFrames });
Continuidad de los datos de audio
Cuando publicas audio, la biblioteca envía silencio hasta tu primer addAudio() llamada, tolera breves intervalos
sin enviar paquetes, tras lo cual recurre a tramas de silencio explícitas y rellena los periodos parciales para evitar la deriva.
Véase Continuidad del audio para conocer el comportamiento completo.
Buenas prácticas:
- Mantener una tasa de audio constante llamando
addAudio()a intervalos regulares que coincidan con la frecuencia de muestreo configurada. - Supervisa las estadísticas del búfer mediante
getMediaBufferStats()para garantizar que los datos de audio sean adecuados - Manejar el
onMediaBufferDrainedfunción de devolución de llamada para detectar cuándo se agota el búfer de audio - Plantéate implementar una estrategia de generación de audio que se adapte a las diferentes cargas de procesamiento.
Tratamiento de datos de vídeo
Formato de vídeo
Los datos de vídeo se intercambian como valores sin signo de 8 bits en un Buffer, en uno de estos tres formatos:
- YUV420P: Formato YUV planar con submuestreo de croma 4:2:0
- RGB24: RGB comprimido, 8 bits por canal
- ARGB32: ARGB comprimido, 8 bits por canal, incluido el canal alfa
Especificaciones de vídeo:
- Resoluciones: Hasta 1920x1080 (Full HD)
- Frecuencia de imagen: 1-30 FPS
- Formato: Valores sin signo de 8 bits en un
Buffer
Procesamiento de fotogramas de vídeo
Maneja los fotogramas de vídeo entrantes en la llamada de retorno del fotograma renderizado:
import type { Subscriber, VideoFrame } from '@vonage/video-connector';
function onRenderFrame(subscriber: Subscriber, frame: VideoFrame): void {
const { width, height, format, data } = frame;
console.log(`Received ${width}x${height} frame in ${format} format`);
// Copy if the frame is needed after this callback returns
const owned = Buffer.from(data);
}
Creación de fotogramas de vídeo
Al publicar un vídeo, crea un Buffer con el tamaño adecuado para el formato y la resolución:
import type { VideoFrame } from '@vonage/video-connector';
const width = 640;
const height = 480;
// YUV420P layout:
// Y plane: width * height
// U plane: (width / 2) * (height / 2)
// V plane: (width / 2) * (height / 2)
const ySize = width * height;
const uvSize = (width / 2) * (height / 2);
const data = Buffer.alloc(ySize + 2 * uvSize);
// Fill with mid-gray
data.fill(128);
const videoFrame: VideoFrame = { data, width, height, format: 'YUV420P' };
client.addVideo(videoFrame);
addVideo() es síncrona. Devuelve true si se aceptó el fotograma, y false si el controlador no está
inicializado o si la llamada nativa falla. Lanza un TypeError si el fotograma presenta un formato incorrecto —un formato desconocido, una
dimensión no positiva, un número de píxeles superior a 1920x1080 o un búfer demasiado pequeño para la resolución declarada—.
Continuidad de fotogramas de vídeo
Cuando publicas un vídeo, la biblioteca envía fotogramas negros hasta que aparece el primer addVideo() La función «call» repite el último
fotograma durante un máximo de 2 segundos; si dejas de enviar fotogramas, pasa a mostrar fotogramas negros. Véase
Continuidad del vídeo para conocer el comportamiento completo.
Buenas prácticas:
- Mantener una velocidad de fotogramas constante llamando a
addVideo()a intervalos regulares que se ajusten a los FPS que hayas configurado - Supervisa las estadísticas del búfer mediante
getMediaBufferStats()para garantizar unos datos de vídeo adecuados - Manejar el
onMediaBufferDrainedfunción de devolución de llamada para detectar cuándo se agota el búfer de vídeo - Considere la posibilidad de aplicar una estrategia de generación de fotogramas que se adapte a las distintas cargas de procesamiento.
Gestión de la memoria intermedia
Comprobación de las estadísticas del búfer
Supervisa el estado de tus buffers multimedia:
const stats = client.getMediaBufferStats();
if (stats.audio) {
console.log(`Audio buffer duration: ${stats.audio.durationMs}ms`);
}
if (stats.video) {
console.log(`Video buffer duration: ${stats.video.durationMs}ms`);
}
Cada campo es null cuando no haya ningún editor activo de ese tipo de medio.
Borrado de memorias intermedias
Borra los búferes de audio y vídeo cuando sea necesario, por ejemplo, para interrumpir la reproducción en mitad de una frase:
if (client.clearMediaBuffers()) {
console.log('Media buffers cleared successfully');
}
Retrollamada de vaciado del búfer
Manejar eventos de drenaje del buffer:
import type { MediaBufferStats } from '@vonage/video-connector';
function onMediaBufferDrained(stats: MediaBufferStats): void {
console.log('Media buffers drained');
if (stats.audio) {
console.log(`Audio buffer: ${stats.audio.durationMs}ms remaining`);
}
if (stats.video) {
console.log(`Video buffer: ${stats.video.durationMs}ms remaining`);
}
}
El onMediaBufferDrained La función de devolución de llamada está registrada en connect(). Se invoca cuando los búferes internos de audio o vídeo
se agotan, e implementa histéresis para que no se active repetidamente mientras el búfer permanezca vacío.
Véase Eventos de vaciado del búfer Para más información.
Obtener información de conexión
Recupera la información de tu conexión local:
const connection = client.getConnection();
if (connection) {
console.log(`Connection ID: ${connection.connectionId}`);
console.log(`Connection data: ${connection.data}`);
console.log(`Created at: ${new Date(connection.creationTime).toISOString()}`);
}
getConnection() devoluciones null cuando el cliente no está conectado.
Llamadas de retorno de eventos
Las funciones de devolución de llamada se agrupan según la llamada que las registra. Recuerda que los eventos del ciclo de vida que solo se producen una vez —
conexión de sesión, creación de un flujo de editor y conexión de suscriptor— se entregan mediante las promesas devueltas
por connect(), publish()y subscribe() en lugar de como funciones de devolución de llamada.
Llamadas de retorno de sesión
Inscrita en connect():
| Devolución de llamada | Firma |
|---|---|
onError |
(session: Session, description: string, code: number) => void |
onDisconnected |
(session: Session) => void |
onConnectionCreated |
(session: Session, connection: Connection) => void |
onConnectionDropped |
(session: Session, connection: Connection) => void |
onStreamReceived |
(session: Session, stream: Stream) => void |
onStreamDropped |
(session: Session, stream: Stream) => void |
onAudioData |
(session: Session, audioData: AudioData) => void |
onReadyForAudio |
(session: Session) => void |
onMediaBufferDrained |
(stats: MediaBufferStats) => void |
import type { Session, Connection, Stream } from '@vonage/video-connector';
function onSessionError(session: Session, description: string, code: number): void {
console.error(`Session error: ${description} (Code: ${code})`);
}
function onSessionDisconnected(session: Session): void {
console.log(`Disconnected from session: ${session.sessionId}`);
}
function onConnectionCreated(session: Session, connection: Connection): void {
console.log(`Participant joined: ${connection.connectionId}`);
console.log(`Connection data: ${connection.data}`);
}
function onConnectionDropped(session: Session, connection: Connection): void {
console.log(`Participant left: ${connection.connectionId}`);
}
function onStreamReceived(session: Session, stream: Stream): void {
console.log(`Stream received: ${stream.streamId} from ${stream.connection.connectionId}`);
// Decide whether to subscribe based on your application logic
}
Llamadas de retorno de editores
Inscrita en publish():
| Devolución de llamada | Firma |
|---|---|
onError |
(publisher: Publisher, description: string, code: number) => void |
onStreamDestroyed |
(publisher: Publisher) => void |
import type { Publisher } from '@vonage/video-connector';
function onPublisherError(publisher: Publisher, description: string, code: number): void {
console.error(`Publisher error: ${description} (Code: ${code})`);
}
function onStreamDestroyed(publisher: Publisher): void {
console.log(`Published stream destroyed: ${publisher.stream.streamId}`);
}
Llamadas de retorno a abonados
Inscrita en subscribe():
| Devolución de llamada | Firma |
|---|---|
onError |
(subscriber: Subscriber, description: string, code: number) => void |
onDisconnected |
(subscriber: Subscriber) => void |
onRenderFrame |
(subscriber: Subscriber, frame: VideoFrame) => void |
onAudioData |
(subscriber: Subscriber, audioData: AudioData) => void |
onCaptionText |
(subscriber: Subscriber, captions: CaptionsData) => void |
import type { Subscriber } from '@vonage/video-connector';
function onSubscriberError(subscriber: Subscriber, description: string, code: number): void {
console.error(`Subscriber error: ${description} (Code: ${code})`);
}
function onSubscriberDisconnected(subscriber: Subscriber): void {
console.log(`Unsubscribed from stream: ${subscriber.stream.streamId}`);
}
Gestión de errores
La biblioteca notifica los problemas de tres formas distintas.
Lanza sincrónicas en caso de entrada no válida. Los argumentos se validan antes de pasar a la capa nativa.
addAudio(), addVideo(), y los ajustes de audio y vídeo que se pasan a connect() lanzar un TypeError cuando un valor
tiene un tipo incorrecto o incumple una restricción documentada. publish() lanza un ValidationError cuando las configuraciones
están bien escritas pero incumplen una regla, como por ejemplo hasAudio y hasVideo siendo ambos false.
import { ValidationError } from '@vonage/video-connector';
try {
await client.publish({ settings: { name: 'Bot', hasAudio: false, hasVideo: false } });
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid publisher settings:', error.message);
}
}
ValidationError es una clase exportada por el paquete, por lo que se puede interceptar con instanceof.
Establecer mensajes de error para las operaciones fallidas. Si una operación falla antes de completarse, su promesa se rechaza con
un Error cuyo mensaje incluye la descripción y el código subyacentes.
try {
await client.connect(applicationId, sessionId, token, { sessionSettings });
} catch (error) {
// e.g. "Connection error [1004]: Authentication error"
// or "Failed to initiate connection. Client may already be connected."
console.error(error);
}
Funciones de devolución de llamada para errores tras la liquidación. Una vez que una operación se ha realizado con éxito, los errores posteriores se notifican al
onError función de devolución de llamada registrada para ese ámbito. Una onError Por lo tanto, la función de devolución de llamada nunca se invoca en caso de un error que
ya haya rechazado la promesa.
Valores booleanos de respuesta para el envío de archivos multimedia. addAudio(), addVideo()y clearMediaBuffers() volver false
en lugar de lanzar una excepción cuando el controlador no está inicializado o la llamada nativa falla.
Limpieza de recursos
Limpie siempre los recursos adecuadamente:
try {
await client.connect(applicationId, sessionId, token, { sessionSettings });
await client.publish({ settings: publisherSettings });
// ... do work ...
} catch (error) {
console.error('Application error:', error);
} finally {
if (client.isPublishing()) {
await client.unpublish().catch(() => console.warn('Failed to stop publishing'));
}
if (client.isConnected()) {
await client.disconnect().catch(() => console.warn('Failed to disconnect'));
}
}