Connecteur vidéo pour Node.js
La bibliothèque Node.js « Vonage Video Connector » vous permet de participer par programmation à des sessions de la Video API Vonage en tant que participant côté serveur. Cette bibliothèque vous permet de vous connecter à des sessions vidéo, de publier et de vous abonner à des flux, ainsi que de traiter des données audio et vidéo en temps réel.
Important La bibliothèque Node.js « Vonage Video Connector » est conçue pour les applications côté serveur et nécessite des identifiants et des jetons valides de la Video API Vonage, dotés des autorisations appropriées.
Cette page présente l'API Node.js. Pour en savoir plus sur les concepts, les formats multimédias et le comportement d'exécution communs à toutes les bibliothèques Video Connector, consultez la Connecteur vidéo guide. Si vous travaillez sous Python, consultez Connecteur vidéo pour Python.
Cette rubrique comprend les sections suivantes :
- Pour commencer
- Exigences
- L'API basée sur les « promesses »
- Structures de données
- Connexion à une session
- Paramètres de la session
- Flux de publication
- S'abonner à des flux
- Traitement des données audio
- Traitement des données vidéo
- Gestion de la mémoire tampon des médias
- Rappels d'événements
- Gestion des erreurs
Pour commencer
Le Vonage Video Connector pour Node.js est distribué sous la forme d'un @vonage/video-connector l'emballage.
Pour installer la bibliothèque, exécutez :
Le paquet est fourni avec des binaires natifs précompilés et ne nécessite aucune étape de compilation. Les déclarations de types TypeScript sont
incluses ; il n'est donc pas nécessaire de @types Le paquet est nécessaire.
Exigences
Cette bibliothèque nécessite Node.js 18 ou une version ultérieure, fonctionnant sur des plateformes Linux x64 ou ARM64. Nous vous recommandons d'utiliser Debian Bookworm, car c'est la distribution sur laquelle cette bibliothèque a été testée de manière la plus approfondie.
Le chargement de la bibliothèque sur une plateforme non prise en charge génère une exception au moment de l'importation, car le module natif correspondant à cette combinaison de plateforme et d'architecture n'existe pas.
L'API basée sur les « promesses »
La bibliothèque Node.js expose les opérations liées au cycle de vie sous forme de « promises » plutôt que de callbacks de fin d'exécution. Cela détermine la structure de l'ensemble de l'API ; il est donc utile de bien comprendre ce principe avant de poursuivre la lecture de cette page.
Chacun des connect(), publish(), subscribe(), disconnect(), unpublish()et unsubscribe() renvoie une
promesse :
| Méthode | Se termine par | Se résout lorsque |
|---|---|---|
connect() |
Session |
La connexion à la session est établie |
publish() |
Publisher |
Votre flux publié a été créé |
subscribe() |
Subscriber |
L'abonnement est activé |
disconnect() |
void |
La déconnexion est terminée |
unpublish() |
void |
La suppression de la publication est terminée |
unsubscribe() |
void |
La désinscription est terminée |
Par conséquent, les événements ponctuels du cycle de vie n'ont pas de fonction de rappel correspondante dans cette bibliothèque — la promesse
est la notification. Il n'y a pas de onConnected, sans éditeur onStreamCreated, et aucun abonné
onConnected pour s'inscrire. Tous les autres événements sont des fonctions de rappel que vous transmettez via l'objet `arguments`.
La gestion des erreurs suit la même structure. Si une erreur se produit avant une opération est finalisée, la promesse est rejetée et
votre onError la fonction de rappel n'est pas appelée ; le rejet constitue la notification d'erreur. Si une erreur se produit après Une fois que
l'opération s'est stabilisée, votre onError La fonction de rappel est appelée comme d'habitude.
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);
}
Important
N'en créez qu'un seul VonageVideoClient une instance par processus. Le SDK sous-jacent ne prend en charge qu'une seule connexion active ; la création de plusieurs instances entraîne un comportement indéfini.
Structures de données
Tous les types ci-dessous sont exportés depuis le package et peuvent être importés en tant que types.
Session
Représente une session Video API de Vonage à laquelle les clients peuvent se connecter :
interface Session {
sessionId: string; // Unique identifier for the session
}
Les Session L'objet est transmis aux fonctions de rappel au niveau de la session afin d'identifier la session qui a déclenché l'événement.
Connexion
Représente la connexion d'un participant à une session :
interface Connection {
connectionId: string; // Unique identifier for the connection
creationTime: number; // Unix timestamp in milliseconds
data: string; // Connection data (encoded in the token)
}
Les données de connexion peuvent être utilisées pour stocker des métadonnées personnalisées sur les participants, telles que les identifiants ou les rôles des utilisateurs.
Il convient de noter que creationTime Il s'agit d'un horodatage Unix numérique exprimé en millisecondes ; il peut donc être transmis directement à
new Date(connection.creationTime).
Flux
Représente un flux média (audio/vidéo) publié par un participant :
interface Stream {
streamId: string; // Unique identifier for the stream
connection: Connection; // The connection that published this stream
}
Éditeur
Représente le flux publié dans la session :
interface Publisher {
stream: Stream; // The underlying stream for this publisher
}
Abonné
Représente un abonnement au flux d'un autre participant :
interface Subscriber {
stream: Stream; // The stream being subscribed to
}
Données audio
Représente les données audio transmises ou reçues :
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
}
Exigences en matière de format audio :
dataça doit être unBuffercontenant des échantillons signés de 16 bits au format « little-endian »- Taux d'échantillonnage valables : 8000, 12000, 16000, 24000, 32000, 44100, 48000 Hz
- Canaux : 1 (mono) ou 2 (stéréo)
data.byteLengthdoit être d'au moinsnumberOfFrames * numberOfChannels * 2octets
VideoFrame
Représente les données de la trame vidéo en cours de transmission ou de réception :
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'
}
Exigences en matière de format vidéo :
dataça doit être unBufferde valeurs non signées de 8 bitswidthetheightil s'agit de propriétés « plates » associées au cadre ; il n'y a pas d'objet de résolution distinct- Résolution maximale : 1920x1080 pixels (2 073 600 pixels au total)
- La taille de la mémoire tampon requise dépend du format :
| Format | Obligatoire data.byteLength |
|---|---|
YUV420P |
width * height * 3 / 2 |
RGB24 |
width * height * 3 |
ARGB32 |
width * height * 4 |
Résolution vidéo
Représente les dimensions d'une image vidéo :
interface VideoResolution {
width: number; // Width in pixels. Must be a positive integer
height: number; // Height in pixels. Must be a positive integer
}
Nombre total de pixels (width * height) ne doit pas dépasser 1 920 × 1 080 (2 073 600). Ce type est utilisé par
SubscriberVideoSettings.preferredResolution; la géométrie de l'éditeur dans SessionVideoPublisherSettings et sur
VideoFrame utilise des caractères plats width et height à la place.
CaptionsData
Représente les données textuelles de la légende reçues d'un flux abonné :
interface CaptionsData {
text: string; // The caption text content
isFinal: boolean; // true for final captions, false for interim/partial captions
}
MediaBufferStats
Fournit des statistiques sur les tampons des supports de publication :
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
}
Les durées sont exprimées en nombres bruts, en millisecondes.
Types de validation
La bibliothèque exporte des types union pour les valeurs qu'elle valide, ce qui permet de détecter les littéraux non valides dès la phase de compilation :
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';
Structures de configuration
Paramètres de session
Configure le comportement au niveau de la session :
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
};
SessionAVSettings
Configure les paramètres audio et vidéo de la session :
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: Définit le format des données audio que vous fournissez par l'intermédiaire de l'application
addAudio(). Les données audio que vous envoyez doivent correspondre à la fréquence d'échantillonnage et au nombre de canaux de cette configuration. - audioAbonnésMix: Définit le format du flux audio mixé que vous recevez de tous les flux auxquels vous êtes abonné via le niveau de session
onAudioDatacallback. La bibliothèque prend automatiquement en charge le mixage de l'audio de plusieurs abonnés et le rééchantillonnage/la conversion des canaux pour correspondre au format que vous avez spécifié.
Pour obtenir des conseils sur le choix de ces formats, consultez Contenu audio destiné à la publication ou à l'abonnement.
SessionAudioSettings
Configure le format audio pour la publication ou la réception de données 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
};
Notez que la propriété s’appelle channels ici, tandis que AudioData utilisations numberOfChannels.
Paramètres de l'éditeur vidéo de session
Configure les paramètres vidéo pour la publication :
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'
};
La largeur et la hauteur sont des propriétés « flat » et non un objet de résolution imbriqué.
Paramètres de journalisation
Contrôle la verbosité de la journalisation :
import type { LoggingSettings } from '@vonage/video-connector';
const loggingSettings: LoggingSettings = {
level: 'INFO' // Required: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE'
};
Paramètres de l'éditeur
Configure votre flux publié :
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
}
};
Remarque : Au moins un des éléments suivants hasAudio ou hasVideo doit être true. En les transmettant tous les deux sous la forme false lance un
ValidationError de manière synchrone à partir de publish().
Remarque : Set (jeu de mots) enableCaptions: true pour permettre aux abonnés de recevoir le texte des sous-titres en direct de ce flux via l'
abonné onCaptionText callback. Les légendes sont désactivées par défaut.
Paramètres audio de l'éditeur
Configure les paramètres audio pour votre flux publié :
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
};
Transmission discontinue (DTX) arrête l'envoi de paquets audio pendant les silences, ce qui permet d'économiser de la bande passante.
Paramètres de l'abonné
Configure le comportement des abonnés :
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
}
};
Les sous-titres doivent être activés côté publication et chaque abonné doit donner son accord via subscribeToCaptions.
Paramètres vidéo de l'abonné
Configure les préférences vidéo pour les abonnés :
import type { SubscriberVideoSettings } from '@vonage/video-connector';
const videoSettings: SubscriberVideoSettings = {
preferredResolution: { width: 640, height: 480 }, // Optional
preferredFrameRate: 15 // Optional, 1-30
};
Ces paramètres demandent au SFU de la Video API une couche de qualité de diffusion simultanée spécifique. Pour savoir comment le SFU les prend en compte, consultez Résolution et fréquence d'images recommandées pour les abonnés.
Connexion à une session
Connexion de base
Pour vous connecter à une session de la Video API Vonage, vous devez disposer de l'identifiant de votre application (clé API si vous utilisez Tokbox), de l'identifiant de session et d'un jeton valide :
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}`);
Connexion avec tous les rappels
Pour une gestion complète des sessions, enregistrez tous les callbacks actifs 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
}
);
Il n'y a pas de onConnected callback — la promesse attendue remplit cette fonction.
Déconnexion d'une session
Déconnectez-vous de la session lorsque vous avez terminé :
await client.disconnect();
Vérification de l'état de la connexion
if (client.isConnected()) {
console.log('Still connected');
}
Paramètres de la session
Configuration audio et vidéo
Configurer les paramètres audio et vidéo de la session pour contrôler le format des données multimédias :
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 }
});
Des valeurs non valides dans ces paramètres provoquent une TypeError de manière synchrone à partir de connect(), avant toute activité réseau.
Configuration de l'enregistrement
Contrôle la verbosité de la journalisation de la console :
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
logging: { level: 'DEBUG' } // Valid: ERROR, WARN, INFO, DEBUG, TRACE
}
});
Migration de session
Activer la migration automatique des sessions en cas de rotation du SFU :
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
enableMigration: true,
av: avSettings,
logging: { level: 'INFO' }
}
});
Flux de publication
Configuration de l'éditeur
Configurez les paramètres de l'éditeur avant de commencer à publier :
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
};
Commencer à publier
Commencer à publier un flux dans la session :
const publisher = await client.publish({
settings: publisherSettings,
onError: onPublisherError,
onStreamDestroyed: onStreamDestroyed
});
console.log(`Publishing stream: ${publisher.stream.streamId}`);
La promesse est résolue dès que le flux a été créé ; il n'y a donc pas onStreamCreated rappel. Le
onStreamDestroyed Le callback ne se déclenche que lorsque le flux est détruit de manière inattendue, par exemple lors d'une suppression forcée côté serveur
; il ne se déclenche pas lorsque vous appelez unpublish() vous-même.
Important
Si vous publiez un fichier audio (hasAudio: true), vous devez attendre que le onReadyForAudio appel de retour avant l'appel addAudio(). Cette fonction de rappel indique que le système audio est initialisé et prêt à recevoir des données audio. Les trames envoyées auparavant sont ignorées par la couche native sans notification. Cette exigence ne s'applique pas aux scénarios de diffusion exclusivement vidéo.
// 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);
La vidéo ne dispose d'aucun signal d'éligibilité équivalent : les images peuvent être transmises dès que publish() décide.
Ajout de données audio
Envoyez des données audio à votre flux publié :
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() est synchrone. Elle renvoie true si la trame a été acceptée, et false si le contrôleur n'est pas
initialisé ou si l'appel natif échoue. Il déclenche une TypeError si la trame est mal formée — par exemple si le
tampon est trop petit pour la géométrie de trame déclarée.
Cesser de publier
Arrêtez la publication lorsque vous avez terminé :
await client.unpublish();
Vérification de l'état de publication
if (client.isPublishing()) {
console.log('Still publishing');
}
S'abonner à des flux
S'abonner aux flux
Lorsqu'un nouveau flux est reçu, vous pouvez vous y abonner pour recevoir des données audio et/ou vidéo :
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);
}
}
Réception des médias souscrits
La bibliothèque fournit des données audio et vidéo via différents callbacks. Pour en savoir plus sur les raisons qui ont motivé cette conception, consultez S'abonner à des flux dans le guide « Video Connector ».
Important
Le Buffer à l'intérieur d'un AudioData ou VideoFrame Le contenu transmis à une fonction de rappel n'est valide que pendant la durée de cette fonction. Si vous devez conserver le média au-delà de la fonction de rappel — pour le mettre en file d'attente ou le traiter de manière asynchrone —, copiez-le d'abord, par exemple à l'aide de Buffer.from(frame.data).
Données vidéo: Les images vidéo sont fournies individuellement pour chaque flux souscrit par l'intermédiaire de l'interface utilisateur. onRenderFrame
fonction de rappel enregistrée au moment de l'abonnement. Chaque appel inclut la subscriber qui identifie le flux
auquel appartient l'image, ce qui vous permet de traiter séparément les vidéos des différents participants.
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);
}
Données audio: Le son mixé est diffusé via le onAudioData enregistré lors de la connect(),
environ toutes les 10 ms. La bibliothèque fusionne automatiquement le son de tous les flux auxquels vous êtes abonné en un seul
flux. Dans cette fonction de rappel, il est impossible de distinguer le son de chaque participant.
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)`
);
}
Données de légende: Cette fonctionnalité est actuellement disponible en version bêta. Le texte des sous-titres est diffusé via
chaque flux souscrit par le biais du onCaptionText rappel, avec le subscriber identifier le flux source et un
CaptionsData objet contenant le texte et précisant si le résultat est définitif ou provisoire.
Note
Pour les onCaptionText Pour que la fonction de rappel puisse recevoir les données de sous-titrage, les sous-titres en direct doivent être activés dans la configuration de la session de la Video API Vonage sous-jacente (en dehors de cette bibliothèque ; voir la Video API Vonage Sous-titres en direct (documentation) et pour le flux de diffusion spécifique qui transmet le son. Définir subscribeToCaptions: true dans les paramètres d'abonnement pour les recevoir.
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}`);
}
Données audio individuelles
Cette fonctionnalité est actuellement disponible en version bêta. Le flux audio individuel est diffusé via le
onAudioData enregistré au moment de l'abonnement par l'intermédiaire de subscribe() — à distinguer du niveau de session
onAudioData, qui achemine le mixage. Le signal audio est transmis dans le format reçu depuis le flux, à savoir PCM linéaire 16 bits,
et ni la fréquence d'échantillonnage ni le nombre de canaux ne peuvent être configurés avant la réception.
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)`
);
}
Se désabonner des flux
Arrêter la réception de médias provenant d'un flux spécifique :
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);
});
}
Traitement des données audio
Format audio
Les données audio sont échangées sous forme d'échantillons PCM linéaires 16 bits signés en format « little-endian » dans un Buffer:
- Taux d'échantillonnage: 8000, 12000, 16000, 24000, 32000, 44100, ou 48000 Hz
- Canaux: 1 (mono) ou 2 (stéréo)
- Format: 16 bits signés, format « little-endian », dans un
Buffer - Taille du cadre: En général, tranches de 20 ms (varie en fonction de la fréquence d'échantillonnage)
Traitement des données audio
Gérer le flux audio entrant dans la fonction de rappel des données audio de la session :
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);
}
Création de données audio
Lorsque vous ajoutez un fichier audio, créez un Buffer d'échantillons 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 });
Continuité des données audio
Lorsque vous publiez un fichier audio, la bibliothèque diffuse un silence jusqu'à votre premier addAudio() appel, tolère de brèves interruptions
sans envoyer de paquets, puis revient à des trames de silence explicites et complète les périodes partielles pour éviter toute dérive.
Voir Continuité audio pour connaître le fonctionnement complet.
Meilleures pratiques :
- Maintenir un débit audio constant en appelant
addAudio()à intervalles réguliers correspondant à la fréquence d'échantillonnage configurée - Surveiller les statistiques de la mémoire tampon à l'aide de
getMediaBufferStats()pour garantir des données audio adéquates - Manipuler les
onMediaBufferDrainedcallback pour détecter l'épuisement de la mémoire tampon audio - Envisager la mise en œuvre d'une stratégie de génération audio qui s'adapte à des charges de traitement variables.
Traitement des données vidéo
Format vidéo
Les données vidéo sont échangées sous forme de valeurs non signées de 8 bits dans un Buffer, dans l'un des trois formats suivants :
- YUV420P: Format YUV planaire avec sous-échantillonnage chromatique 4:2:0
- RGB24: RVB compressé, 8 bits par canal
- ARGB32: ARGB compressé, 8 bits par canal, alpha compris
Spécifications vidéo :
- Résolutions: Jusqu'à 1920x1080 (Full HD)
- Taux de rafraîchissement: 1-30 FPS
- Format: valeurs non signées de 8 bits dans un
Buffer
Traitement des images vidéo
Traite les images vidéo entrantes dans le rappel de l'image de rendu :
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);
}
Création d'images vidéo
Lorsque vous publiez une vidéo, créez un Buffer dimensionné en fonction du format et de la résolution :
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() est synchrone. Elle renvoie true si la trame a été acceptée, et false si le contrôleur n'est pas
initialisé ou si l'appel natif échoue. Il déclenche une TypeError si la trame présente une anomalie : un format inconnu, une
dimension non positive, un nombre de pixels supérieur à 1920 × 1080 ou une mémoire tampon trop petite pour la résolution déclarée.
Continuité de l'image vidéo
Lorsque vous publiez une vidéo, la bibliothèque envoie des images noires jusqu'à ce que votre première addVideo() La fonction « call » répète votre dernière
image pendant 2 secondes maximum si vous cessez de fournir des images, puis affiche des images noires. Voir
Continuité vidéo pour connaître le fonctionnement complet.
Meilleures pratiques :
- Maintenir une fréquence d'images constante en appelant
addVideo()à intervalles réguliers en fonction de votre FPS configuré - Surveiller les statistiques de la mémoire tampon à l'aide de
getMediaBufferStats()pour garantir des données vidéo adéquates - Manipuler les
onMediaBufferDrainedcallback pour détecter l'épuisement de la mémoire tampon vidéo - Envisager la mise en œuvre d'une stratégie de génération de trames qui s'adapte à des charges de traitement variables.
Gestion de la mémoire tampon des médias
Vérification des statistiques de la mémoire tampon
Surveillez l'état de vos mémoires tampons :
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`);
}
Chaque champ est null lorsqu'aucun éditeur de ce type de média n'est actif.
Effacer les mémoires tampons des médias
Videz les tampons audio et vidéo lorsque cela est nécessaire, par exemple pour interrompre la lecture en cours de lecture :
if (client.clearMediaBuffers()) {
console.log('Media buffers cleared successfully');
}
Buffer drained callback
Gérer les événements de vidange de la mémoire tampon :
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`);
}
}
Les onMediaBufferDrained la fonction de rappel est enregistrée dans connect(). Elle est appelée lorsque les tampons audio ou vidéo internes
sont vides, et intègre un mécanisme d'hystérésis afin de ne pas se déclencher de manière répétée tant que le tampon reste vide.
Voir Événements de vidange du tampon pour plus de détails.
Obtenir des informations sur la connexion
Récupérez vos informations de connexion locale :
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() retours null lorsque le client n'est pas connecté.
Rappels d'événements
Les callbacks sont regroupés en fonction de l'appel qui les enregistre. N'oubliez pas que les événements du cycle de vie qui ne se produisent qu'une seule fois —
connexion à la session, création du flux de l'éditeur et connexion de l'abonné — sont transmis par les promesses renvoyées
par connect(), publish()et subscribe() plutôt que sous forme de callbacks.
Rappels de session
Enregistré à connect():
| Rappel | Signature |
|---|---|
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
}
Rappels de l'éditeur
Enregistré à publish():
| Rappel | Signature |
|---|---|
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}`);
}
Rappels de l'abonné
Enregistré à subscribe():
| Rappel | Signature |
|---|---|
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}`);
}
Gestion des erreurs
La bibliothèque signale les problèmes de trois manières différentes.
Exceptions synchrones en cas de saisie non valide. Les arguments sont validés avant de passer à la couche native.
addAudio(), addVideo(), et les paramètres audio/vidéo transmis à connect() lancer un TypeError lorsqu'une valeur
est d'un type incorrect ou enfreint une contrainte documentée. publish() lance un ValidationError lorsque les paramètres
sont correctement typés mais enfreignent une règle, par exemple hasAudio et hasVideo les deux étant 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 est une classe exportée par le paquet ; elle peut donc être interceptée avec instanceof.
Prévoir des rejets en cas d'échec des opérations. Si une opération échoue avant d'être validée, sa promesse est rejetée avec
un Error dont le message contient la description et le code sous-jacents.
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);
}
Fonctions de rappel en cas d'erreurs après le règlement. Une fois qu'une opération a abouti, les échecs ultérieurs sont signalés au
onError rappel enregistré pour cette portée. Un onError La fonction de rappel n'est donc jamais appelée en cas d'échec ayant
déjà entraîné le rejet de la promesse.
Valeurs booléennes renvoyées lors de l'envoi de fichiers multimédias. addAudio(), addVideo()et clearMediaBuffers() retour false
plutôt que de générer une exception lorsque le contrôleur n'est pas initialisé ou que l'appel natif échoue.
Nettoyage des ressources
Nettoyez toujours les ressources de manière appropriée :
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'));
}
}