WebSocketServer.clientsByCamera
This commit is contained in:
@@ -12,7 +12,8 @@
|
|||||||
<body>
|
<body>
|
||||||
<video id="v" controls autoplay muted></video>
|
<video id="v" controls autoplay muted></video>
|
||||||
<script>
|
<script>
|
||||||
const wsUrl = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
|
const cameraId = new URLSearchParams(location.search).get('camera') ?? 'v1';
|
||||||
|
const wsUrl = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws/${cameraId}`;
|
||||||
const player = mpegts.createPlayer(
|
const player = mpegts.createPlayer(
|
||||||
{ type: 'mpegts', isLive: true, url: wsUrl },
|
{ type: 'mpegts', isLive: true, url: wsUrl },
|
||||||
{ liveBufferLatencyChasing: true, liveBufferLatencyMaxLatency: 1.5 }
|
{ liveBufferLatencyChasing: true, liveBufferLatencyMaxLatency: 1.5 }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { log } from '../helpers/log.js'
|
import { log } from '../helpers/log.js'
|
||||||
import { lastTopicSegment } from '../helpers/topic-match.js'
|
import { lastTopicSegment } from '../helpers/topic-match.js'
|
||||||
|
import { onEdgeChunk } from '../stream/edge-chunk-relay.js'
|
||||||
import type { TopicHandler } from '../types.js'
|
import type { TopicHandler } from '../types.js'
|
||||||
|
|
||||||
const LOG_INTERVAL_MS = 10_000
|
const LOG_INTERVAL_MS = 10_000
|
||||||
@@ -9,15 +10,12 @@ const lastLogByCamera = new Map<string, number>()
|
|||||||
export const handleEdge5VideoV2: TopicHandler = async (topic, payload) => {
|
export const handleEdge5VideoV2: TopicHandler = async (topic, payload) => {
|
||||||
const cameraId = lastTopicSegment(topic)
|
const cameraId = lastTopicSegment(topic)
|
||||||
|
|
||||||
if (!cameraId) {
|
|
||||||
console.warn('edge5.video.v2.invalid_topic', { topic })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const lastLog = lastLogByCamera.get(cameraId) ?? 0
|
const lastLog = lastLogByCamera.get(cameraId) ?? 0
|
||||||
if (now - lastLog >= LOG_INTERVAL_MS) {
|
if (now - lastLog >= LOG_INTERVAL_MS) {
|
||||||
log(`edge5.video.v2 [${cameraId}] ↑ ${payload.length}B`)
|
log(`edge5.video.v2 [${cameraId}] ↑ ${payload.length}B`)
|
||||||
lastLogByCamera.set(cameraId, now)
|
lastLogByCamera.set(cameraId, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEdgeChunk(cameraId, payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ export const handleEdge5Video: TopicHandler = async (_topic, payload) => {
|
|||||||
log(`edge5.video ↑ ${payload.length}B`)
|
log(`edge5.video ↑ ${payload.length}B`)
|
||||||
lastLog = now
|
lastLog = now
|
||||||
}
|
}
|
||||||
onEdgeChunk(payload)
|
onEdgeChunk('v1', payload);
|
||||||
}
|
}
|
||||||
@@ -2,30 +2,40 @@ import type { IncomingMessage } from 'http'
|
|||||||
import { WebSocket, WebSocketServer } from 'ws'
|
import { WebSocket, WebSocketServer } from 'ws'
|
||||||
|
|
||||||
import { log } from '../helpers/log.js'
|
import { log } from '../helpers/log.js'
|
||||||
|
import { lastTopicSegment } from '../helpers/topic-match.js'
|
||||||
|
|
||||||
const wsPort = Number(process.env.WS_PORT)
|
const wsPort = Number(process.env.WS_PORT)
|
||||||
|
|
||||||
const wss = new WebSocketServer({ port: wsPort, perMessageDeflate: false })
|
const wss = new WebSocketServer({ port: wsPort, perMessageDeflate: false })
|
||||||
const clients = new Set<WebSocket>()
|
|
||||||
|
const clientsByCamera = new Map<string, Set<WebSocket>>()
|
||||||
|
|
||||||
|
function getClients(cameraId: string): Set<WebSocket> {
|
||||||
|
let clients = clientsByCamera.get(cameraId)
|
||||||
|
if (!clients) {
|
||||||
|
clients = new Set()
|
||||||
|
clientsByCamera.set(cameraId, clients)
|
||||||
|
}
|
||||||
|
return clients
|
||||||
|
}
|
||||||
|
|
||||||
wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {
|
wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {
|
||||||
clients.add(ws)
|
log(`ws.connection ${req.url}`);
|
||||||
log('ws.client_connected', {
|
const cameraId = req.url ? lastTopicSegment(req.url) : 'v1';
|
||||||
clients: clients.size,
|
|
||||||
ip: req.socket.remoteAddress,
|
const clients = getClients(cameraId);
|
||||||
})
|
clients.add(ws);
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
clients.delete(ws)
|
clients.delete(ws)
|
||||||
log('ws.client_closed', { clients: clients.size })
|
if (clients.size === 0) clientsByCamera.delete(cameraId)
|
||||||
})
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
wss.on('listening', () => {
|
export function onEdgeChunk(cameraId: string, chunk: Buffer): void {
|
||||||
log('ws.server_started', { port: wsPort })
|
const clients = clientsByCamera.get(cameraId)
|
||||||
})
|
if (!clients) return
|
||||||
|
|
||||||
export function onEdgeChunk(chunk: Buffer): void {
|
|
||||||
for (const client of clients) {
|
for (const client of clients) {
|
||||||
if (client.readyState === WebSocket.OPEN) {
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
client.send(chunk)
|
client.send(chunk)
|
||||||
|
|||||||
Reference in New Issue
Block a user