marcosremar2 commited on
Commit
ef89fa8
·
1 Parent(s): 7c26f37

chore: Clean up webrtc-latency-test directory - remove POC duplications

Browse files

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