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
- Requisitos
- Estruturas de dados
- Conectando-se a uma sessão
- Configurações da sessão
- Transmissões de publicação
- Inscrever-se em canais
- Tratamento de dados de áudio
- Tratamento de dados de vídeo
- Gerenciamento do buffer de mídia
- Callbacks de eventos
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 (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_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ê.
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_mixemSessionAVSettingspara atender às suas necessidades de processamento - Receba o texto das legendas de cada transmissão assinada por meio de
on_caption_text_cbpara 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
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
# 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_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
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
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
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_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
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()