Video-Connector für Node.js
Mit der Vonage Video Connector Node.js-Bibliothek können Sie programmgesteuert als serverseitiger Teilnehmer an Vonage Video API-Sitzungen teilnehmen. Diese Bibliothek ermöglicht es Ihnen, eine Verbindung zu Videositzungen herzustellen, Streams zu veröffentlichen und zu abonnieren sowie Audio- und Videodaten in Echtzeit zu verarbeiten.
Wichtig Die Vonage Video Connector Node.js-Bibliothek ist für serverseitige Anwendungen konzipiert und erfordert gültige Anmeldedaten für die Vonage Video API sowie Tokens mit den entsprechenden Berechtigungen.
Diese Seite befasst sich mit der Node.js-API. Informationen zu den Concepts, Medienformaten und dem Laufzeitverhalten, die allen Video Connector-Bibliotheken gemeinsam sind, finden Sie unter Video-Anschluss Anleitung. Wenn Sie mit Python arbeiten, lesen Sie Video-Connector für Python.
Dieses Thema umfasst die folgenden Abschnitte:
- Erste Schritte
- Anforderungen
- Die Promise-basierte API
- Datenstrukturen
- Verbinden mit einer Sitzung
- Einstellungen der Sitzung
- Veröffentlichung von Datenströmen
- Abonnieren von Streams
- Verarbeitung von Audiodaten
- Verarbeitung von Videodaten
- Verwaltung der Medienpuffer
- Ereignis-Rückrufe
- Fehlerbehandlung
Erste Schritte
Der Vonage Video Connector für Node.js wird als @vonage/video-connector Paket.
Um die Bibliothek zu installieren, führen Sie aus:
Das Paket enthält vorgefertigte native Binärdateien und erfordert keinen Kompilierungsschritt. TypeScript-Typdeklarationen sind
enthalten, sodass keine separate @types Das Paket wird benötigt.
Anforderungen
Diese Bibliothek erfordert Node.js 18 oder höher auf Linux-x64- oder ARM64-Plattformen. Wir empfehlen die Verwendung von Debian Bookworm, da diese Distribution am gründlichsten getestet wurde.
Das Laden der Bibliothek auf einer nicht unterstützten Plattform führt beim Import zu einem Fehler, da das native Modul für diese Kombination aus Plattform und Architektur nicht vorhanden ist.
Die Promise-basierte API
Die Node.js-Bibliothek stellt Lebenszyklusoperationen als Promises und nicht als Callbacks nach Abschluss der Ausführung bereit. Dies prägt die gesamte API, daher lohnt es sich, dies zu verstehen, bevor man den Rest dieser Seite liest.
Jedes der connect(), publish(), subscribe(), disconnect(), unpublish()und unsubscribe() gibt ein
Promise zurück:
| Methode | Endet mit | Wird aufgelöst, wenn |
|---|---|---|
connect() |
Session |
Die Sitzungsverbindung wurde hergestellt |
publish() |
Publisher |
Ihr veröffentlichter Stream wurde erstellt |
subscribe() |
Subscriber |
Das Abonnement wurde eingerichtet |
disconnect() |
void |
Die Trennung ist abgeschlossen |
unpublish() |
void |
Die Rücknahme der Veröffentlichung ist abgeschlossen |
unsubscribe() |
void |
Die Abmeldung ist abgeschlossen |
Daher gibt es für die einmaligen Lebenszyklusereignisse in dieser Bibliothek keinen entsprechenden Callback – das Promise
ist die Benachrichtigung. Es gibt keine onConnected, kein Verlag onStreamCreated, und kein Abonnent
onConnected um sich zu registrieren. Jedes andere Ereignis ist ein Callback, den Sie im Argumente-Objekt übergeben.
Die Fehlerbehandlung folgt derselben Aufteilung. Wenn ein Fehler auftritt vor Eine Transaktion wird abgewickelt, das Versprechen wird abgelehnt und
dein onError Der Callback wird nicht aufgerufen; die Ablehnung dient als Fehlermeldung. Tritt ein Fehler auf, nach Sobald
die Transaktion abgewickelt ist, wird Ihre onError Der Callback wird wie gewohnt aufgerufen.
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);
}
Wichtig
Nur eine erstellen VonageVideoClient eine Instanz pro Prozess. Das zugrunde liegende SDK unterstützt nur eine einzige aktive Verbindung; das Erstellen mehrerer Instanzen führt zu undefiniertem Verhalten.
Datenstrukturen
Alle unten aufgeführten Typen werden aus dem Paket exportiert und können als Typen importiert werden.
Sitzung
Stellt eine Vonage Video API-Sitzung dar, mit der sich Clients verbinden können:
interface Session {
sessionId: string; // Unique identifier for the session
}
Die Session Das Objekt wird an Callbacks auf Sitzungsebene übergeben, um festzustellen, welche Sitzung das Ereignis ausgelöst hat.
Verbindung
Stellt die Verbindung eines Teilnehmers zu einer Sitzung dar:
interface Connection {
connectionId: string; // Unique identifier for the connection
creationTime: number; // Unix timestamp in milliseconds
data: string; // Connection data (encoded in the token)
}
Verbindungsdaten können verwendet werden, um benutzerdefinierte Metadaten über Teilnehmer zu speichern, z. B. Benutzer-IDs oder Rollen.
Beachten Sie, dass creationTime ist ein numerischer Unix-Zeitstempel in Millisekunden, sodass er direkt an
new Date(connection.creationTime).
Stream
Stellt einen Medienstrom (Audio/Video) dar, der von einem Teilnehmer veröffentlicht wurde:
interface Stream {
streamId: string; // Unique identifier for the stream
connection: Connection; // The connection that published this stream
}
Herausgeber
Stellt Ihren veröffentlichten Stream in der Sitzung dar:
interface Publisher {
stream: Stream; // The underlying stream for this publisher
}
Abonnent
Stellt ein Abonnement für den Stream eines anderen Teilnehmers dar:
interface Subscriber {
stream: Stream; // The stream being subscribed to
}
AudioDaten
Stellt die gesendeten oder empfangenen Audiodaten dar:
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
}
Anforderungen an das Audioformat:
datamuss einBufferdie 16-Bit-Samples mit Vorzeichen im Little-Endian-Format enthalten- Gültige Abtastraten: 8000, 12000, 16000, 24000, 32000, 44100, 48000 Hz
- Kanäle: 1 (Mono) oder 2 (Stereo)
data.byteLengthmuss mindestensnumberOfFrames * numberOfChannels * 2Bytes
VideoFrame
Stellt die gesendeten oder empfangenen Videobilddaten dar:
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'
}
Anforderungen an das Videoformat:
datamuss einBuffervon 8-Bit-Werten ohne Vorzeichenwidthundheightsind flache Eigenschaften des Frames; es gibt kein separates Auflösungsobjekt- Maximale Auflösung: 1920x1080 Pixel (2.073.600 Pixel insgesamt)
- Die erforderliche Puffergröße hängt vom Format ab:
| Format | Erforderlich data.byteLength |
|---|---|
YUV420P |
width * height * 3 / 2 |
RGB24 |
width * height * 3 |
ARGB32 |
width * height * 4 |
VideoAuflösung
Stellt die Abmessungen eines Videobildes dar:
interface VideoResolution {
width: number; // Width in pixels. Must be a positive integer
height: number; // Height in pixels. Must be a positive integer
}
Gesamtanzahl der Pixel (width * height) darf 1920 × 1080 (2.073.600) nicht überschreiten. Dieser Typ wird verwendet von
SubscriberVideoSettings.preferredResolution; die Publisher-Geometrie in SessionVideoPublisherSettings und weiter
VideoFrame verwendet flache width und height Eigenschaften.
BildunterschriftenDaten
Stellt die von einem abonnierten Stream empfangenen Beschriftungstextdaten dar:
interface CaptionsData {
text: string; // The caption text content
isFinal: boolean; // true for final captions, false for interim/partial captions
}
MediaBufferStats
Liefert Statistiken zu den Puffern der Ausgabemedien:
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
}
Die Zeitangaben sind reine Zahlen in Millisekunden.
Validierungstypen
Die Bibliothek exportiert Vereinigungstypen für die von ihr validierten Werte, sodass ungültige Literale bereits zur Kompilierungszeit erkannt werden:
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';
Konfiguration der Strukturen
SessionSettings
Konfiguriert das Verhalten auf Sitzungsebene:
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
Konfiguriert die Audio- und Videoeinstellungen für die Sitzung:
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: Definiert das Format für Audiodaten, die Sie über
addAudio(). Die von Ihnen gesendeten Audiodaten müssen mit der Abtastrate und der Kanalanzahl dieser Konfiguration übereinstimmen. - audioSubscribersMix: Legt das Format für den gemischten Ton fest, den Sie von allen abonnierten Streams über die Sitzungsebene empfangen
onAudioDataRückruf. Die Bibliothek übernimmt automatisch das Mischen der Audiodaten mehrerer Teilnehmer und die Neuabtastung/Kanalumwandlung in das von Ihnen angegebene Format.
Hinweise zur Auswahl dieser Formate finden Sie unter Audio zum Veröffentlichen im Vergleich zum Abonnieren.
SessionAudioSettings
Konfiguriert das Audioformat für die Veröffentlichung oder den Empfang von Audiodaten:
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
};
Beachten Sie, dass die Eigenschaft den Namen channels hier, während AudioData Verwendungszwecke numberOfChannels.
SessionVideoPublisherSettings
Konfiguriert die Videoeinstellungen für die Veröffentlichung:
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'
};
Breite und Höhe sind flache Eigenschaften und kein verschachteltes Auflösungsobjekt.
LoggingSettings
Steuert die Ausführlichkeit der Protokollierung:
import type { LoggingSettings } from '@vonage/video-connector';
const loggingSettings: LoggingSettings = {
level: 'INFO' // Required: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE'
};
PublisherSettings
Konfiguriert Ihren veröffentlichten Stream:
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
}
};
Anmerkung: Mindestens eine der hasAudio oder hasVideo muss sein true. Beide als false löst einen
ValidationError synchron von publish().
Anmerkung: Satz enableCaptions: true damit Abonnenten Live-Untertitel aus diesem Stream über den
Abonnenten onCaptionText Rückruf. Untertitel sind standardmäßig deaktiviert.
PublisherAudioSettings
Konfiguriert die Audioeinstellungen für Ihren veröffentlichten Stream:
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
};
Diskontinuierliche Übertragung (DTX) sendet bei Stille keine Audiopakete mehr, um Bandbreite zu sparen.
SubscriberSettings
Konfiguriert das Teilnehmerverhalten:
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
}
};
Untertitel müssen auf der Veröffentlichungsseite aktiviert und für jeden Abonnenten einzeln freigeschaltet werden über subscribeToCaptions.
SubscriberVideoSettings
Konfiguriert die Videoeinstellungen für Abonnenten:
import type { SubscriberVideoSettings } from '@vonage/video-connector';
const videoSettings: SubscriberVideoSettings = {
preferredResolution: { width: 640, height: 480 }, // Optional
preferredFrameRate: 15 // Optional, 1-30
};
Diese Einstellungen fordern eine bestimmte Simulcast-Qualitätsstufe von der Vonage Video API SFU an. Eine Erläuterung dazu, wie die SFU diese Einstellungen umsetzt, finden Sie unter Bevorzugte Auflösung und Bildfrequenz für Abonnenten.
Verbinden mit einer Sitzung
Grundlegende Verbindung
Um eine Verbindung zu einer Vonage Video API-Sitzung herzustellen, benötigen Sie Ihre Anwendungs-ID (API-Schlüssel bei Verwendung von Tokbox), die Sitzungs-ID sowie ein gültiges Token:
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}`);
Verbindung mit allen Rückrufen
Um die vollständige Verwaltung der Sitzungen zu gewährleisten, registrieren Sie alle verfügbaren laufenden Callbacks:
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
}
);
Es gibt kein onConnected Callback – das erwartete Promise erfüllt diesen Zweck.
Trennen der Verbindung zu einer Sitzung
Trennen Sie die Verbindung, wenn die Sitzung beendet ist:
await client.disconnect();
Verbindungsstatus prüfen
if (client.isConnected()) {
console.log('Still connected');
}
Einstellungen der Sitzung
Audio- und Videokonfiguration
Konfigurieren Sie die Audio- und Videoeinstellungen für die Sitzung, um das Format der Mediendaten zu steuern:
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 }
});
Ungültige Werte in diesen Einstellungen lösen einen TypeError synchron von connect(), bevor jegliche Netzwerkaktivität stattfindet.
Konfiguration der Protokollierung
Steuern Sie die Ausführlichkeit der Konsolenprotokollierung:
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
logging: { level: 'DEBUG' } // Valid: ERROR, WARN, INFO, DEBUG, TRACE
}
});
Migration von Sitzungen
Aktivieren Sie die automatische Sitzungsmigration im Falle einer SFU-Rotation:
await client.connect(applicationId, sessionId, token, {
sessionSettings: {
enableMigration: true,
av: avSettings,
logging: { level: 'INFO' }
}
});
Veröffentlichung von Datenströmen
Konfiguration des Herausgebers
Konfigurieren Sie die Publisher-Einstellungen, bevor Sie mit der Veröffentlichung beginnen:
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
};
Veröffentlichung beginnen
Beginn der Veröffentlichung eines Streams in der Sitzung:
const publisher = await client.publish({
settings: publisherSettings,
onError: onPublisherError,
onStreamDestroyed: onStreamDestroyed
});
console.log(`Publishing stream: ${publisher.stream.streamId}`);
Das Promise wird aufgelöst, sobald der Stream erstellt wurde; daher gibt es kein onStreamCreated Callback. Der
onStreamDestroyed Der Callback wird nur ausgelöst, wenn der Stream unerwartet zerstört wird, beispielsweise durch eine serverseitig erzwungene
Aufhebung der Veröffentlichung – er wird nicht ausgelöst, wenn Sie unpublish() selbst.
Wichtig
Wenn Sie Audioinhalte veröffentlichen (hasAudio: true), müssen Sie warten, bis die onReadyForAudio Callback vor dem Aufruf addAudio(). Dieser Callback zeigt an, dass das Audiosystem initialisiert wurde und bereit ist, Audiodaten entgegenzunehmen. Zuvor übermittelte Frames werden von der nativen Ebene stillschweigend verworfen. Diese Anforderung gilt nicht für Veröffentlichungsszenarien, bei denen ausschließlich Video übertragen wird.
// 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);
Für Videos gibt es kein entsprechendes Bereitschaftssignal: Bilder können übermittelt werden, sobald publish() beschließt.
Hinzufügen von Audiodaten
Senden Sie Audiodaten an Ihren veröffentlichten Stream:
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() ist synchron. Sie gibt true wenn der Frame akzeptiert wurde, und false wenn der Controller nicht
initialisiert ist oder der native Aufruf fehlschlägt. Es wird ein TypeError wenn der Frame fehlerhaft ist – beispielsweise wenn der
Puffer für die deklarierte Frame-Geometrie zu klein ist.
Veröffentlichung stoppen
Beenden Sie die Veröffentlichung, wenn Sie fertig sind:
await client.unpublish();
Veröffentlichungsstatus prüfen
if (client.isPublishing()) {
console.log('Still publishing');
}
Abonnieren von Streams
Streams abonnieren
Wenn ein neuer Stream empfangen wird, abonnieren Sie ihn, um Audio- und/oder Videodaten zu empfangen:
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);
}
}
Empfang von abonnierten Medien
Die Bibliothek stellt Audio- und Videodaten über verschiedene Callbacks bereit. Die Gründe für diesen Ansatz finden Sie unter Abonnieren von Streams im Video-Connector-Handbuch.
Wichtig
Die Buffer innerhalb eines AudioData oder VideoFrame Die an einen Callback übergebene Mediendatei ist nur für die Dauer dieses Callbacks gültig. Wenn Sie die Mediendatei über den Callback hinaus behalten müssen – um sie in die Warteschlange zu stellen oder asynchron zu verarbeiten –, kopieren Sie sie zunächst, beispielsweise mit Buffer.from(frame.data).
Video-Daten: Die Videobilder werden einzeln pro abonniertem Stream über die onRenderFrame
Callback, der zum Zeitpunkt des Abonnements registriert wurde. Jeder Aufruf umfasst den subscriber die angibt, zu welchem Stream
der Frame gehört, sodass Sie das Videomaterial verschiedener Teilnehmer separat verarbeiten können.
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);
}
Audio-Daten: Der gemischte Ton wird über den onAudioData Callback registriert während connect(),
etwa alle 10 ms. Die Bibliothek mischt die Audiosignale aller abonnierten Streams automatisch zu einem einzigen
Stream zusammen. In diesem Callback können Sie die Audiosignale der einzelnen Teilnehmer nicht voneinander unterscheiden.
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)`
);
}
Daten zur Beschriftung: Diese Funktion ist derzeit als Beta-Funktion verfügbar. Der Untertitelstext wird pro
abonniertem Stream über den onCaptionText Callback, mit dem subscriber Identifizierung des Quellstroms und eines
CaptionsData Objekt, das den Text enthält, sowie die Angabe, ob es sich um ein endgültiges oder ein vorläufiges Ergebnis handelt.
Hinweis
Für die onCaptionText Callback zum Empfangen von Untertiteldaten: Live-Untertitel müssen in der zugrunde liegenden Konfiguration der Vonage Video API-Sitzung aktiviert sein (außerhalb dieser Bibliothek; siehe die Vonage Video API Live-Unterschriften (Dokumentation) und für den jeweiligen Publisher-Stream, der Audio sendet. Setze subscribeToCaptions: true in den Abonnenteneinstellungen, um diese zu erhalten.
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}`);
}
Einzelne Audiodaten
Diese Funktion ist derzeit als Beta-Funktion verfügbar. Der Audio-Stream der einzelnen Kanäle wird über den
onAudioData Callback, der zum Zeitpunkt der Anmeldung über subscribe() — im Gegensatz zur Sitzungsebene
onAudioData, über das der Mix übertragen wird. Das Audiosignal wird in dem Format übertragen, in dem es vom Stream empfangen wird, nämlich Linear PCM 16 Bit,
und weder die Abtastrate noch die Kanalanzahl können vor dem Empfang konfiguriert werden.
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)`
);
}
Abbestellen von Streams
Beenden Sie den Empfang von Medien aus einem bestimmten Stream:
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);
});
}
Verarbeitung von Audiodaten
Audioformat
Audiodaten werden als lineare PCM-Samples mit 16 Bit, vorzeichenbehaftet und im Little-Endian-Format, in einem Buffer:
- Musterpreise8000, 12000, 16000, 24000, 32000, 44100, oder 48000 Hz
- Kanäle: 1 (Mono) oder 2 (Stereo)
- Format: 16-Bit, vorzeichenbehaftet, Little-Endian in einem
Buffer - Rahmengröße: In der Regel 20ms-Blöcke (variiert je nach Abtastrate)
Verarbeitung von Audiodaten
Verarbeitung eingehender Audiodaten im Callback für Audiodaten der Sitzung:
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);
}
Erstellen von Audiodaten
Wenn Sie Audio hinzufügen, erstellen Sie eine Buffer von 16-Bit-Samples:
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 });
Kontinuität der Audiodaten
Wenn Sie Audio veröffentlichen, sendet die Bibliothek Stille, bis Ihr erster addAudio() call toleriert kurze Lücken
ohne das Senden von Paketen, greift dann auf explizite Stille-Frames zurück und füllt Teilperioden auf, um eine Abweichung zu verhindern.
Siehe Audio-Kontinuität um das gesamte Verhalten zu verstehen.
Bewährte Praktiken:
- Behalten Sie eine konsistente Audiorate bei, indem Sie
addAudio()in regelmäßigen Abständen entsprechend der eingestellten Abtastrate - Pufferstatistiken überwachen mit
getMediaBufferStats()um angemessene Audiodaten zu gewährleisten - Behandeln Sie die
onMediaBufferDrainedCallback, um zu erkennen, wenn der Audiopuffer erschöpft ist - Erwägen Sie die Implementierung einer Audiogenerierungsstrategie, die sich an unterschiedliche Verarbeitungslasten anpasst
Verarbeitung von Videodaten
Video-Format
Videodaten werden als 8-Bit-Werte ohne Vorzeichen in einem Buffer, in einem der drei Formate:
- YUV420P: Planares YUV-Format mit 4:2:0 Chroma-Unterabtastung
- RGB24: Packed RGB, 8 Bit pro Kanal
- ARGB32: ARGB-Daten im komprimierten Format, 8 Bit pro Kanal einschließlich Alpha-Kanal
Video-Spezifikationen:
- Entschließungen: Bis zu 1920x1080 (Full HD)
- Bildfrequenzen: 1-30 FPS
- Format: 8-Bit-Werte ohne Vorzeichen in einem
Buffer
Verarbeitung von Videobildern
Behandelt eingehende Videobilder im Rendering-Frame-Callback:
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);
}
Erstellen von Videobildern
Erstellen Sie beim Veröffentlichen von Videos ein Buffer an das Format und die Auflösung angepasst:
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() ist synchron. Sie gibt true wenn der Frame akzeptiert wurde, und false wenn der Controller nicht
initialisiert ist oder der native Aufruf fehlschlägt. Es wird ein TypeError wenn der Frame fehlerhaft ist – ein unbekanntes Format, eine
nicht positive Abmessung, eine Pixelanzahl über 1920×1080 oder ein Puffer, der für die angegebene Auflösung zu klein ist.
Kontinuität der Videobilder
Wenn Sie ein Video veröffentlichen, sendet die Bibliothek schwarze Bilder, bis Ihr erstes addVideo() call wiederholt Ihren letzten
Frame bis zu 2 Sekunden lang, wenn Sie keine Frames mehr liefern, und wechselt dann zu schwarzen Frames. Siehe
Videokontinuität um das gesamte Verhalten zu verstehen.
Bewährte Praktiken:
- Behalten Sie eine konstante Bildrate bei, indem Sie
addVideo()in regelmäßigen Abständen entsprechend Ihrer konfigurierten FPS - Pufferstatistiken überwachen mit
getMediaBufferStats()um ausreichende Videodaten zu gewährleisten - Behandeln Sie die
onMediaBufferDrainedCallback, um zu erkennen, wenn der Videopuffer erschöpft ist - Erwägen Sie die Implementierung einer Strategie zur Generierung von Frames, die sich an unterschiedliche Verarbeitungslasten anpasst.
Verwaltung der Medienpuffer
Überprüfung der Pufferstatistik
Überwachen Sie den Zustand Ihrer Medienpuffer:
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`);
}
Jedes Feld ist null wenn kein Herausgeber dieses Medientyps aktiv ist.
Löschen von Medienpuffern
Leeren Sie bei Bedarf sowohl den Audio- als auch den Videopuffer, beispielsweise um die Wiedergabe mitten in einer Äußerung zu unterbrechen:
if (client.clearMediaBuffers()) {
console.log('Media buffers cleared successfully');
}
Puffer entleert Rückruf
Behandlung von Pufferentleerungsereignissen:
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`);
}
}
Die onMediaBufferDrained Der Callback ist registriert in connect(). Sie wird aufgerufen, wenn die internen Audio- oder Videopuffer
leer sind, und implementiert eine Hysterese, damit sie nicht wiederholt ausgelöst wird, solange der Puffer leer bleibt.
Siehe Puffer-Drain-Ereignisse für Einzelheiten.
Verbindungsinformationen abrufen
Rufen Sie Ihre lokalen Verbindungsinformationen ab:
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() gibt zurück. null wenn der Client nicht verbunden ist.
Ereignis-Rückrufe
Callbacks werden nach dem Aufruf gruppiert, durch den sie registriert wurden. Beachten Sie, dass die einmalig auftretenden Lebenszyklusereignisse –
„Sitzung verbunden“, „Publisher-Stream erstellt“ und „Abonnent verbunden“ – durch die Promises übermittelt werden, die
von connect(), publish()und subscribe() anstatt als Callbacks.
Session-Rückrufe
Eingetragen in connect():
| Rückruf | Unterschrift |
|---|---|
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
}
Verleger-Rückrufe
Eingetragen in publish():
| Rückruf | Unterschrift |
|---|---|
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}`);
}
Rückrufe von Abonnenten
Eingetragen in subscribe():
| Rückruf | Unterschrift |
|---|---|
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}`);
}
Fehlerbehandlung
Die Bibliothek meldet Probleme auf drei verschiedene Arten.
Synchrone Ausnahmen bei ungültiger Eingabe. Argumente werden vor dem Übergang in die native Ebene validiert.
addAudio(), addVideo(), und die an connect() einen werfen TypeError wenn ein Wert
den falschen Typ hat oder gegen eine dokumentierte Einschränkung verstößt. publish() löst einen ValidationError wenn die Einstellungen
korrekt eingegeben wurden, aber gegen eine Regel verstoßen, wie zum Beispiel hasAudio und hasVideo beides sind 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 ist eine vom Paket exportierte Klasse, sodass sie mit instanceof.
Lege fest, wie bei fehlgeschlagenen Operationen eine Ablehnung behandelt werden soll. Wenn eine Operation fehlschlägt, bevor sie abgeschlossen ist, wird ihr Promise mit
einem Error dessen Nachricht die zugrunde liegende Beschreibung und den Code enthält.
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);
}
Callbacks bei Fehlern nach der Abwicklung. Sobald ein Vorgang erfolgreich abgeschlossen wurde, werden spätere Fehler an die
onError für diesen Geltungsbereich registrierter Callback. Ein onError Der Callback wird daher niemals für einen Fehler aufgerufen, bei dem
das Promise bereits abgelehnt wurde.
Boolesche Rückgabewerte bei der Einreichung von Medien. addAudio(), addVideo()und clearMediaBuffers() Zurück false
anstatt einen Fehler auszulösen, wenn der Controller nicht initialisiert ist oder der native Aufruf fehlschlägt.
Bereinigung von Ressourcen
Reinigen Sie die Ressourcen immer ordnungsgemäß:
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'));
}
}