feat(mcap): add copy mode for multi-camera exports
Add an opt-in multi-camera copy layout for zed_svo_to_mcap. copy mode preserves each camera's original timestamps and cadence, writes namespaced /zedN topics without /bundle, and supports common-overlap or full-range export windows. Update the batch wrapper, Python validator, RGBD viewer, and documentation so copy-layout MCAP files are treated as a first-class format rather than invalid bundled output. Validation: - cmake --build build --target zed_svo_to_mcap -j4 - uv run python -m py_compile scripts/zed_batch_svo_to_mcap.py scripts/mcap_bundle_validator.py scripts/mcap_rgbd_viewer.py - build/bin/zed_svo_to_mcap --segment-dir /workspaces/data/kindergarten/bar/2026-03-18T11-59-41 --output /tmp/bar_11-59-41_copy_common.mcap --encoder-device nvidia --depth-mode neural_plus --depth-size optimal --bundle-policy copy --copy-range common - uv run python scripts/mcap_bundle_validator.py /tmp/bar_11-59-41_copy_common.mcap - uv run --extra viewer python scripts/mcap_rgbd_viewer.py /tmp/bar_11-59-41_copy_common.mcap --summary-only
This commit is contained in:
+43
-3
@@ -1,9 +1,10 @@
|
||||
# MCAP Layout
|
||||
|
||||
`cvmmap-streamer` writes two related MCAP layouts:
|
||||
`cvmmap-streamer` writes three related MCAP layouts:
|
||||
|
||||
- single-camera MCAP export
|
||||
- bundled multi-camera MCAP export
|
||||
- bundled multi-camera timeline export
|
||||
- multi-camera copy export
|
||||
|
||||
This document covers the topic layout, schema types, and timestamp semantics for both.
|
||||
|
||||
@@ -40,6 +41,30 @@ Bundled export namespaces each camera stream and adds one bundle manifest topic:
|
||||
|
||||
`strict` is still available. In strict mode, bundle membership is thresholded by timestamp skew and `--sync-tolerance-ms` applies. In `nearest` mode, `--sync-tolerance-ms` is not used.
|
||||
|
||||
Because `nearest` emits on the slowest common timeline, faster cameras can legitimately end up with the same message count as slower cameras in bundled output.
|
||||
|
||||
## Multi-Camera Copy Layout
|
||||
|
||||
`copy` export also namespaces each camera stream under `/zedN/...`, but it does not emit `/bundle`.
|
||||
|
||||
| Topic | Schema / Encoding | Notes |
|
||||
|------|------|------|
|
||||
| `/zedN/video` | `foxglove.CompressedVideo` | Per-camera encoded video |
|
||||
| `/zedN/depth` | `cvmmap_streamer.DepthMap` | Per-camera depth |
|
||||
| `/zedN/calibration` | `foxglove.CameraCalibration` | Per-camera video intrinsics |
|
||||
| `/zedN/depth_calibration` | `foxglove.CameraCalibration` | Written only when exported depth resolution differs from video |
|
||||
| `/zedN/pose` | `foxglove.PoseInFrame` | Optional per-camera pose |
|
||||
| `/zedN/body` | raw `cvmmap.body_tracking.v1` | Optional raw body packets; see [mcap_body_tracking.md](./mcap_body_tracking.md) |
|
||||
|
||||
`copy` preserves each camera topic exactly as an independent stream inside one MCAP:
|
||||
|
||||
- no `/bundle` topic
|
||||
- no shared bundle index
|
||||
- no resampling to a common timeline
|
||||
- original per-camera timestamps and cadence are preserved
|
||||
|
||||
`--copy-range common` trims each camera to the common overlap window without resampling. `--copy-range full` preserves each camera’s full readable timestamp range from the grouped segment.
|
||||
|
||||
## Bundle Manifest Contract
|
||||
|
||||
`/bundle` is the authoritative grouping contract for bundled MCAP files. Consumers should not infer grouping from identical MCAP `logTime` values or from matching per-camera timestamps.
|
||||
@@ -76,6 +101,8 @@ The bundled MCAP contract intentionally separates bundle time from per-camera sa
|
||||
|
||||
This means a single bundle can legitimately contain different per-camera timestamps, especially in `nearest` mode.
|
||||
|
||||
`copy` has no separate bundle time. Its `/zedN/video`, `/zedN/depth`, and `/zedN/pose` messages all use the original per-camera sample timestamp directly.
|
||||
|
||||
## Corruption And Partial Bundles
|
||||
|
||||
Bundled `nearest` export is resilient to ZED `CORRUPTED FRAME` runs:
|
||||
@@ -91,6 +118,13 @@ Bundled `strict` export stays strict:
|
||||
- corruption is skipped internally until recovery
|
||||
- only fully present, threshold-qualified groups are emitted
|
||||
|
||||
`copy` is also resilient to ZED `CORRUPTED FRAME` runs:
|
||||
|
||||
- unreadable tail frames are treated as end-of-stream
|
||||
- mid-stream corruption is skipped until a readable frame is found
|
||||
- there is no placeholder or manifest entry because `copy` has no grouping contract
|
||||
- the affected camera topic simply resumes at the recovered readable frame
|
||||
|
||||
## Validation Expectations
|
||||
|
||||
For single-camera MCAP files, the current validation contract is:
|
||||
@@ -111,4 +145,10 @@ For bundled MCAP files, the current validation contract is:
|
||||
|
||||
That is why a partially written MCAP with topic presence but mismatched counts is treated as invalid.
|
||||
|
||||
The repository-level Python helper [scripts/mcap_bundle_validator.py](../scripts/mcap_bundle_validator.py) now understands both layouts and reports which one it found before applying the corresponding validation rules.
|
||||
For multi-camera `copy` MCAP files, the current validation contract is:
|
||||
|
||||
- `/bundle` must not exist
|
||||
- each camera must have `/zedN/video`, `/zedN/depth`, and `/zedN/calibration`
|
||||
- for each camera, `/zedN/video` and `/zedN/depth` message counts must match
|
||||
|
||||
The repository-level Python helper [scripts/mcap_bundle_validator.py](../scripts/mcap_bundle_validator.py) now understands all three layouts and reports which one it found before applying the corresponding validation rules.
|
||||
|
||||
Reference in New Issue
Block a user