Skip to content

TACLINK tab

TACLINK is the first tab of the streams tile. It shows every peer-to-peer video track currently published into the operation’s room, regardless of source — handheld Android drones, DJI docks, bodycams, phone cameras, anything with a published video track.

What’s in the grid

Each row in status.video.streams[] becomes a card with:

  • Live thumbnail — the first frame of the track, updated on each keyframe.
  • Drone name — resolved via Firebase asset lookup from the peer’s drone ID. Before resolution completes, the drone ID itself is shown. DJI peers fall back to the dock SN if no drone is bound.
  • Device badge — text pill showing what the source is: e.g. “Dock 3” for DJI dock aircraft, “Android” for handhelds, “Bodycam” for worn cameras.
  • DJI badge — only on DJI peers (peerId.startsWith('dji-dock-')). Reads “DJI Cloud API” in a distinct accent colour so operators know that clicking it will route through the DJI Cloud transport.
  • Debug latency (debug mode only) — milliseconds, polled every 2 s from getStats().
  • Open button — emits the streamSelected event to the rest of the console.

Secondary drone tracks are hidden

Most drones produce more than one video track — e.g. a primary FPV track + a zoom lens + a thermal view. Only the primary track shows on the TACLINK grid. The secondary tracks (those whose trackId starts with VIDEO_DRONE_TRACK_ID + '_') are accessible from inside the drone-stream tile once you’ve selected that drone’s primary. The rationale: grid-level cycling should represent “which drone”, not “which lens”, so operators aren’t overwhelmed with duplicate thumbnails.

DJI peer detection

The tile uses isDjiPeer(peerId)peerId.startsWith('dji-dock-') — to detect DJI participants. Two DJI identity formats land in the room per dock:

  • Telemetry peer — identity dji-dock-{sn}. Publishes the protobuf data channel (dock status, aircraft status, flight-status telemetry). Doesn’t carry video.
  • Video ingress peer — identity dji-dock-{sn}-{videoSlug}. Published by the WHIP ingress (either the autopilot or a manual streams/start). Carries the actual H.264 track.

TACLINK shows the video peer in the grid. When the card is opened, the tile resolves the telemetry peer (telemetryPeerIdForDjiPeer(peerId) strips the video slug) so dock status reads still work — dockStatusByPeer[telemetryId] and aircraftStatusByPeer[telemetryId] lookups target the right peer.

The dock model chip

For DJI peers, the card shows a “Dock 1 / Dock 2 / Dock 3” chip based on the dock’s reported model. Resolution:

  1. Look up dockStatusByPeer[telemetryId].dockModel.
  2. If empty, fall back to aircraftStatusByPeer[telemetryId].dockModel.
  3. Normalise the string (e.g. dock_3Dock 3).

If neither source has a model string yet (fresh connection, telemetry not yet published), the chip renders as “DJI” without a version number.

Opening a stream

Click any card or the explicit Open button. The tile emits streamSelected with:

{
peerId: 'dji-dock-1ZNDH1D0010098-1ZNDH1D0010098_39_0_7_normal_0',
trackId: 'video-1',
isDrone: true,
dockSn: '1ZNDH1D0010098',
mode: 'djiCloud'
}

…or for a handheld:

{
peerId: 'peer-abc-123',
trackId: 'video-drone-main',
isDrone: true,
mode: 'taclink'
}

The parent console listens for this and swaps the drone-stream tile’s bound source.

Cycling through streams

Header-level chevron buttons cycle the selected stream forward/backward. Position indicator N / total. Wrap-around at ends.

Filters & sorting

The TACLINK grid currently has no filter UI — every active stream renders. If you have many simultaneous streams, the grid handles up to ~16 thumbnails gracefully before performance degrades (all thumbnails are live, not static — they really are decoding). For high-count scenarios, open specific streams into the drone-stream tile and leave the grid collapsed.

Sort order is effectively “publication order” — streams appear in the order they joined the room. Newer streams land at the bottom.

Known limitations

  • No grid filters (by type, drone, team) in the current build. Planned.
  • No per-stream mute from the grid — mute happens inside the drone-stream tile.
  • No pinning — you can’t pin a specific stream to always render at position 0.