Conector de vídeo

A biblioteca Python Vonage Video Connector permite que você participe programaticamente de sessões da Video API do Vonage como participante do lado do servidor. Essa biblioteca permite que você se conecte a sessões de vídeo, publique e assine fluxos, e processe dados de áudio e vídeo em tempo real.

A biblioteca lida automaticamente com a conectividade WebRTC, o processamento de mídia e o gerenciamento de sessões, permitindo que você se concentre na criação da lógica do seu aplicativo. O áudio é transmitido como dados PCM linear de 16 bits, e o vídeo é transmitido como quadros de 8 bits nos formatos YUV420P, RGB24 ou ARGB32, todos com taxas de amostragem, resoluções e configurações de canais configuráveis.

Importante A biblioteca Python do Vonage Video Connector foi projetada para aplicativos do lado do servidor e requer credenciais e tokens válidos da Video API do Vonage, com as permissões adequadas.

Este tópico inclui as seguintes seções:

Introdução

O SDK do servidor do Vonage Video Connector está disponível no PyPI como conector-de-vídeo-da-Vonage.

Para instalar a biblioteca, execute:

pip install vonage-video-connector

Requisitos

Esta biblioteca requer o Python 3.13 em execução nas plataformas Linux AMD64 e ARM64. Recomendamos o uso do Debian Bookworm, pois é a distribuição na qual ela foi testada de forma mais completa.

Estruturas de dados

A biblioteca Python do Vonage Video Connector utiliza várias estruturas de dados importantes para representar sessões, conexões, fluxos e dados de áudio. Compreender essas estruturas é essencial para trabalhar com a biblioteca de maneira eficaz.

Sessão

Representa uma sessão da Video API da Vonage à qual os clientes podem se conectar:

from vonage_video_connector.models import Session

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

O Session O objeto é passado a várias funções de retorno de chamada para identificar qual sessão acionou o evento.

Conexão

Representa a conexão de um participante a uma sessão:

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)

Os dados de conexão podem ser usados para armazenar metadados personalizados sobre os participantes, como IDs de usuário ou funções.

Transmissão

Representa um fluxo de mídia (áudio/vídeo) publicado por um 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

Os streams são criados quando os participantes publicam mídia e são usados para se inscrever e receber seus dados de áudio/vídeo.

Editora

Representa seu stream publicado na sessão:

from vonage_video_connector.models import Publisher

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

O Publisher O objeto é utilizado em callbacks relacionados ao editor e representa seu próprio fluxo de mídia publicado.

Assinante

Representa uma assinatura do stream de outro participante:

from vonage_video_connector.models import Subscriber

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

O Subscriber O objeto é utilizado em callbacks relacionados ao assinante e representa sua assinatura para receber a mídia de outro participante.

Dados de áudio

Representa dados de áudio que estão sendo transmitidos ou recebidos:

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 áudio:

  • O buffer de amostra deve conter inteiros assinados de 16 bits
  • Taxas de amostragem válidas: 8.000, 12.000, 16.000, 24.000, 32.000, 44.100, 48.000 Hz
  • Canais: 1 (mono) ou 2 (estéreo)
  • O tamanho do buffer deve ser suficiente para acomodar: number_of_frames * number_of_channels amostras

VideoFrame

Representa os dados de um quadro de vídeo que estão sendo transmitidos ou recebidos:

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:

  • O buffer de quadro deve conter caracteres de 8 bits sem sinal
  • Formatos válidos: YUV420P, RGB24 (BGR), ARGB32 (BGRA)
  • Resolução máxima: 1920 x 1080 pixels (2.073.600 pixels no total)
  • O tamanho do buffer varia de acordo com o formato e a resolução

Resolução do vídeo

Representa as dimensões de um quadro 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
)

Dados das legendas

Representa os dados de texto das legendas recebidos de um fluxo ao qual se está inscrito:

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

Fornece estatísticas sobre os buffers de mídia:

from vonage_video_connector.models import MediaBufferStats, AudioBufferStats, VideoBufferStats

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

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

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

Estruturas de configuração

Configurações da sessão

Configura o comportamento no nível da sessão:

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
)

Configurações da sessão AV

Configura as opções de áudio e vídeo para a sessão:

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

Entendendo a configuração de áudio:

O SessionAVSettings permite que você configure diferentes formatos de áudio para publicação e recepção:

  • editor_de_áudio: Define o formato dos dados de áudio que você fornece por meio de add_audio(). Os dados de áudio que você enviar devem corresponder à taxa de amostragem e ao número de canais desta configuração.

  • mix_de_assinantes_de_áudio: Define o formato do áudio mixado que você recebe de todas as transmissões assinadas por meio do on_audio_data_cb callback. A biblioteca lida automaticamente com a mixagem do áudio de vários assinantes e com a reamostragem e conversão de canais para se adequar ao formato especificado por você.

Essa separação permite que você otimize de acordo com o seu caso de uso. Por exemplo:

  • Publique em estéreo (2 canais) para obter uma saída de alta qualidade, ao mesmo tempo em que recebe uma mixagem mono (1 canal) para simplificar o processamento
  • Transmita a 16 kHz para voz e receba a 48 kHz para reprodução em alta fidelidade
  • Utilize diferentes taxas de amostragem para publicação e assinatura, de acordo com os requisitos do seu fluxo de processamento de áudio

Configurações de Áudio da Sessão

Configura o formato de áudio para publicar ou receber dados de áudio:

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

Configurações do Editor de Vídeos da Sessão

Configura as opções de vídeo para publicação:

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

Configurações de registro

Controla o nível de detalhamento do registro:

from vonage_video_connector.models import LoggingSettings

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

Configurações do editor

Configura seu stream 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
)

Observação: Pelo menos um dos has_audio ou has_video deve ser True.

Observação: Conjunto enable_captions=True para permitir que os assinantes recebam legendas em tempo real desta transmissão por meio do on_caption_text_cb callback. As legendas estão desativadas por padrão.

Configurações de Áudio do Editor

Configura as opções de áudio da sua transmissão 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
)

Transmissão descontínua (DTX) deixa de enviar pacotes de áudio durante os momentos de silêncio, economizando largura de banda.

Configurações do assinante

Configura o comportamento do assinante:

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

Observação: Pelo menos um dos subscribe_to_audio ou subscribe_to_video deve ser True.

Configurações de vídeo do assinante

Configura as preferências de vídeo para os assinantes:

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

Entendendo as configurações preferidas:

Ao assinar transmissões roteadas que utilizam transmissão simultânea, a SFU (Unidade de Encaminhamento Seletivo) da Video API do Vonage pode enviar diferentes níveis de qualidade do vídeo. O preferred_resolution e preferred_framerate As configurações permitem que você solicite uma camada de qualidade específica:

  • resolução_preferida: Solicita uma camada espacial específica (resolução). A SFU enviará a camada que mais se aproximar da sua preferência.
  • taxa de quadros preferida: Solicita uma camada temporal específica (taxa de quadros). O SFU enviará a camada que mais se aproximar da sua preferência.

Essas preferências ajudam a otimizar o uso da largura de banda e os requisitos de processamento do lado do assinante, solicitando apenas o nível de qualidade necessário, em vez de receber sempre a melhor qualidade disponível.

Relações entre estruturas de dados

As estruturas de dados estão relacionadas na seguinte hierarquia:

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)

Conectando-se a uma sessão

Conexão básica

Para se conectar a uma sessão da Video API da Vonage, você precisa do ID do seu aplicativo (chave de API, se estiver usando o Tokbox), do ID da sessão e de um 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
)

Conexão com todos os callbacks

Para um gerenciamento completo da sessão, implemente todos os callbacks disponíveis:

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
)

Desconectando-se de uma sessão

Desconecte-se da sessão quando terminar:

success = client.disconnect()

Configurações da sessão

Configuração de áudio e vídeo

Configure as definições de áudio e vídeo da sessão para controlar o formato dos dados de mídia:

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
)

Configuração de registro

Controle o nível de detalhamento dos registros no console:

from vonage_video_connector.models import LoggingSettings

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

Migração de sessão

Ativar a migração automática de sessões em caso de rotação da SFU:

from vonage_video_connector.models import SessionSettings

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

Transmissões de publicação

Configuração do editor

Configure as configurações do editor antes de começar 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
)

Comece a publicar

Comece a publicar um fluxo na sessão:

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 Se você estiver publicando áudio (has_audio=True), é preciso aguardar até que o on_ready_for_audio_cb função de retorno a ser chamada antes da chamada add_audio(). Essa chamada de retorno indica que o sistema de áudio está inicializado e pronto para receber dados de áudio. Esse requisito não se aplica a cenários de publicação exclusivamente de 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)

Inserção de dados de áudio

Envie dados de áudio para sua transmissão 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)

Interromper a publicação

Interromper a publicação quando terminar:

success = client.unpublish()

Inscrever-se em canais

Inscreva-se nos canais

Quando um novo stream for recebido, inscreva-se nele para receber dados de áudio e/ou 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
    )

Recebimento de mídias assinadas

Quando você se inscreve em streams, a biblioteca transmite dados de áudio e vídeo por meio de diferentes callbacks:

Dados de vídeo: Os quadros de vídeo são transmitidos individualmente por cada fluxo assinado por meio do on_render_frame_cb callback. Cada chamada de callback inclui o subscriber objeto que identifica a qual fluxo o quadro de vídeo pertence. Isso permite que você processe separadamente os vídeos de diferentes participantes.

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

Dados de áudio: O áudio é transmitido como um único fluxo mixado por meio do on_audio_data_cb callback registrado durante connect(). A biblioteca combina automaticamente o áudio de todos os fluxos assinados em um único fluxo de áudio. Não é possível distinguir o áudio de participantes individuais neste callback.

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

Dados da legenda: Atualmente, esse recurso está disponível na versão beta. O texto da legenda é transmitido individualmente para cada stream inscrito por meio do on_caption_text_cb callback. Cada chamada inclui o subscriber objeto que identifica o fluxo de origem e um CaptionsData objeto que contém o texto e indica se se trata de um resultado final ou provisório.

Nota Para o on_caption_text_cb Para que a função de retorno de chamada receba os dados das legendas, as legendas em tempo real devem estar habilitadas na configuração da sessão da Video API do Vonage Video (fora desta biblioteca; consulte a documentação sobre legendas em tempo real da Video API do Vonage Video) e para o stream específico do editor que está enviando o áudio.

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

As legendas podem ser:

  • Provisório (is_final=False): Resultados de reconhecimento parcial que podem ser atualizados à medida que mais fala for processada. Útil para exibir transcrições em tempo real.
  • Final (is_final=True): Resultados de reconhecimento finalizados que não sofrerão alterações. Utilize-os para armazenamento, processamento posterior ou geração de transcrições definitivas.

Esse design permite que você:

  • Processar o vídeo de cada participante de forma independente para tarefas como gerenciamento de layout, gravação individual ou efeitos de vídeo por transmissão
  • Receba áudio pré-mixado, otimizado para reprodução ou processamento posterior, sem a necessidade de mixagem manual
  • Configure o formato de áudio misto por meio de audio_subscribers_mix em SessionAVSettings para atender às suas necessidades de processamento
  • Receba o texto das legendas de cada transmissão assinada por meio de on_caption_text_cb para transcrição em tempo real ou casos de uso de conversão de fala em texto

Dados de áudio individuais

Atualmente, esse recurso está disponível na versão beta. O áudio de um stream específico pode ser recuperado por meio do on_audio_data_cb callback registrado no momento da assinatura por meio de subscribe(). O áudio é transmitido no formato recebido do stream — PCM linear de 16 bits — e nem a taxa de amostragem nem o número de canais podem ser configurados antes da recepção.

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

Cancelar a inscrição em canais

Para deixar de receber conteúdo de um stream específico:

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

Tratamento de dados de áudio

Formato de áudio

Os dados de áudio são fornecidos como inteiros assinados de 16 bits no formato PCM linear, com as seguintes características:

  • Taxas de amostragem: 8.000, 12.000, 16.000, 24.000, 32.000, 44.100 ou 48.000 Hz
  • Canais: 1 (mono) ou 2 (estéreo)
  • Formato: inteiros assinados de 16 bits em um memoryview buffer
  • Tamanho do quadro: Normalmente, blocos de 20 ms (varia de acordo com a taxa de amostragem)

Processamento de dados de áudio

Processe o áudio recebido na função de retorno de chamada de dados de áudio:

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)

Criação de dados de áudio

Ao adicionar áudio, crie arquivos com a formatação correta 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)

Continuidade dos dados de áudio

Ao publicar áudio, a biblioteca gerencia automaticamente a continuidade do áudio em diversas situações:

Publicação inicial: Quando você começar a publicar áudio (por meio de publish() com has_audio=True), a biblioteca envia automaticamente silêncio (quadros de áudio preenchidos com zeros) até que você forneça seus primeiros dados de áudio por meio de add_audio(). Isso garante que o fluxo de áudio fique imediatamente disponível para os assinantes, sem que seja necessário aguardar que seu aplicativo gere os dados de áudio.

Tolerância ao silêncio: Se você interromper temporariamente o envio de dados de áudio por meio de add_audio(), a biblioteca tolera breves intervalos ao não enviar nenhum pacote de áudio. Esse período de histerese evita o envio de pacotes de silêncio desnecessários durante atrasos momentâneos no processamento.

Silêncio explícito: Após o período de tolerância, caso não haja novos dados de áudio disponíveis, a biblioteca passa a enviar quadros de silêncio explícitos (áudio preenchido com zeros). Isso mantém o fluxo de áudio, ao mesmo tempo em que indica que nenhum áudio ativo está sendo fornecido.

Limpeza do buffer: Se você fornecer dados de áudio correspondentes a menos de um período completo, a biblioteca descarregará os dados restantes e os preencherá com silêncio para manter a sincronização correta e evitar desvios de áudio.

Melhores práticas:

  • Mantenha uma taxa de áudio consistente chamando add_audio() em intervalos regulares, de acordo com a taxa de amostragem configurada
  • Monitore as estatísticas do buffer usando get_media_buffer_stats() para garantir dados de áudio adequados
  • Lidar com o on_media_buffer_drained_cb função de retorno para detectar quando o buffer de áudio estiver vazio
  • Considere implementar uma estratégia de geração de áudio que se adapte às variações na carga de processamento

Esse gerenciamento automático de áudio garante que seu fluxo de áudio publicado permaneça contínuo e com a sincronização correta, mesmo durante interrupções temporárias na disponibilidade de dados.

Tratamento de dados de vídeo

Formato de vídeo

Os dados de vídeo são transmitidos como caracteres sem sinal de 8 bits em um dos três formatos:

  • YUV420P: Formato YUV planar com subamostragem de crominância 4:2:0
  • RGB24: Formato BGR de 24 bits (8 bits por canal)
  • ARGB32: formato BGRA de 32 bits com canal alfa

Especificações do vídeo:

  • Resoluções: Até 1920x1080 (Full HD)
  • Taxas de quadros: 1-30 FPS
  • Formato: caracteres de 8 bits sem sinal em um memoryview buffer

Processamento de quadros de vídeo

Processe os quadros de vídeo recebidos na função de retorno de chamada do quadro de renderização:

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)

Criação de quadros de vídeo

Ao publicar um vídeo, crie um arquivo devidamente formatado 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)

Continuidade dos quadros de vídeo

Ao publicar um vídeo, a biblioteca gerencia automaticamente a continuidade dos quadros em diversas situações:

Publicação inicial: Quando você começar a publicar vídeos (por meio de publish() com has_video=True), a biblioteca envia automaticamente quadros pretos até que você forneça seu primeiro quadro por meio de add_video(). Isso garante que o fluxo de vídeo fique imediatamente disponível para os assinantes, sem que seja necessário aguardar que seu aplicativo gere os dados de vídeo.

Repetição do último quadro: Se você deixar de fornecer quadros de vídeo por meio de add_video(), a biblioteca repetirá automaticamente o último quadro que você forneceu. Isso garante uma reprodução suave para os assinantes, sem interrupções. O último quadro será repetido por até 2 segundos.

Opção alternativa para moldura preta: Após o período máximo de repetição (2 segundos), a biblioteca passa a transmitir quadros em preto. Isso indica aos assinantes que os dados de vídeo não estão mais sendo fornecidos ativamente, embora o fluxo de vídeo continue sendo mantido.

Melhores práticas:

  • Mantenha uma taxa de quadros consistente chamando add_video() em intervalos regulares, de acordo com o FPS configurado por você
  • Monitore as estatísticas do buffer usando get_media_buffer_stats() para garantir dados de vídeo adequados
  • Lidar com o on_media_buffer_drained_cb função de retorno para detectar quando o buffer de vídeo estiver vazio
  • Considere implementar uma estratégia de geração de quadros que se adapte às variações na carga de processamento

Esse gerenciamento automático de quadros garante que o fluxo de vídeo publicado permaneça contínuo, mesmo durante interrupções temporárias na disponibilidade de dados.

Gerenciamento do buffer de mídia

Verificando as estatísticas do buffer

Monitore o estado dos seus buffers de mídia:

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

Limpeza dos buffers de mídia

Limpe os buffers de áudio e vídeo quando necessário:

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

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

Callback de esvaziamento do buffer

Tratar eventos de esvaziamento do 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")

Entendendo os eventos de esvaziamento do buffer:

O on_media_buffer_drained_cb A função de retorno de chamada é invocada quando os buffers internos de áudio ou vídeo se esgotam. Isso ocorre quando os dados de mídia estão sendo transmitidos para a sessão a uma taxa que excede a taxa na qual novos dados de mídia estão sendo fornecidos por meio de add_audio() ou add_video() chamadas.

Essa notificação serve para alertá-lo de que você deve aumentar o ritmo de produção de conteúdo ou ajustar sua estratégia de publicação para manter um fluxo contínuo de conteúdo. Monitorar esses eventos ajuda a evitar lacunas ou interrupções no seu fluxo de publicações.

Comportamento da histerese de retorno:

A função de retorno implementa histerese para evitar acionamentos excessivos. Após o evento inicial de esvaziamento, a função de retorno não será chamada novamente até que o buffer seja reabastecido com novos dados de mídia e, posteriormente, volte a ficar vazio. Isso evita uma enxurrada de notificações repetidas enquanto o buffer permanece vazio.

Obtendo informações de conexão

Recupere as informações da sua conexão 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}")

Callbacks de eventos

Callbacks de sessão

Tratar eventos no nível da sessão:

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

Callbacks de conexão

Monitorar as conexões dos 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}")

Callbacks de fluxo

Tratar eventos de fluxo:

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

Callbacks do editor

Gerenciar eventos de publicação:

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

Retornos de chamada dos assinantes

Tratar eventos de assinatura:

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

Callbacks do buffer de mídia

Tratar eventos do buffer de mídia:

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

Limpeza de recursos

Sempre libere os recursos corretamente:

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