Commit ·
ef89fa8
1
Parent(s): 7c26f37
chore: Clean up webrtc-latency-test directory - remove POC duplications
Browse filesRemoved duplicate files from old WebRTC POC:
- docs/AUTOMATED_TESTING.md, TESTING_INSTRUCTIONS.md
- requirements.txt (old POC dependencies)
- scripts/ (install.sh, start.sh, stop.sh)
- server/ (webrtc-server.py, webrtc-client.html)
- tests/ (test_latency_playwright.py)
Now webrtc-latency-test/ contains ONLY the microservices architecture:
- gateway/ (orchestrator)
- services/ (Whisper, LLM, TTS, MuseTalk)
- shared/proto/ (Protocol Buffers)
- start-all.sh, stop-all.sh (microservices scripts)
- test_latency.py (microservices test)
- docker-compose.yml, README.md, ARCHITECTURE.md
Result: Cleaner, focused project structure for VAST.ai deployment
- webrtc-latency-test/docs/AUTOMATED_TESTING.md +0 -391
- webrtc-latency-test/docs/TESTING_INSTRUCTIONS.md +0 -178
- webrtc-latency-test/requirements.txt +0 -5
- webrtc-latency-test/scripts/install.sh +0 -125
- webrtc-latency-test/scripts/start.sh +0 -211
- webrtc-latency-test/scripts/stop.sh +0 -141
- webrtc-latency-test/server/webrtc-client.html +0 -366
- webrtc-latency-test/server/webrtc-server.py +0 -130
- webrtc-latency-test/tests/test_latency_playwright.py +0 -259
webrtc-latency-test/docs/AUTOMATED_TESTING.md
DELETED
|
@@ -1,391 +0,0 @@
|
|
| 1 |
-
# 🧪 Teste Automatizado de Latência com Playwright
|
| 2 |
-
|
| 3 |
-
## 📋 Visão Geral
|
| 4 |
-
|
| 5 |
-
Este documento descreve como executar testes automatizados de latência usando Playwright headless para medir a performance do servidor WebRTC com um ou múltiplos usuários simultâneos.
|
| 6 |
-
|
| 7 |
-
## 🎯 Objetivos
|
| 8 |
-
|
| 9 |
-
- Testar latência de forma automatizada e repetível
|
| 10 |
-
- Medir latência com múltiplos usuários simultâneos
|
| 11 |
-
- Gerar relatórios JSON com métricas detalhadas
|
| 12 |
-
- Validar escalabilidade do servidor
|
| 13 |
-
|
| 14 |
-
## 📦 Requisitos
|
| 15 |
-
|
| 16 |
-
### Instalação
|
| 17 |
-
|
| 18 |
-
```bash
|
| 19 |
-
pip3 install playwright
|
| 20 |
-
playwright install chromium
|
| 21 |
-
```
|
| 22 |
-
|
| 23 |
-
## 🚀 Como Executar
|
| 24 |
-
|
| 25 |
-
### Teste com 1 Usuário
|
| 26 |
-
|
| 27 |
-
```bash
|
| 28 |
-
python3 test_latency_playwright.py 1 true
|
| 29 |
-
```
|
| 30 |
-
|
| 31 |
-
### Teste com Múltiplos Usuários
|
| 32 |
-
|
| 33 |
-
```bash
|
| 34 |
-
# 5 usuários simultâneos
|
| 35 |
-
python3 test_latency_playwright.py 5 true
|
| 36 |
-
|
| 37 |
-
# 10 usuários simultâneos
|
| 38 |
-
python3 test_latency_playwright.py 10 true
|
| 39 |
-
|
| 40 |
-
# 3 usuários sequencialmente
|
| 41 |
-
python3 test_latency_playwright.py 3 false
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
### Parâmetros
|
| 45 |
-
|
| 46 |
-
```bash
|
| 47 |
-
python3 test_latency_playwright.py <num_usuarios> <simultaneo>
|
| 48 |
-
```
|
| 49 |
-
|
| 50 |
-
- `<num_usuarios>`: Número de usuários para testar (padrão: 1)
|
| 51 |
-
- `<simultaneo>`: `true` para simultâneo, `false` para sequencial (padrão: true)
|
| 52 |
-
|
| 53 |
-
## 📊 O Que o Teste Faz
|
| 54 |
-
|
| 55 |
-
### Para Cada Usuário:
|
| 56 |
-
|
| 57 |
-
1. **Abre navegador headless** (Chromium)
|
| 58 |
-
2. **Navega para o servidor** (mede tempo de navegação)
|
| 59 |
-
3. **Clica em "Conectar"** (mede tempo de conexão)
|
| 60 |
-
4. **Aguarda estabilização** (3 segundos)
|
| 61 |
-
5. **Captura 10 medições de latência**:
|
| 62 |
-
- Captura timestamp local
|
| 63 |
-
- Lê timestamp do vídeo do servidor
|
| 64 |
-
- Calcula diferença (latência)
|
| 65 |
-
6. **Captura FPS final**
|
| 66 |
-
7. **Fecha navegador**
|
| 67 |
-
|
| 68 |
-
### Métricas Coletadas:
|
| 69 |
-
|
| 70 |
-
- ✅ Tempo de navegação (page load)
|
| 71 |
-
- ✅ Tempo de conexão WebRTC
|
| 72 |
-
- ✅ Latência (10 medições por usuário)
|
| 73 |
-
- Média
|
| 74 |
-
- Mínima
|
| 75 |
-
- Máxima
|
| 76 |
-
- ✅ FPS (frames por segundo)
|
| 77 |
-
- ✅ Taxa de sucesso de conexão
|
| 78 |
-
|
| 79 |
-
## 📈 Saída do Teste
|
| 80 |
-
|
| 81 |
-
### Console:
|
| 82 |
-
|
| 83 |
-
```
|
| 84 |
-
🎬 Teste Automatizado de Latência WebRTC
|
| 85 |
-
============================================================
|
| 86 |
-
|
| 87 |
-
🧪 Iniciando teste com 5 usuário(s)...
|
| 88 |
-
Modo: Simultâneo
|
| 89 |
-
Servidor: http://38.117.87.48:9000
|
| 90 |
-
|
| 91 |
-
Usuário 0: Conectado em 245.32ms
|
| 92 |
-
Usuário 1: Conectado em 278.91ms
|
| 93 |
-
Usuário 2: Conectado em 231.45ms
|
| 94 |
-
Usuário 3: Conectado em 289.12ms
|
| 95 |
-
Usuário 4: Conectado em 254.67ms
|
| 96 |
-
✅ Usuário 0: Latência=267.45ms, FPS=29.8
|
| 97 |
-
✅ Usuário 1: Latência=289.12ms, FPS=28.5
|
| 98 |
-
✅ Usuário 2: Latência=251.34ms, FPS=30.0
|
| 99 |
-
✅ Usuário 3: Latency=298.67ms, FPS=28.9
|
| 100 |
-
✅ Usuário 4: Latência=276.23ms, FPS=29.2
|
| 101 |
-
|
| 102 |
-
============================================================
|
| 103 |
-
📊 RESULTADOS AGREGADOS
|
| 104 |
-
============================================================
|
| 105 |
-
Usuários testados: 5
|
| 106 |
-
Conexões bem-sucedidas: 5/5
|
| 107 |
-
Taxa de sucesso: 100.0%
|
| 108 |
-
Tempo total do teste: 45.23s
|
| 109 |
-
|
| 110 |
-
📈 LATÊNCIA
|
| 111 |
-
Média: 276.56ms
|
| 112 |
-
Mínima: 251.34ms
|
| 113 |
-
Máxima: 298.67ms
|
| 114 |
-
Medidas totais: 50
|
| 115 |
-
|
| 116 |
-
⏱️ TEMPO DE CONEXÃO
|
| 117 |
-
Média: 259.89ms
|
| 118 |
-
Mínima: 231.45ms
|
| 119 |
-
Máxima: 289.12ms
|
| 120 |
-
|
| 121 |
-
🎬 FPS
|
| 122 |
-
Média: 29.3
|
| 123 |
-
Mínimo: 28.5
|
| 124 |
-
Máximo: 30.0
|
| 125 |
-
|
| 126 |
-
🎯 CLASSIFICAÇÃO
|
| 127 |
-
✅ BOM (276.56ms) - Ideal para conversação
|
| 128 |
-
============================================================
|
| 129 |
-
|
| 130 |
-
💾 Resultados salvos em: /root/livekit-poc/latency_test_20251224_143727.json
|
| 131 |
-
|
| 132 |
-
✅ Teste concluído!
|
| 133 |
-
```
|
| 134 |
-
|
| 135 |
-
### Arquivo JSON:
|
| 136 |
-
|
| 137 |
-
```json
|
| 138 |
-
{
|
| 139 |
-
"test_config": {
|
| 140 |
-
"num_users": 5,
|
| 141 |
-
"concurrent": true,
|
| 142 |
-
"server_url": "http://38.117.87.48:9000",
|
| 143 |
-
"test_date": "2024-12-24T14:37:27.123456"
|
| 144 |
-
},
|
| 145 |
-
"results": {
|
| 146 |
-
"successful_connections": 5,
|
| 147 |
-
"success_rate": 100.0,
|
| 148 |
-
"total_test_time": 45.23,
|
| 149 |
-
"latency": {
|
| 150 |
-
"all_measurements": [267.45, 289.12, 251.34, 298.67, 276.23, ...],
|
| 151 |
-
"avg": 276.56,
|
| 152 |
-
"min": 251.34,
|
| 153 |
-
"max": 298.67,
|
| 154 |
-
"num_measurements": 50
|
| 155 |
-
},
|
| 156 |
-
"connection_time": {
|
| 157 |
-
"all": [245.32, 278.91, 231.45, 289.12, 254.67],
|
| 158 |
-
"avg": 259.89,
|
| 159 |
-
"min": 231.45,
|
| 160 |
-
"max": 289.12
|
| 161 |
-
},
|
| 162 |
-
"fps": {
|
| 163 |
-
"all": [29.8, 28.5, 30.0, 28.9, 29.2],
|
| 164 |
-
"avg": 29.3,
|
| 165 |
-
"min": 28.5,
|
| 166 |
-
"max": 30.0
|
| 167 |
-
}
|
| 168 |
-
},
|
| 169 |
-
"user_results": [...]
|
| 170 |
-
}
|
| 171 |
-
```
|
| 172 |
-
|
| 173 |
-
## 🔧 Executando Testes
|
| 174 |
-
|
| 175 |
-
### Opção 1: Via SSH Tunnel (Recomendado)
|
| 176 |
-
|
| 177 |
-
1. Crie o tunnel SSH em um terminal:
|
| 178 |
-
|
| 179 |
-
```bash
|
| 180 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 -L 9000:localhost:9000
|
| 181 |
-
```
|
| 182 |
-
|
| 183 |
-
2. Em outro terminal, execute o teste:
|
| 184 |
-
|
| 185 |
-
```bash
|
| 186 |
-
cd /tmp
|
| 187 |
-
python3 test_latency_playwright.py 5 true
|
| 188 |
-
```
|
| 189 |
-
|
| 190 |
-
**Nota**: O teste usará `http://localhost:9000` por padrão. Para usar o tunnel, edite a linha:
|
| 191 |
-
|
| 192 |
-
```python
|
| 193 |
-
SERVER_URL = "http://localhost:9000" # Mude de 38.117.87.48 para localhost
|
| 194 |
-
```
|
| 195 |
-
|
| 196 |
-
### Opção 2: No Servidor Remoto
|
| 197 |
-
|
| 198 |
-
1. Copie o script para o servidor:
|
| 199 |
-
|
| 200 |
-
```bash
|
| 201 |
-
scp -i ~/.ssh/id_rsa -P 43060 test_latency_playwright.py root@38.117.87.48:/root/livekit-poc/
|
| 202 |
-
```
|
| 203 |
-
|
| 204 |
-
2. Instale Playwright no servidor:
|
| 205 |
-
|
| 206 |
-
```bash
|
| 207 |
-
ssh -o StrictHostKeyChecking=no -p 43060 root@38.117.87.48
|
| 208 |
-
pip3 install playwright
|
| 209 |
-
playwright install chromium
|
| 210 |
-
```
|
| 211 |
-
|
| 212 |
-
3. Execute o teste:
|
| 213 |
-
|
| 214 |
-
```bash
|
| 215 |
-
cd /root/livekit-poc
|
| 216 |
-
python3 test_latency_playwright.py 10 true
|
| 217 |
-
```
|
| 218 |
-
|
| 219 |
-
### Opção 3: Localmente com Servidor Acessível
|
| 220 |
-
|
| 221 |
-
Se o servidor for acessível externamente (firewall aberto):
|
| 222 |
-
|
| 223 |
-
```bash
|
| 224 |
-
python3 test_latency_playwright.py 5 true
|
| 225 |
-
```
|
| 226 |
-
|
| 227 |
-
## 📊 Interpretação dos Resultados
|
| 228 |
-
|
| 229 |
-
### Latência
|
| 230 |
-
|
| 231 |
-
| Latência | Classificação | Uso |
|
| 232 |
-
|----------|--------------|-----|
|
| 233 |
-
| < 100ms | ✅ Excelente | Quase imperceptível |
|
| 234 |
-
| 100-300ms | ✅ Bom | Ideal para conversação |
|
| 235 |
-
| 300-500ms | ⚠️ Aceitável | Pequenos delays possíveis |
|
| 236 |
-
| > 500ms | ❌ Ruim | Latência muito alta |
|
| 237 |
-
|
| 238 |
-
### FPS
|
| 239 |
-
|
| 240 |
-
| FPS | Classificação |
|
| 241 |
-
|-----|--------------|
|
| 242 |
-
| 28-30 | ✅ Excelente |
|
| 243 |
-
| 25-27 | ✅ Bom |
|
| 244 |
-
| 20-24 | ⚠️ Aceitável |
|
| 245 |
-
| < 20 | ❌ Ruim |
|
| 246 |
-
|
| 247 |
-
### Taxa de Sucesso
|
| 248 |
-
|
| 249 |
-
| Taxa | Classificação |
|
| 250 |
-
|------|--------------|
|
| 251 |
-
| 100% | ✅ Excelente |
|
| 252 |
-
| 90-99% | ✅ Bom |
|
| 253 |
-
| 80-89% | ⚠️ Aceitável |
|
| 254 |
-
| < 80% | ❌ Problemas |
|
| 255 |
-
|
| 256 |
-
## 🧪 Cenários de Teste
|
| 257 |
-
|
| 258 |
-
### Teste Básico (1 Usuário)
|
| 259 |
-
```bash
|
| 260 |
-
python3 test_latency_playwright.py 1 true
|
| 261 |
-
```
|
| 262 |
-
**Objetivo**: Validar funcionamento básico do servidor
|
| 263 |
-
|
| 264 |
-
### Teste de Carga (5 Usuários)
|
| 265 |
-
```bash
|
| 266 |
-
python3 test_latency_playwright.py 5 true
|
| 267 |
-
```
|
| 268 |
-
**Objetivo**: Testar com carga moderada
|
| 269 |
-
|
| 270 |
-
### Teste de Estresse (10 Usuários)
|
| 271 |
-
```bash
|
| 272 |
-
python3 test_latency_playwright.py 10 true
|
| 273 |
-
```
|
| 274 |
-
**Objetivo**: Testar limite atual do servidor
|
| 275 |
-
|
| 276 |
-
### Teste Sequencial (3 Usuários)
|
| 277 |
-
```bash
|
| 278 |
-
python3 test_latency_playwright.py 3 false
|
| 279 |
-
```
|
| 280 |
-
**Objetivo**: Testar se servidor se recupera entre conexões
|
| 281 |
-
|
| 282 |
-
## 📈 Analisando Resultados
|
| 283 |
-
|
| 284 |
-
### Ler arquivo JSON:
|
| 285 |
-
|
| 286 |
-
```python
|
| 287 |
-
import json
|
| 288 |
-
|
| 289 |
-
with open('latency_test_20251224_143727.json', 'r') as f:
|
| 290 |
-
data = json.load(f)
|
| 291 |
-
|
| 292 |
-
print(f"Latência média: {data['results']['latency']['avg']:.2f}ms")
|
| 293 |
-
print(f"FPS médio: {data['results']['fps']['avg']:.1f}")
|
| 294 |
-
print(f"Taxa de sucesso: {data['results']['success_rate']:.1f}%")
|
| 295 |
-
```
|
| 296 |
-
|
| 297 |
-
### Comparar Múltiplos Testes:
|
| 298 |
-
|
| 299 |
-
```python
|
| 300 |
-
import json
|
| 301 |
-
import glob
|
| 302 |
-
|
| 303 |
-
files = glob.glob('latency_test_*.json')
|
| 304 |
-
for file in sorted(files):
|
| 305 |
-
with open(file, 'r') as f:
|
| 306 |
-
data = json.load(f)
|
| 307 |
-
print(f"{file}: {data['results']['latency']['avg']:.2f}ms")
|
| 308 |
-
```
|
| 309 |
-
|
| 310 |
-
## 🔧 Troubleshooting
|
| 311 |
-
|
| 312 |
-
### Erro: "Page.goto: Timeout 30000ms exceeded"
|
| 313 |
-
|
| 314 |
-
**Causa**: Servidor não acessível
|
| 315 |
-
**Solução**:
|
| 316 |
-
- Verifique se servidor está rodando: `ps aux | grep webrtc-server`
|
| 317 |
-
- Use SSH tunnel se firewall bloquear acesso direto
|
| 318 |
-
|
| 319 |
-
### Erro: "Erro ao conectar"
|
| 320 |
-
|
| 321 |
-
**Causa**: WebRTC connection falhou
|
| 322 |
-
**Solução**:
|
| 323 |
-
- Verifique logs do servidor: `tail -f /tmp/webrtc.log`
|
| 324 |
-
- Verifique se navegador tem suporte a WebRTC
|
| 325 |
-
|
| 326 |
-
### Latência muito alta (> 500ms)
|
| 327 |
-
|
| 328 |
-
**Causas possíveis**:
|
| 329 |
-
- Rede lenta
|
| 330 |
-
- Sobrecarga do servidor
|
| 331 |
-
- Problemas no WebRTC
|
| 332 |
-
|
| 333 |
-
**Solução**:
|
| 334 |
-
- Execute com menos usuários
|
| 335 |
-
- Verifique recursos do servidor: `top`
|
| 336 |
-
- Teste rede: `ping 38.117.87.48`
|
| 337 |
-
|
| 338 |
-
## 📝 Relatório de Teste Exemplo
|
| 339 |
-
|
| 340 |
-
```markdown
|
| 341 |
-
# Relatório de Teste de Latência - 24/12/2024
|
| 342 |
-
|
| 343 |
-
## Configuração do Teste
|
| 344 |
-
- Usuários: 10
|
| 345 |
-
- Modo: Simultâneo
|
| 346 |
-
- Servidor: http://38.117.87.48:9000
|
| 347 |
-
|
| 348 |
-
## Resultados
|
| 349 |
-
|
| 350 |
-
### Latência
|
| 351 |
-
- Média: 289.45ms ✅
|
| 352 |
-
- Mínima: 251.23ms
|
| 353 |
-
- Máxima: 342.67ms
|
| 354 |
-
|
| 355 |
-
### Conexão
|
| 356 |
-
- Sucesso: 10/10 (100%)
|
| 357 |
-
- Tempo médio: 267.89ms
|
| 358 |
-
|
| 359 |
-
### Performance
|
| 360 |
-
- FPS médio: 29.1 ✅
|
| 361 |
-
|
| 362 |
-
## Conclusão
|
| 363 |
-
✅ **APROVADO** - Servidor apresenta latência aceitável para conversação
|
| 364 |
-
- Latência dentro do esperado (100-300ms)
|
| 365 |
-
- Taxa de sucesso de 100%
|
| 366 |
-
- FPS estável em 30
|
| 367 |
-
- Recomendado para produção até 20 usuários simultâneos
|
| 368 |
-
|
| 369 |
-
## Recomendações
|
| 370 |
-
1. Monitorar latência com 20+ usuários
|
| 371 |
-
2. Considerar LiveKit Server para escala
|
| 372 |
-
3. Adicionar monitoramento contínuo
|
| 373 |
-
```
|
| 374 |
-
|
| 375 |
-
## 🚀 Próximos Passos
|
| 376 |
-
|
| 377 |
-
1. **Automatizar Testes**: Criar cron job para testes periódicos
|
| 378 |
-
2. **Dashboard**: Criar dashboard com histórico de testes
|
| 379 |
-
3. **Alertas**: Configurar alertas para latência > 300ms
|
| 380 |
-
4. **CI/CD**: Integrar testes no pipeline de deploy
|
| 381 |
-
5. **Stress Test**: Testar com 50+ usuários
|
| 382 |
-
|
| 383 |
-
## 📚 Referências
|
| 384 |
-
|
| 385 |
-
- [Playwright Documentation](https://playwright.dev/python/)
|
| 386 |
-
- [WebRTC Performance](https://webrtc.org/getting-started/performance)
|
| 387 |
-
- [Latency Best Practices](https://docs.livekit.io/guides/performance)
|
| 388 |
-
|
| 389 |
-
---
|
| 390 |
-
|
| 391 |
-
**Teste automatizado pronto para uso!** 🎯
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/docs/TESTING_INSTRUCTIONS.md
DELETED
|
@@ -1,178 +0,0 @@
|
|
| 1 |
-
# 🎬 Instruções de Teste - WebRTC Latency Test
|
| 2 |
-
|
| 3 |
-
## ✅ Servidor Ativo
|
| 4 |
-
|
| 5 |
-
**Status**: 🟢 RODANDO
|
| 6 |
-
**URL**: http://38.117.87.48:9000
|
| 7 |
-
**Porta**: 9000
|
| 8 |
-
|
| 9 |
-
## 📝 Como Testar
|
| 10 |
-
|
| 11 |
-
### Opção 1: Acesso Direto (se a porta 9000 estiver aberta no firewall)
|
| 12 |
-
|
| 13 |
-
Abra seu navegador e acesse:
|
| 14 |
-
```
|
| 15 |
-
http://38.117.87.48:9000
|
| 16 |
-
```
|
| 17 |
-
|
| 18 |
-
### Opção 2: Via SSH Tunnel (mais seguro)
|
| 19 |
-
|
| 20 |
-
1. Abra um terminal no seu computador local
|
| 21 |
-
2. Crie o tunnel SSH:
|
| 22 |
-
|
| 23 |
-
```bash
|
| 24 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 -L 9000:localhost:9000
|
| 25 |
-
```
|
| 26 |
-
|
| 27 |
-
3. Mantenha o terminal aberto
|
| 28 |
-
4. Abra seu navegador e acesse:
|
| 29 |
-
```
|
| 30 |
-
http://localhost:9000
|
| 31 |
-
```
|
| 32 |
-
|
| 33 |
-
## 🧪 Teste de Latência
|
| 34 |
-
|
| 35 |
-
1. **Conecte**: Clique no botão "Conectar"
|
| 36 |
-
2. **Observe**: O vídeo com timestamps começará a ser exibido
|
| 37 |
-
3. **Meça**: Compare o timestamp no vídeo com o horário local
|
| 38 |
-
|
| 39 |
-
### Como Calcular a Latência
|
| 40 |
-
|
| 41 |
-
```
|
| 42 |
-
Latência = Hora Local - Timestamp no Vídeo
|
| 43 |
-
```
|
| 44 |
-
|
| 45 |
-
Exemplo:
|
| 46 |
-
- Timestamp no vídeo: `14:30:15.500`
|
| 47 |
-
- Hora local ao receber: `14:30:15.750`
|
| 48 |
-
- **Latência: 250ms**
|
| 49 |
-
|
| 50 |
-
## 📊 Métricas Exibidas
|
| 51 |
-
|
| 52 |
-
O cliente mostra:
|
| 53 |
-
- **FPS Recebidos**: Frames por segundo no cliente
|
| 54 |
-
- **Frames Totais**: Total de frames recebidos
|
| 55 |
-
- **Tempo Decorrido**: Tempo desde o início da conexão
|
| 56 |
-
- **Latência Estimada**: Compare manualmente os timestamps
|
| 57 |
-
|
| 58 |
-
## 🎯 Expectativas
|
| 59 |
-
|
| 60 |
-
### Latência Ideal
|
| 61 |
-
- **< 100ms**: Excelente (quase imperceptível)
|
| 62 |
-
- **100-300ms**: ✅ Bom (ideal para conversação)
|
| 63 |
-
- **300-500ms**: Aceitável
|
| 64 |
-
- **> 500ms**: Ruim (notável delay)
|
| 65 |
-
|
| 66 |
-
### FPS Esperado
|
| 67 |
-
- **30 FPS**: Ideal para vídeo suave
|
| 68 |
-
- **15-25 FPS**: Aceitável
|
| 69 |
-
- **< 15 FPS**: Problema na conexão ou servidor
|
| 70 |
-
|
| 71 |
-
## 🔍 Diagnóstico
|
| 72 |
-
|
| 73 |
-
### Vídeo não aparece
|
| 74 |
-
- Verifique se o servidor está rodando:
|
| 75 |
-
```bash
|
| 76 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "ps aux | grep webrtc-server"
|
| 77 |
-
```
|
| 78 |
-
|
| 79 |
-
### Conexão falha
|
| 80 |
-
- Verifique logs do servidor:
|
| 81 |
-
```bash
|
| 82 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "tail -f /tmp/webrtc-server.log"
|
| 83 |
-
```
|
| 84 |
-
|
| 85 |
-
### Latência muito alta (> 500ms)
|
| 86 |
-
- Verifique sua conexão de internet
|
| 87 |
-
- Verifique carga do servidor:
|
| 88 |
-
```bash
|
| 89 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "top -n 1 | head -20"
|
| 90 |
-
```
|
| 91 |
-
|
| 92 |
-
## 📋 Arquivos no Servidor
|
| 93 |
-
|
| 94 |
-
Localização: `/root/livekit-poc/`
|
| 95 |
-
- `webrtc-server.py` - Servidor WebRTC
|
| 96 |
-
- `webrtc-client.html` - Cliente web
|
| 97 |
-
- `requirements.txt` - Dependências Python
|
| 98 |
-
- `README.md` - Documentação completa
|
| 99 |
-
|
| 100 |
-
## 🛠 Comandos Úteis
|
| 101 |
-
|
| 102 |
-
### Verificar status do servidor
|
| 103 |
-
```bash
|
| 104 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "ps aux | grep webrtc-server"
|
| 105 |
-
```
|
| 106 |
-
|
| 107 |
-
### Verificar logs em tempo real
|
| 108 |
-
```bash
|
| 109 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "tail -f /tmp/webrtc-server.log"
|
| 110 |
-
```
|
| 111 |
-
|
| 112 |
-
### Reiniciar o servidor
|
| 113 |
-
```bash
|
| 114 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "cd /root/livekit-poc && pkill -f webrtc-server && nohup python3 webrtc-server.py > /tmp/webrtc-server.log 2>&1 &"
|
| 115 |
-
```
|
| 116 |
-
|
| 117 |
-
### Parar o servidor
|
| 118 |
-
```bash
|
| 119 |
-
ssh -i ~/.ssh/id_rsa -p 43060 root@38.117.87.48 "pkill -f webrtc-server"
|
| 120 |
-
```
|
| 121 |
-
|
| 122 |
-
## 🎨 O que você verá no vídeo
|
| 123 |
-
|
| 124 |
-
O vídeo gerado mostra:
|
| 125 |
-
- **Título**: "WebRTC Latency Test"
|
| 126 |
-
- **Timestamp**: Hora atual com milissegundos (preciso)
|
| 127 |
-
- **Frame Number**: Contador de frames
|
| 128 |
-
- **FPS**: Taxa de frames por segundo
|
| 129 |
-
- **Elapsed Time**: Tempo decorrido desde o início
|
| 130 |
-
- **Borda Colorida**: Muda de cor continuamente (visualiza atualização)
|
| 131 |
-
|
| 132 |
-
## 💡 Dicas
|
| 133 |
-
|
| 134 |
-
1. **Teste múltiplas vezes**: A latência pode variar
|
| 135 |
-
2. **Teste em diferentes momentos**: Condições de rede mudam
|
| 136 |
-
3. **Teste em diferentes dispositivos**: Desktop vs mobile
|
| 137 |
-
4. **Compare com outros serviços**: Use como baseline
|
| 138 |
-
|
| 139 |
-
## 📊 Relatório de Testes
|
| 140 |
-
|
| 141 |
-
Após testar, documente:
|
| 142 |
-
```
|
| 143 |
-
Data: ___________
|
| 144 |
-
Hora: ___________
|
| 145 |
-
Dispositivo: ___________
|
| 146 |
-
Conexão: ___________
|
| 147 |
-
|
| 148 |
-
Latência Média: _______ ms
|
| 149 |
-
Latência Mínima: _______ ms
|
| 150 |
-
Latência Máxima: _______ ms
|
| 151 |
-
FPS Recebidos: _______
|
| 152 |
-
|
| 153 |
-
Observações: ___________
|
| 154 |
-
```
|
| 155 |
-
|
| 156 |
-
## 🚀 Próximos Passos
|
| 157 |
-
|
| 158 |
-
Após validar a latência, você pode:
|
| 159 |
-
|
| 160 |
-
1. **Usar LiveKit Server**: Para produção com múltiplos usuários
|
| 161 |
-
2. **Adicionar Áudio**: Para conversação full-duplex
|
| 162 |
-
3. **Escalabilidade**: Testar com múltiplas conexões simultâneas
|
| 163 |
-
4. **Integrar com LLM**: Criar avatar conversacional
|
| 164 |
-
|
| 165 |
-
## 📞 Suporte
|
| 166 |
-
|
| 167 |
-
Se tiver problemas:
|
| 168 |
-
1. Verifique os logs do servidor
|
| 169 |
-
2. Verifique a conexão de rede
|
| 170 |
-
3. Use as ferramentas de debug do navegador:
|
| 171 |
-
- Chrome: `chrome://webrtc-internals`
|
| 172 |
-
- Firefox: `about:webrtc`
|
| 173 |
-
|
| 174 |
-
---
|
| 175 |
-
|
| 176 |
-
**Servidor configurado e pronto para testes!** 🎉
|
| 177 |
-
|
| 178 |
-
Acesse: http://38.117.87.48:9000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/requirements.txt
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
aiortc>=1.6.0
|
| 2 |
-
aiohttp>=3.8.0
|
| 3 |
-
opencv-python>=4.8.0
|
| 4 |
-
numpy>=1.24.0
|
| 5 |
-
av>=10.0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/scripts/install.sh
DELETED
|
@@ -1,125 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
#
|
| 3 |
-
# WebRTC Latency Test POC - Installation Script
|
| 4 |
-
# Installs dependencies for the WebRTC latency testing system
|
| 5 |
-
#
|
| 6 |
-
|
| 7 |
-
set -e
|
| 8 |
-
|
| 9 |
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 10 |
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
| 11 |
-
|
| 12 |
-
GREEN='\033[0;32m'
|
| 13 |
-
CYAN='\033[0;36m'
|
| 14 |
-
YELLOW='\033[1;33m'
|
| 15 |
-
NC='\033[0m'
|
| 16 |
-
|
| 17 |
-
success() { echo -e "${GREEN}✓${NC} $1"; }
|
| 18 |
-
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
|
| 19 |
-
header() { echo -e "\n${CYAN}$1${NC}"; }
|
| 20 |
-
|
| 21 |
-
echo ""
|
| 22 |
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
| 23 |
-
echo "║ WEBRTC LATENCY TEST - INSTALLATION ║"
|
| 24 |
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
| 25 |
-
echo ""
|
| 26 |
-
|
| 27 |
-
# ============================================================
|
| 28 |
-
# 1. CHECK PYTHON
|
| 29 |
-
# ============================================================
|
| 30 |
-
header "[1/5] Checking Python version..."
|
| 31 |
-
|
| 32 |
-
PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
|
| 33 |
-
PYTHON_MAJOR=$(echo $PYTHON_VERSION | cut -d. -f1)
|
| 34 |
-
PYTHON_MINOR=$(echo $PYTHON_VERSION | cut -d. -f2)
|
| 35 |
-
|
| 36 |
-
if [ "$PYTHON_MAJOR" -ge 3 ] && [ "$PYTHON_MINOR" -ge 8 ]; then
|
| 37 |
-
success "Python $PYTHON_VERSION detected"
|
| 38 |
-
else
|
| 39 |
-
echo -e "${YELLOW}⚠${NC} Python 3.8+ required (found $PYTHON_VERSION)"
|
| 40 |
-
echo " Please install Python 3.8 or later"
|
| 41 |
-
exit 1
|
| 42 |
-
fi
|
| 43 |
-
|
| 44 |
-
# ============================================================
|
| 45 |
-
# 2. INSTALL PYTHON DEPENDENCIES
|
| 46 |
-
# ============================================================
|
| 47 |
-
header "[2/5] Installing Python dependencies..."
|
| 48 |
-
|
| 49 |
-
cd "$PROJECT_DIR"
|
| 50 |
-
|
| 51 |
-
if [ -f "requirements.txt" ]; then
|
| 52 |
-
pip3 install -r requirements.txt --quiet
|
| 53 |
-
success "Python dependencies installed"
|
| 54 |
-
else
|
| 55 |
-
# Fallback: install common dependencies
|
| 56 |
-
pip3 install --quiet \
|
| 57 |
-
aiortc>=1.6.0 \
|
| 58 |
-
aiohttp>=3.8.0 \
|
| 59 |
-
opencv-python>=4.8.0 \
|
| 60 |
-
numpy>=1.24.0 \
|
| 61 |
-
av>=10.0.0 \
|
| 62 |
-
playwright>=1.40.0
|
| 63 |
-
success "Python dependencies installed (fallback)"
|
| 64 |
-
fi
|
| 65 |
-
|
| 66 |
-
# ============================================================
|
| 67 |
-
# 3. INSTALL PLAYWRIGHT BROWSER
|
| 68 |
-
# ============================================================
|
| 69 |
-
header "[3/5] Installing Playwright and Chromium..."
|
| 70 |
-
|
| 71 |
-
pip3 install playwright --quiet
|
| 72 |
-
|
| 73 |
-
# Install Chromium (headless browser for automated tests)
|
| 74 |
-
playwright install chromium --quiet 2>/dev/null || {
|
| 75 |
-
echo " Downloading Chromium (may take a moment)..."
|
| 76 |
-
playwright install chromium
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
success "Playwright and Chromium installed"
|
| 80 |
-
|
| 81 |
-
# ============================================================
|
| 82 |
-
# 4. VERIFY DOCKER
|
| 83 |
-
# ============================================================
|
| 84 |
-
header "[4/5] Checking Docker..."
|
| 85 |
-
|
| 86 |
-
if command -v docker &> /dev/null; then
|
| 87 |
-
DOCKER_VERSION=$(docker --version | awk '{print $3}')
|
| 88 |
-
success "Docker $DOCKER_VERSION detected"
|
| 89 |
-
else
|
| 90 |
-
warn "Docker not found (optional for LiveKit)"
|
| 91 |
-
echo " To install Docker:"
|
| 92 |
-
echo " curl -fsSL https://get.docker.com -o get-docker.sh"
|
| 93 |
-
echo " sh get-docker.sh"
|
| 94 |
-
fi
|
| 95 |
-
|
| 96 |
-
# ============================================================
|
| 97 |
-
# 5. CREATE LOG DIRECTORY
|
| 98 |
-
# ============================================================
|
| 99 |
-
header "[5/5] Setting up log directories..."
|
| 100 |
-
|
| 101 |
-
mkdir -p /tmp/webrtc-logs 2>/dev/null || true
|
| 102 |
-
success "Log directories created"
|
| 103 |
-
|
| 104 |
-
# ============================================================
|
| 105 |
-
# COMPLETE
|
| 106 |
-
# ============================================================
|
| 107 |
-
echo ""
|
| 108 |
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
| 109 |
-
echo "║ INSTALLATION COMPLETE! ║"
|
| 110 |
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
| 111 |
-
echo ""
|
| 112 |
-
echo " Components installed:"
|
| 113 |
-
echo " • Python WebRTC server (aiortc)"
|
| 114 |
-
echo " • OpenCV for video processing"
|
| 115 |
-
echo " • Playwright for automated tests"
|
| 116 |
-
echo " • Chromium headless browser"
|
| 117 |
-
echo ""
|
| 118 |
-
echo " To start the server:"
|
| 119 |
-
echo " $SCRIPT_DIR/start_webrtc.sh"
|
| 120 |
-
echo ""
|
| 121 |
-
echo " To run automated tests:"
|
| 122 |
-
echo " python3 test_latency_playwright.py <users> <concurrent>"
|
| 123 |
-
echo ""
|
| 124 |
-
success "Installation completed successfully!"
|
| 125 |
-
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/scripts/start.sh
DELETED
|
@@ -1,211 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
#
|
| 3 |
-
# WebRTC Latency Test POC - Start Script
|
| 4 |
-
# Starts the WebRTC server for latency testing
|
| 5 |
-
#
|
| 6 |
-
|
| 7 |
-
set -e
|
| 8 |
-
|
| 9 |
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 10 |
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
| 11 |
-
|
| 12 |
-
GREEN='\033[0;32m'
|
| 13 |
-
YELLOW='\033[1;33m'
|
| 14 |
-
CYAN='\033[0;36m'
|
| 15 |
-
RED='\033[0;31m'
|
| 16 |
-
NC='\033[0m'
|
| 17 |
-
|
| 18 |
-
success() { echo -e "${GREEN}✓${NC} $1"; }
|
| 19 |
-
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
|
| 20 |
-
error() { echo -e "${RED}✗${NC} $1"; }
|
| 21 |
-
header() { echo -e "\n${CYAN}$1${NC}"; }
|
| 22 |
-
|
| 23 |
-
# Default configuration
|
| 24 |
-
HOST="0.0.0.0"
|
| 25 |
-
PORT=9000
|
| 26 |
-
LOG_FILE="/tmp/webrtc-server.log"
|
| 27 |
-
PID_FILE="/tmp/webrtc-server.pid"
|
| 28 |
-
SERVER_SCRIPT="$PROJECT_DIR/webrtc-server-fixed.py"
|
| 29 |
-
|
| 30 |
-
# Parse arguments
|
| 31 |
-
while [[ $# -gt 0 ]]; do
|
| 32 |
-
case $1 in
|
| 33 |
-
-p|--port)
|
| 34 |
-
PORT="$2"
|
| 35 |
-
shift 2
|
| 36 |
-
;;
|
| 37 |
-
-h|--host)
|
| 38 |
-
HOST="$2"
|
| 39 |
-
shift 2
|
| 40 |
-
;;
|
| 41 |
-
--no-daemon)
|
| 42 |
-
NO_DAEMON=true
|
| 43 |
-
shift
|
| 44 |
-
;;
|
| 45 |
-
*)
|
| 46 |
-
echo "Unknown option: $1"
|
| 47 |
-
echo "Usage: $0 [--port PORT] [--host HOST] [--no-daemon]"
|
| 48 |
-
exit 1
|
| 49 |
-
;;
|
| 50 |
-
esac
|
| 51 |
-
done
|
| 52 |
-
|
| 53 |
-
echo ""
|
| 54 |
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
| 55 |
-
echo "║ WEBRTC LATENCY TEST - START SERVER ║"
|
| 56 |
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
| 57 |
-
echo ""
|
| 58 |
-
|
| 59 |
-
# ============================================================
|
| 60 |
-
# CHECK IF SERVER SCRIPT EXISTS
|
| 61 |
-
# ============================================================
|
| 62 |
-
if [ ! -f "$SERVER_SCRIPT" ]; then
|
| 63 |
-
error "Server script not found: $SERVER_SCRIPT"
|
| 64 |
-
echo " Please install the project first:"
|
| 65 |
-
echo " $SCRIPT_DIR/install_webrtc.sh"
|
| 66 |
-
exit 1
|
| 67 |
-
fi
|
| 68 |
-
|
| 69 |
-
# ============================================================
|
| 70 |
-
# CHECK IF ALREADY RUNNING
|
| 71 |
-
# ============================================================
|
| 72 |
-
if [ -f "$PID_FILE" ]; then
|
| 73 |
-
PID=$(cat "$PID_FILE")
|
| 74 |
-
if ps -p "$PID" > /dev/null 2>&1; then
|
| 75 |
-
header "Server already running"
|
| 76 |
-
echo " PID: $PID"
|
| 77 |
-
echo " Port: $PORT"
|
| 78 |
-
echo ""
|
| 79 |
-
echo " To stop: $SCRIPT_DIR/stop_webrtc.sh"
|
| 80 |
-
exit 0
|
| 81 |
-
else
|
| 82 |
-
# Stale PID file, remove it
|
| 83 |
-
rm -f "$PID_FILE"
|
| 84 |
-
fi
|
| 85 |
-
fi
|
| 86 |
-
|
| 87 |
-
# ============================================================
|
| 88 |
-
# STOP EXISTING PROCESSES
|
| 89 |
-
# ============================================================
|
| 90 |
-
header "Stopping existing WebRTC server processes..."
|
| 91 |
-
|
| 92 |
-
# Kill any existing webrtc-server processes
|
| 93 |
-
pkill -f "webrtc-server" 2>/dev/null || true
|
| 94 |
-
sleep 1
|
| 95 |
-
|
| 96 |
-
# Kill any process using the port
|
| 97 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 98 |
-
lsof -ti :$PORT | xargs kill -9 2>/dev/null || true
|
| 99 |
-
fi
|
| 100 |
-
|
| 101 |
-
success "Cleanup completed"
|
| 102 |
-
|
| 103 |
-
# ============================================================
|
| 104 |
-
# START SERVER
|
| 105 |
-
# ============================================================
|
| 106 |
-
header "Starting WebRTC server..."
|
| 107 |
-
|
| 108 |
-
cd "$PROJECT_DIR"
|
| 109 |
-
|
| 110 |
-
if [ "$NO_DAEMON" = "true" ]; then
|
| 111 |
-
# Run in foreground
|
| 112 |
-
echo " Starting in foreground mode..."
|
| 113 |
-
echo " Host: $HOST"
|
| 114 |
-
echo " Port: $PORT"
|
| 115 |
-
echo " Log: $LOG_FILE"
|
| 116 |
-
echo ""
|
| 117 |
-
python3 -u "$SERVER_SCRIPT" > "$LOG_FILE" 2>&1
|
| 118 |
-
else
|
| 119 |
-
# Run in background
|
| 120 |
-
echo " Host: $HOST"
|
| 121 |
-
echo " Port: $PORT"
|
| 122 |
-
echo " Log: $LOG_FILE"
|
| 123 |
-
echo ""
|
| 124 |
-
|
| 125 |
-
# Start in background with nohup
|
| 126 |
-
nohup python3 -u "$SERVER_SCRIPT" > "$LOG_FILE" 2>&1 &
|
| 127 |
-
SERVER_PID=$!
|
| 128 |
-
|
| 129 |
-
# Save PID
|
| 130 |
-
echo $SERVER_PID > "$PID_FILE"
|
| 131 |
-
|
| 132 |
-
# Wait a moment for startup
|
| 133 |
-
sleep 3
|
| 134 |
-
|
| 135 |
-
# Check if process is still running
|
| 136 |
-
if ps -p $SERVER_PID > /dev/null 2>&1; then
|
| 137 |
-
success "Server started successfully"
|
| 138 |
-
echo " PID: $SERVER_PID"
|
| 139 |
-
|
| 140 |
-
# Wait for port to be listening
|
| 141 |
-
for i in {1..10}; do
|
| 142 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 143 |
-
success "Server listening on port $PORT"
|
| 144 |
-
break
|
| 145 |
-
fi
|
| 146 |
-
sleep 1
|
| 147 |
-
done
|
| 148 |
-
else
|
| 149 |
-
error "Server failed to start"
|
| 150 |
-
echo " Check log: $LOG_FILE"
|
| 151 |
-
rm -f "$PID_FILE"
|
| 152 |
-
exit 1
|
| 153 |
-
fi
|
| 154 |
-
fi
|
| 155 |
-
|
| 156 |
-
# ============================================================
|
| 157 |
-
# DISPLAY STATUS
|
| 158 |
-
# ============================================================
|
| 159 |
-
header "SERVER STATUS"
|
| 160 |
-
echo ""
|
| 161 |
-
|
| 162 |
-
printf " %-15s %-10s %s\n" "Service" "Port" "Status"
|
| 163 |
-
echo " ─────────────────────────────────"
|
| 164 |
-
|
| 165 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 166 |
-
printf " %-15s %-10s ${GREEN}✓ Online${NC}\n" "WebRTC Server" "$PORT"
|
| 167 |
-
|
| 168 |
-
# Show last few log lines
|
| 169 |
-
if [ -f "$LOG_FILE" ]; then
|
| 170 |
-
echo ""
|
| 171 |
-
echo " Last log entries:"
|
| 172 |
-
tail -5 "$LOG_FILE" | sed 's/^/ /'
|
| 173 |
-
fi
|
| 174 |
-
else
|
| 175 |
-
printf " %-15s %-10s ${RED}✗ Offline${NC}\n" "WebRTC Server" "$PORT"
|
| 176 |
-
echo ""
|
| 177 |
-
error "Server is not responding"
|
| 178 |
-
exit 1
|
| 179 |
-
fi
|
| 180 |
-
|
| 181 |
-
# ============================================================
|
| 182 |
-
# ACCESS INFORMATION
|
| 183 |
-
# ============================================================
|
| 184 |
-
header "ACCESS INFORMATION"
|
| 185 |
-
echo ""
|
| 186 |
-
|
| 187 |
-
if [ "$HOST" = "0.0.0.0" ]; then
|
| 188 |
-
echo " Local access: http://localhost:$PORT"
|
| 189 |
-
echo " Network access: http://$(hostname -I | awk '{print $1}'):$PORT"
|
| 190 |
-
else
|
| 191 |
-
echo " Server URL: http://$HOST:$PORT"
|
| 192 |
-
fi
|
| 193 |
-
|
| 194 |
-
echo ""
|
| 195 |
-
echo " Web client: Open http://localhost:$PORT in browser"
|
| 196 |
-
echo " Logs: tail -f $LOG_FILE"
|
| 197 |
-
echo ""
|
| 198 |
-
|
| 199 |
-
# ============================================================
|
| 200 |
-
# QUICK TEST COMMANDS
|
| 201 |
-
# ============================================================
|
| 202 |
-
echo " Quick test commands:"
|
| 203 |
-
echo " • Manual test: Open http://localhost:$PORT in browser"
|
| 204 |
-
echo " • Auto test: python3 test_latency_playwright.py 1 true"
|
| 205 |
-
echo " • Load test: python3 test_latency_playwright.py 5 true"
|
| 206 |
-
echo ""
|
| 207 |
-
echo " To stop server: $SCRIPT_DIR/stop_webrtc.sh"
|
| 208 |
-
echo ""
|
| 209 |
-
|
| 210 |
-
success "WebRTC server started successfully!"
|
| 211 |
-
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/scripts/stop.sh
DELETED
|
@@ -1,141 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
#
|
| 3 |
-
# WebRTC Latency Test POC - Stop Script
|
| 4 |
-
# Stops the WebRTC server
|
| 5 |
-
#
|
| 6 |
-
|
| 7 |
-
set -e
|
| 8 |
-
|
| 9 |
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 10 |
-
|
| 11 |
-
GREEN='\033[0;32m'
|
| 12 |
-
YELLOW='\033[1;33m'
|
| 13 |
-
RED='\033[0;31m'
|
| 14 |
-
CYAN='\033[0;36m'
|
| 15 |
-
NC='\033[0m'
|
| 16 |
-
|
| 17 |
-
success() { echo -e "${GREEN}✓${NC} $1"; }
|
| 18 |
-
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
|
| 19 |
-
error() { echo -e "${RED}✗${NC} $1"; }
|
| 20 |
-
header() { echo -e "\n${CYAN}$1${NC}"; }
|
| 21 |
-
|
| 22 |
-
PID_FILE="/tmp/webrtc-server.pid"
|
| 23 |
-
PORT=9000
|
| 24 |
-
|
| 25 |
-
echo ""
|
| 26 |
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
| 27 |
-
echo "║ WEBRTC LATENCY TEST - STOP SERVER ║"
|
| 28 |
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
| 29 |
-
echo ""
|
| 30 |
-
|
| 31 |
-
# ============================================================
|
| 32 |
-
# CHECK IF SERVER IS RUNNING
|
| 33 |
-
# ============================================================
|
| 34 |
-
header "Checking server status..."
|
| 35 |
-
|
| 36 |
-
SERVER_RUNNING=false
|
| 37 |
-
|
| 38 |
-
# Check PID file
|
| 39 |
-
if [ -f "$PID_FILE" ]; then
|
| 40 |
-
PID=$(cat "$PID_FILE")
|
| 41 |
-
if ps -p "$PID" > /dev/null 2>&1; then
|
| 42 |
-
echo " Found server process (PID: $PID)"
|
| 43 |
-
SERVER_RUNNING=true
|
| 44 |
-
else
|
| 45 |
-
warn "PID file exists but process not running (stale)"
|
| 46 |
-
rm -f "$PID_FILE"
|
| 47 |
-
fi
|
| 48 |
-
fi
|
| 49 |
-
|
| 50 |
-
# Also check for processes using the port
|
| 51 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 52 |
-
echo " Found process listening on port $PORT"
|
| 53 |
-
SERVER_RUNNING=true
|
| 54 |
-
fi
|
| 55 |
-
|
| 56 |
-
if [ "$SERVER_RUNNING" = false ]; then
|
| 57 |
-
echo ""
|
| 58 |
-
echo " ${YELLOW}Server is not running${NC}"
|
| 59 |
-
echo ""
|
| 60 |
-
exit 0
|
| 61 |
-
fi
|
| 62 |
-
|
| 63 |
-
# ============================================================
|
| 64 |
-
# STOP SERVER
|
| 65 |
-
# ============================================================
|
| 66 |
-
header "Stopping WebRTC server..."
|
| 67 |
-
|
| 68 |
-
# Kill by PID if available
|
| 69 |
-
if [ -f "$PID_FILE" ]; then
|
| 70 |
-
PID=$(cat "$PID_FILE")
|
| 71 |
-
if ps -p "$PID" > /dev/null 2>&1; then
|
| 72 |
-
echo " Stopping server (PID: $PID)..."
|
| 73 |
-
kill $PID 2>/dev/null || true
|
| 74 |
-
|
| 75 |
-
# Wait for process to stop
|
| 76 |
-
for i in {1..5}; do
|
| 77 |
-
if ! ps -p $PID > /dev/null 2>&1; then
|
| 78 |
-
success "Server stopped"
|
| 79 |
-
break
|
| 80 |
-
fi
|
| 81 |
-
sleep 1
|
| 82 |
-
done
|
| 83 |
-
|
| 84 |
-
# Force kill if still running
|
| 85 |
-
if ps -p $PID > /dev/null 2>&1; then
|
| 86 |
-
echo " Force killing server..."
|
| 87 |
-
kill -9 $PID 2>/dev/null || true
|
| 88 |
-
fi
|
| 89 |
-
fi
|
| 90 |
-
rm -f "$PID_FILE"
|
| 91 |
-
fi
|
| 92 |
-
|
| 93 |
-
# Kill any webrtc-server processes
|
| 94 |
-
echo " Killing webrtc-server processes..."
|
| 95 |
-
pkill -f "webrtc-server" 2>/dev/null || true
|
| 96 |
-
|
| 97 |
-
# Kill any process using the port
|
| 98 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 99 |
-
echo " Killing process using port $PORT..."
|
| 100 |
-
lsof -ti :$PORT | xargs kill -9 2>/dev/null || true
|
| 101 |
-
fi
|
| 102 |
-
|
| 103 |
-
# Wait a moment
|
| 104 |
-
sleep 2
|
| 105 |
-
|
| 106 |
-
success "Server stopped"
|
| 107 |
-
|
| 108 |
-
# ============================================================
|
| 109 |
-
# VERIFY STOPPED
|
| 110 |
-
# ============================================================
|
| 111 |
-
header "Verifying..."
|
| 112 |
-
|
| 113 |
-
STILL_RUNNING=false
|
| 114 |
-
|
| 115 |
-
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
| 116 |
-
error "Something is still listening on port $PORT"
|
| 117 |
-
STILL_RUNNING=true
|
| 118 |
-
elif pgrep -f "webrtc-server" > /dev/null 2>&1; then
|
| 119 |
-
error "Some webrtc-server processes still running"
|
| 120 |
-
STILL_RUNNING=true
|
| 121 |
-
fi
|
| 122 |
-
|
| 123 |
-
if [ "$STILL_RUNNING" = false ]; then
|
| 124 |
-
success "All processes stopped"
|
| 125 |
-
fi
|
| 126 |
-
|
| 127 |
-
# ============================================================
|
| 128 |
-
# DISPLAY LOG
|
| 129 |
-
# ============================================================
|
| 130 |
-
header "Last log entries"
|
| 131 |
-
|
| 132 |
-
LOG_FILE="/tmp/webrtc-server.log"
|
| 133 |
-
if [ -f "$LOG_FILE" ]; then
|
| 134 |
-
tail -10 "$LOG_FILE" | sed 's/^/ /'
|
| 135 |
-
else
|
| 136 |
-
echo " No log file found"
|
| 137 |
-
fi
|
| 138 |
-
|
| 139 |
-
echo ""
|
| 140 |
-
success "Stop completed!"
|
| 141 |
-
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/server/webrtc-client.html
DELETED
|
@@ -1,366 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="pt-BR">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>WebRTC Latency Test</title>
|
| 7 |
-
<style>
|
| 8 |
-
* {
|
| 9 |
-
margin: 0;
|
| 10 |
-
padding: 0;
|
| 11 |
-
box-sizing: border-box;
|
| 12 |
-
}
|
| 13 |
-
body {
|
| 14 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 15 |
-
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
| 16 |
-
min-height: 100vh;
|
| 17 |
-
display: flex;
|
| 18 |
-
flex-direction: column;
|
| 19 |
-
align-items: center;
|
| 20 |
-
padding: 20px;
|
| 21 |
-
color: #fff;
|
| 22 |
-
}
|
| 23 |
-
h1 {
|
| 24 |
-
margin-bottom: 20px;
|
| 25 |
-
text-align: center;
|
| 26 |
-
font-size: 2em;
|
| 27 |
-
color: #00d4ff;
|
| 28 |
-
text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
|
| 29 |
-
}
|
| 30 |
-
.container {
|
| 31 |
-
max-width: 1200px;
|
| 32 |
-
width: 100%;
|
| 33 |
-
}
|
| 34 |
-
.video-container {
|
| 35 |
-
background: #0a0a0a;
|
| 36 |
-
border-radius: 10px;
|
| 37 |
-
padding: 20px;
|
| 38 |
-
margin-bottom: 20px;
|
| 39 |
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
| 40 |
-
}
|
| 41 |
-
video {
|
| 42 |
-
width: 100%;
|
| 43 |
-
max-width: 640px;
|
| 44 |
-
display: block;
|
| 45 |
-
margin: 0 auto;
|
| 46 |
-
border-radius: 5px;
|
| 47 |
-
background: #000;
|
| 48 |
-
}
|
| 49 |
-
.controls {
|
| 50 |
-
display: flex;
|
| 51 |
-
gap: 10px;
|
| 52 |
-
margin-top: 20px;
|
| 53 |
-
justify-content: center;
|
| 54 |
-
}
|
| 55 |
-
button {
|
| 56 |
-
padding: 12px 24px;
|
| 57 |
-
font-size: 16px;
|
| 58 |
-
font-weight: bold;
|
| 59 |
-
border: none;
|
| 60 |
-
border-radius: 5px;
|
| 61 |
-
cursor: pointer;
|
| 62 |
-
transition: all 0.3s ease;
|
| 63 |
-
text-transform: uppercase;
|
| 64 |
-
letter-spacing: 1px;
|
| 65 |
-
}
|
| 66 |
-
button:disabled {
|
| 67 |
-
opacity: 0.5;
|
| 68 |
-
cursor: not-allowed;
|
| 69 |
-
}
|
| 70 |
-
.btn-connect {
|
| 71 |
-
background: linear-gradient(135deg, #00d4ff 0%, #0099cc 100%);
|
| 72 |
-
color: #000;
|
| 73 |
-
}
|
| 74 |
-
.btn-connect:hover:not(:disabled) {
|
| 75 |
-
transform: translateY(-2px);
|
| 76 |
-
box-shadow: 0 4px 15px rgba(0, 212, 255, 0.4);
|
| 77 |
-
}
|
| 78 |
-
.btn-disconnect {
|
| 79 |
-
background: linear-gradient(135deg, #ff4757 0%, #cc3845 100%);
|
| 80 |
-
color: #fff;
|
| 81 |
-
}
|
| 82 |
-
.btn-disconnect:hover:not(:disabled) {
|
| 83 |
-
transform: translateY(-2px);
|
| 84 |
-
box-shadow: 0 4px 15px rgba(255, 71, 87, 0.4);
|
| 85 |
-
}
|
| 86 |
-
.stats {
|
| 87 |
-
display: grid;
|
| 88 |
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 89 |
-
gap: 15px;
|
| 90 |
-
margin-top: 20px;
|
| 91 |
-
}
|
| 92 |
-
.stat-card {
|
| 93 |
-
background: rgba(255, 255, 255, 0.05);
|
| 94 |
-
border-radius: 8px;
|
| 95 |
-
padding: 15px;
|
| 96 |
-
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 97 |
-
}
|
| 98 |
-
.stat-label {
|
| 99 |
-
font-size: 12px;
|
| 100 |
-
color: #888;
|
| 101 |
-
margin-bottom: 5px;
|
| 102 |
-
text-transform: uppercase;
|
| 103 |
-
letter-spacing: 1px;
|
| 104 |
-
}
|
| 105 |
-
.stat-value {
|
| 106 |
-
font-size: 24px;
|
| 107 |
-
font-weight: bold;
|
| 108 |
-
color: #00d4ff;
|
| 109 |
-
}
|
| 110 |
-
.status {
|
| 111 |
-
text-align: center;
|
| 112 |
-
padding: 10px;
|
| 113 |
-
border-radius: 5px;
|
| 114 |
-
margin-bottom: 15px;
|
| 115 |
-
font-weight: bold;
|
| 116 |
-
}
|
| 117 |
-
.status.connected {
|
| 118 |
-
background: rgba(0, 212, 255, 0.2);
|
| 119 |
-
color: #00d4ff;
|
| 120 |
-
border: 1px solid #00d4ff;
|
| 121 |
-
}
|
| 122 |
-
.status.disconnected {
|
| 123 |
-
background: rgba(255, 71, 87, 0.2);
|
| 124 |
-
color: #ff4757;
|
| 125 |
-
border: 1px solid #ff4757;
|
| 126 |
-
}
|
| 127 |
-
.status.connecting {
|
| 128 |
-
background: rgba(255, 200, 0, 0.2);
|
| 129 |
-
color: #ffc800;
|
| 130 |
-
border: 1px solid #ffc800;
|
| 131 |
-
}
|
| 132 |
-
.info {
|
| 133 |
-
background: rgba(255, 200, 0, 0.1);
|
| 134 |
-
border-left: 4px solid #ffc800;
|
| 135 |
-
padding: 10px 15px;
|
| 136 |
-
margin-top: 20px;
|
| 137 |
-
border-radius: 5px;
|
| 138 |
-
}
|
| 139 |
-
.info p {
|
| 140 |
-
font-size: 14px;
|
| 141 |
-
color: #ffc800;
|
| 142 |
-
line-height: 1.6;
|
| 143 |
-
}
|
| 144 |
-
.timestamp-display {
|
| 145 |
-
font-family: 'Courier New', monospace;
|
| 146 |
-
font-size: 14px;
|
| 147 |
-
color: #0f0;
|
| 148 |
-
margin-top: 10px;
|
| 149 |
-
padding: 10px;
|
| 150 |
-
background: #000;
|
| 151 |
-
border-radius: 5px;
|
| 152 |
-
text-align: center;
|
| 153 |
-
}
|
| 154 |
-
</style>
|
| 155 |
-
</head>
|
| 156 |
-
<body>
|
| 157 |
-
<h1>🎬 WebRTC Latency Test</h1>
|
| 158 |
-
|
| 159 |
-
<div class="container">
|
| 160 |
-
<div id="status" class="status disconnected">Desconectado</div>
|
| 161 |
-
|
| 162 |
-
<div class="video-container">
|
| 163 |
-
<video id="video" autoplay playsinline muted></video>
|
| 164 |
-
<div class="timestamp-display" id="timestampDisplay">
|
| 165 |
-
Waiting for video stream...
|
| 166 |
-
</div>
|
| 167 |
-
</div>
|
| 168 |
-
|
| 169 |
-
<div class="controls">
|
| 170 |
-
<button id="connectBtn" class="btn-connect" onclick="connect()">Conectar</button>
|
| 171 |
-
<button id="disconnectBtn" class="btn-disconnect" onclick="disconnect()" disabled>Desconectar</button>
|
| 172 |
-
</div>
|
| 173 |
-
|
| 174 |
-
<div class="stats">
|
| 175 |
-
<div class="stat-card">
|
| 176 |
-
<div class="stat-label">FPS Recebidos</div>
|
| 177 |
-
<div class="stat-value" id="fps">0</div>
|
| 178 |
-
</div>
|
| 179 |
-
<div class="stat-card">
|
| 180 |
-
<div class="stat-label">Frames Totais</div>
|
| 181 |
-
<div class="stat-value" id="totalFrames">0</div>
|
| 182 |
-
</div>
|
| 183 |
-
<div class="stat-card">
|
| 184 |
-
<div class="stat-label">Tempo Decorrido</div>
|
| 185 |
-
<div class="stat-value" id="elapsed">0s</div>
|
| 186 |
-
</div>
|
| 187 |
-
<div class="stat-card">
|
| 188 |
-
<div class="stat-label">Latência Estimada</div>
|
| 189 |
-
<div class="stat-value" id="latency">--</div>
|
| 190 |
-
</div>
|
| 191 |
-
</div>
|
| 192 |
-
|
| 193 |
-
<div class="info">
|
| 194 |
-
<p><strong>Como medir a latência:</strong></p>
|
| 195 |
-
<p>1. Conecte ao servidor WebRTC</p>
|
| 196 |
-
<p>2. Observe o timestamp no vídeo (Time: HH:MM:SS.mmm)</p>
|
| 197 |
-
<p>3. Compare com o horário local exibido abaixo</p>
|
| 198 |
-
<p>4. Latência ≈ Hora Local - Timestamp no Vídeo</p>
|
| 199 |
-
<p><strong>Latência ideal para conversação: 100-300ms</strong></p>
|
| 200 |
-
</div>
|
| 201 |
-
</div>
|
| 202 |
-
|
| 203 |
-
<script>
|
| 204 |
-
let pc = null;
|
| 205 |
-
let videoTrack = null;
|
| 206 |
-
let frameCount = 0;
|
| 207 |
-
let startTime = null;
|
| 208 |
-
let connectionStartTime = null;
|
| 209 |
-
|
| 210 |
-
async function connect() {
|
| 211 |
-
const statusDiv = document.getElementById('status');
|
| 212 |
-
const connectBtn = document.getElementById('connectBtn');
|
| 213 |
-
const disconnectBtn = document.getElementById('disconnectBtn');
|
| 214 |
-
|
| 215 |
-
statusDiv.className = 'status connecting';
|
| 216 |
-
statusDiv.textContent = 'Conectando...';
|
| 217 |
-
connectBtn.disabled = true;
|
| 218 |
-
|
| 219 |
-
try {
|
| 220 |
-
// Criar RTCPeerConnection
|
| 221 |
-
pc = new RTCPeerConnection({
|
| 222 |
-
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
| 223 |
-
});
|
| 224 |
-
|
| 225 |
-
// Lidar com incoming track
|
| 226 |
-
pc.ontrack = (event) => {
|
| 227 |
-
console.log('Track received:', event.track.kind);
|
| 228 |
-
if (event.track.kind === 'video') {
|
| 229 |
-
videoTrack = event.track;
|
| 230 |
-
const videoElement = document.getElementById('video');
|
| 231 |
-
videoElement.srcObject = event.streams[0];
|
| 232 |
-
startMeasurement();
|
| 233 |
-
}
|
| 234 |
-
};
|
| 235 |
-
|
| 236 |
-
pc.onconnectionstatechange = () => {
|
| 237 |
-
console.log('Connection state:', pc.connectionState);
|
| 238 |
-
if (pc.connectionState === 'connected') {
|
| 239 |
-
statusDiv.className = 'status connected';
|
| 240 |
-
statusDiv.textContent = 'Conectado via WebRTC';
|
| 241 |
-
disconnectBtn.disabled = false;
|
| 242 |
-
connectionStartTime = Date.now();
|
| 243 |
-
} else if (pc.connectionState === 'disconnected' ||
|
| 244 |
-
pc.connectionState === 'failed') {
|
| 245 |
-
statusDiv.className = 'status disconnected';
|
| 246 |
-
statusDiv.textContent = 'Desconectado';
|
| 247 |
-
connectBtn.disabled = false;
|
| 248 |
-
disconnectBtn.disabled = true;
|
| 249 |
-
}
|
| 250 |
-
};
|
| 251 |
-
|
| 252 |
-
// Criar offer
|
| 253 |
-
const offer = await pc.createOffer({ offerToReceiveVideo: true });
|
| 254 |
-
await pc.setLocalDescription(offer);
|
| 255 |
-
|
| 256 |
-
// Aguardar ICE gathering
|
| 257 |
-
await new Promise(resolve => {
|
| 258 |
-
if (pc.iceGatheringState === 'complete') {
|
| 259 |
-
resolve();
|
| 260 |
-
} else {
|
| 261 |
-
pc.onicegatheringstatechange = () => {
|
| 262 |
-
if (pc.iceGatheringState === 'complete') {
|
| 263 |
-
resolve();
|
| 264 |
-
}
|
| 265 |
-
};
|
| 266 |
-
}
|
| 267 |
-
});
|
| 268 |
-
|
| 269 |
-
// Enviar offer ao servidor
|
| 270 |
-
const response = await fetch('/offer', {
|
| 271 |
-
method: 'POST',
|
| 272 |
-
headers: { 'Content-Type': 'application/json' },
|
| 273 |
-
body: JSON.stringify({ sdp: pc.localDescription.sdp })
|
| 274 |
-
});
|
| 275 |
-
|
| 276 |
-
if (!response.ok) {
|
| 277 |
-
throw new Error('Falha ao conectar ao servidor');
|
| 278 |
-
}
|
| 279 |
-
|
| 280 |
-
const answer = await response.json();
|
| 281 |
-
|
| 282 |
-
// Configurar remote description
|
| 283 |
-
await pc.setRemoteDescription(new RTCSessionDescription({
|
| 284 |
-
type: answer.type,
|
| 285 |
-
sdp: answer.sdp
|
| 286 |
-
}));
|
| 287 |
-
|
| 288 |
-
console.log('WebRTC connection established');
|
| 289 |
-
|
| 290 |
-
} catch (error) {
|
| 291 |
-
console.error('Connection error:', error);
|
| 292 |
-
statusDiv.className = 'status disconnected';
|
| 293 |
-
statusDiv.textContent = 'Erro: ' + error.message;
|
| 294 |
-
connectBtn.disabled = false;
|
| 295 |
-
disconnectBtn.disabled = true;
|
| 296 |
-
}
|
| 297 |
-
}
|
| 298 |
-
|
| 299 |
-
async function disconnect() {
|
| 300 |
-
if (pc) {
|
| 301 |
-
pc.close();
|
| 302 |
-
pc = null;
|
| 303 |
-
}
|
| 304 |
-
|
| 305 |
-
const videoElement = document.getElementById('video');
|
| 306 |
-
videoElement.srcObject = null;
|
| 307 |
-
|
| 308 |
-
document.getElementById('status').className = 'status disconnected';
|
| 309 |
-
document.getElementById('status').textContent = 'Desconectado';
|
| 310 |
-
document.getElementById('connectBtn').disabled = false;
|
| 311 |
-
document.getElementById('disconnectBtn').disabled = true;
|
| 312 |
-
|
| 313 |
-
stopMeasurement();
|
| 314 |
-
}
|
| 315 |
-
|
| 316 |
-
function startMeasurement() {
|
| 317 |
-
frameCount = 0;
|
| 318 |
-
startTime = Date.now();
|
| 319 |
-
updateDisplay();
|
| 320 |
-
|
| 321 |
-
setInterval(updateDisplay, 1000);
|
| 322 |
-
}
|
| 323 |
-
|
| 324 |
-
function stopMeasurement() {
|
| 325 |
-
document.getElementById('timestampDisplay').textContent = 'Waiting for video stream...';
|
| 326 |
-
}
|
| 327 |
-
|
| 328 |
-
function updateDisplay() {
|
| 329 |
-
if (!startTime) return;
|
| 330 |
-
|
| 331 |
-
const elapsed = (Date.now() - startTime) / 1000;
|
| 332 |
-
const currentTime = new Date();
|
| 333 |
-
const timestampText = currentTime.toLocaleTimeString() + '.' +
|
| 334 |
-
currentTime.getMilliseconds().toString().padStart(3, '0');
|
| 335 |
-
|
| 336 |
-
const fps = (frameCount / elapsed).toFixed(1);
|
| 337 |
-
|
| 338 |
-
document.getElementById('fps').textContent = fps;
|
| 339 |
-
document.getElementById('totalFrames').textContent = frameCount;
|
| 340 |
-
document.getElementById('elapsed').textContent = elapsed.toFixed(1) + 's';
|
| 341 |
-
|
| 342 |
-
document.getElementById('timestampDisplay').textContent =
|
| 343 |
-
`Local Time: ${timestampText} | Frame: ${frameCount} | FPS: ${fps}`;
|
| 344 |
-
|
| 345 |
-
// Estimativa de latência (baseada em análise visual)
|
| 346 |
-
// Em produção, você faria OCR para ler o timestamp do vídeo
|
| 347 |
-
if (connectionStartTime) {
|
| 348 |
-
const connectionTime = (Date.now() - connectionStartTime) / 1000;
|
| 349 |
-
document.getElementById('latency').textContent = 'Compare manualmente';
|
| 350 |
-
}
|
| 351 |
-
}
|
| 352 |
-
|
| 353 |
-
// Atualizar contador de frames ( aproximado via polling do vídeo)
|
| 354 |
-
setInterval(() => {
|
| 355 |
-
const video = document.getElementById('video');
|
| 356 |
-
if (!video.paused && !video.ended && video.readyState >= 2) {
|
| 357 |
-
frameCount++;
|
| 358 |
-
}
|
| 359 |
-
}, 1000 / 30); // Aprox 30 FPS
|
| 360 |
-
|
| 361 |
-
window.onload = function() {
|
| 362 |
-
console.log('WebRTC Latency Test loaded');
|
| 363 |
-
};
|
| 364 |
-
</script>
|
| 365 |
-
</body>
|
| 366 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/server/webrtc-server.py
DELETED
|
@@ -1,130 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
WebRTC Server Simples para Teste de Latência
|
| 4 |
-
Versão simplificada sem borda animada
|
| 5 |
-
"""
|
| 6 |
-
import asyncio
|
| 7 |
-
import logging
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
import numpy as np
|
| 10 |
-
import cv2
|
| 11 |
-
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
|
| 12 |
-
from av import VideoFrame
|
| 13 |
-
from aiohttp import web
|
| 14 |
-
|
| 15 |
-
logging.basicConfig(level=logging.INFO)
|
| 16 |
-
logger = logging.getLogger(__name__)
|
| 17 |
-
|
| 18 |
-
class VideoGeneratorTrack(VideoStreamTrack):
|
| 19 |
-
"""Track que gera vídeo com timestamps"""
|
| 20 |
-
|
| 21 |
-
def __init__(self):
|
| 22 |
-
super().__init__()
|
| 23 |
-
self.frame_count = 0
|
| 24 |
-
self.start_time = datetime.now()
|
| 25 |
-
self.fps = 30
|
| 26 |
-
|
| 27 |
-
async def recv(self):
|
| 28 |
-
pts, time_base = await self.next_timestamp()
|
| 29 |
-
|
| 30 |
-
# Criar frame
|
| 31 |
-
frame = np.zeros((480, 640, 3), dtype=np.uint8)
|
| 32 |
-
|
| 33 |
-
# Fundo azul escuro
|
| 34 |
-
frame[:, :] = [20, 30, 50]
|
| 35 |
-
|
| 36 |
-
# Timestamp atual
|
| 37 |
-
now = datetime.now()
|
| 38 |
-
timestamp = now.strftime("%H:%M:%S.%f")[:-3]
|
| 39 |
-
elapsed = (now - self.start_time).total_seconds()
|
| 40 |
-
|
| 41 |
-
# Adicionar textos
|
| 42 |
-
cv2.putText(frame, "WebRTC Latency Test", (200, 100),
|
| 43 |
-
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 3)
|
| 44 |
-
|
| 45 |
-
cv2.putText(frame, f"Time: {timestamp}", (50, 220),
|
| 46 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
|
| 47 |
-
|
| 48 |
-
cv2.putText(frame, f"Frame: #{self.frame_count}", (50, 270),
|
| 49 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 200, 0), 2)
|
| 50 |
-
|
| 51 |
-
cv2.putText(frame, f"FPS: {self.fps}", (50, 320),
|
| 52 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 200, 0), 2)
|
| 53 |
-
|
| 54 |
-
cv2.putText(frame, f"Elapsed: {elapsed:.2f}s", (50, 370),
|
| 55 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 200, 255), 2)
|
| 56 |
-
|
| 57 |
-
# Borda azul simples (sem animação)
|
| 58 |
-
cv2.rectangle(frame, (10, 10), (630, 470), (0, 212, 255), 4)
|
| 59 |
-
|
| 60 |
-
self.frame_count += 1
|
| 61 |
-
|
| 62 |
-
# Converter para VideoFrame
|
| 63 |
-
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 64 |
-
video_frame = VideoFrame.from_ndarray(frame_rgb, format="rgb24")
|
| 65 |
-
video_frame.pts = pts
|
| 66 |
-
video_frame.time_base = time_base
|
| 67 |
-
|
| 68 |
-
return video_frame
|
| 69 |
-
|
| 70 |
-
class WebRTCServer:
|
| 71 |
-
def __init__(self):
|
| 72 |
-
self.pc = RTCPeerConnection()
|
| 73 |
-
self.video_track = VideoGeneratorTrack()
|
| 74 |
-
self.pc.addTrack(self.video_track)
|
| 75 |
-
|
| 76 |
-
async def handle_offer(self, offer_sdp):
|
| 77 |
-
offer = RTCSessionDescription(sdp=offer_sdp, type="offer")
|
| 78 |
-
await self.pc.setRemoteDescription(offer)
|
| 79 |
-
|
| 80 |
-
answer = await self.pc.createAnswer()
|
| 81 |
-
await self.pc.setLocalDescription(answer)
|
| 82 |
-
|
| 83 |
-
return {
|
| 84 |
-
"sdp": self.pc.localDescription.sdp,
|
| 85 |
-
"type": self.pc.localDescription.type
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
web_rtc_server = None
|
| 89 |
-
|
| 90 |
-
async def offer(request):
|
| 91 |
-
params = await request.json()
|
| 92 |
-
offer_sdp = params.get("sdp")
|
| 93 |
-
|
| 94 |
-
if not offer_sdp:
|
| 95 |
-
return web.json_response({"error": "No SDP provided"}, status=400)
|
| 96 |
-
|
| 97 |
-
global web_rtc_server
|
| 98 |
-
web_rtc_server = WebRTCServer()
|
| 99 |
-
|
| 100 |
-
answer = await web_rtc_server.handle_offer(offer_sdp)
|
| 101 |
-
logger.info("Received WebRTC offer, sent answer")
|
| 102 |
-
|
| 103 |
-
return web.json_response(answer)
|
| 104 |
-
|
| 105 |
-
async def index(request):
|
| 106 |
-
with open('/root/livekit-poc/webrtc-client.html') as f:
|
| 107 |
-
return web.Response(text=f.read(), content_type='text/html')
|
| 108 |
-
|
| 109 |
-
async def main():
|
| 110 |
-
app = web.Application()
|
| 111 |
-
app.router.add_get('/', index)
|
| 112 |
-
app.router.add_post('/offer', offer)
|
| 113 |
-
|
| 114 |
-
runner = web.AppRunner(app)
|
| 115 |
-
await runner.setup()
|
| 116 |
-
|
| 117 |
-
site = web.TCPSite(runner, '0.0.0.0', 9000)
|
| 118 |
-
await site.start()
|
| 119 |
-
|
| 120 |
-
logger.info("WebRTC Server rodando em http://0.0.0.0:9000")
|
| 121 |
-
logger.info("Abra o navegador e acesse: http://38.117.87.48:9000")
|
| 122 |
-
|
| 123 |
-
# Manter rodando
|
| 124 |
-
await asyncio.Event().wait()
|
| 125 |
-
|
| 126 |
-
if __name__ == "__main__":
|
| 127 |
-
try:
|
| 128 |
-
asyncio.run(main())
|
| 129 |
-
except KeyboardInterrupt:
|
| 130 |
-
logger.info("Servidor interrompido")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webrtc-latency-test/tests/test_latency_playwright.py
DELETED
|
@@ -1,259 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Teste Automatizado de Latência com Playwright
|
| 4 |
-
Testa a latência do servidor WebRTC com múltiplos usuários simultâneos
|
| 5 |
-
"""
|
| 6 |
-
import asyncio
|
| 7 |
-
import time
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
from playwright.async_api import async_playwright
|
| 10 |
-
import json
|
| 11 |
-
|
| 12 |
-
SERVER_URL = "http://38.117.87.48:9000"
|
| 13 |
-
|
| 14 |
-
async def test_single_user(user_id):
|
| 15 |
-
"""Testa latência com um único usuário"""
|
| 16 |
-
results = {
|
| 17 |
-
"user_id": user_id,
|
| 18 |
-
"connected": False,
|
| 19 |
-
"connection_time": 0,
|
| 20 |
-
"latency_measurements": [],
|
| 21 |
-
"fps": 0,
|
| 22 |
-
"errors": []
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
async with async_playwright() as p:
|
| 26 |
-
try:
|
| 27 |
-
# Iniciar browser headless
|
| 28 |
-
browser = await p.chromium.launch(headless=True)
|
| 29 |
-
context = await browser.new_context()
|
| 30 |
-
page = await context.new_page()
|
| 31 |
-
|
| 32 |
-
# Navegar para a página
|
| 33 |
-
start_time = time.time()
|
| 34 |
-
await page.goto(SERVER_URL)
|
| 35 |
-
navigation_time = (time.time() - start_time) * 1000
|
| 36 |
-
results["navigation_time"] = navigation_time
|
| 37 |
-
|
| 38 |
-
# Clicar no botão de conectar
|
| 39 |
-
connect_start = time.time()
|
| 40 |
-
await page.click("#connectBtn")
|
| 41 |
-
|
| 42 |
-
# Aguardar conexão
|
| 43 |
-
try:
|
| 44 |
-
await page.wait_for_selector('.status.connected', timeout=10000)
|
| 45 |
-
connection_time = (time.time() - connect_start) * 1000
|
| 46 |
-
results["connection_time"] = connection_time
|
| 47 |
-
results["connected"] = True
|
| 48 |
-
print(f"Usuário {user_id}: Conectado em {connection_time:.2f}ms")
|
| 49 |
-
except Exception as e:
|
| 50 |
-
results["errors"].append(f"Erro ao conectar: {str(e)}")
|
| 51 |
-
await browser.close()
|
| 52 |
-
return results
|
| 53 |
-
|
| 54 |
-
# Aguardar estabilização do vídeo
|
| 55 |
-
await asyncio.sleep(3)
|
| 56 |
-
|
| 57 |
-
# Capturar 10 medições de latência
|
| 58 |
-
for i in range(10):
|
| 59 |
-
try:
|
| 60 |
-
# Capturar timestamp local
|
| 61 |
-
local_time = datetime.now()
|
| 62 |
-
|
| 63 |
-
# Capturar timestamp do vídeo (do elemento de texto)
|
| 64 |
-
timestamp_text = await page.locator("#timestampDisplay").text_content()
|
| 65 |
-
|
| 66 |
-
# Extrair timestamp do vídeo (formato: "Local Time: HH:MM:SS.mmm | Frame: X | FPS: Y")
|
| 67 |
-
if "Local Time:" in timestamp_text:
|
| 68 |
-
parts = timestamp_text.split("Local Time: ")[1].split(" |")[0]
|
| 69 |
-
video_timestamp = datetime.strptime(parts, "%H:%M:%S.%f")
|
| 70 |
-
|
| 71 |
-
# Calcular diferença (se mesmo minuto, caso contrário ignora)
|
| 72 |
-
if local_time.hour == video_timestamp.hour and \
|
| 73 |
-
local_time.minute == video_timestamp.minute:
|
| 74 |
-
diff_ms = (local_time - video_timestamp).total_seconds() * 1000
|
| 75 |
-
results["latency_measurements"].append(diff_ms)
|
| 76 |
-
|
| 77 |
-
await asyncio.sleep(0.5) # Esperar 500ms entre medições
|
| 78 |
-
except Exception as e:
|
| 79 |
-
results["errors"].append(f"Erro na medição {i}: {str(e)}")
|
| 80 |
-
|
| 81 |
-
# Capturar FPS final
|
| 82 |
-
try:
|
| 83 |
-
fps_text = await page.locator("#fps").text_content()
|
| 84 |
-
results["fps"] = float(fps_text) if fps_text else 0
|
| 85 |
-
except:
|
| 86 |
-
pass
|
| 87 |
-
|
| 88 |
-
# Fechar browser
|
| 89 |
-
await browser.close()
|
| 90 |
-
|
| 91 |
-
except Exception as e:
|
| 92 |
-
results["errors"].append(f"Erro geral: {str(e)}")
|
| 93 |
-
|
| 94 |
-
# Calcular estatísticas de latência
|
| 95 |
-
if results["latency_measurements"]:
|
| 96 |
-
results["avg_latency"] = sum(results["latency_measurements"]) / len(results["latency_measurements"])
|
| 97 |
-
results["min_latency"] = min(results["latency_measurements"])
|
| 98 |
-
results["max_latency"] = max(results["latency_measurements"])
|
| 99 |
-
results["num_measurements"] = len(results["latency_measurements"])
|
| 100 |
-
|
| 101 |
-
return results
|
| 102 |
-
|
| 103 |
-
async def test_multiple_users(num_users, concurrent=True):
|
| 104 |
-
"""Testa latência com múltiplos usuários"""
|
| 105 |
-
print(f"\n🧪 Iniciando teste com {num_users} usuário(s)...")
|
| 106 |
-
print(f"Modo: {'Simultâneo' if concurrent else 'Sequencial'}")
|
| 107 |
-
print(f"Servidor: {SERVER_URL}\n")
|
| 108 |
-
|
| 109 |
-
start_time = time.time()
|
| 110 |
-
|
| 111 |
-
if concurrent:
|
| 112 |
-
# Executar todos os testes simultaneamente
|
| 113 |
-
tasks = [test_single_user(i) for i in range(num_users)]
|
| 114 |
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
| 115 |
-
else:
|
| 116 |
-
# Executar testes sequencialmente
|
| 117 |
-
results = []
|
| 118 |
-
for i in range(num_users):
|
| 119 |
-
result = await test_single_user(i)
|
| 120 |
-
results.append(result)
|
| 121 |
-
|
| 122 |
-
total_time = time.time() - start_time
|
| 123 |
-
|
| 124 |
-
# Processar resultados
|
| 125 |
-
all_latencies = []
|
| 126 |
-
connection_times = []
|
| 127 |
-
fps_values = []
|
| 128 |
-
successful = 0
|
| 129 |
-
|
| 130 |
-
for result in results:
|
| 131 |
-
if isinstance(result, Exception):
|
| 132 |
-
print(f"❌ Usuário com erro: {result}")
|
| 133 |
-
continue
|
| 134 |
-
|
| 135 |
-
if result["connected"]:
|
| 136 |
-
successful += 1
|
| 137 |
-
if "latency_measurements" in result and result["latency_measurements"]:
|
| 138 |
-
all_latencies.extend(result["latency_measurements"])
|
| 139 |
-
if "connection_time" in result:
|
| 140 |
-
connection_times.append(result["connection_time"])
|
| 141 |
-
if "fps" in result and result["fps"] > 0:
|
| 142 |
-
fps_values.append(result["fps"])
|
| 143 |
-
|
| 144 |
-
latency_str = f"{result['avg_latency']:.2f}ms" if "avg_latency" in result else "N/A"
|
| 145 |
-
print(f"✅ Usuário {result['user_id']}: Latência={latency_str}, FPS={result['fps']:.1f}")
|
| 146 |
-
else:
|
| 147 |
-
print(f"❌ Usuário {result['user_id']}: Falha na conexão")
|
| 148 |
-
if result["errors"]:
|
| 149 |
-
print(f" Erros: {result['errors']}")
|
| 150 |
-
|
| 151 |
-
# Estatísticas globais
|
| 152 |
-
print("\n" + "="*60)
|
| 153 |
-
print("📊 RESULTADOS AGREGADOS")
|
| 154 |
-
print("="*60)
|
| 155 |
-
print(f"Usuários testados: {num_users}")
|
| 156 |
-
print(f"Conexões bem-sucedidas: {successful}/{num_users}")
|
| 157 |
-
print(f"Taxa de sucesso: {(successful/num_users)*100:.1f}%")
|
| 158 |
-
print(f"Tempo total do teste: {total_time:.2f}s")
|
| 159 |
-
|
| 160 |
-
if all_latencies:
|
| 161 |
-
print(f"\n📈 LATÊNCIA")
|
| 162 |
-
print(f" Média: {sum(all_latencies)/len(all_latencies):.2f}ms")
|
| 163 |
-
print(f" Mínima: {min(all_latencies):.2f}ms")
|
| 164 |
-
print(f" Máxima: {max(all_latencies):.2f}ms")
|
| 165 |
-
print(f" Medidas totais: {len(all_latencies)}")
|
| 166 |
-
|
| 167 |
-
if connection_times:
|
| 168 |
-
print(f"\n⏱️ TEMPO DE CONEXÃO")
|
| 169 |
-
print(f" Média: {sum(connection_times)/len(connection_times):.2f}ms")
|
| 170 |
-
print(f" Mínima: {min(connection_times):.2f}ms")
|
| 171 |
-
print(f" Máxima: {max(connection_times):.2f}ms")
|
| 172 |
-
|
| 173 |
-
if fps_values:
|
| 174 |
-
print(f"\n🎬 FPS")
|
| 175 |
-
print(f" Média: {sum(fps_values)/len(fps_values):.1f}")
|
| 176 |
-
print(f" Mínimo: {min(fps_values):.1f}")
|
| 177 |
-
print(f" Máximo: {max(fps_values):.1f}")
|
| 178 |
-
|
| 179 |
-
# Classificação da latência
|
| 180 |
-
if all_latencies:
|
| 181 |
-
avg_latency = sum(all_latencies)/len(all_latencies)
|
| 182 |
-
print(f"\n🎯 CLASSIFICAÇÃO")
|
| 183 |
-
if avg_latency < 100:
|
| 184 |
-
print(f" ✅ EXCELENTE ({avg_latency:.2f}ms) - Quase imperceptível")
|
| 185 |
-
elif avg_latency < 300:
|
| 186 |
-
print(f" ✅ BOM ({avg_latency:.2f}ms) - Ideal para conversação")
|
| 187 |
-
elif avg_latency < 500:
|
| 188 |
-
print(f" ⚠️ ACEITÁVEL ({avg_latency:.2f}ms) - Pequenos delays possíveis")
|
| 189 |
-
else:
|
| 190 |
-
print(f" ❌ RUIM ({avg_latency:.2f}ms) - Latência muito alta")
|
| 191 |
-
|
| 192 |
-
print("="*60)
|
| 193 |
-
|
| 194 |
-
# Salvar resultados em JSON
|
| 195 |
-
output = {
|
| 196 |
-
"test_config": {
|
| 197 |
-
"num_users": num_users,
|
| 198 |
-
"concurrent": concurrent,
|
| 199 |
-
"server_url": SERVER_URL,
|
| 200 |
-
"test_date": datetime.now().isoformat()
|
| 201 |
-
},
|
| 202 |
-
"results": {
|
| 203 |
-
"successful_connections": successful,
|
| 204 |
-
"success_rate": (successful/num_users)*100 if num_users > 0 else 0,
|
| 205 |
-
"total_test_time": total_time,
|
| 206 |
-
"latency": {
|
| 207 |
-
"all_measurements": all_latencies,
|
| 208 |
-
"avg": sum(all_latencies)/len(all_latencies) if all_latencies else None,
|
| 209 |
-
"min": min(all_latencies) if all_latencies else None,
|
| 210 |
-
"max": max(all_latencies) if all_latencies else None,
|
| 211 |
-
"num_measurements": len(all_latencies)
|
| 212 |
-
},
|
| 213 |
-
"connection_time": {
|
| 214 |
-
"all": connection_times,
|
| 215 |
-
"avg": sum(connection_times)/len(connection_times) if connection_times else None,
|
| 216 |
-
"min": min(connection_times) if connection_times else None,
|
| 217 |
-
"max": max(connection_times) if connection_times else None
|
| 218 |
-
},
|
| 219 |
-
"fps": {
|
| 220 |
-
"all": fps_values,
|
| 221 |
-
"avg": sum(fps_values)/len(fps_values) if fps_values else None,
|
| 222 |
-
"min": min(fps_values) if fps_values else None,
|
| 223 |
-
"max": max(fps_values) if fps_values else None
|
| 224 |
-
}
|
| 225 |
-
},
|
| 226 |
-
"user_results": results
|
| 227 |
-
}
|
| 228 |
-
|
| 229 |
-
# Salvar em arquivo
|
| 230 |
-
filename = f"/root/livekit-poc/latency_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
| 231 |
-
with open(filename, 'w') as f:
|
| 232 |
-
json.dump(output, f, indent=2)
|
| 233 |
-
print(f"\n💾 Resultados salvos em: {filename}")
|
| 234 |
-
|
| 235 |
-
return output
|
| 236 |
-
|
| 237 |
-
async def main():
|
| 238 |
-
"""Função principal"""
|
| 239 |
-
import sys
|
| 240 |
-
|
| 241 |
-
# Parâmetros padrão
|
| 242 |
-
num_users = 1
|
| 243 |
-
concurrent = True
|
| 244 |
-
|
| 245 |
-
# Ler argumentos da linha de comando
|
| 246 |
-
if len(sys.argv) > 1:
|
| 247 |
-
num_users = int(sys.argv[1])
|
| 248 |
-
if len(sys.argv) > 2:
|
| 249 |
-
concurrent = sys.argv[2].lower() == 'true'
|
| 250 |
-
|
| 251 |
-
print("🎬 Teste Automatizado de Latência WebRTC")
|
| 252 |
-
print("="*60)
|
| 253 |
-
|
| 254 |
-
await test_multiple_users(num_users, concurrent)
|
| 255 |
-
|
| 256 |
-
print("\n✅ Teste concluído!")
|
| 257 |
-
|
| 258 |
-
if __name__ == "__main__":
|
| 259 |
-
asyncio.run(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|