Node.js用Video Connector

Vonage Video Connector Node.js ライブラリを使用すると、サーバーサイドの参加者として、Vonage Video API セッションにプログラム経由で参加することができます。このライブラリを使用すると、ビデオセッションへの接続、ストリームの公開および購読、 ならびにリアルタイムの音声および映像データの処理が可能になります。

重要 Vonage Video Connector Node.js ライブラリは、サーバーサイドアプリケーション向けに設計されており、有効な Vonage Video API の認証情報と、適切な権限を持つトークンが必要です。

このページでは、Node.js API について解説しています。すべての Video Connector ライブラリに共通する Concepts、メディア形式、および実行時の動作については、 ビデオコネクター ガイド。Pythonで作業している場合は、 以下を参照してください。 Python用Video Connector.

このトピックには以下のセクションが含まれます:

スタート

Node.js 用の Vonage Video Connector は、 @vonage/video-connector パッケージで提供される。

ライブラリをインストールするには

npm install @vonage/video-connector

このパッケージには、あらかじめビルド済みのネイティブバイナリが同梱されており、コンパイル作業は不要です。TypeScriptの型宣言も 含まれているため、別途 @types パッケージが必要です。

必要条件

このライブラリを使用するには、Linux x64 または ARM64 プラットフォーム上で動作する Node.js 18 以降が必要です。 このライブラリは Debian Bookworm で最も徹底的にテストされているため、同ディストリビューションの使用を推奨します。

サポートされていないプラットフォームでライブラリを読み込むと、その プラットフォームとアーキテクチャの組み合わせに対応するネイティブモジュールが存在しないため、インポート時に例外が発生します。

プロミスベースのAPI

この Node.js ライブラリでは、ライフサイクル操作が、完了時のコールバックではなく Promise として公開されています。これは API 全体の設計に影響を与えるため、このページの残りの部分を読む前に、この点を理解しておく価値があります。

それぞれ connect(), publish(), subscribe(), disconnect(), unpublish()そして unsubscribe() プロミスを 返します:

方法 次のように解決されます 以下の場合に解決されます
connect() Session セッション接続が確立されました
publish() Publisher 公開されたストリームが作成されました
subscribe() Subscriber 定期購読が設定されました
disconnect() void 切断が完了しました
unpublish() void 公開取り消しが完了しました
unsubscribe() void 登録解除が完了しました

その結果、このライブラリでは、ワンショット型のライフサイクルイベントに対応するコールバックは存在しません。プロミスは は その通知。そこには onConnected、出版社なし onStreamCreated、そして購読者はいない onConnected 登録するには。それ以外のイベントはすべて、引数オブジェクトとして渡すコールバックです。

エラー処理も同様の構成となっています。エラーが発生した場合は 以前 操作が完了すると、プロミスで拒否され、 あなたの onError コールバックは呼び出されません。リジェクトがエラー通知となります。エラーが発生した場合は その後 その 操作が完了しました。あなたの onError コールバックは通常通り呼び出されます。

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);
}

重要 1つだけ作成する VonageVideoClient プロセスごとに1つのインスタンス。基盤となるSDKは、アクティブな接続を1つしかサポートしておらず、複数のインスタンスを作成すると未定義の挙動が生じます。

データ構造

以下のすべての型は、パッケージからエクスポートされており、型としてインポートすることができます。

セッション

クライアントが接続できる Vonage Video API セッションを表します:

interface Session {
  sessionId: string;  // Unique identifier for the session
}

について Session オブジェクトは、どのセッションがイベントをトリガーしたかを特定するために、セッションレベルのコールバックに渡されます。

接続

セッションへの参加者の接続を表します:

interface Connection {
  connectionId: string;  // Unique identifier for the connection
  creationTime: number;  // Unix timestamp in milliseconds
  data: string;          // Connection data (encoded in the token)
}

接続データは、ユーザーIDやロールなど、参加者に関するカスタム・メタデータを保存するために使用できる。

ということに注意してほしい。 creationTime これはミリ秒単位の数値のUnixタイムスタンプであるため、そのまま new Date(connection.creationTime).

ストリーム

参加者によって公開されたメディアストリーム(オーディオ/ビデオ)を表します:

interface Stream {
  streamId: string;       // Unique identifier for the stream
  connection: Connection; // The connection that published this stream
}

出版社

セッション内の公開ストリームを表します:

interface Publisher {
  stream: Stream;  // The underlying stream for this publisher
}

購読者

他の参加者のストリームへのサブスクリプションを表します:

interface Subscriber {
  stream: Stream;  // The stream being subscribed to
}

オーディオデータ

送受信されるオーディオデータを表す:

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
}

オーディオフォーマットの要件:

  • data きっと……に違いない Buffer 16ビットの符号付きリトルエンディアン形式のサンプルを含む
  • 有効なサンプルレート8000、12000、16000、24000、32000、44100、48000 Hz
  • チャンネル1(モノラル)または2(ステレオ)
  • data.byteLength 少なくとも numberOfFrames * numberOfChannels * 2 バイト

ビデオフレーム

送受信されるビデオフレームデータを表す:

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'
}

ビデオフォーマットの要件

  • data きっと……に違いない Buffer 8ビットの符号なし値の
  • width そして height これらはフレーム上のフラットプロパティであり、独立した解像度オブジェクトは存在しません
  • 最大解像度1920x1080ピクセル(合計207万3600ピクセル)
  • 必要なバッファサイズはフォーマットによって異なります:
フォーマット 必須 data.byteLength
YUV420P width * height * 3 / 2
RGB24 width * height * 3
ARGB32 width * height * 4

ビデオ解像度

ビデオフレームの寸法を表す:

interface VideoResolution {
  width: number;   // Width in pixels. Must be a positive integer
  height: number;  // Height in pixels. Must be a positive integer
}

総画素数 (width * height) は 1920 × 1080 (2,073,600) を超えてはなりません。このタイプは、 SubscriberVideoSettings.preferredResolution; 内のパブリッシャー・ジオメトリ SessionVideoPublisherSettings そして、さらに VideoFrame フラットを使用する width そして height プロパティの代わりに。

キャプションデータ

購読ストリームから受信したキャプションテキストデータを表す:

interface CaptionsData {
  text: string;     // The caption text content
  isFinal: boolean; // true for final captions, false for interim/partial captions
}

メディアバッファ統計

公開用メディアバッファに関する統計情報を提供します:

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
}

持続時間は、ミリ秒単位の単純な数値です。

検証の種類

このライブラリは、検証対象の値に対してユニオン型をエクスポートするため、無効なリテラルはコンパイル時に検出されます:

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';

構成構造

セッション設定

セッションレベルの動作を設定する:

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
};

セッションAV設定

セッションのオーディオとビデオの設定を行います:

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:を介して提供するオーディオデータのフォーマットを定義します。 addAudio(). 送信するオーディオデータは、この設定のサンプルレートおよびチャンネル数と一致している必要があります。
  • オーディオ・サブスクライバー・ミックス: セッションレベルを通じて、購読しているすべてのストリームから受信する混合オーディオのフォーマットを定義します。 onAudioData をコールバックします。このライブラリーは、複数のサブスクライバーのオーディオのミキシングと、指定したフォーマットに合わせたリサンプリング/チャンネル変換を自動的に処理する。

これらの形式の選び方については、以下を参照してください。 配信用と購読用の音声の違い.

セッションオーディオ設定

オーディオデータを公開または受信するためのオーディオフォーマットを設定します:

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
};

なお、このプロパティの名前は channels ここで、一方で AudioData 用途 numberOfChannels.

セッション動画パブリッシャー設定

パブリッシングのためのビデオ設定を行います:

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'
};

幅と高さは、ネストされた解像度オブジェクトではなく、フラットなプロパティです。

ロギング設定

ロギングの冗長性を制御する:

import type { LoggingSettings } from '@vonage/video-connector';

const loggingSettings: LoggingSettings = {
  level: 'INFO'  // Required: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE'
};

パブリッシャー設定

公開ストリームを設定します:

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
  }
};

注: 少なくとも1つ hasAudio または hasVideo でなければならない。 true. 両方を次のように渡す false をスローする ValidationError 〜から同期して publish().

注: セット enableCaptions: true 加入者が、このストリームからのライブキャプションテキストを 加入者を通じて受信できるようにするため onCaptionText コールバックを使用します。デフォルトではキャプションは無効になっています。

パブリッシャーオーディオ設定

公開ストリームのオーディオ設定を行います:

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
};

不連続伝送(DTX) 無音時のオーディオパケット送信を停止し、帯域幅を節約。

購読者設定

加入者の動作を設定する:

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
  }
};

キャプションは、公開側で有効化されている必要があり、購読者ごとに以下の手順でオプトインする必要があります。 subscribeToCaptions.

加入者ビデオ設定

加入者のビデオ設定を行う:

import type { SubscriberVideoSettings } from '@vonage/video-connector';

const videoSettings: SubscriberVideoSettings = {
  preferredResolution: { width: 640, height: 480 },  // Optional
  preferredFrameRate: 15                             // Optional, 1-30
};

これらの設定は、Vonage Video API SFU に対して特定の同時配信品質レイヤーを要求するものです。SFU がこれらを どのように処理するかについては、以下を参照してください。 推奨される加入者の解像度とフレームレート.

セッションへの接続

基本接続

Vonage Video API セッションに接続するには、アプリケーション ID(Tokbox を使用する場合は API キー)、セッション ID、 および有効なトークンが必要です:

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}`);

すべてのコールバックとの接続

セッションを完全に管理するには、利用可能なすべての進行中のコールバックを登録してください:

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
  }
);

~はありません onConnected コールバック — 待機中のプロミスがその役割を果たします。

セッションからの切断

終了したらセッションを切断する:

await client.disconnect();

接続状態の確認

if (client.isConnected()) {
  console.log('Still connected');
}

セッション設定

オーディオとビデオの設定

セッションのオーディオとビデオの設定を行い、メディアデータのフォーマットを制御する:

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 }
});

これらの設定に無効な値が含まれていると、 TypeError 〜から同期して connect()、ネットワーク通信を行う前に。

ロギング設定

コンソールロギングの冗長性を制御する:

await client.connect(applicationId, sessionId, token, {
  sessionSettings: {
    logging: { level: 'DEBUG' }  // Valid: ERROR, WARN, INFO, DEBUG, TRACE
  }
});

セッション移行

SFUのローテーション時にセッションの自動移行を有効にする:

await client.connect(applicationId, sessionId, token, {
  sessionSettings: {
    enableMigration: true,
    av: avSettings,
    logging: { level: 'INFO' }
  }
});

パブリッシング・ストリーム

発行者構成

パブリッシングを開始する前に、パブリッシャー設定を行います:

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
};

出版開始

セッションへのストリームの公開を開始する:

const publisher = await client.publish({
  settings: publisherSettings,
  onError: onPublisherError,
  onStreamDestroyed: onStreamDestroyed
});

console.log(`Publishing stream: ${publisher.stream.streamId}`);

プロミス(Promise)はストリームが作成されると解決されるため、 onStreamCreated コールバック。その onStreamDestroyed このコールバックは、サーバー側による強制的な 公開解除など、ストリームが予期せず破棄された場合にのみ発火します。以下を呼び出した場合には発火しません。 unpublish() あなた自身。

重要 音声コンテンツを公開する場合は(hasAudio: true)、その場合は、 onReadyForAudio 呼び出し前のコールバック addAudio(). このコールバックは、オーディオシステムが初期化され、オーディオデータを受け入れる準備が整ったことを示します。それ以前に送信されたフレームは、ネイティブレイヤーによって黙って破棄されます。この要件は、ビデオのみの配信シナリオには適用されません。

// 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);

動画には、これに対応する準備完了信号がありません。フレームは、準備が整い次第、すぐに送信されることがあります。 publish() 解決する。

オーディオデータの追加

公開ストリームにオーディオデータを送信します:

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() は同期処理です。これを呼び出すと、 true そのフレームが受け入れられた場合、および false コントローラが 初期化されていない場合、またはネイティブ呼び出しが失敗した場合。この場合、 TypeError フレームの形式が不正な場合 — たとえば、 宣言されたフレームのジオメトリに対してバッファが小さすぎる場合など。

出版中止

終了したら公開を中止する:

await client.unpublish();

公開状態の確認

if (client.isPublishing()) {
  console.log('Still publishing');
}

ストリームの購読

ストリームを購読する

新しいストリームを受信したら、そのストリームに登録し、オーディオやビデオデータを受信する:

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);
  }
}

購読メディアの受信

このライブラリは、さまざまなコールバックを通じてオーディオおよびビデオを配信します。この設計の理由については、以下を参照してください。 ストリームの購読 『Video Connector』ガイド内。

重要 について Buffer ~の内部で AudioData または VideoFrame コールバックに渡されたメディアは、そのコールバックの実行中のみ有効です。コールバック終了後もメディアを保持する必要がある場合(キューに追加したり、非同期で処理したりする場合など)は、まず、例えば次のようにしてメディアをコピーしてください。 Buffer.from(frame.data).

ビデオデータ:ビデオフレームは、ストリームごとに個別に配信されます。 onRenderFrame サブスクリプション時に登録されたコールバック。各呼び出しには、 subscriber そのフレームがどのストリームに属するかを識別するもので、 これにより、異なる参加者からの動画を個別に処理することができます。

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);
}

音声データ: ミックスされた音声は、以下の経由で配信されます。 onAudioData コールバックは connect(), およそ10ミリ秒ごとに。このライブラリは、サブスクライブされたすべてのストリームからの音声を自動的に1つの ストリームにミックスします。このコールバック内では、個々の参加者の音声を区別することはできません。

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)`
  );
}

キャプションデータ: この機能は現在ベータ版として提供されている。 キャプションテキストは、 購読中のストリームごとに、 onCaptionText コールバック、その subscriber ソースストリームと CaptionsData テキストと、結果が最終的なものか暫定的なものかを格納するオブジェクト。

注 については onCaptionText キャプションデータを受け取るためのコールバックを使用するには、基盤となるVonage Video APIのセッション設定(このライブラリの外側)でライブキャプションが有効になっている必要があります(Vonage Video APIを参照)。 ライブ・キャプション (ドキュメント)および、オーディオを送信している特定のパブリッシャー・ストリームに対して設定します。 subscribeToCaptions: true 受信するには、加入者設定で設定を行ってください。

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}`);
}

個別の音声データ

この機能は現在ベータ版として提供されている。 個々のストリームの音声は、 onAudioData を通じてサブスクリプション時に登録される。 subscribe() — セッションレベルとは区別される onAudioData、この信号を伝送するものです。音声はストリームから受信した形式、つまりリニアPCM 16ビットで到着し、 受信前にサンプルレートやチャンネル数を設定することはできません。

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)`
  );
}

ストリームの配信停止

特定のストリームからのメディア受信を停止する:

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);
  });
}

オーディオデータの取り扱い

オーディオ・フォーマット

オーディオデータは、リニアPCM 16ビット符号付きリトルエンディアン形式のサンプルとして、 Buffer:

  • サンプルレート8000、12000、16000、24000、32000、44100、または48000 Hz
  • チャンネル:1(モノラル)または2(ステレオ)
  • フォーマット: 16ビットの符号付きリトルエンディアン形式の Buffer
  • フレームサイズ:通常20msチャンク(サンプルレートにより異なる)

オーディオデータの処理

セッションのオーディオデータコールバックで、入力オーディオを処理します:

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);
}

オーディオデータの作成

音声を追加する際は、 Buffer 16ビットサンプルの:

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 });

オーディオ・データの連続性

音声を公開すると、ライブラリは最初の addAudio() コール中は、パケットを送信しない短い間隔を許容し、 その後、明示的なサイレンスフレームに切り替わり、ドリフトを防ぐために部分的な周期をパディングします。 参照: 音声の連続性 動作の全容については。

ベストプラクティス:

  • 通話で安定した音声レートを維持する addAudio() 設定したサンプルレートに合わせて一定間隔で
  • を使用してバッファ統計を監視する。 getMediaBufferStats() 十分なオーディオデータを確保する
  • を扱う。 onMediaBufferDrained オーディオバッファが枯渇したことを検出するコールバック
  • さまざまな処理負荷に適応するオーディオ生成ストラテジーの導入を検討する

ビデオデータの取り扱い

ビデオフォーマット

ビデオデータは、8ビットの符号なし値として、 Buffer、以下の3つの形式のいずれかで:

  • YUV420P:4:2:0クロマサブサンプリングによるプレーナーYUVフォーマット
  • RGB24: パケット化されたRGB、チャンネルあたり8ビット
  • ARGB32: ARGB対応、アルファチャンネルを含め各チャンネルあたり8ビット

ビデオ仕様:

  • 決議:最大1920x1080(フルHD)
  • フレームレート:1-30 FPS
  • フォーマット: 8ビットの符号なし値が Buffer

ビデオフレームの処理

レンダー・フレーム・コールバックで、入力されるビデオ・フレームを処理する:

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);
}

ビデオフレームの作成

動画を公開する際は、 Buffer フォーマットと解像度に合わせてサイズ調整済み:

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() は同期処理です。これを呼び出すと、 true そのフレームが受け入れられた場合、および false コントローラが 初期化されていない場合、またはネイティブ呼び出しが失敗した場合。この場合、 TypeError フレームに不備がある場合 — 未知のフォーマット、 正の値ではない寸法、1920x1080を超えるピクセル数、または宣言された解像度に対してバッファが小さすぎる場合。

ビデオフレームの連続性

動画を公開すると、ライブラリは最初の addVideo() call は、フレームの供給を停止した場合、直前の フレームを最大 2 秒間繰り返し表示し、その後、黒のフレームに切り替わります。詳細は 動画の連続性 動作の全容については。

ベストプラクティス:

  • フレームレートを一定に保つには addVideo() 設定したFPSと同じ間隔で
  • を使用してバッファ統計を監視する。 getMediaBufferStats() 十分なビデオデータを確保する
  • を扱う。 onMediaBufferDrained ビデオバッファが枯渇したことを検出するコールバック。
  • さまざまな処理負荷に適応するフレーム生成戦略の導入を検討する。

メディア・バッファ管理

バッファ統計のチェック

メディアバッファの状態を監視する:

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`);
}

各フィールドは null そのメディアタイプのアクティブなパブリッシャーが存在しない場合。

メディアバッファのクリア

必要に応じて、音声および動画のバッファをクリアします。たとえば、発話途中で再生を中断する場合などです:

if (client.clearMediaBuffers()) {
  console.log('Media buffers cleared successfully');
}

バッファ排出コールバック

バッファドレインイベントを処理する:

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`);
  }
}

について onMediaBufferDrained コールバックは、以下に登録されています。 connect(). これは、内部のオーディオまたはビデオ バッファが枯渇した際に呼び出され、バッファが空のままである間は繰り返し発火しないようヒステリシスを実装しています。 参照: バッファの排出イベント 詳細はこちら。

接続情報の取得

ローカル接続情報を取得する:

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() 収益 null クライアントが接続されていない場合。

イベント・コールバック

コールバックは、それらを登録した呼び出しごとにグループ化されます。ワンショットのライフサイクルイベント、すなわち 「セッション接続」「パブリッシャースストリームの作成」「サブスクライバー接続」は、 から返されるプロミスによって配信されることに注意してください。 connect(), publish()そして subscribe() コールバックとしてではなく。

セッション・コールバック

登録地: connect():

コールバック 署名
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
}

発行者コールバック

登録地: publish():

コールバック 署名
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}`);
}

サブスクライバーのコールバック

登録地: subscribe():

コールバック 署名
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}`);
}

エラー処理

このライブラリは、3つの異なる方法で問題を報告します。

無効な入力に対して同期的なスローが発生します。 引数は、ネイティブ層に渡される前に検証されます。 addAudio(), addVideo()、および以下に渡されたオーディオ/ビデオ設定 connect() ~を投げる TypeError 値の 型が正しくない場合や、文書化された制約に違反している場合。 publish() をスローする ValidationError 設定が 正しく記述されているにもかかわらず、次のようなルールに違反している場合、 hasAudio そして hasVideo どちらも 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 このクラスはパッケージからエクスポートされているため、次のようにして捕捉することができます。 instanceof.

操作が失敗した場合は、エラーを返してください。 操作が決済される前に失敗した場合、そのプロミスは でリジェクトされます。 Error そのメッセージには、基礎となる説明とコードが含まれています。

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);
}

決済後のエラーに対するコールバック。 操作が一度成功すると、その後の失敗は onError そのスコープに登録されているコールバック。An onError したがって、すでにプロミスを拒否した失敗については、 コールバックは決して呼び出されません。

メディアの送信に関するブール値の戻り値。 addAudio(), addVideo()そして clearMediaBuffers() 戻る false コントローラーが初期化されていない場合やネイティブ呼び出しが失敗した場合に例外をスローするのではなく、

資源のクリーンアップ

資源の後始末は常に適切に:

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'));
  }
}