diff --git a/server/src/services/roomCameraService/configuration.js b/server/src/services/roomCameraService/configuration.js index 8914d3cb..b922641b 100644 --- a/server/src/services/roomCameraService/configuration.js +++ b/server/src/services/roomCameraService/configuration.js @@ -1,7 +1,7 @@ // Room-Camera Configuration // Purpose: Defines the optional named snapshot and stream camera catalog. // Scope: Contains configuration metadata only and never contacts a camera. -const { strictObject, string, boolean } = require('../../configuration/schemaHelpers'); +const { strictObject, string, boolean, integer } = require('../../configuration/schemaHelpers'); module.exports = { key: 'roomCameras', @@ -10,6 +10,7 @@ module.exports = { // initial configuration while the feature switch prevents network requests. defaultValue: { enabled: false, + streamIntervalMs: 2000, cameras: [ { id: 'lobby', @@ -29,6 +30,7 @@ module.exports = { }, schema: strictObject({ enabled: boolean({ description: 'Immediately publishes the configured room-camera catalog and enables camera snapshots and streams.' }), + streamIntervalMs: integer({ title: 'Stream interval (ms)', description: 'Interval for sending room camera images to clients. Lowering this increases bandwidth usage FAST because it sends a full image each time.', minimum: 1 }), cameras: { type: 'array', description: 'Room cameras available to the web UI and replay system.', diff --git a/server/src/services/roomCameraService/index.js b/server/src/services/roomCameraService/index.js index e7906dab..29ba80c2 100644 --- a/server/src/services/roomCameraService/index.js +++ b/server/src/services/roomCameraService/index.js @@ -8,10 +8,12 @@ const replay = require('../replayEngineV2/roomCameraReplayBuilder'); const { loadConfig, registerConfigurationHandler } = require('../../configuration'); let enabled = false; +let streamIntervalMs = 2000; const snapshotEngine = createSnapshotEngine({ getRoomCameras, roomCameraEvents }); function applyRoomCameraConfig(roomCameraConfig = {}) { enabled = Boolean(roomCameraConfig.enabled); + streamIntervalMs = roomCameraConfig.streamIntervalMs ?? 2000; // Loading an empty catalog on disable causes the snapshot engine's existing // update listener to close every stream and timer without unregistering the // stable browser gateway. @@ -19,6 +21,7 @@ function applyRoomCameraConfig(roomCameraConfig = {}) { } registerRoomCameraSocketGateway({ + getStreamIntervalMs: () => streamIntervalMs, getRoomCamera, getRoomCameras, getRoomCameraState: snapshotEngine.getRoomCameraState, diff --git a/server/src/services/roomCameraService/socketGateway.js b/server/src/services/roomCameraService/socketGateway.js index b74f076d..5178c4ac 100644 --- a/server/src/services/roomCameraService/socketGateway.js +++ b/server/src/services/roomCameraService/socketGateway.js @@ -8,7 +8,6 @@ const { isAdmin, isLockdownAdmin, getRole } = require('../roleService'); const SUBSCRIBE_LIMIT = 50; const SUBSCRIBE_WINDOW_MS = 10000; -const STREAM_INTERVAL_MS = 2000; function passesMode(socket) { const mode = getMode(); @@ -20,7 +19,7 @@ function passesMode(socket) { return true; } -function registerRoomCameraSocketGateway({ getRoomCamera, getRoomCameras, getRoomCameraState, roomCameraStreamEvents }) { +function registerRoomCameraSocketGateway({ getRoomCamera, getRoomCameras, getRoomCameraState, roomCameraStreamEvents, getStreamIntervalMs }) { const cameraSubscribers = new Map(); const socketSubscriptions = new Map(); const subscribeBuckets = new Map(); @@ -82,7 +81,7 @@ function registerRoomCameraSocketGateway({ getRoomCamera, getRoomCameras, getRoo } const lastSent = lastMap.get(id) || 0; const now = ts || Date.now(); - if (now - lastSent < STREAM_INTERVAL_MS) return; + if (now - lastSent < getStreamIntervalMs()) return; lastMap.set(id, now); sendFrame(socket, id, { ts }, buffer); });