Conector de vídeo para Python
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.
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.
Esta página aborda a API do Python. Para conhecer os Concepts, formatos de mídia e comportamento em tempo de execução comuns a todas as bibliotecas do Video Connector, consulte o Conector de vídeo guia.
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:
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_channelsamostras
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, ARGB32
- 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, defaults to 640
height=480 # int: Height in pixels, defaults to 480
)
Ambos devem ser números inteiros positivos, e o número total de pixels não deve exceder 1920 × 1080 (2.073.600).
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,
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"
)
)
- 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_cbcallback. 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ê.
Para obter orientações sobre como escolher esses formatos, consulte Áudio para publicação versus áudio para assinatura.
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: Optional, defaults to 48000
number_of_channels=1 # int: Optional, 1 (mono) or 2 (stereo), defaults to 2
)
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), # Required: resolution in pixels
fps=30, # int: Optional, 1-30, defaults to 30
format="YUV420P" # str: Optional, defaults to "YUV420P"
)
Ao contrário dos outros modelos de configuração, resolution é obrigatório. VideoResolution A resolução padrão é de 640x480.
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: Optional, ERROR, WARN, INFO, DEBUG, or TRACE, defaults to WARN
)
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
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
)
)
Observação: Pelo menos um dos subscribe_to_audio ou subscribe_to_video deve ser True.
Observação: Conjunto subscribe_to_captions=True para receber o texto das legendas desta transmissão por meio do
on_caption_text_cb callback. As legendas também devem estar ativadas no lado da publicação.
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)
)
Essas configurações solicitam um nível específico de qualidade de transmissão simultânea da SFU da Video API da Vonage. Para saber como a SFU as processa, consulte Resolução e taxa de quadros preferenciais do assinante.
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,
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() é assíncrona e retorna assim que a tentativa de conexão for iniciada. Um valor de retorno de
True significa que a solicitação foi aceita e encaminhada, não que o cliente esteja conectado à sessão. Aguarde
até que on_connected_cb deve ser chamado antes de considerar a sessão como conectada e antes de publicar ou
assinar.
Um valor de retorno de False significa que a solicitação foi rejeitada antes de ser enviada, por exemplo, porque o cliente
já está conectado a uma sessão ou porque as informações da sessão estão incompletas. Erros que ocorrem após o
envio da solicitação, como um token expirado ou inválido, são relatados a on_error_cb em vez de
por meio do valor de retorno.
Conexão com todos os callbacks
Para um gerenciamento completo da sessão, implemente todos os callbacks disponíveis:
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
)
Desconectando-se de uma sessão
Desconecte-se da sessão quando terminar:
success = client.disconnect()
disconnect() retornos True assim que tudo tiver sido demolido, e também retorna True se o cliente não estivesse
conectado a uma sessão desde o início.
Verificando o estado da conexão
if client.is_connected():
print("Still connected")
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.
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)
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)
add_audio() retornos True se o quadro foi aceito, e False se o pipeline de publicação não estiver pronto ou
a chamada nativa falhar. A construção do AudioData levanta uma ValidationError se o quadro estiver malformado — por
exemplo, se o buffer for pequeno demais para a geometria declarada do quadro.
Interromper a publicação
Interromper a publicação quando terminar:
success = client.unpublish()
Verificando o status de publicação
if client.is_publishing():
print("Still publishing")
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,
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
)
Recebimento de mídias assinadas
Quando você se inscreve em streams, a biblioteca fornece dados de áudio e vídeo por meio de diferentes callbacks. Para conhecer o raciocínio por trás desse design, consulte Inscrever-se em canais no guia do Video Connector.
Importante
O memoryview dentro de um AudioData ou VideoFrame A mídia entregue a uma função de retorno de chamada é válida apenas enquanto essa função estiver em execução. Se você precisar manter a mídia após o término da função de retorno de chamada — para colocá-la na fila ou processá-la de forma assíncrona —, copie-a primeiro, por exemplo, com audio_data.sample_buffer.tobytes() ou bytes(video_frame.frame_buffer).
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 (fora desta biblioteca; consulte a Video API do Vonage Legendas em tempo real (documentação) e para o fluxo específico do transmissor que está enviando áudio. Defina subscribe_to_captions=True nas configurações do assinante para recebê-las.
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
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
memoryviewbuffer - 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
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)
Continuidade dos dados de áudio
Quando você publica um arquivo de áudio, a biblioteca envia silêncio até o seu primeiro add_audio() chamada, tolera breves intervalos
sem o envio de pacotes; em seguida, recorre a quadros de silêncio explícitos e preenche períodos parciais para evitar desvios.
Veja Continuidade de áudio para conhecer o comportamento completo.
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_cbfunçã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
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: RGB compactado, 8 bits por canal
- ARGB32: ARGB compactado, 8 bits por canal, incluindo 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
memoryviewbuffer
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
success = client.add_video(video_frame)
add_video() retornos True se o quadro foi aceito, e False se o pipeline de publicação não estiver pronto ou
a chamada nativa falhar. A construção do VideoFrame levanta uma ValidationError se o quadro estiver com formato inválido — um
formato desconhecido, uma dimensão negativa, uma contagem de pixels superior a 1920x1080 ou um buffer muito pequeno para a
resolução declarada.
Continuidade dos quadros de vídeo
Quando você publica um vídeo, a biblioteca envia quadros pretos até o seu primeiro add_video() A função `call` repete o seu último
quadro por até 2 segundos; caso você pare de fornecer quadros, ela volta a exibir quadros pretos. Veja
Continuidade do vídeo para conhecer o comportamento completo.
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_cbfunçã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
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")
Cada campo é None quando não houver nenhum editor ativo para esse tipo de mídia.
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")
O on_media_buffer_drained_cb A função de retorno é chamada quando os buffers internos de áudio ou vídeo se esgotam e
implementa histerese para que não seja acionada repetidamente enquanto o buffer permanecer vazio. Consulte
Eventos de esvaziamento do buffer para mais detalhes.
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}")
get_connection() retornos None quando o cliente não está conectado.
Callbacks de eventos
Callbacks de sessão
Registrado em connect():
| Retorno de chamada | Assinatura |
|---|---|
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 |
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")
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}")
Callbacks do editor
Registrado em publish():
| Retorno de chamada | Assinatura |
|---|---|
on_error_cb |
(publisher: Publisher, description: str, code: int) -> None |
on_stream_created_cb |
(publisher: Publisher) -> None |
on_stream_destroyed_cb |
(publisher: Publisher) -> None |
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
Registrado em subscribe():
| Retorno de chamada | Assinatura |
|---|---|
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 |
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}")
Tratamento de erros
A biblioteca relata problemas de quatro maneiras diferentes.
ValidationError quando um modelo é construído. Cada modelo de configuração e de mídia é um
pydantic modelo que valida seus próprios campos, de modo que valores inválidos são rejeitados no momento em que
o objeto é criado, e não quando ele é passado para o cliente. Taxas de amostragem, número de canais, níveis de log,
formatos de pixel, taxas de quadros, resoluções, tamanho dos elementos do buffer e capacidade do buffer são todos verificados.
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}")
Observe que format e level os valores não diferenciam maiúsculas de minúsculas e são normalizados para maiúsculas, portanto
format="yuv420p" e LoggingSettings(level="info") ambas são aceitas.
TypeError e AttributeError dos métodos do cliente. A camada nativa lê os atributos de que precisa a partir dos
objetos que você passa. Ela gera uma exceção AttributeError quando um atributo esperado está faltando, e TypeError quando um
argumento ou atributo tem o tipo incorreto, inclusive quando uma função de retorno de chamada não pode ser chamada.
try:
client.add_audio(audio_data)
except (TypeError, AttributeError) as e:
print(f"Malformed audio data: {e}")
False valores de retorno para operações com falha. Cada método do cliente retorna um bool em vez de ser atribuído a
uma falha operacional. connect() retornos False se o cliente já estiver conectado a uma sessão, publish()
retornos False se já estiver em publicação, e add_audio() e add_video() retornar False se o
fluxo de publicação não estiver pronto. Verifique o resultado, em vez de presumir que tudo deu certo.
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 funções de retorno para erros de tempo de execução. Falhas que ocorrem após a aceitação de uma chamada — incluindo
a própria falha na tentativa de conexão — são relatadas ao on_error_cb registrados para esse escopo, com uma
descrição e um código numérico. Os escopos de sessão, editor e assinante têm, cada um, os seus próprios.
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,
)
Limpeza de recursos
Sempre libere os recursos corretamente:
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()