Skip to content

camera: lossless raw thermal viewing and playback - #1760

Merged
tridge merged 3 commits into
ArduPilot:masterfrom
tridge:pr-camera2
Sep 22, 2026
Merged

tridge merged 3 commits into
ArduPilot:masterfrom
tridge:pr-camera2

Conversation

@tridge

@tridge tridge commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Add viewing and playback of the lossless 16-bit raw thermal stream that AP_CameraGimbal MT11 firmware advertises as stream 3 (ArduPilot/AP_CameraGimbal#31).

  • camera view rawthermal (or camera view 3) opens the FFV1/Matroska HTTP stream with PyAV and shows it with white-hot greyscale by default, optional Inferno and Turbo palettes, per-frame minimum and maximum temperatures, and hover temperature readout. The popup menu can pause and save the native 640x512 little-endian samples with their apcg.thermal.v1 metadata. While paused, a left click projects the pixel to terrain using that frame's embedded position and attitudes.
  • mavvidplay plays the .raw.mkv recordings written by the camera and by thermal_to_video.py, indexing the per-frame telemetry from Matroska block additions and auto-ranging the 16-bit radiometric frames for display.
  • The reader opens the live stream with FFmpeg frame-rate probing disabled. The camera omits DefaultDuration because the rate changes live, so probing read up to 64 KB of frames inside the 3 s open timeout; with highly compressible frames an interrupted probe left the demuxer's I/O context in error while the open still succeeded, and the reader failed once the probe-buffered packets were consumed.

MAVLink stream type 200 and the APCG block-additional mapping are private firmware/MAVProxy values, not upstream allocations, and are documented as experimental in the module README. Requires PyAV 18.1 or later with packet side-data support.

Tested with the AP_CameraGimbal SITL raw thermal stream and SupportProxy relay tests, and with tests/test_raw_thermal.py, tests/test_video_telemetry.py and tests/test_mavproxy_camera.py.

Decode FFV1 frames with their capture-time metadata for temperature readout, native sample saves and paused pixel projection. Default to greyscale with optional palettes and show temperatures in a readable panel below the image.
thermal_to_video.py writes 16-bit FFV1/Matroska thermal recordings whose
per-frame apcg.telemetry.v1 snapshot lives in Matroska block additions,
not H.264/H.265 SEI. Teach the player to handle them.

VideoIndex detects an FFV1 stream and indexes it with PyAV, reading each
frame's block-additional apcg.thermal.v1 metadata and exposing the nested
telemetry, with the thermal lens HFOV taken from the outer metadata when
the snapshot lacks one. FrameReader gains a PyAV thermal decoder: it seeks
by presentation timestamp (every frame is independently coded) and
auto-ranges the 16-bit radiometric image to 8-bit grey with 1st/99th
percentile scaling. OpenCV alone flattens the 16-bit frames to near-black.

Frames captured before the flight log started keep their reconstructed
clock with a null pose and simply show without an overlay. Adds parser
tests for the block-additional metadata.
The camera omits Matroska DefaultDuration because the stream rate can
change live, so FFmpeg probed the frame rate by reading frames until the
64 KB probesize was spent. Highly compressible frames (SITL test
pattern, or a uniform scene at a low rate) made that take longer than
the 3 s open timeout; the interrupted probe left the demuxer's I/O
context in error while the open still succeeded, and the reader failed
as soon as the probe-buffered packets were consumed.

Codec parameters come from CodecPrivate and the first decoded packet,
so opening now completes on the first frame.
@tridge
tridge merged commit b35c93f into ArduPilot:master Sep 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant