Conector de vídeo

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

La biblioteca se encarga de la conectividad WebRTC, el procesamiento de medios y la gestión de sesiones de forma automática, lo que le permite centrarse en la creación de la lógica de su aplicación. en crear la lógica de su aplicación. El audio se transmite como datos PCM lineales de 16 bits, y el vídeo como fotogramas de 8 bits en formatos YUV420P, RGB24 o ARGB32. en formatos YUV420P, RGB24 o ARGB32, todo ello con frecuencias de muestreo, resoluciones y configuraciones de canal configurables.

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

Este tema incluye las siguientes secciones:

Beta pública

Vonage Video Connector se encuentra en fase beta y está disponible en PyPi como vonage-vídeo-conector.

Para instalar la biblioteca, ejecute

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 Python de Vonage Video Connector utiliza varias estructuras de datos clave para representar sesiones, conexiones, secuencias y datos de audio. Comprender estas estructuras es esencial para trabajar con la biblioteca de manera 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

En Session se pasa a varias funciones de llamada de retorno para identificar qué sesión desencadenó 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 ID de usuario o funciones.

Corriente

Representa un flujo multimedia (audio/vídeo) publicado 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

Los flujos se crean cuando los participantes publican medios y se utilizan para suscribirse a la recepción de sus datos de audio/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

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

Abonado

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

En Subscriber se utiliza en las retrollamadas relacionadas con los suscriptores y representa su suscripción para recibir los medios de otro participante.

AudioData

Representa los datos de audio que se transmiten o 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 fotogramas 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:

  • La memoria intermedia debe contener caracteres de 8 bits sin signo
  • Formatos válidos: YUV420P, RGB24 (BGR), ARGB32 (BGRA)
  • 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
    height=480   # int: Height in pixels
)

CaptionsData

Representa los datos de texto de los subtítulos recibidos de un flujo 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 las memorias intermedias:

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 la 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 parámetros de audio y vídeo de la sesión:

from vonage_video_connector.models import SessionAVSettings, SessionAudioSettings, SessionVideoPublisherSettings

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

Comprender la configuración de audio:

En SessionAVSettings permite configurar distintos formatos de audio para la publicación y la recepción:

  • audio_publisher: Define el formato de los datos de audio que se proporcionan 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 recibe de todos los flujos suscritos a través del 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.

Esta separación le permite optimizar para su caso de uso. Por ejemplo:

  • Publica en estéreo (2 canales) para obtener una salida de alta calidad mientras recibes una mezcla mono (1 canal) para simplificar el procesamiento
  • Publicación a 16 kHz para voz y recepción a 48 kHz para reproducción de alta fidelidad
  • Utiliza distintas frecuencias de muestreo para la publicación y la suscripción en función de los requisitos de tu canal de procesamiento de audio

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: Sample rate (8000-48000 Hz)
    number_of_channels=1     # int: Channels - 1 (mono) or 2 (stereo)
)

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),  # Resolution in pixels
    fps=30,              # int: Frames per second (1-30)
    format="YUV420P"     # str: Video format (YUV420P, RGB24, or ARGB32)
)

LoggingSettings

Controla la verbosidad del registro:

from vonage_video_connector.models import LoggingSettings

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

PublisherSettings

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 permitir que los abonados reciban subtítulos en directo de este flujo a través de la función on_caption_text_cb callback. Los subtítulos están desactivados por defecto.

PublisherAudioSettings

Configura los ajustes de audio para su flujo publicado:

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 abonado

Configura el comportamiento de los abonados:

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

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

Comprender las configuraciones preferidas:

Al suscribirse a transmisiones enrutadas que utilizan la transmisión simultánea, la SFU (unidad de reenvío selectivo) de la Video API de Vonage puede enviar diferentes capas de calidad del video. El sitio preferred_resolution y preferred_framerate le permiten solicitar una capa de calidad específica:

  • resolución_preferida: Solicita una capa espacial específica (resolución). La SFU enviará la capa que más se ajuste a sus preferencias.
  • velocidad_preferida: Solicita una capa temporal específica (frecuencia de imagen). La SFU enviará la capa que más se acerque a sus preferencias.

Estas preferencias ayudan a optimizar el uso del ancho de banda y los requisitos de procesamiento en el lado del abonado solicitando sólo el nivel de calidad que necesita, en lugar de recibir siempre la máxima calidad disponible.

Relaciones entre estructuras de datos

Las estructuras de datos se relacionan en la siguiente jerarquía:

Session
├── Connection (multiple participants)
│   └── Stream (participant's published media)
│       ├── Publisher (your published stream)
│       └── Subscriber (your subscription to their stream)
├── AudioData (flowing through streams)
└── VideoFrame (flowing through streams)

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 de API si usas Tokbox), ID de sesión y un token válido:

from vonage_video_connector import VonageVideoClient
from vonage_video_connector.models import SessionSettings, 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
success = 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
)

Conexión con todas las devoluciones de llamada

Para una gestión completa de la sesión, implemente todas las retrollamadas disponibles:

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

Desconexión de una sesión

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

success = client.disconnect()

Configuración de la sesión

Configuración de audio y vídeo

Configure las opciones 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 la verbosidad del registro de 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

Configure 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

Comienza 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 que se invocará 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 escenarios de publicación de solo vídeo.

# 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íe datos de audio a su flujo publicado:

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)

Dejar de publicar

Deje de publicar cuando haya terminado:

success = client.unpublish()

Suscripción a flujos

Suscribirse a los flujos

Cuando reciba un nuevo flujo, suscríbase a él 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,
        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 streams, la biblioteca entrega datos de audio y vídeo a través de diferentes callbacks:

Datos de vídeo: Los fotogramas de vídeo se entregan individualmente por flujo suscrito a través del on_render_frame_cb devolución de llamada. Cada invocación de devolución de llamada incluye el parámetro 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 mixto a través del on_audio_data_cb registrada durante connect(). La biblioteca mezcla automáticamente el audio de todos los flujos suscritos en un único flujo de audio. No se puede distinguir entre el audio de los participantes individuales en esta devolución de llamada.

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 devolución de llamada. Cada invocación incluye el subscriber que identifica el flujo de origen y un CaptionsData que contiene el texto y si se trata de un resultado final o provisional.

Nota Para el on_caption_text_cb para recibir datos de subtítulos, los subtítulos en vivo deben estar habilitados en la configuración de la sesión subyacente de la API de Video de Vonage (fuera de esta biblioteca; consulta la documentación sobre subtítulos en vivo de la API de Video de Vonage) y para el flujo editor específico que está enviando audio.

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

Los pies de foto pueden ser

  • Provisional (is_final=False): Resultados parciales del reconocimiento que pueden actualizarse a medida que se procesa más habla. Útil para mostrar transcripciones en directo.
  • Final (is_final=True): Resultados de reconocimiento finalizados que no cambiarán. Utilícelos para almacenamiento, procesamiento posterior o salida de transcripción definitiva.

Este diseño le permite:

  • Procese el vídeo de cada participante de forma independiente para tareas como la gestión del diseño, la grabación individual o los efectos de vídeo por secuencia.
  • Recibe audio premezclado optimizado para su reproducción o procesamiento posterior sin necesidad de mezclarlo manualmente
  • Configure el formato de audio mixto mediante audio_subscribers_mix en SessionAVSettings para responder a sus necesidades de tratamiento
  • Recibir texto de subtítulos por flujo suscrito a través de on_caption_text_cb para transcripciones en directo o casos de uso de voz a texto

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

Detener la recepción de medios de un flujo específico:

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 entregan como enteros con signo PCM lineal de 16 bits 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

# 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

Al publicar audio, la biblioteca gestiona automáticamente la continuidad del audio en varios escenarios:

Publicación inicial: Cuando empiece a publicar audio (a través de publish() con has_audio=True), la biblioteca envía automáticamente silencio (fotogramas de audio sin relleno) hasta que usted proporcione sus primeros datos de audio a través de add_audio(). Esto garantiza que el flujo de audio esté disponible inmediatamente para los abonados sin esperar a que su aplicación genere datos de audio.

Tolerancia al silencio: Si deja temporalmente de proporcionar datos de audio a través de add_audio()la biblioteca tolera breves lagunas no enviando ningún paquete de audio. Este periodo de histéresis evita paquetes de silencio innecesarios durante retrasos momentáneos en el procesamiento.

Silencio explícito: Tras el periodo de tolerancia, si no hay nuevos datos de audio disponibles, la biblioteca pasa a enviar tramas de silencio explícito (audio relleno de cero). Esto mantiene el flujo de audio al tiempo que indica que no se está proporcionando audio activo.

Descarga del búfer: Si proporciona menos de un periodo completo de datos de audio, la biblioteca eliminará los datos restantes y los rellenará con silencio para mantener la sincronización correcta y evitar la desviación de audio.

Buenas prácticas:

  • Mantener una tasa de audio constante llamando add_audio() a intervalos regulares que coincidan con la frecuencia de muestreo configurada.
  • Supervisar las estadísticas del búfer mediante get_media_buffer_stats() para garantizar unos datos de audio adecuados
  • Manejar el on_media_buffer_drained_cb callback para detectar cuándo se agota el búfer de audio
  • Considera la posibilidad de implantar una estrategia de generación de audio que se adapte a las distintas cargas de procesamiento

Esta gestión automática del audio garantiza que el flujo de audio publicado se mantenga continuo y con la sincronización adecuada, incluso durante las interrupciones temporales en la disponibilidad de datos.

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
  • RGB24Formato BGR de 24 bits (8 bits por canal)
  • ARGB32: Formato BGRA de 32 bits con 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
client.add_video(video_frame)

Continuidad de fotogramas de vídeo

Al publicar vídeo, la biblioteca gestiona automáticamente la continuidad de fotogramas en varios escenarios:

Publicación inicial: Cuando empiece a publicar vídeo (a través de publish() con has_video=True), la biblioteca envía automáticamente fotogramas negros hasta que usted proporcione su primer fotograma mediante add_video(). Esto garantiza que el flujo de vídeo esté disponible inmediatamente para los abonados sin esperar a que su aplicación genere datos de vídeo.

Repetición del último fotograma: Si deja de proporcionar fotogramas de vídeo a través de add_video()la biblioteca repetirá automáticamente el último fotograma proporcionado. Esto garantiza una reproducción fluida y sin interrupciones para los abonados. El último fotograma se repetirá durante un máximo de 2 segundos.

Retroceso del marco negro: Tras el periodo máximo de repetición (2 segundos), la biblioteca pasa a publicar fotogramas negros. Esto indica a los abonados que ya no se proporcionan activamente datos de vídeo mientras se mantiene el flujo de vídeo.

Buenas prácticas:

  • Mantener una velocidad de fotogramas constante llamando a add_video() a intervalos regulares que coincidan con los FPS configurados
  • Supervisar 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 callback 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.

Esta gestión automática de fotogramas garantiza que el flujo de vídeo publicado se mantenga continuo incluso durante las interrupciones temporales en la disponibilidad de datos.

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

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

Comprensión de los eventos de drenaje del búfer:

En on_media_buffer_drained_cb se invoca cuando se agotan los búferes internos de audio o vídeo. Esto ocurre cuando los datos multimedia se transmiten a la sesión a una velocidad superior a la velocidad a la que se proporcionan nuevos datos multimedia a través de add_audio() o add_video() llamadas.

Esta devolución de llamada sirve como notificación de que debe aumentar su ritmo de producción de medios o ajustar su estrategia de publicación para mantener un flujo continuo de medios. La supervisión de estos eventos ayuda a evitar lagunas o interrupciones en el flujo publicado.

Comportamiento de la histéresis de devolución de llamada:

La llamada de retorno implementa histéresis para evitar un disparo excesivo. Tras el evento de vaciado inicial, la llamada de retorno no volverá a invocarse hasta que el búfer se reponga con nuevos datos multimedia y, posteriormente, vuelva a vaciarse. Esto evita una avalancha de notificaciones repetidas mientras el búfer permanece vacío.

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

Reactivación de eventos

Llamadas de sesión

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

Devoluciones de llamada de conexión

Supervisar las conexiones de los participantes:

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

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

Llamadas de retorno

Manejar eventos de flujo:

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

Gestiona los 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

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

Llamadas al búfer de medios

Gestiona los eventos de la memoria intermedia:

def on_media_buffer_drained(stats):
    """Handle media buffer drain events"""
    print("Media buffers have been drained")
    
    if stats.audio:
        duration_s = stats.audio.duration.total_seconds()
        print(f"Audio buffer: {duration_s}s")
    
    if stats.video:
        duration_s = stats.video.duration.total_seconds()
        print(f"Video buffer: {duration_s}s")

Limpieza de recursos

Limpie siempre los recursos adecuadamente:

try:
    # Your application logic
    success = client.connect(...)
    # ... 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()