Skip to content

Watch a flight, live or recorded

A gusty hover hop on real ArduPilot SITL, rendered from its own log by render_video: 6 m/s from the west with seeded Dryden gusts, climb to 9 m, hold 12 s, land in QLAND.

Every picture shows the same scene in one of three layouts:

  • fpv: a nose camera with a HUD. It shows height, airspeed, climb rate and the true wind with its gust, plus the mode, armed state, throttle, lift-motor demand, current, voltage, energy and the latest autopilot messages.
  • chase: a camera behind and above the aircraft, showing its track, its shadow and the wind on a compass.
  • pip: the FPV view with the chase view inset.
video_feed(session_id="d224b4")
{ "on": true, "layout": "pip", "viewers": 0, "encoder": "h264_nvenc",
"vlc": "vlc http://127.0.0.1:8440/video.ts",
"page": "http://127.0.0.1:8440/",
"frame": "http://127.0.0.1:8440/frame.jpg" }

Open the vlc line in VLC or mpv, or the page in a browser, which plays an MJPEG stream with nothing to install. The feed listens on MCARDUPILOT_VIDEO_PORT_BASE (8400) plus the instance number, on 127.0.0.1 unless MCARDUPILOT_VIDEO_HOST says otherwise. To change the view live, call video_feed(session_id, layout="chase"). on=false stops the feed, and closing the session stops it too.

The aircraft’s position and attitude come from SITL itself. Every session launches SITL with --enable-fgview, which makes it send its simulated state to UDP 5503 + 10 × instance at every physics step, as it would to FlightGear. That costs SITL about 4% more CPU and changes nothing about the flight. A SITL that sends nothing still gets a picture, from the session’s telemetry at a few hertz.

Ask for the recording when you open the session:

open_sitl(label="gusty-hop", record_video="pip", wind={...})
...
close_sitl(session_id) # "video": {"video_id": "a09ddb", "status": "rendering", ...}
video_status(video_id="a09ddb")

Or render any log afterwards. A log is named the way log_summary takes it: a session id, a kept log’s name, or a path.

render_video(log="20261003T225739Z-video-demo-gusty-hop.BIN", layout="chase")
render_video(log="...", t0_s=60, t1_s=75) # just the part you care about

A recording is drawn from SITL’s own records in the log, so it shows the truth, not the estimator:

  • Pose: from SIM, about 10 Hz for attitude and position, and SIM2, a few hundred hertz for position and velocity.
  • Wind: from the SIM_WIND_SPD and SIM_WIND_DIR changes the gust model sent.
  • Speed: from the logged SIM_SPEEDUP.

Log time is sim time, so the video plays at the speed the aircraft flew, whatever speedup the session ran at, and idle-paced pauses disappear. By default it runs from four seconds before arming to four seconds after disarming. Rendering runs faster than real time: the 51-second flight above took 33 s at 960×540, 5 MB with NVENC.

A kept log’s video goes beside it as <stem>.<layout>.mp4. The log budget counts it and deletes it with the log, and pin_log spares both. A log the server doesn’t own, such as a live session’s or another project’s, renders into <data_dir>/videos instead, so nothing is written next to files that belong to someone else.

A log from a real aircraft has no SIM records. It still renders, from the estimator’s attitude and position, and its wind is then labelled WIND est.

video_frame(session_id="d224b4") # the picture now
video_frame(log="...gusty-hop.BIN", t_s=75.0, layout="chase")

video_frame returns one frame as an image. For a session with a live feed it is exactly the frame on the viewer’s screen. It’s the quickest way for an agent to check what a person is looking at before describing it.

ffmpeg on PATH. Encoding uses NVENC when ffmpeg can open it, else libx264. Set MCARDUPILOT_VIDEO_ENCODER to choose one. MCARDUPILOT_VIDEO_SIZE (960x540) and MCARDUPILOT_VIDEO_FPS (30) apply to both live feeds and renders.