Conector de vídeo para Python

La biblioteca de Python «Vonage Video Connector» te permite participar mediante programación en sesiones de la Video API de Vonage como participante del lado del servidor. Esta biblioteca te permite conectarte a sesiones de vídeo, publicar y suscribirte a flujos, y procesar datos de audio y vídeo en tiempo real.

Importante La biblioteca de Python «Vonage Video Connector» está diseñada para aplicaciones del lado del servidor y requiere credenciales y tokens válidos de la Video API de Vonage con los permisos adecuados.

Esta página trata sobre la API de Python. Para conocer los Concepts, los formatos multimedia y el comportamiento en tiempo de ejecución comunes a todas las bibliotecas de Video Connector, consulta la Conector de vídeo guía.

Este tema incluye las siguientes secciones:

Primeros pasos

Vonage Video Connector Server SDK está disponible en PyPI como vonage-vídeo-conector.

Para instalar la biblioteca, ejecuta:

pip install vonage-video-connector

Requisitos

Esta biblioteca requiere Python 3.13 para plataformas Linux AMD64 y ARM64. Recomendamos utilizar Debian Bookworm, ya que es la distribución en la que se ha probado más a fondo.

Estructuras de datos

La biblioteca de Python «Vonage Video Connector» utiliza varias estructuras de datos clave para representar sesiones, conexiones, flujos y datos de audio. Comprender estas estructuras es fundamental para trabajar con la biblioteca de forma eficaz.

Sesión

Representa una sesión de la Video API de Vonage a la que se pueden conectar los clientes:

from vonage_video_connector.models import Session

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

El Session El objeto se pasa a varias funciones de devolución de llamada para identificar qué sesión ha desencadenado el evento.

Conexión

Representa la conexión de un participante a una sesión:

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)

Los datos de conexión pueden utilizarse para almacenar metadatos personalizados sobre los participantes, como los identificadores de usuario o sus funciones.

Transmisión

Representa una transmisión multimedia (audio/vídeo) publicada por un participante:

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

Las transmisiones se crean cuando los participantes publican contenidos multimedia y se utilizan para suscribirse y recibir sus datos de audio y vídeo.

Editorial

Representa su flujo publicado en la sesión:

from vonage_video_connector.models import Publisher

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

El Publisher se utiliza en las retrollamadas relacionadas con la publicación y representa su propio flujo multimedia publicado.

Suscriptor

Representa una suscripción al flujo de otro participante:

from vonage_video_connector.models import Subscriber

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

El Subscriber Este objeto se utiliza en las llamadas de retorno relacionadas con los suscriptores y representa tu suscripción para recibir los medios de otro participante.

AudioData

Representa los datos de audio que se transmiten o se reciben:

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

Requisitos de formato de audio:

  • El búfer de muestra debe contener enteros con signo de 16 bits
  • Frecuencias de muestreo válidas: 8000, 12000, 16000, 24000, 32000, 44100, 48000 Hz
  • Canales: 1 (mono) o 2 (estéreo)
  • El tamaño del búfer debe ser adecuado: number_of_frames * number_of_channels muestras

VideoFrame

Representa los datos de un fotograma de vídeo que se están transmitiendo o recibiendo:

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)

Requisitos de formato de vídeo:

  • El búfer de fotogramas debe contener caracteres sin signo de 8 bits
  • Formatos válidos: YUV420P, RGB24, ARGB32
  • Resolución máxima: 1920x1080 píxeles (2.073.600 píxeles en total)
  • El tamaño del búfer varía según el formato y la resolución

Resolución de vídeo

Representa las dimensiones de un fotograma de vídeo:

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
)

Ambos deben ser números enteros positivos, y el número total de píxeles no debe superar los 1920 × 1080 (2 073 600).

Datos de los subtítulos

Representa los datos del texto de los subtítulos recibidos de una transmisión a la que se está suscrito:

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

Proporciona estadísticas sobre los búferes multimedia:

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

Estructuras de configuración

Configuración de sesión

Configura el comportamiento a nivel de sesión:

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
)

SesiónAVSettings

Configura los ajustes de audio y vídeo para la sesión:

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"
    )
)
  • audio_publisher: Define el formato de los datos de audio que se facilitan a través de add_audio(). Los datos de audio que envíes deben coincidir con la frecuencia de muestreo y el número de canales de esta configuración.
  • audio_subscribers_mix: Define el formato del audio mezclado que recibes de todas las transmisiones a las que estás suscrito a través de la on_audio_data_cb callback. La biblioteca se encarga automáticamente de mezclar el audio de varios abonados y de remuestrear/convertir los canales para que coincidan con el formato especificado.

Para obtener orientación sobre cómo elegir estos formatos, consulta El audio para la publicación frente al audio para la suscripción.

SessionAudioSettings

Configura el formato de audio para publicar o recibir datos de 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
)

SessionVideoPublisherSettings

Configura los ajustes de vídeo para la publicación:

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

A diferencia de los demás modelos de configuración, resolution es obligatorio. VideoResolution Por defecto, tiene una resolución de 640x480.

Configuración de registro

Controla la verbosidad del registro:

from vonage_video_connector.models import LoggingSettings

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

Configuración del editor

Configura el flujo publicado:

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
)

Nota: Al menos uno de has_audio o has_video debe ser True.

Nota: Establecer enable_captions=True para que los suscriptores puedan recibir el texto de los subtítulos en directo de esta retransmisión a través de la on_caption_text_cb callback. Los subtítulos están desactivados por defecto.

Configuración de audio del editor

Configura los ajustes de audio de tu transmisión publicada:

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
)

Transmisión discontinua (DTX) deja de enviar paquetes de audio durante el silencio, ahorrando ancho de banda.

Configuración del suscriptor

Configura el comportamiento de los suscriptores:

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

Nota: Al menos uno de subscribe_to_audio o subscribe_to_video debe ser True.

Nota: Establecer subscribe_to_captions=True para recibir el texto de los subtítulos de esta transmisión a través del on_caption_text_cb callback. Las leyendas también deben estar activadas en el lado de la publicación.

SubscriberVideoSettings

Configura las preferencias de vídeo de los abonados:

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

Estos ajustes solicitan un nivel específico de calidad de retransmisión simultánea a la SFU de la Video API de Vonage. Para obtener una explicación de cómo los tiene en cuenta la SFU, consulta Resolución y frecuencia de fotogramas recomendadas para los suscriptores.

Conectarse a una sesión

Conexión básica

Para conectarte a una sesión de la Video API de Vonage, necesitas tu ID de aplicación (clave API si utilizas Tokbox), el ID de sesión y un token válido:

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() es asíncrona y devuelve un resultado tan pronto como se inicia el intento de conexión. Un valor de retorno de True significa que la solicitud se ha aceptado y se ha tramitado, no que el cliente esté conectado a la sesión. Espera a que on_connected_cb que debe invocarse antes de considerar la sesión como conectada y antes de publicar o suscribirse.

Un valor de retorno de False significa que la solicitud fue rechazada antes de ser enviada, por ejemplo, porque el cliente ya está conectado a una sesión o porque la información de la sesión está incompleta. Los errores que se producen después de que la solicitud haya sido enviada, como un token caducado o no válido, se notifican a on_error_cb en lugar de a través del valor de retorno.

Conexión con todas las funciones de devolución de llamada

Para gestionar la sesión al completo, implementa todas las funciones de devolución de llamada 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
)

Desconectarse de una sesión

Desconéctese de la sesión cuando haya terminado:

success = client.disconnect()

disconnect() devoluciones True una vez que se haya derribado todo, y también vuelve True si el cliente no estaba conectado a una sesión desde el principio.

Comprobación del estado de la conexión

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

Configuración de la sesión

Configuración de audio y vídeo

Configura los ajustes de audio y vídeo de la sesión para controlar el formato de los datos multimedia:

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
)

Configuración del registro

Controla el nivel de detalle de los mensajes de registro en la consola:

from vonage_video_connector.models import LoggingSettings

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

Migración de sesiones

Activar la migración automática de sesiones en caso de rotación de SFU:

from vonage_video_connector.models import SessionSettings

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

Flujos de publicación

Configuración del editor

Configura los ajustes del editor antes de empezar a publicar:

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
)

Empezar a publicar

Empieza a publicar un flujo en la sesión:

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
)

Importante Si va a publicar audio (has_audio=True), debe esperar a que se publique el archivo on_ready_for_audio_cb función de retorno que se ejecutará antes de llamar a add_audio(). Esta llamada de retorno indica que el sistema de audio está inicializado y listo para aceptar datos de audio. Este requisito no se aplica a los casos de publicación exclusivamente de vídeo.

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)

Añadir datos de audio

Envía datos de audio a tu transmisión publicada:

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() devoluciones True si se aceptó el fotograma, y False si el proceso de publicación no está listo o la llamada nativa falla. La construcción del AudioData plantea una ValidationError si la trama presenta un error de formato — por ejemplo, si el búfer es demasiado pequeño para la geometría de la trama declarada.

Dejar de publicar

Deje de publicar cuando haya terminado:

success = client.unpublish()

Comprobación del estado de publicación

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

Suscripción a flujos

Suscribirse a los flujos

Cuando se reciba una nueva transmisión, suscríbete a ella para recibir datos de audio y/o vídeo:

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
    )

Recepción de medios suscritos

Cuando te suscribes a flujos, la biblioteca envía datos de audio y vídeo a través de diferentes funciones de devolución de llamada. Para conocer los motivos que justifican este diseño, consulta Suscripción a flujos en la guía de Video Connector.

Importante En memoryview dentro de un AudioData o VideoFrame El contenido entregado a una función de devolución de llamada solo es válido mientras dura dicha función. Si necesitas conservar el contenido multimedia más allá de la función de devolución de llamada —para ponerlo en cola o procesarlo de forma asíncrona—, cópialo primero, por ejemplo, con audio_data.sample_buffer.tobytes() o bytes(video_frame.frame_buffer).

Datos de vídeo: Los fotogramas de vídeo se entregan individualmente por flujo suscrito a través del on_render_frame_cb función de devolución de llamada. Cada invocación de la función de devolución de llamada incluye el subscriber que identifica a qué flujo pertenece el fotograma de vídeo. Esto le permite procesar vídeo de distintos participantes por separado.

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

Datos de audio: El audio se transmite como un único flujo mezclado a través del on_audio_data_cb registrada durante connect(). La biblioteca combina automáticamente el audio de todas las transmisiones a las que se está suscrito en una única transmisión de audio. En esta llamada de retorno no es posible distinguir el audio de cada participante individualmente.

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

Datos del pie de foto: Esta función está disponible actualmente en versión beta. El texto de los subtítulos se entrega individualmente por flujo suscrito a través de la función on_caption_text_cb llamada de retorno. Cada invocación incluye el subscriber que identifica el flujo de origen y un CaptionsData objeto que contiene el texto y si se trata de un resultado definitivo o provisional.

Nota Para el on_caption_text_cb Para que la función de devolución de llamada reciba los datos de los subtítulos, es necesario que los subtítulos en directo estén habilitados en la configuración de la sesión de la Video API de Vonage subyacente (fuera de esta biblioteca; consulta la Video API de Vonage Subtítulos en directo documentación) y para el flujo específico del emisor que está enviando audio. Establecer subscribe_to_captions=True en la configuración de suscriptor para recibirlas.

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

Datos de audio individuales

Esta función está disponible actualmente en versión beta. El audio de un flujo individual puede recuperarse a través de la función on_audio_data_cb función de devolución de llamada registrada en el momento de la suscripción a través de subscribe(). El audio se entrega en el formato recibido del flujo - PCM lineal de 16 bits - y ni la frecuencia de muestreo ni el número de canales pueden configurarse antes de la recepción.

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

Darse de baja de streams

Dejar de recibir contenido multimedia de una fuente concreta:

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

Tratamiento de datos de audio

Formato de audio

Los datos de audio se transmiten como enteros con signo de 16 bits en formato PCM lineal, con las siguientes características:

  • Tasas de muestreo: 8000, 12000, 16000, 24000, 32000, 44100 o 48000 Hz
  • Canales: 1 (mono) o 2 (estéreo)
  • Formato: enteros con signo de 16 bits en un memoryview búfer
  • Tamaño del marco: Típicamente 20ms trozos (varía según la frecuencia de muestreo)

Procesamiento de datos de audio

Maneja el audio entrante en el callback de datos de 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)

Creación de datos de audio

Al añadir audio, cree archivos con el formato adecuado AudioData objetos:

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)

Continuidad de los datos de audio

Cuando publicas audio, la biblioteca envía silencio hasta tu primer add_audio() llamada, tolera breves intervalos sin enviar paquetes, tras lo cual recurre a tramas de silencio explícitas y rellena los periodos parciales para evitar la deriva. Véase Continuidad del audio para conocer el comportamiento completo.

Buenas prácticas:

  • Mantener una tasa de audio constante llamando add_audio() a intervalos regulares que coincidan con la frecuencia de muestreo configurada.
  • Supervisa las estadísticas del búfer mediante get_media_buffer_stats() para garantizar que los datos de audio sean adecuados
  • Manejar el on_media_buffer_drained_cb función de devolución de llamada para detectar cuándo se agota el búfer de audio
  • Plantéate implementar una estrategia de generación de audio que se adapte a las diferentes cargas de procesamiento.

Tratamiento de datos de vídeo

Formato de vídeo

Los datos de vídeo se entregan como caracteres sin signo de 8 bits en uno de estos tres formatos:

  • YUV420P: Formato YUV planar con submuestreo de croma 4:2:0
  • RGB24: RGB comprimido, 8 bits por canal
  • ARGB32: ARGB comprimido, 8 bits por canal, incluido el canal alfa

Especificaciones de vídeo:

  • Resoluciones: Hasta 1920x1080 (Full HD)
  • Frecuencia de imagen: 1-30 FPS
  • FormatoCaracteres de 8 bits sin signo en un memoryview búfer

Procesamiento de fotogramas de vídeo

Maneja los fotogramas de vídeo entrantes en la llamada de retorno del fotograma renderizado:

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)

Creación de fotogramas de vídeo

Al publicar vídeo, cree un formato adecuado VideoFrame objetos:

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() devoluciones True si se aceptó el fotograma, y False si el proceso de publicación no está listo o la llamada nativa falla. La construcción del VideoFrame plantea una ValidationError si el fotograma presenta un formato incorrecto —un formato desconocido, una dimensión no positiva, un número de píxeles superior a 1920x1080 o un búfer demasiado pequeño para la resolución declarada—.

Continuidad de fotogramas de vídeo

Cuando publicas un vídeo, la biblioteca envía fotogramas negros hasta que aparece el primer add_video() La función «call» repite el último fotograma durante un máximo de 2 segundos; si dejas de enviar fotogramas, pasa a mostrar fotogramas negros. Véase Continuidad del vídeo para conocer el comportamiento completo.

Buenas prácticas:

  • Mantener una velocidad de fotogramas constante llamando a add_video() a intervalos regulares que se ajusten a los FPS que hayas configurado
  • Supervisa las estadísticas del búfer mediante get_media_buffer_stats() para garantizar unos datos de vídeo adecuados
  • Manejar el on_media_buffer_drained_cb función de devolución de llamada para detectar cuándo se agota el búfer de vídeo
  • Considere la posibilidad de aplicar una estrategia de generación de fotogramas que se adapte a las distintas cargas de procesamiento.

Gestión de la memoria intermedia

Comprobación de las estadísticas del búfer

Supervisa el estado de tus buffers multimedia:

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

Cada campo es None cuando no haya ningún editor activo de ese tipo de medio.

Borrado de memorias intermedias

Borra los búferes de audio y vídeo cuando sea necesario:

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

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

Retrollamada de vaciado del búfer

Manejar eventos de drenaje del buffer:

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

El on_media_buffer_drained_cb La función de devolución de llamada se activa cuando se agotan los búferes internos de audio o vídeo, e implementa histéresis para que no se active repetidamente mientras el búfer permanezca vacío. Véase Eventos de vaciado del búfer Para más información.

Obtener información de conexión

Recupera la información de tu conexión local:

# 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() devoluciones None cuando el cliente no está conectado.

Llamadas de retorno de eventos

Llamadas de retorno de sesión

Inscrita en connect():

Devolución de llamada Firma
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

Manejar eventos a nivel de sesión:

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

Llamadas de retorno de editores

Inscrita en publish():

Devolución de llamada Firma
on_error_cb (publisher: Publisher, description: str, code: int) -> None
on_stream_created_cb (publisher: Publisher) -> None
on_stream_destroyed_cb (publisher: Publisher) -> None

Gestionar eventos de publicación:

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

Llamadas de retorno a abonados

Inscrita en subscribe():

Devolución de llamada Firma
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

Gestionar eventos de suscripción:

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

Gestión de errores

La biblioteca notifica los problemas de cuatro formas distintas.

ValidationError cuando se construye un modelo. Cada modelo de entorno y de medios es un pydantic modelo que valida sus propios campos, de modo que los valores no válidos se rechazan en el momento en que se crea el objeto, en lugar de cuando se envía al cliente. Se comprueban las frecuencias de muestreo, el número de canales, los niveles de registro, los formatos de píxeles, las frecuencias de fotogramas, las resoluciones, el tamaño de los elementos del búfer y la capacidad del búfer.

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

Ten en cuenta que format y level Los valores no distinguen entre mayúsculas y minúsculas y se normalizan a mayúsculas, por lo que format="yuv420p" y LoggingSettings(level="info") Se aceptan ambas opciones.

TypeError y AttributeError de los métodos del cliente. La capa nativa lee los atributos que necesita de los objetos que le pasas. Genera un AttributeError cuando falta un atributo esperado, y TypeError cuando un argumento o atributo tiene un tipo incorrecto, incluso cuando una función de devolución de llamada no se puede invocar.

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

False valores de retorno para las operaciones fallidas. Cada método de cliente devuelve un bool en lugar de basarse en un fallo operativo. connect() devoluciones False si el cliente ya está conectado a una sesión, publish() devoluciones False si ya se está publicando, y add_audio() y add_video() volver False si el proceso de publicación no está listo. Comprueba el resultado en lugar de dar por hecho que se ha realizado correctamente.

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 funciones de devolución de llamada para errores de ejecución. Los fallos que se producen después de que se haya aceptado una llamada —incluido el propio intento de conexión fallido— se notifican al on_error_cb registrados para ese ámbito, con una descripción y un código numérico. Los ámbitos de sesión, editor y suscriptor tienen cada uno los suyos propios.

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

Limpieza de recursos

Limpie siempre los recursos adecuadamente:

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