marcosremar2's picture
Add SDK and improve video transition synchronization
3acaae2
Raw
History Blame Contribute Delete
3.72 kB
/**
* Exemplo básico de uso do Avatar Stream SDK
*/
import { AvatarStreamSDK, StreamStatus, StreamMetrics } from '../src';
// Elemento canvas do HTML
const canvas = document.getElementById('avatar-canvas') as HTMLCanvasElement;
// Elementos de UI para métricas
const statusEl = document.getElementById('status')!;
const latencyEl = document.getElementById('latency')!;
const fpsEl = document.getElementById('fps')!;
const videoDurationEl = document.getElementById('video-duration')!;
const audioDurationEl = document.getElementById('audio-duration')!;
const syncDiffEl = document.getElementById('sync-diff')!;
// Criar instância do SDK
const avatar = new AvatarStreamSDK({
wsUrl: 'ws://localhost:8080/ws',
canvas,
// Callbacks de status
onStatusChange: (status: StreamStatus) => {
console.log('Status:', status);
statusEl.textContent = status;
// Atualizar cor baseado no status
statusEl.className = '';
if (status === 'playing') statusEl.classList.add('status-playing');
else if (status === 'error') statusEl.classList.add('status-error');
else if (status === 'completed') statusEl.classList.add('status-completed');
},
// Callback de métricas (chamado frequentemente)
onMetricsUpdate: (metrics: StreamMetrics) => {
fpsEl.textContent = metrics.currentFps.toFixed(0);
videoDurationEl.textContent = metrics.videoDuration.toFixed(2) + 's';
audioDurationEl.textContent = metrics.audioDuration.toFixed(2) + 's';
// Colorir diferença de sync
const diff = metrics.syncDiff;
syncDiffEl.textContent = (diff >= 0 ? '+' : '') + diff.toFixed(2) + 's';
if (Math.abs(diff) < 0.1) {
syncDiffEl.style.color = 'green';
} else if (Math.abs(diff) < 0.5) {
syncDiffEl.style.color = 'orange';
} else {
syncDiffEl.style.color = 'red';
}
},
// Callback quando o primeiro frame chega
onFirstFrame: (latencyMs: number) => {
console.log(`Primeiro frame: ${latencyMs}ms`);
},
// Callback quando o playback inicia
onPlaybackStart: (latencyMs: number) => {
console.log(`Playback iniciado: ${latencyMs}ms`);
latencyEl.textContent = latencyMs + 'ms';
},
// Callback quando o stream termina
onComplete: (summary) => {
console.log('Stream completo:', summary);
console.log(`Total frames: ${summary.totalFrames}`);
console.log(`Duração: ${summary.durationSeconds.toFixed(2)}s`);
console.log(`Sync final: ${summary.finalSyncDiff.toFixed(3)}s`);
},
// Callback de erro
onError: (error) => {
console.error('Erro:', error);
alert(`Erro: ${error.message}`);
},
});
// Botão de conectar
document.getElementById('btn-connect')?.addEventListener('click', async () => {
try {
await avatar.connect();
console.log('Conectado!');
} catch (e) {
console.error('Erro ao conectar:', e);
}
});
// Botão de gerar
document.getElementById('btn-generate')?.addEventListener('click', async () => {
const text = (document.getElementById('text-input') as HTMLTextAreaElement)?.value;
const voice = (document.getElementById('voice-select') as HTMLSelectElement)?.value;
if (!text) {
alert('Digite um texto!');
return;
}
try {
await avatar.generate(text, voice);
} catch (e) {
console.error('Erro ao gerar:', e);
}
});
// Botão de parar
document.getElementById('btn-stop')?.addEventListener('click', () => {
avatar.stop();
});
// Botão de desconectar
document.getElementById('btn-disconnect')?.addEventListener('click', () => {
avatar.disconnect();
});
// Conectar automaticamente ao carregar
avatar.connect().then(() => {
console.log('Conectado automaticamente');
}).catch(e => {
console.log('Auto-connect falhou:', e);
});