Episode layout
session.export(out_dir, visualizer=...) produces a self-contained directory that downstream code can consume without ever touching the original MCAP. Anything not present in the recording (no mesh topic, no IMU, no hand annotations) is skipped and reported in the manifest log.
Directory tree
File-by-file
rgb.mp4
H.264 video at the recording's frame rate (chosen while recording from the App), encoded with libx264 at CRF 18 / preset medium / +faststart. Dimensions match session.rgb_intrinsics.{width,height}.
If you ran session.add_rgb_frame(i, rgb) in your loop (e.g. to write face-blurred frames instead of the originals), that stream is captured to a temp file as you go and moved into place at export time. Otherwise the SDK does a fresh pass over session.frames().
Skipped when: no RGB intrinsics in the recording, or ffmpeg is missing on PATH.
mesh.ply
Binary little-endian PLY of the SLAM map mesh from /map/mesh. Vertex-coloured if the message carried per-vertex colours. Coordinates are in world frame, metres.
import trimesh
mesh = trimesh.load("episodes/run_01/mesh.ply")
print(mesh.vertices.shape, mesh.faces.shape)Skipped when: no /map/mesh topic in the recording.
thumbnail.jpg
The middle frame of the recording (num_rgb_frames // 2), written at JPEG quality 85. If you streamed post-processed frames via session.add_rgb_frame, the mid-frame snapshot from that stream is used instead, so the thumbnail matches the video.
You can override entirely:
session.export("...", thumbnail_rgb=my_rgb_array) # or skip_thumbnail=Truevisualization.rrd
Rerun's native recording file. Contains the per-frame RGB + depth panels, hand overlays, frustums, the 3D map, and IMU traces, exactly what you logged with Visualizer.log_frame. Open with:
rerun episodes/run_01/visualization.rrdOnly written when you pass visualizer=... to session.export.
annotation.hdf5
The single biggest output. Holds depth frames, camera poses, IMU, hand annotations, and metadata. See HDF5 schema for the full group/dataset map with shapes and dtypes.
calibrations/
Camera intrinsics + distortion in raw .npy form for downstream code that doesn't want to parse the HDF5. meta.json mirrors the same data as JSON, with widths/heights and the distortion model name (e.g. "plumb_bob").
{
"rgb": {
"width": 1280,
"height": 720,
"distortion_model": "plumb_bob",
"files": {"K": "rgb_K.npy", "D": "rgb_D.npy"}
},
"depth": {
"width": 640,
"height": 360,
"distortion_model": "plumb_bob",
"files": {"K": "depth_K.npy", "D": "depth_D.npy"},
"units": "mm (uint16)"
},
"R_optical_to_link": "R_optical_to_link.npy"
}The manifest
session.export(...) returns a dict and logs the same as INFO:
{
"saved": ["rgb.mp4", "annotation.hdf5:/depth", "thumbnail.jpg",
"mesh.ply", "calibrations/", "annotation.hdf5:/cam-pose",
"annotation.hdf5:/imu", "annotation.hdf5:/hand-pose",
"annotation.hdf5:/metadata", "visualization.rrd"],
"skipped": [],
}Items that couldn't be produced are listed under skipped with a reason in parentheses, e.g. "mesh.ply (no /map/mesh topic)" or "rgb.mp4 (ffmpeg not on PATH)".
No partial-state. If a stage fails, the corresponding file is absent and the failure is recorded. The export call never throws for missing upstream data.
See also
- Episode export guide, the call signature and kwargs.
- HDF5 schema, the
annotation.hdf5layout in detail.