Python用Video Connector

Vonage Video Connector Python ライブラリを使用すると、Vonage Video API セッションにサーバ側の参加者としてプログラム的に参加することができます。 セッションに参加できるようになります。このライブラリは、ビデオセッションへの接続、ストリームの公開と購読を可能にします、 そしてリアルタイムのオーディオとビデオデータを処理することができます。

重要 Vonage Video Connector Python ライブラリはサーバサイド・アプリケーション用に設計されており、有効な Vonage Video API 認証情報とトークン、および適切なパーミッションが必要です。

このページでは、Python API について解説しています。すべての Video Connector ライブラリに共通する Concepts、メディア形式、および実行時の動作については、 ビデオコネクター を案内する。

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

はじめに

Vonage Video Connector Server SDK は PyPI で入手可能です。 ビデオコネクタ.

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

pip install vonage-video-connector

必要条件

このライブラリには、Linux AMD64およびARM64プラットフォームで動作するPython 3.13が必要です。 Debian Bookwormを使用することをお勧めします。

データ構造

Vonage Video Connector Python ライブラリは、セッション、接続、ストリーム、およびオーディオデータを表現するために、いくつかの主要なデータ構造を使用します。これらの構造を理解することは、ライブラリを効果的に使用するために不可欠です。

セッション

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

from vonage_video_connector.models import Session

# Session object properties
session.id  # str: Unique identifier for the session

について Session オブジェクトは、どのセッションがイベントをトリガーしたかを識別するために、様々なコールバック関数に渡される。

接続

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

from vonage_video_connector.models import Connection

# Connection object properties
connection.id             # str: Unique identifier for the connection
connection.creation_time  # datetime: When the connection was established
connection.data          # str: Connection data (encoded in the token)

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

ストリーム

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

from vonage_video_connector.models import Stream

# Stream object properties
stream.id          # str: Unique identifier for the stream
stream.connection  # Connection: The underlying connection that published this stream

ストリームは、参加者がメディアを公開するときに作成され、参加者のオーディオ/ビデオデータを受信するために購読するために使用されます。

出版社

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

from vonage_video_connector.models import Publisher

# Publisher object properties
publisher.stream  # Stream: The underlying stream for this publisher

について Publisher オブジェクトはパブリッシャー関連のコールバックで使用され、あなた自身の公開メディアストリームを表します。

購読者

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

from vonage_video_connector.models import Subscriber

# Subscriber object properties
subscriber.stream  # Stream: The underlying stream for this subscriber

について Subscriber オブジェクトは購読者関連のコールバックで使用され、他の参加者のメディアを受信する購読を表します。

オーディオデータ

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

from vonage_video_connector.models import AudioData

# AudioData object properties
audio_data.sample_buffer      # memoryview: 16-bit signed integer audio samples
audio_data.sample_rate        # int: Sample rate (8000-48000 Hz)
audio_data.number_of_channels # int: 1 (mono) or 2 (stereo)
audio_data.number_of_frames   # int: Number of audio frames

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

  • サンプル・バッファは16ビット符号付き整数でなければならない。
  • 有効なサンプルレート8000、12000、16000、24000、32000、44100、48000 Hz
  • チャンネル1(モノラル)または2(ステレオ)
  • バッファーの大きさ number_of_frames * number_of_channels サンプル

ビデオフレーム

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

from vonage_video_connector.models import VideoFrame, VideoResolution

# VideoFrame object properties
video_frame.frame_buffer  # memoryview: 8-bit unsigned char video frame data
video_frame.resolution    # VideoResolution: Width and height in pixels
video_frame.format        # str: Video format (YUV420P, RGB24, or ARGB32)

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

  • フレーム・バッファには8ビットの符号なし文字が含まれていなければならない。
  • 有効な形式:YUV420P、RGB24、ARGB32
  • 最大解像度1920x1080ピクセル(合計207万3600ピクセル)
  • バッファサイズはフォーマットと解像度によって異なる

ビデオ解像度

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

from vonage_video_connector.models import VideoResolution

# VideoResolution object properties
resolution = VideoResolution(
    width=640,   # int: Width in pixels, defaults to 640
    height=480   # int: Height in pixels, defaults to 480
)

どちらも正の整数でなければならず、ピクセル総数は1920 × 1080(2,073,600)を超えてはなりません。

キャプションデータ

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

from vonage_video_connector.models import CaptionsData

# CaptionsData object properties
captions_data.text      # str: The caption text content
captions_data.is_final  # bool: True for final captions, False for interim/partial captions

メディアバッファ統計

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

from vonage_video_connector.models import MediaBufferStats, AudioBufferStats, VideoBufferStats

# MediaBufferStats object properties
stats.audio  # Optional[AudioBufferStats]: Audio buffer statistics
stats.video  # Optional[VideoBufferStats]: Video buffer statistics

# AudioBufferStats properties
stats.audio.duration  # timedelta: Duration of queued audio

# VideoBufferStats properties
stats.video.duration  # timedelta: Duration of queued video

構成構造

セッション設定

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

from vonage_video_connector.models import SessionSettings

session_settings = SessionSettings(
    enable_migration=False,  # bool: Enable automatic session migration
    av=av_settings,          # Optional[SessionAVSettings]: Audio/video configuration
    logging=logging_settings # Optional[LoggingSettings]: Logging configuration
)

セッションAV設定

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

from vonage_video_connector.models import (
    SessionAVSettings,
    SessionAudioSettings,
    SessionVideoPublisherSettings,
    VideoResolution,
)

av_settings = SessionAVSettings(
    audio_publisher=SessionAudioSettings(sample_rate=48000, number_of_channels=2),
    audio_subscribers_mix=SessionAudioSettings(sample_rate=48000, number_of_channels=1),
    video_publisher=SessionVideoPublisherSettings(
        resolution=VideoResolution(width=1280, height=720),
        fps=30,
        format="YUV420P"
    )
)
  • オーディオ・パブリッシャー:を介して提供するオーディオデータのフォーマットを定義します。 add_audio().送信するオーディオデータは、このコンフィギュレーションのサンプルレートとチャンネル数と一致していなければなりません。
  • audio_subscribers_mix(オーディオ・サブスクライバーズ・ミックス:を経由して、すべての契約ストリームから受信するミックス音声のフォーマットを定義します。 on_audio_data_cb をコールバックします。このライブラリーは、複数のサブスクライバーのオーディオのミキシングと、指定したフォーマットに合わせたリサンプリング/チャンネル変換を自動的に処理する。

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

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

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

from vonage_video_connector.models import SessionAudioSettings

audio_settings = SessionAudioSettings(
    sample_rate=48000,       # int: Optional, defaults to 48000
    number_of_channels=1     # int: Optional, 1 (mono) or 2 (stereo), defaults to 2
)

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

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

from vonage_video_connector.models import SessionVideoPublisherSettings, VideoResolution

video_settings = SessionVideoPublisherSettings(
    resolution=VideoResolution(width=1280, height=720),  # Required: resolution in pixels
    fps=30,              # int: Optional, 1-30, defaults to 30
    format="YUV420P"     # str: Optional, defaults to "YUV420P"
)

他の設定モデルとは異なり、 resolution が必要です。 VideoResolution デフォルトでは640×480に設定されています。

ロギング設定

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

from vonage_video_connector.models import LoggingSettings

logging_settings = LoggingSettings(
    level="INFO"  # str: Optional, ERROR, WARN, INFO, DEBUG, or TRACE, defaults to WARN
)

パブリッシャー設定

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

from vonage_video_connector.models import PublisherSettings, PublisherAudioSettings

publisher_settings = PublisherSettings(
    name="My Application",           # str: Name for your published stream (required, min 1 char)
    has_audio=True,                  # bool: Whether to publish audio
    has_video=True,                  # bool: Whether to publish video
    enable_captions=False,           # bool: Whether to enable live captions for this stream (default: False)
    audio_settings=audio_settings    # Optional[PublisherAudioSettings]: Audio configuration
)

注: 少なくとも1つ has_audio または has_video でなければならない。 True.

注: セット enable_captions=True を使って、購読者がこのストリームからライブ・キャプション・テキストを受信できるようにする。 on_caption_text_cb コールバックを使用します。デフォルトではキャプションは無効になっています。

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

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

from vonage_video_connector.models import PublisherAudioSettings

audio_settings = PublisherAudioSettings(
    enable_stereo_mode=True,  # bool: Publish in stereo (True) or mono (False)
    enable_opus_dtx=False     # bool: Enable discontinuous transmission
)

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

購読者設定

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

from vonage_video_connector.models import SubscriberSettings, SubscriberVideoSettings, VideoResolution

subscriber_settings = SubscriberSettings(
    subscribe_to_audio=True,     # bool: Whether to subscribe to audio
    subscribe_to_video=True,     # bool: Whether to subscribe to video
    subscribe_to_captions=False, # bool: Whether to subscribe to live captions (default: False)
    video_settings=SubscriberVideoSettings(
        preferred_resolution=VideoResolution(width=640, height=480),
        preferred_framerate=15
    )
)

注: 少なくとも1つ subscribe_to_audio または subscribe_to_video でなければならない。 True.

注: セット subscribe_to_captions=True このストリームからのキャプションテキストを、 on_caption_text_cb コールバック。また、公開側でもキャプションを有効にする必要があります。

加入者ビデオ設定

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

from vonage_video_connector.models import SubscriberVideoSettings, VideoResolution

video_settings = SubscriberVideoSettings(
    preferred_resolution=VideoResolution(width=640, height=480),  # Optional
    preferred_framerate=15  # Optional: Preferred FPS (1-30)
)

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

セッションへの接続

基本接続

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

from vonage_video_connector import VonageVideoClient
from vonage_video_connector.models import (
    SessionSettings,
    SessionAVSettings,
    SessionAudioSettings,
    LoggingSettings,
)

# Create client instance
client = VonageVideoClient()

# Configure session settings
session_settings = SessionSettings(
    enable_migration=False,
    av=SessionAVSettings(
        audio_subscribers_mix=SessionAudioSettings(
            sample_rate=48000,
            number_of_channels=1
        )
    ),
    logging=LoggingSettings(level="INFO")
)

# Connect to session
connecting = client.connect(
    application_id="your_application_id",
    session_id="your_session_id",
    token="your_token",
    session_settings=session_settings,
    on_connected_cb=on_session_connected,
    on_error_cb=on_session_error
)

connect() 非同期であり、接続の試行が開始され次第、直ちに返ります。戻り値は True とは、そのリクエストが受理され、処理が開始されたことを意味します。 違う クライアントがセッションに接続されていることを確認します。 接続されるまで待機します。 on_connected_cb セッションを接続済みとみなす前、およびパブリッシュや サブスクライブを行う前に呼び出される必要があります。

戻り値は False これは、リクエストが送信される前に拒否されたことを意味します。例えば、クライアントが すでにセッションに接続されている場合や、セッション情報が不完全な場合などが挙げられます。リクエストが 送信された後に発生するエラー(トークンの有効期限切れや無効なトークンなど)は、 on_error_cb ではなく、 戻り値を通じて。

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

完全なセッション管理のためには、利用可能なすべてのコールバックを実装する:

connecting = client.connect(
    application_id="your_application_id",
    session_id="your_session_id",
    token="your_token",
    session_settings=session_settings,
    on_error_cb=on_session_error,
    on_connected_cb=on_session_connected,
    on_disconnected_cb=on_session_disconnected,
    on_connection_created_cb=on_connection_created,
    on_connection_dropped_cb=on_connection_dropped,
    on_stream_received_cb=on_stream_received,
    on_stream_dropped_cb=on_stream_dropped,
    on_audio_data_cb=on_audio_data,
    on_ready_for_audio_cb=on_ready_for_audio,
    on_media_buffer_drained_cb=on_media_buffer_drained
)

セッションからの切断

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

success = client.disconnect()

disconnect() 収益 True すべてが取り壊された後、また戻ってくる True そもそもクライアントが セッションに接続されていなかった場合。

接続状態の確認

if client.is_connected():
    print("Still connected")

セッション設定

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

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

from vonage_video_connector.models import (
    SessionAVSettings, 
    SessionAudioSettings, 
    SessionVideoPublisherSettings,
    VideoResolution
)

# Configure audio for publisher and subscriber mix
audio_publisher = SessionAudioSettings(
    sample_rate=48000,      # Valid: 8000, 12000, 16000, 24000, 32000, 44100, 48000
    number_of_channels=2    # 1 for mono, 2 for stereo
)

audio_subscribers_mix = SessionAudioSettings(
    sample_rate=48000,
    number_of_channels=1
)

# Configure video publisher settings
video_publisher = SessionVideoPublisherSettings(
    resolution=VideoResolution(width=1280, height=720),
    fps=30,
    format="YUV420P"  # Valid: YUV420P, RGB24, ARGB32
)

# Combine into session AV settings
av_settings = SessionAVSettings(
    audio_publisher=audio_publisher,
    audio_subscribers_mix=audio_subscribers_mix,
    video_publisher=video_publisher
)

ロギング設定

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

from vonage_video_connector.models import LoggingSettings

# Configure logging level
logging_settings = LoggingSettings(
    level="DEBUG"  # Valid: ERROR, WARN, INFO, DEBUG, TRACE
)

セッション移行

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

from vonage_video_connector.models import SessionSettings

session_settings = SessionSettings(
    enable_migration=True,  # Enable automatic migration
    av=av_settings,
    logging=logging_settings
)

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

発行者構成

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

from vonage_video_connector.models import PublisherSettings, PublisherAudioSettings

# Configure publisher audio settings
audio_settings = PublisherAudioSettings(
    enable_stereo_mode=True,   # Publish in stereo
    enable_opus_dtx=False      # Enable discontinuous transmission for bandwidth savings
)

# Create publisher settings for audio and video
publisher_settings = PublisherSettings(
    name="AI Assistant Bot",
    has_audio=True,
    has_video=True,
    enable_captions=True,
    audio_settings=audio_settings
)

# Or audio-only publisher
audio_only_settings = PublisherSettings(
    name="Audio Bot",
    has_audio=True,
    has_video=False,
    enable_captions=False,
    audio_settings=audio_settings
)

出版開始

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

success = client.publish(
    settings=publisher_settings,
    on_error_cb=on_publisher_error,
    on_stream_created_cb=on_stream_created,
    on_stream_destroyed_cb=on_stream_destroyed
)

重要 オーディオをパブリッシュする場合(has_audio=True)、次のようになります。 on_ready_for_audio_cb を呼び出す前に呼び出される。 add_audio().このコールバックは、オーディオシステムが初期化され、オーディオデータを受け入れる準備ができていることを示します。この要件は、ビデオのみのパブリッシングシナリオには適用されません。

import time

# Example: Wait for audio system to be ready
audio_ready = False

def on_ready_for_audio(session):
    global audio_ready
    audio_ready = True
    print("Audio system ready - can now add audio")

# Connect with the callback
client.connect(
    application_id="your_application_id",
    session_id="your_session_id",
    token="your_token",
    session_settings=session_settings,
    on_ready_for_audio_cb=on_ready_for_audio
)

# Publish
client.publish(settings=publisher_settings)

# Wait for audio to be ready before adding audio
while not audio_ready:
    time.sleep(0.01)

# Now safe to add audio
client.add_audio(audio_data)

オーディオデータの追加

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

from vonage_video_connector.models import AudioData

# Create audio data (example with 16-bit PCM samples)
audio_buffer = memoryview(your_audio_samples)  # Must be 16-bit signed integers

audio_data = AudioData(
    sample_buffer=audio_buffer,
    sample_rate=48000,
    number_of_channels=1,
    number_of_frames=960  # 20ms at 48kHz
)

# Add audio to the published stream
success = client.add_audio(audio_data)

add_audio() 収益 True そのフレームが受け入れられた場合、および False パブリッシングパイプラインの準備が整っていない場合や、 ネイティブ呼び出しが失敗した場合。 AudioData を引き起こす ValidationError フレームの形式が不正な場合―― たとえば、宣言されたフレームのジオメトリに対してバッファが小さすぎる場合など。

出版中止

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

success = client.unpublish()

公開状態の確認

if client.is_publishing():
    print("Still publishing")

ストリームの購読

ストリームを購読する

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

from vonage_video_connector.models import SubscriberSettings, SubscriberVideoSettings, VideoResolution

def on_stream_received(session, stream):
    print(f"New stream received: {stream.id}")
    print(f"From connection: {stream.connection.id}")
    
    # Configure subscriber settings (optional)
    subscriber_settings = SubscriberSettings(
        subscribe_to_audio=True,
        subscribe_to_video=True,
        subscribe_to_captions=True,
        video_settings=SubscriberVideoSettings(
            preferred_resolution=VideoResolution(width=640, height=480),
            preferred_framerate=15
        )
    )
    
    # Subscribe to the stream
    success = client.subscribe(
        stream=stream,
        settings=subscriber_settings,
        on_error_cb=on_subscriber_error,
        on_connected_cb=on_subscriber_connected,
        on_disconnected_cb=on_subscriber_disconnected,
        on_render_frame_cb=on_render_frame,
        on_audio_data_cb=on_subscriber_audio_data,
        on_caption_text_cb=on_caption_text
    )

購読メディアの受信

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

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

ビデオデータ:ビデオフレームは、ストリームごとに個別に配信されます。 on_render_frame_cb コールバックを呼び出します。各コールバック呼び出しには subscriber オブジェクトは、ビデオフレームがどのストリームに属するかを識別します。これにより、異なる参加者からのビデオを個別に処理できます。

def on_render_frame(subscriber, video_frame):
    """Called for each subscribed stream's video frames"""
    stream_id = subscriber.stream.id
    width = video_frame.resolution.width
    height = video_frame.resolution.height
    print(f"Video frame from stream {stream_id}: {width}x{height}")
    # Process video for this specific stream

音声データ:音声は、単一のミックスストリームとして配信されます。 on_audio_data_cb コールバックは connect().ライブラリは、購読しているすべてのストリームの音声を1つの音声ストリームに自動的にミックスします。このコールバックでは、個々の参加者の音声を区別することはできません。

def on_audio_data(session, audio_data):
    """Called with mixed audio from all subscribed streams"""
    sample_rate = audio_data.sample_rate
    channels = audio_data.number_of_channels
    print(f"Mixed audio from all subscribers: {sample_rate}Hz, {channels} channel(s)")
    # Process the combined audio from all participants

キャプションデータ: この機能は現在ベータ版として提供されている。 キャプション・テキストは、購読しているストリームごとに、以下の方法で個別に配信されます。 on_caption_text_cb コールバック。それぞれの呼び出しには subscriber ソースストリームを識別するオブジェクトと CaptionsData オブジェクトにテキストが含まれ、それが最終結果か中間結果かを示す。

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

def on_caption_text(subscriber, captions_data):
    """Called when caption text is received from a subscribed stream"""
    stream_id = subscriber.stream.id
    status = "final" if captions_data.is_final else "interim"
    print(f"Caption ({status}) from stream {stream_id}: {captions_data.text}")
    # Process interim captions for live display, final captions for storage or further processing

個別の音声データ

この機能は現在ベータ版として提供されている。 個々のストリーム音声は on_audio_data_cb を通じてサブスクリプション時に登録される。 subscribe().音声はストリームから受信したフォーマット(リニアPCM 16ビット)で配信され、サンプルレートやチャンネル数は受信前に設定することはできません。

def on_subscriber_audio_data(subscriber, audio_data):
    """Called with individual audio from the stream subscribed to"""
    sample_rate = audio_data.sample_rate
    channels = audio_data.number_of_channels
    print(f"Individual audio from stream {subscriber.stream.id}: {sample_rate}Hz, {channels} channel(s)")
    # Process the individual audio from this stream

ストリームの配信停止

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

def on_stream_dropped(session, stream):
    print(f"Stream dropped: {stream.id}")
    
    # Unsubscribe from the stream
    success = client.unsubscribe(stream)

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

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

オーディオデータは、リニアPCMの16ビット符号付き整数として配信され、以下の特性を持つ:

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

オーディオデータの処理

オーディオ・データ・コールバックで受信オーディオを処理する:

def on_audio_data(session, audio_data):
    """Process incoming audio data from subscribed streams"""
    
    # Access audio properties
    sample_rate = audio_data.sample_rate
    channels = audio_data.number_of_channels
    frames = audio_data.number_of_frames
    
    # Access the audio buffer (memoryview of 16-bit signed integers)
    audio_buffer = audio_data.sample_buffer
    
    print(f"Received {frames} frames at {sample_rate}Hz, {channels} channel(s)")
    
    # Convert to bytes if needed
    audio_bytes = audio_buffer.tobytes()
    
    # Process the audio (e.g., transcription, analysis, etc.)
    process_audio(audio_buffer, sample_rate, channels)
    
    # Generate response audio and add it back
    response_audio = generate_response(audio_buffer)
    if response_audio:
        client.add_audio(response_audio)

オーディオデータの作成

オーディオを追加する場合は、適切なフォーマットで AudioData オブジェクトがある:

import array
import math

from vonage_video_connector.models import AudioData

# Create 16-bit PCM audio samples (example: sine wave)
sample_rate = 48000
duration_ms = 20  # 20ms frame
num_samples = int(sample_rate * duration_ms / 1000)

# Generate audio samples as 16-bit signed integers
samples = array.array('h')  # 'h' = signed short (16-bit)
for i in range(num_samples):
    # Example: generate a 440Hz sine wave
    sample = int(32767 * 0.5 * math.sin(2 * math.pi * 440 * i / sample_rate))
    samples.append(sample)

# Create AudioData object
audio_data = AudioData(
    sample_buffer=memoryview(samples),
    sample_rate=sample_rate,
    number_of_channels=1,
    number_of_frames=num_samples
)

# Add the audio
client.add_audio(audio_data)

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

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

ベストプラクティス:

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

ビデオデータの取り扱い

ビデオフォーマット

ビデオデータは、8ビットの符号なし文字として、3つのフォーマットのうちの1つで配信される:

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

ビデオ仕様:

  • 決議:最大1920x1080(フルHD)
  • フレームレート:1-30 FPS
  • フォーマットの8ビット符号なし文字。 memoryview バッファ

ビデオフレームの処理

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

def on_render_frame(subscriber, video_frame):
    """Process incoming video frames from subscribed streams"""
    
    # Access video properties
    width = video_frame.resolution.width
    height = video_frame.resolution.height
    format = video_frame.format
    
    # Access the video buffer (memoryview of 8-bit unsigned chars)
    frame_buffer = video_frame.frame_buffer
    
    print(f"Received {width}x{height} frame in {format} format")
    
    # Convert to bytes if needed
    frame_bytes = frame_buffer.tobytes()
    
    # Process the video frame (e.g., computer vision, recording, etc.)
    process_video(frame_buffer, width, height, format)

ビデオフレームの作成

動画を公開する場合は、適切なフォーマットで作成してください。 VideoFrame オブジェクトがある:

import array
from vonage_video_connector.models import VideoFrame, VideoResolution

# Create a video frame (example: solid color in YUV420P format)
width = 640
height = 480

# YUV420P format calculation:
# Y plane: width * height
# U plane: (width/2) * (height/2)
# V plane: (width/2) * (height/2)
y_size = width * height
uv_size = (width // 2) * (height // 2)
total_size = y_size + 2 * uv_size

# Create frame buffer as 8-bit unsigned chars
frame_data = array.array('B', [128] * total_size)  # 'B' = unsigned char (8-bit)

# Create VideoFrame object
video_frame = VideoFrame(
    frame_buffer=memoryview(frame_data),
    resolution=VideoResolution(width=width, height=height),
    format="YUV420P"
)

# Add the video frame
success = client.add_video(video_frame)

add_video() 収益 True そのフレームが受け入れられた場合、および False パブリッシングパイプラインの準備が整っていない場合や、 ネイティブ呼び出しが失敗した場合。 VideoFrame を引き起こす ValidationError フレームの形式が不正な場合 — 不明な形式、正の値ではない寸法、1920×1080を超えるピクセル数、または宣言された 解像度に対してバッファが小さすぎる場合。

ビデオフレームの連続性

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

ベストプラクティス:

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

メディア・バッファ管理

バッファ統計のチェック

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

# Get current buffer statistics
stats = client.get_media_buffer_stats()

if stats.audio:
    print(f"Audio buffer duration: {stats.audio.duration.total_seconds()}s")

if stats.video:
    print(f"Video buffer duration: {stats.video.duration.total_seconds()}s")

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

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

必要に応じて、オーディオとビデオの両方のバッファをクリアする:

# Clear all media buffers
success = client.clear_media_buffers()

if success:
    print("Media buffers cleared successfully")

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

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

def on_media_buffer_drained(stats):
    """Called when media buffers are drained"""
    print("Media buffers drained")
    
    if stats.audio:
        print(f"Audio buffer: {stats.audio.duration.total_seconds()}s remaining")
    
    if stats.video:
        print(f"Video buffer: {stats.video.duration.total_seconds()}s remaining")

について on_media_buffer_drained_cb コールバックは、内部のオーディオまたはビデオバッファが空になったときに呼び出され、 バッファが空のままである間は繰り返し呼び出されないようヒステリシスが実装されています。詳細は バッファの排出イベント 詳細はこちら。

接続情報の取得

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

# Get the local connection
connection = client.get_connection()

if connection:
    print(f"Connection ID: {connection.id}")
    print(f"Connection data: {connection.data}")
    print(f"Created at: {connection.creation_time}")

get_connection() 収益 None クライアントが接続されていない場合。

イベント・コールバック

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

登録地: connect():

コールバック 署名
on_error_cb (session: Session, description: str, code: int) -> None
on_connected_cb (session: Session) -> None
on_disconnected_cb (session: Session) -> None
on_connection_created_cb (session: Session, connection: Connection) -> None
on_connection_dropped_cb (session: Session, connection: Connection) -> None
on_stream_received_cb (session: Session, stream: Stream) -> None
on_stream_dropped_cb (session: Session, stream: Stream) -> None
on_audio_data_cb (session: Session, audio_data: AudioData) -> None
on_ready_for_audio_cb (session: Session) -> None
on_media_buffer_drained_cb (stats: MediaBufferStats) -> None

セッションレベルのイベントを処理する:

def on_session_error(session, error_description, error_code):
    """Handle session errors"""
    print(f"Session error: {error_description} (Code: {error_code})")

def on_session_connected(session):
    """Handle successful session connection"""
    print(f"Connected to session: {session.id}")

def on_session_disconnected(session):
    """Handle session disconnection"""
    print(f"Disconnected from session: {session.id}")

def on_ready_for_audio(session):
    """Called when the audio system is ready"""
    print("Audio system ready - can now add audio")

def on_connection_created(session, connection):
    """Handle new participant joining"""
    print(f"Participant joined: {connection.id}")
    print(f"Connection data: {connection.data}")

def on_connection_dropped(session, connection):
    """Handle participant leaving"""
    print(f"Participant left: {connection.id}")

def on_stream_received(session, stream):
    """Handle new streams from other participants"""
    print(f"Stream received: {stream.id} from connection {stream.connection.id}")
    # Decide whether to subscribe based on your application logic

def on_stream_dropped(session, stream):
    """Handle streams being removed"""
    print(f"Stream dropped: {stream.id}")

発行者コールバック

登録地: publish():

コールバック 署名
on_error_cb (publisher: Publisher, description: str, code: int) -> None
on_stream_created_cb (publisher: Publisher) -> None
on_stream_destroyed_cb (publisher: Publisher) -> None

パブリッシング・イベントを処理する:

def on_publisher_error(publisher, error_description, error_code):
    """Handle publisher errors"""
    print(f"Publisher error: {error_description} (Code: {error_code})")

def on_stream_created(publisher):
    """Handle successful stream creation"""
    print(f"Published stream created: {publisher.stream.id}")

def on_stream_destroyed(publisher):
    """Handle stream destruction"""
    print(f"Published stream destroyed: {publisher.stream.id}")

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

登録地: subscribe():

コールバック 署名
on_error_cb (subscriber: Subscriber, description: str, code: int) -> None
on_connected_cb (subscriber: Subscriber) -> None
on_disconnected_cb (subscriber: Subscriber) -> None
on_render_frame_cb (subscriber: Subscriber, video_frame: VideoFrame) -> None
on_audio_data_cb (subscriber: Subscriber, audio_data: AudioData) -> None
on_caption_text_cb (subscriber: Subscriber, captions_data: CaptionsData) -> None

購読イベントを処理する:

def on_subscriber_error(subscriber, error_description, error_code):
    """Handle subscriber errors"""
    print(f"Subscriber error: {error_description} (Code: {error_code})")

def on_subscriber_connected(subscriber):
    """Handle successful subscription"""
    print(f"Subscribed to stream: {subscriber.stream.id}")

def on_subscriber_disconnected(subscriber):
    """Handle subscription disconnection"""
    print(f"Unsubscribed from stream: {subscriber.stream.id}")

def on_render_frame(subscriber, video_frame):
    """Handle incoming video frames"""
    width = video_frame.resolution.width
    height = video_frame.resolution.height
    print(f"Video frame: {width}x{height} in {video_frame.format} format")

def on_caption_text(subscriber, captions_data):
    """Handle incoming caption text"""
    status = "final" if captions_data.is_final else "interim"
    print(f"Caption ({status}) from stream {subscriber.stream.id}: {captions_data.text}")

エラー処理

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

ValidationError モデルが構築される際。 あらゆる設定やメディアモデルは、 pydantic 自身のフィールドを検証するモデルであるため、無効な値は、 オブジェクトがクライアントに渡される時点ではなく、オブジェクトが作成される時点で拒否されます。サンプリングレート、チャンネル数、ログレベル、 ピクセル形式、フレームレート、解像度、バッファ要素サイズ、およびバッファ容量がすべてチェックされます。

from pydantic import ValidationError

from vonage_video_connector.models import PublisherSettings, SessionAudioSettings

try:
    publisher_settings = PublisherSettings(name="Bot", has_audio=False, has_video=False)
except ValidationError as e:
    # "At least one of has_audio(False) or has_video (False) must be set to true."
    print(f"Invalid publisher settings: {e}")

try:
    audio_settings = SessionAudioSettings(sample_rate=44000)
except ValidationError as e:
    # "44000 is not one of the allowed: [8000, 12000, 16000, 24000, 32000, 44100, 48000]"
    print(f"Invalid audio settings: {e}")

ということに注意してほしい。 format そして level 値は大文字と小文字を区別せず、すべて大文字に変換されるため、 format="yuv420p" そして LoggingSettings(level="info") どちらも受け入れられます。

TypeError そして AttributeError クライアントのメソッドから。 ネイティブレイヤーは、渡された オブジェクトから必要な属性を読み取ります。これにより、 AttributeError 期待される属性が欠落している場合、および TypeError 引数や属性の型が正しくない場合、およびコールバックが呼び出せない場合など。

try:
    client.add_audio(audio_data)
except (TypeError, AttributeError) as e:
    print(f"Malformed audio data: {e}")

False 失敗した操作の戻り値。 すべてのクライアントメソッドは、 bool 運用上の不具合を理由に 引き上げるのではなく、 connect() 収益 False クライアントがすでにセッションに接続されている場合、 publish() リターン False すでに公開されている場合、および add_audio() そして add_video() 戻る False もし 公開パイプラインの準備が整っていない場合は、成功したと決めつけずに、結果を確認してください。

if not client.connect(
    application_id="your_application_id",
    session_id="your_session_id",
    token="your_token",
    session_settings=session_settings,
):
    print("Failed to start connecting - the client may already be connected")

on_error_cb 実行時エラーに対するコールバック。 通話の受諾後に発生する障害( 接続試行自体の失敗を含む)は、 on_error_cb そのスコープに登録されており、 説明と数値コードが指定されています。セッション、パブリッシャー、サブスクライバーの各スコープには、それぞれ独自のものが設定されています。

def on_session_error(session, error_description, error_code):
    print(f"Session error: {error_description} (Code: {error_code})")

client.connect(
    application_id="your_application_id",
    session_id="your_session_id",
    token="your_token",
    on_error_cb=on_session_error,
)

資源のクリーンアップ

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

try:
    # Your application logic
    connecting = client.connect(
        application_id="your_application_id",
        session_id="your_session_id",
        token="your_token",
        session_settings=session_settings,
    )
    # ... do work ...

except Exception as e:
    print(f"Application error: {e}")

finally:
    # Clean up resources
    if client.is_publishing():
        client.unpublish()

    if client.is_connected():
        client.disconnect()