Skip to content

DJI auto-stream

When a DJI dock is online and assigned to a mission, ARGUS auto-provisions a TACLINK WHIP ingress and tells the dock to push its aircraft camera feed into it. No operator button. No second tool. The stream just appears in the mission’s room within a few seconds and gets torn down cleanly when the dock is unassigned or goes offline.

This page covers what the autopilot does, the preconditions it waits for, how to override it, and what to do when things don’t work.

The preconditions

The autopilot watches a per-dock composite state and only acts when all three conditions are true:

  1. dji_docks/{sn}.online === true — the dock is currently connected to EMQX.
  2. dji_docks/{sn}.missionId is set — the dock has been assigned to an operation.
  3. dji_docks/{sn}.liveCapacity.device_list reports at least one aircraft camera.

The third condition matters because live_capacity is the DJI field telling us which cameras are actually available to stream. A dock with no aircraft attached or one still in cover-closed startup has no available capacity — the autopilot waits for update_topo + live_capacity to settle before acting.

The 2-second debounce

After a dock update Firestore-writes, the autopilot waits 2 seconds for state to settle before acting. On dock-online the composite state (online, missionId, capacity) tends to write in a rapid burst over a few hundred ms; debouncing coalesces them into one reconcile.

What the autopilot does when preconditions match

  1. Create a WHIP ingress via livekit-server-sdk’s IngressClient, with:
    • roomName = mission id.
    • participantIdentity = dji-dock-{sn}-{videoSlug} (prefix matches the telemetry peer so isDjiPeer() catches both).
    • participantMetadata = JSON {source: 'dji-cloud', role: 'video', dockSn, videoId, dockModel, autopilot: true} so the webapp can classify without prefix-sniffing.
  2. Publish MQTT live_start_push to the dock with url_type: 4 (WHIP) and the composed WHIP URL.
  3. Dock streams H.264 over HTTPS WHIP → TACLINK ingress → participants subscribe to the video track.

No argus-api round-trip is needed — argus-dji calls LiveKit directly using the credentials it already has. This keeps the path resilient to argus-api outages.

Which camera + lens is auto-selected

By default the autopilot picks the aircraft’s normal-0 camera — the wide-lens, default-quality stream. Operators can switch lens (wide / zoom / ir) and quality (smooth / SD / HD / UHD) mid-stream via the drone-stream tile controls — the autopilot doesn’t fight the manual change.

Teardown

The autopilot also watches for teardown conditions:

  • missionId: null → set was what started us.
  • missionId: set → null — dock was unassigned. Tear down.
  • online: true → false — dock went offline. Tear down.
  • Dock doc deleted. Tear down.
  • Mission completed. Tear down.

On teardown, ARGUS publishes live_stop_push to the dock + deletes the LiveKit ingress. No leaked ingresses or orphan MQTT publish sessions.

Race conditions handled

  • Snapshot races — if the dock flaps online/offline in quick succession, the 2 s debounce absorbs it.
  • Mission swap — if missionId changes (dock reassigned mid-op), the autopilot tears the old ingress down and provisions a new one for the new mission’s room.
  • Double-reconcile — the reconcile is idempotent — if it runs twice with the same state, the second run finds no work to do.

Manual override

You can still start a stream manually even with auto-stream running:

  • POST /api/dji/streams/start — argus-api’s manual-start route. Useful for starting a second camera (e.g. the dock’s FPV in addition to the aircraft’s wide cam). The manual route uses the same identity + metadata scheme so the webapp renders it alongside auto-stream participants seamlessly.
  • Operator can also disable auto-stream per mission via Mission detail → Configure → DJI → Disable auto-stream — leaves existing streams alone but won’t start new ones.

Fallback: RTMP

If the dock NACKs WHIP (live_start_push returns non-zero), argus-dji returns the failure flag up to argus-api’s /api/dji/streams/fallback-rtmp route, which creates an RTMP ingress and retries. The retry uses the same room + participant-metadata scheme — the stream lands in the room either way, just with slightly higher latency for RTMP. This is the only case where RTMP is used; auto-stream prefers WHIP universally.

Troubleshooting

  • Stream doesn’t appear. Check the three preconditions. Most common cause: liveCapacity not yet populated (aircraft still booting).
  • Ingress leaks. The autopilot is rigorous about cleanup; if you see an ingress not torn down after a mission ends, report the mission ID to support — there may be a rare race we haven’t caught yet.
  • Stream drops mid-flight. Usually a network issue on the dock side — check dji_docks/{sn}.online state and the dock’s WAN link.