Connecteur vidéo pour Python

La bibliothèque Python du connecteur vidéo de Vonage vous permet de participer de manière programmatique aux sessions de l'API Video de Vonage en tant que participant côté serveur. Cette bibliothèque vous permet de vous connecter à des sessions vidéo, de publier des flux et de vous y abonner, et de traiter des données audio et vidéo en temps réel.

Important La bibliothèque Python du connecteur vidéo de Vonage est conçue pour les applications côté serveur et nécessite des informations d'identification et des jetons valides de l'API Video de Vonage avec les autorisations appropriées.

Cette page présente l'API Python. Pour en savoir plus sur les concepts, les formats multimédias et le comportement d'exécution communs à toutes les bibliothèques Video Connector, consultez la Connecteur vidéo guide.

Cette rubrique comprend les sections suivantes :

Pour commencer

Le SDK de Vonage Video Connector Server est disponible sur PyPI en tant que vonage-video-connector.

Pour installer la bibliothèque, exécutez :

pip install vonage-video-connector

Exigences

Cette bibliothèque nécessite Python 3.13 sous Linux AMD64 et ARM64. Nous recommandons d'utiliser Debian Bookworm car c'est la distribution où elle a été testée de la manière la plus complète.

Structures de données

La bibliothèque Python du connecteur vidéo de Vonage utilise plusieurs structures de données clés pour représenter les sessions, les connexions, les flux et les données audio. Il est essentiel de comprendre ces structures pour travailler efficacement avec la bibliothèque.

Session

Représente une session Video API de Vonage à laquelle les clients peuvent se connecter :

from vonage_video_connector.models import Session

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

Les Session est transmis à diverses fonctions de rappel afin d'identifier la session qui a déclenché l'événement.

Connexion

Représente la connexion d'un participant à une 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)

Les données de connexion peuvent être utilisées pour stocker des métadonnées personnalisées sur les participants, telles que les identifiants ou les rôles des utilisateurs.

Flux

Représente un flux média (audio/vidéo) publié par un participant :

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

Les flux sont créés lorsque les participants publient des médias et sont utilisés pour s'abonner afin de recevoir leurs données audio/vidéo.

Éditeur

Représente le flux publié dans la session :

from vonage_video_connector.models import Publisher

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

Les Publisher est utilisé dans les rappels liés à la publication et représente votre propre flux de médias publié.

Abonné

Représente un abonnement au flux d'un autre participant :

from vonage_video_connector.models import Subscriber

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

Les Subscriber est utilisé dans les rappels liés à l'abonnement et représente votre abonnement à la réception des médias d'un autre participant.

Données audio

Représente les données audio transmises ou reçues :

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

Exigences en matière de format audio :

  • Le tampon d'échantillonnage doit contenir des entiers signés de 16 bits
  • Taux d'échantillonnage valables : 8000, 12000, 16000, 24000, 32000, 44100, 48000 Hz
  • Canaux : 1 (mono) ou 2 (stéréo)
  • La taille de la mémoire tampon doit être adaptée : number_of_frames * number_of_channels échantillons

VideoFrame

Représente les données de la trame vidéo en cours de transmission ou de réception :

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)

Exigences en matière de format vidéo :

  • Le tampon de trame doit contenir des caractères non signés de 8 bits
  • Formats pris en charge : YUV420P, RGB24, ARGB32
  • Résolution maximale : 1920x1080 pixels (2 073 600 pixels au total)
  • La taille de la mémoire tampon varie en fonction du format et de la résolution

Résolution vidéo

Représente les dimensions d'une image vidéo :

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
)

Ces deux nombres doivent être des entiers positifs, et le nombre total de pixels ne doit pas dépasser 1 920 × 1 080 (2 073 600).

CaptionsData

Représente les données textuelles de la légende reçues d'un flux abonné :

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

MediaBufferStats

Fournit des statistiques sur les tampons de média :

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

Structures de configuration

Paramètres de session

Configure le comportement au niveau de la session :

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
)

SessionAVSettings

Configure les paramètres audio et vidéo de la session :

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"
    )
)
  • éditeur_audio: Définit le format des données audio que vous fournissez par l'intermédiaire de l'application add_audio(). Les données audio que vous envoyez doivent correspondre à la fréquence d'échantillonnage et au nombre de canaux de cette configuration.
  • audio_subscribers_mix: Définit le format de l'audio mixte que vous recevez de tous les flux souscrits via l'application on_audio_data_cb callback. La bibliothèque prend automatiquement en charge le mixage de l'audio de plusieurs abonnés et le rééchantillonnage/la conversion des canaux pour correspondre au format que vous avez spécifié.

Pour obtenir des conseils sur le choix de ces formats, consultez Contenu audio destiné à la publication ou à l'abonnement.

SessionAudioSettings

Configure le format audio pour la publication ou la réception de données audio :

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
)

Paramètres de l'éditeur vidéo de session

Configure les paramètres vidéo pour la publication :

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"
)

Contrairement aux autres modèles de paramètres, resolution est obligatoire. VideoResolution La résolution par défaut est de 640 × 480.

Paramètres de journalisation

Contrôle la verbosité de la journalisation :

from vonage_video_connector.models import LoggingSettings

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

Paramètres de l'éditeur

Configure votre flux publié :

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
)

Remarque : Au moins un des éléments suivants has_audio ou has_video doit être True.

Remarque : Set (jeu de mots) enable_captions=True pour permettre aux abonnés de recevoir le texte du sous-titre en direct de ce flux via la fonction on_caption_text_cb callback. Les légendes sont désactivées par défaut.

Paramètres audio de l'éditeur

Configure les paramètres audio pour votre flux publié :

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
)

Transmission discontinue (DTX) arrête l'envoi de paquets audio pendant les silences, ce qui permet d'économiser de la bande passante.

Paramètres de l'abonné

Configure le comportement des abonnés :

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

Remarque : Au moins un des éléments suivants subscribe_to_audio ou subscribe_to_video doit être True.

Remarque : Set (jeu de mots) subscribe_to_captions=True pour recevoir le texte des sous-titres de ce flux via le on_caption_text_cb rappel. Les sous-titres doivent également être activés du côté de la publication.

Paramètres vidéo de l'abonné

Configure les préférences vidéo pour les abonnés :

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

Ces paramètres demandent au SFU de la Video API une couche de qualité de diffusion simultanée spécifique. Pour savoir comment le SFU les prend en compte, consultez Résolution et fréquence d'images recommandées pour les abonnés.

Connexion à une session

Connexion de base

Pour vous connecter à une session Video API de Vonage, vous avez besoin de votre identifiant d'application (clé API si vous utilisez Tokbox), de l'identifiant de session et d'un jeton valide :

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() est asynchrone et renvoie un résultat dès que la tentative de connexion a été lancée. Une valeur de retour de True signifie que la demande a été acceptée et transmise, pas que le client soit connecté à la session. Attendre que on_connected_cb à appeler avant de considérer la session comme connectée, et avant de publier ou de s'abonner.

Une valeur de retour de False signifie que la requête a été rejetée avant d’être envoyée, par exemple parce que le client est déjà connecté à une session ou que les informations relatives à la session sont incomplètes. Les échecs survenant après l’ envoi de la requête, tels qu’un jeton expiré ou non valide, sont signalés à on_error_cb plutôt que par le biais de la valeur de retour.

Connexion avec tous les rappels

Pour une gestion complète de la session, il faut mettre en œuvre tous les rappels disponibles :

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
)

Déconnexion d'une session

Déconnectez-vous de la session lorsque vous avez terminé :

success = client.disconnect()

disconnect() retours True une fois que tout aura été démoli, et revient également True si le client n'était pas déjà connecté à une session.

Vérification de l'état de la connexion

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

Paramètres de la session

Configuration audio et vidéo

Configurer les paramètres audio et vidéo de la session pour contrôler le format des données multimédias :

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
)

Configuration de l'enregistrement

Contrôle la verbosité de la journalisation de la console :

from vonage_video_connector.models import LoggingSettings

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

Migration de session

Activer la migration automatique des sessions en cas de rotation du SFU :

from vonage_video_connector.models import SessionSettings

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

Flux de publication

Configuration de l'éditeur

Configurez les paramètres de l'éditeur avant de commencer à publier :

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
)

Commencer à publier

Commencer à publier un flux dans la session :

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
)

Important Si vous publiez de l'audio (has_audio=True), vous devez attendre que la commande on_ready_for_audio_cb à invoquer avant d'appeler add_audio(). Ce rappel indique que le système audio est initialisé et prêt à accepter des données audio. Cette exigence ne s'applique pas aux scénarios de publication de vidéos uniquement.

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)

Ajout de données audio

Envoyez des données audio à votre flux publié :

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() retours True si la trame a été acceptée, et False si le pipeline de publication n'est pas prêt ou si l'appel natif échoue. La construction du AudioData soulève une ValidationError si la trame est mal formée — par exemple si la mémoire tampon est trop petite pour la géométrie de trame déclarée.

Cesser de publier

Arrêtez la publication lorsque vous avez terminé :

success = client.unpublish()

Vérification de l'état de publication

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

S'abonner à des flux

S'abonner aux flux

Lorsqu'un nouveau flux est reçu, vous pouvez vous y abonner pour recevoir des données audio et/ou vidéo :

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
    )

Réception des médias souscrits

Lorsque vous vous abonnez à des flux, la bibliothèque transmet les données audio et vidéo via différents callbacks. Pour comprendre le raisonnement qui sous-tend cette conception, consultez S'abonner à des flux dans le guide « Video Connector ».

Important Le memoryview à l'intérieur d'un AudioData ou VideoFrame Le contenu transmis à une fonction de rappel n'est valide que pendant la durée de cette fonction. Si vous devez conserver le média au-delà de la fonction de rappel — pour le mettre en file d'attente ou le traiter de manière asynchrone —, copiez-le d'abord, par exemple à l'aide de audio_data.sample_buffer.tobytes() ou bytes(video_frame.frame_buffer).

Données vidéo: Les images vidéo sont fournies individuellement pour chaque flux souscrit par l'intermédiaire de l'interface utilisateur. on_render_frame_cb callback. Chaque invocation d'un rappel comprend l'élément subscriber qui identifie le flux auquel la trame vidéo appartient. Cela vous permet de traiter séparément les vidéos provenant de différents participants.

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

Données audio: L'audio est diffusé sous la forme d'un flux mixte unique par le biais de l'interface utilisateur. on_audio_data_cb enregistré lors de la connect(). La bibliothèque mélange automatiquement l'audio de tous les flux souscrits en un seul flux audio. Il n'est pas possible de distinguer l'audio des participants individuels dans ce rappel.

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

Données de légende: Cette fonctionnalité est actuellement disponible en version bêta. Le texte du sous-titre est diffusé individuellement pour chaque flux souscrit par l'intermédiaire de l'application on_caption_text_cb callback. Chaque invocation comprend l'élément subscriber identifiant le flux source et un objet CaptionsData l'objet contenant le texte et s'il s'agit d'un résultat final ou intermédiaire.

Note Pour les on_caption_text_cb Pour que la fonction de rappel puisse recevoir les données de sous-titrage, les sous-titres en direct doivent être activés dans la configuration de la session de la Video API Vonage sous-jacente (en dehors de cette bibliothèque ; voir la Video API Vonage Sous-titres en direct (documentation) et pour le flux de diffusion spécifique qui transmet le son. Définir subscribe_to_captions=True dans les paramètres d'abonnement pour les recevoir.

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

Données audio individuelles

Cette fonctionnalité est actuellement disponible en version bêta. Les flux audio individuels peuvent être récupérés par le biais de la fonction on_audio_data_cb enregistré au moment de l'abonnement par l'intermédiaire de subscribe(). L'audio est livré dans le format reçu du flux - PCM linéaire 16 bits - et ni la fréquence d'échantillonnage ni le nombre de canaux ne peuvent être configurés avant la réception.

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

Se désabonner des flux

Arrêter la réception de médias provenant d'un flux spécifique :

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

Traitement des données audio

Format audio

Les données audio sont fournies sous forme d'entiers signés PCM linéaires de 16 bits avec les caractéristiques suivantes :

  • Taux d'échantillonnage: 8000, 12000, 16000, 24000, 32000, 44100, ou 48000 Hz
  • Canaux: 1 (mono) ou 2 (stéréo)
  • Format: Entiers signés de 16 bits dans un memoryview tampon
  • Taille du cadre: En général, tranches de 20 ms (varie en fonction de la fréquence d'échantillonnage)

Traitement des données audio

Traite l'audio entrant dans le callback des données audio :

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)

Création de données audio

Lorsque vous ajoutez de l'audio, créez des fichiers AudioData objets :

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)

Continuité des données audio

Lorsque vous publiez un fichier audio, la bibliothèque diffuse un silence jusqu'à votre premier add_audio() appel, tolère de brèves interruptions sans envoyer de paquets, puis revient à des trames de silence explicites et complète les périodes partielles pour éviter toute dérive. Voir Continuité audio pour connaître le fonctionnement complet.

Meilleures pratiques :

  • Maintenir un débit audio constant en appelant add_audio() à intervalles réguliers correspondant à la fréquence d'échantillonnage configurée
  • Surveiller les statistiques de la mémoire tampon à l'aide de get_media_buffer_stats() pour garantir des données audio adéquates
  • Manipuler les on_media_buffer_drained_cb callback pour détecter l'épuisement de la mémoire tampon audio
  • Envisager la mise en œuvre d'une stratégie de génération audio qui s'adapte à des charges de traitement variables.

Traitement des données vidéo

Format vidéo

Les données vidéo sont fournies sous forme de caractères non signés de 8 bits dans l'un des trois formats suivants :

  • YUV420P: Format YUV planaire avec sous-échantillonnage chromatique 4:2:0
  • RGB24: RVB compressé, 8 bits par canal
  • ARGB32: ARGB compressé, 8 bits par canal, alpha compris

Spécifications vidéo :

  • Résolutions: Jusqu'à 1920x1080 (Full HD)
  • Taux de rafraîchissement: 1-30 FPS
  • Format: caractères non signés de 8 bits dans un memoryview tampon

Traitement des images vidéo

Traite les images vidéo entrantes dans le rappel de l'image de rendu :

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)

Création d'images vidéo

Lorsque vous publiez une vidéo, créez des images correctement formatées. VideoFrame objets :

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() retours True si la trame a été acceptée, et False si le pipeline de publication n'est pas prêt ou si l'appel natif échoue. La construction du VideoFrame soulève une ValidationError si la trame est mal formée — un format inconnu, une dimension non positive, un nombre de pixels supérieur à 1920x1080, ou un tampon trop petit pour la résolution déclarée.

Continuité de l'image vidéo

Lorsque vous publiez une vidéo, la bibliothèque envoie des images noires jusqu'à ce que votre première add_video() La fonction « call » répète votre dernière image pendant 2 secondes maximum si vous cessez de fournir des images, puis affiche des images noires. Voir Continuité vidéo pour connaître le fonctionnement complet.

Meilleures pratiques :

  • Maintenir une fréquence d'images constante en appelant add_video() à intervalles réguliers en fonction de votre FPS configuré
  • Surveiller les statistiques de la mémoire tampon à l'aide de get_media_buffer_stats() pour garantir des données vidéo adéquates
  • Manipuler les on_media_buffer_drained_cb callback pour détecter l'épuisement de la mémoire tampon vidéo
  • Envisager la mise en œuvre d'une stratégie de génération de trames qui s'adapte à des charges de traitement variables.

Gestion de la mémoire tampon des médias

Vérification des statistiques de la mémoire tampon

Surveillez l'état de vos mémoires tampons :

# 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")

Chaque champ est None lorsqu'aucun éditeur de ce type de média n'est actif.

Effacer les mémoires tampons des médias

Effacez les tampons audio et vidéo si nécessaire :

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

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

Buffer drained callback

Gérer les événements de vidange de la mémoire tampon :

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")

Les on_media_buffer_drained_cb La fonction de rappel est appelée lorsque les tampons audio ou vidéo internes sont vides, et elle intègre un mécanisme d'hystérésis afin de ne pas se déclencher de manière répétée tant que le tampon reste vide. Voir Événements de vidange du tampon pour plus de détails.

Obtenir des informations sur la connexion

Récupérez vos informations de connexion locale :

# 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() retours None lorsque le client n'est pas connecté.

Rappels d'événements

Rappels de session

Enregistré à connect():

Rappel Signature
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

Gérer les événements au niveau de la session :

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

Rappels de l'éditeur

Enregistré à publish():

Rappel Signature
on_error_cb (publisher: Publisher, description: str, code: int) -> None
on_stream_created_cb (publisher: Publisher) -> None
on_stream_destroyed_cb (publisher: Publisher) -> None

Gérer les événements de publication :

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

Rappels de l'abonné

Enregistré à subscribe():

Rappel Signature
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

Gérer les événements liés à l'abonnement :

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

Gestion des erreurs

La bibliothèque signale les problèmes de quatre manières différentes.

ValidationError lorsqu'un modèle est élaboré. Chaque modèle de paramètres et de médias est un pydantic modèle qui valide ses propres champs, de sorte que les valeurs non valides sont rejetées au moment de la création de l'objet plutôt qu'au moment où celui-ci est transmis au client. Les fréquences d'échantillonnage, le nombre de canaux, les niveaux de journalisation, les formats de pixels, les fréquences d'images, les résolutions, la taille des éléments de la mémoire tampon et la capacité de la mémoire tampon sont tous vérifiés.

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

Il convient de noter que format et level Les valeurs ne tiennent pas compte de la casse et sont converties en majuscules, donc format="yuv420p" et LoggingSettings(level="info") sont toutes les deux acceptées.

TypeError et AttributeError à partir des méthodes du client. La couche native lit les attributs dont elle a besoin à partir des objets que vous lui transmettez. Elle déclenche AttributeError lorsqu'un attribut attendu est manquant, et TypeError lorsqu'un argument ou un attribut est d'un type incorrect, y compris lorsqu'une fonction de rappel n'est pas invoquable.

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

False valeurs de retour pour les opérations ayant échoué. Chaque méthode cliente renvoie un bool plutôt que de mettre en avant une défaillance opérationnelle. connect() retours False si le client est déjà connecté à une session, publish() rendements False s'il est déjà en cours de publication, et add_audio() et add_video() retour False si le processus de publication n'est pas prêt. Vérifiez le résultat plutôt que de partir du principe que l'opération a réussi.

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 fonctions de rappel en cas d'erreurs d'exécution. Les échecs survenant après l'acceptation d'un appel — y compris l'échec de la tentative de connexion elle-même — sont signalés au on_error_cb enregistrés pour ce périmètre, accompagnés d'une description et d'un code numérique. Les périmètres « session », « éditeur » et « abonné » ont chacun les leurs.

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,
)

Nettoyage des ressources

Nettoyez toujours les ressources de manière appropriée :

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()