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:
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_channelsmuestras
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_cbcallback. 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
memoryviewbú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_cbfunció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
memoryviewbú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_cbfunció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()