Video Overlay Lambda

Last Updated: 2026-07-21

The Video Overlay Lambda burns the flight telemetry instrument band into an uploaded on-board video, and — before that — builds the browser-playable proxy the sync wizard scrubs. It is a thin wrapper around core.ts, the same function the local CLI calls. The web-app UI that drives it is the Flight Deck; the render model it reuses is the overlay pipeline.

What it does

The handler (src/index.ts) runs one of two modes, chosen by event.mode:

  • proxy — runs first, straight after upload. It makes a small, browser-playable H.264 copy of the whole recording (the raw camera file is Motion JPEG in AVI and no browser can decode it), preserving the source's timeline exactly. It is variant/chunk-agnostic and ends the job at AWAITING_SYNC, parked on a human to find the liftoff frame.
  • render — runs once liftoff is marked, and burns the overlay onto the source. It is the default when mode is absent (event.mode ?? 'render') — a deliberate back-compat default so an in-flight event enqueued by a previous version of the app, which carried no mode, still means render.

The Lambda is invoked directly by a server action (InvocationType: 'Event', fire-and-forget), not by an S3 notification, and it reports progress by writing the RenderJob node — the web client polls that node for status.

Telemetry & the shared model

Telemetry is fetched inside the Lambda (fetchOverlayInput(flightId) in flights.ts), not passed in. The render then reuses the shared @rocket-club/flight-overlay model — the same buildOverlayModelcomputeLayoutdrawOverlayFrame pipeline (and resolveChunk for the chunk window) that the browser preview uses — so the burned-in pixels match the preview by construction. See the overlay pipeline for that "one renderer, three hosts" contract.

Trim arithmetic

src/trim.ts maps the flight's timeline onto the source video's. Two clocks are in play:

  • Flight time — seconds since detected liftoff, t = 0. Everything in the telemetry and the overlay model uses this.
  • Source time — seconds into the uploaded file.

liftoffVideoTimeS (from the sync wizard) is the only bridge between them:

sourceTime = liftoffVideoTimeS + flightTime

computeTrimPlan trims the source to the selected chunk window rather than the whole flight. The chunk bounds chunkStartS / chunkEndS are optional and default to today's full window (chunkStartS → overlayStartS, chunkEndS → landingT), so omitting them — the full chunk — reproduces the pre-chunk plan byte-for-byte. A named chunk that begins after the camera already stopped (e.g. descent chosen but the recording died at apogee) is rejected with a user-facing message rather than producing a broken clip.

Source of truth

  • Lambda package: data/platform/lambda/video-overlay/
  • Handler + modes: data/platform/lambda/video-overlay/src/index.ts
  • Render core (CLI + Lambda): data/platform/lambda/video-overlay/src/core.ts
  • Trim arithmetic: data/platform/lambda/video-overlay/src/trim.ts
  • Scrub proxy: data/platform/lambda/video-overlay/src/proxy.ts