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

Der Vonage Video Connector für Node.js wird als @vonage/video-connector Paket.

Um die Bibliothek zu installieren, führen Sie aus:

npm install @vonage/video-connector

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:

  • data muss ein Buffer die 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.byteLength muss mindestens numberOfFrames * numberOfChannels * 2 Bytes

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:

  • data muss ein Buffer von 8-Bit-Werten ohne Vorzeichen
  • width und height sind 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 onAudioData Rü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 onMediaBufferDrained Callback, 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 onMediaBufferDrained Callback, 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'));
  }
}