Usar polling HTTP em vez de WebSocket para receber dados de detecção do backend, já que apenas a porta 8001 está exposta via ngrok.
const ws = new WebSocket(`${wsUrl}/ws/detection`);
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === "detection_update") {
addData(message.data);
}
};// Poll a cada 100ms para atualizações em tempo real
const pollDetectionData = async () => {
try {
const response = await fetch(`${backendUrl}/detection-data/${droneId}`);
if (response.ok) {
const result = await response.json();
if (result.data && result.data.total_persons !== undefined) {
console.log("Detection data received:", result.data);
addData(result.data);
}
} else if (response.status === 404) {
console.warn("Client not found on backend");
}
} catch (error) {
console.error("Error polling detection data:", error);
}
};
pollingIntervalRef.current = setInterval(pollDetectionData, 100);- ❌ Removido:
webSocketRef - ✅ Adicionado:
pollingIntervalRef
// No disconnect e error handling
if (pollingIntervalRef.current) {
clearInterval(pollingIntervalRef.current);
pollingIntervalRef.current = null;
}Erro de hidratação do React porque componentes com estado do cliente estavam sendo renderizados no servidor (SSR).
frontend/app/[drone_id]/layout.tsx
"use client"; // ← Adicionado
import RTCProvider from "./_components/rtc";
import VideoProvider from "./_components/video/provider";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<RTCProvider>
<VideoProvider>{children}</VideoProvider>
</RTCProvider>
);
}frontend/app/[drone_id]/page.tsx
"use client"; // ← Adicionado
// ...imports...
export default function Page() { // ← Removido "async"
return (
<ResizablePanelGroup ...>
{/* ... */}
</ResizablePanelGroup>
);
}┌──────────────────────────────────────────────────────┐
│ Backend (rtc_server.py) - Porta 8001 │
│ │
│ 1. ProcessedVideoTrack processa frames com YOLO │
│ 2. detection_data armazenado em memory │
│ 3. GET /detection-data/{client_id} retorna dados │
└──────────────────────────────────────────────────────┘
↓
HTTP Polling (a cada 100ms)
↓
┌──────────────────────────────────────────────────────┐
│ Frontend (RTCProvider) │
│ │
│ 1. setInterval chama fetch() a cada 100ms │
│ 2. Recebe JSON com detection data │
│ 3. addData() adiciona ao dataHistory[] │
│ 4. Graph component renderiza gráficos │
└──────────────────────────────────────────────────────┘
Response:
{
"client_id": "drone-1",
"data": {
"total_persons": 5,
"average_confidence": 0.85,
"positions": [
{
"id": 0,
"x_center": 666.5284423828125,
"y_center": 445.75885009765625,
"width": 943.052490234375,
"height": 544.9644775390625,
"confidence": 0.8326842784881592,
"normalized_x": 0.5207253456115722,
"normalized_y": 0.6191095140245225,
"normalized_width": 0.7367597579956054,
"normalized_height": 0.7568951076931424
}
],
"timestamp": 1704567890123,
"frame_number": 150,
"client_id": "drone-1"
},
"timestamp": 1704567890.123
}// Cliente conecta via WebRTC
await connect(droneId);
// WebRTC estabelecido para vídeo
// Polling HTTP iniciado para dados de detecção// A cada 100ms:
1. fetch(`http://localhost:8001/detection-data/${droneId}`)
2. Se response.ok → parse JSON
3. Se data.total_persons existe → addData(data)
4. Dados adicionados ao dataHistory[]
5. Graph component re-renderiza automaticamentedisconnect() {
// Para WebRTC
peerConnection.close();
// Para polling HTTP
clearInterval(pollingIntervalRef.current);
}- ✅ Funciona com ngrok - Apenas porta 8001 necessária
- ✅ Simples de debugar - Requisições HTTP visíveis no Network tab
- ✅ Sem problemas de WebSocket - Evita issues com proxies/firewalls
- ✅ Retry automático - fetch() pode ser facilmente retry-ado
- 🔸 Mais overhead - Requisições HTTP a cada 100ms
- 🔸 Latência ligeiramente maior - Comparado com WebSocket push
- 🔸 Mais tráfego - Headers HTTP em cada request
cd backend
python rtc_server.py# Teste manual
curl http://localhost:8001/detection-data/test-idcd frontend
npm run dev- Abrir console (F12)
- Ir para
/drone-1 - Conectar ao stream
- Ver logs:
"Detection data received:" - Ver gráficos atualizando em tempo real
- Abrir DevTools → Network
- Filtrar por
detection-data - Ver requisições a cada 100ms
- Status: 200 OK com dados JSON
// Em rtc/index.tsx, linha 221
pollingIntervalRef.current = setInterval(pollDetectionData, 100);
// ^^^
// msRecomendações:
50ms- Tempo real mais responsivo (mais tráfego)100ms- Balanceado (padrão) ✅200ms- Mais leve (menos tráfego)500ms- Economiza banda (menos real-time)
- Backend já tinha o endpoint -
/detection-data/{client_id}já existia - Dados em memória -
ProcessedVideoTrack.last_detection_dataarmazena último frame - Client ID deve existir - O cliente precisa ter uma conexão WebRTC ativa
- Erro 404 é normal - Se o cliente ainda não conectou ou desconectou
- Adicionar retry logic - Se fetch falhar, tentar novamente
- Adaptive polling - Ajustar frequência baseado em carga
- Compression - Usar gzip nas respostas HTTP
- Caching - Evitar enviar dados duplicados
- Server-Sent Events (SSE) - Alternativa ao polling mais eficiente