Skip to content

Streams overview

The streams tile is a unified browser for every live video source the mission has. It has three tabs and a single selection event that the rest of the console listens for.

The three tabs

Every peer-to-peer video track currently published into the mission room — handheld Android drones, DJI dock aircraft, bodycams, phone cameras. Rendered as a live- thumbnail grid.

VMS

CCTV and IP cameras attached to this mission — HLS, MJPEG, MP4 direct, or embed. Includes an add-camera form, per-camera test, PTZ controls, and map-integration metadata.

DOCKS

DJI docks currently assigned to this mission, scoped to your org. Status, battery, activity text, and a one-click open.

See per-tab pages for details:

What the tile reads from the room

The tile subscribes to WebRtcService.status$ and reflects status.video.streams[]. Each entry carries:

  • peerId — the LiveKit participant identity (e.g. dji-dock-{sn} for a DJI dock’s telemetry peer; dji-dock-{sn}-{videoSlug} for its video ingress; other IDs for handheld peers).
  • trackId — the specific video track within that peer. Secondary drone tracks (zoom/thermal) carry a VIDEO_DRONE_TRACK_ID + '_…' suffix and are hidden from the grid.
  • stream — the MediaStream itself.
  • active — whether the track is currently producing frames.

Drone-name resolution runs in a reactive effect: every new peer id is looked up against Firestore assets (firebaseApi.getAsset(droneId)) to render a friendly callsign instead of a UUID. Results cache in a droneNames signal so subsequent renders are free.

Opening a stream (the selection event)

Clicking any stream thumbnail emits the streamSelected event from the tile. The payload shape:

{
peerId: string;
trackId: string;
isDrone: boolean;
dockSn?: string;
mode?: 'taclink' | 'djiCloud';
}
  • For regular drone peers, isDrone: true, no dockSn, mode: 'taclink'.
  • For DJI dock peers, isDrone: true, dockSn resolved from the peer id, mode: 'djiCloud' — this tells the drone-stream tile to route through the DJI Cloud transport instead of TACLINK.
  • For VMS cameras, a separate flow (no streamSelected event) routes into the camera-stream tile or the VMS player pane.

The parent component (usually operation.component) listens for the event and switches the bound drone-stream tile to the selected stream.

Debug-mode overlays

With debug mode enabled, every stream card gains a small latency badge showing round-trip time in milliseconds. The tile polls RTCPeerConnection.getStats() for each connected peer every 2 seconds and pulls currentRoundTripTime from the selected candidate pair. Values above ~200 ms are a sign of packet loss or a TURN relay; below 100 ms indicates a healthy direct or fast-relay path.

Debug badges are additive — if you enable them before opening TACLINK, they show up for every stream once you flip to that tab.

Cycling between streams

The tile header has prev/next chevron buttons when more than one stream is available. They call cyclePrevStream() / cycleNextStream() which advance the selection modulo the stream count, wrap-around at the ends. The header shows the current position as N / total.

No keyboard shortcuts are wired for cycle; operators who want shortcuts should rebind them through the controls page.

Tab persistence

The current tab is stored in the activeTab signal — it’s session-scoped (not persisted across reloads). If you always want TACLINK first, that’s the default — other tabs are opt-in per-session.