roboto.formats.mcap
Fetching and decoding topic data stored in MCAP files.
Covers chunk-index-driven prefetching over HTTP range requests and decoding of JSON-, ROS1-, and ROS2-encoded messages with field-path projection.
Submodules
Package Contents
Accessor
Reads one path’s value out of a decoded message and writes it into an Accumulator.
The accessor is compiled once per fields set and reused for every subsequent message in the same read pass. Compilation resolves time-field name remapping and sequence boundaries against a sample message, so per-call work is just attribute access plus accumulator writes.
END_OF_STREAM
Sentinel returned by McapReader.next_decoded() when the stream is exhausted.
A decoded message value can legitimately be None (a JSON null payload), so exhaustion cannot be signaled with None without making a real null-valued message indistinguishable from end-of-stream. Callers test result is END_OF_STREAM to detect exhaustion and treat every other value – None included – as a delivered message.
McapReader
Reader for processing MCAP files with field projection.
Provides an iterator interface for reading decoded messages from MCAP files, filtered by log time and optionally by topic, and projected to selected fields. Handles JSON, msgpack, and the ROS/CDR encodings (ros1msg / ros2msg / ros2idl / omgidl).
Parameters
stream IO[bytes]fields collections.start_time Optional[int]end_time Optional[int]log_time_order booltopic_name Optional[str]Properties
McapReader.field_paths
Get the path of each field being projected, in the order the fields were given at initialization.
Returns
One tuple per field, its path_in_schema:
the path components from the schema root to the field.
McapReader.has_next
Check if there are more messages available to read.
Returns
True if there are more messages to read, False otherwise.
McapReader.next()
Read and return the next decoded message.
Advances the reader to the next message and returns it as a DecodedMessage object, or None if no more messages are available.
Returns
DecodedMessage containing the next message data, or None if no more messages.
Usage
while reader.has_next:
message = reader.next()
if message:
data = message.to_dict()
print(f"Message at {data.get('log_time')}: {data}")McapReader.next_decoded()
Read and return the next message’s raw decoded value, advancing the reader.
The raw value is what the format decoder produced – a dict for JSON-encoded messages, and nested dict / sequence / scalar values for ROS/CDR encodings – with no projection applied. Callers that want projected dictionary output use next() and DecodedMessage.to_dict() instead.
A decoded value of None is a real message (a JSON null payload) and is delivered as such. Exhaustion is signaled with the dedicated END_OF_STREAM sentinel instead, so callers must test result is END_OF_STREAM rather than result is None to detect the end.
Returns
The decoded message value, or END_OF_STREAM if no more messages are available.
Properties
McapReader.next_envelope_timestamp
Get the envelope timestamps of the next message to be read.
Returns
The next message’s log_time and publish_time in nanoseconds, or both math.inf if no more messages.
McapReader.next_message_is_time_aligned()
Check if the next message has the specified timestamp.
Used for time-aligned reading when merging data from multiple readers.
Parameters
timestamp Union[int, float]Timestamp to check against in nanoseconds.
Returns
True if the next message has the specified timestamp, False otherwise.
Resolution
A resolved accessor path: a no-op, a simple attribute chain, or a per-element sequence crossing.
Build one with none_resolution(), simple_resolution(), or sequence_resolution(), then compile it with build_accessor().
build_accessor()
Compile a resolution into an Accessor that reads its path into an accumulator.
The resolution carries the (possibly time-remapped) structure; this only selects the matching runtime walk. It does not sample, so a caller that built the resolution from a schema can compile without a message in hand.
Parameters
resolution ResolutionReturn type
compile_accessors()
Compile one accessor per field. Does not cache; callers manage caching.
Returns a tuple of (accessors, fully_resolved). fully_resolved is False if any path traversed an empty sequence in sample and the inner shape past it had to be guessed. Callers maintaining a cross-message cache should not cache speculative compilations, since the next message may need a different shape.
Parameters
Return type
getter_for()
The shared attribute getter for a decoded message.
Every decoder materializes messages as dicts (and the dict getter resolves a non-dict JSON payload to no attributes), so one getter serves every message.
Parameters
message AnyReturn type
none_resolution()
A resolution whose accessor is a no-op (the path is absent on a message).
Return type
open_for_window()
Open a remote MCAP file for reading, prefetching only the chunks in a log-time window and of one topic.
Reads the file’s summary section to locate its chunk index, then prefetches in parallel each chunk whose message log times intersect [start_time, end_time) and, given a topic_name, whose chunk index lists a message index for a channel of that topic. Later reads of those chunks are answered from the reader’s in-memory cache. The returned reader is positioned at the start of the file. The caller owns it and must close() it.
The window is matched against the chunk index’s log-time bounds; when row timestamps come from somewhere other than the message log time, pass no bounds (prefetching then covers every chunk) and filter rows after decode.
McapReader reads through mcap.reader.SeekingReader.iter_messages(), which picks chunks for a window and topic by the same log-time and channel rules, with one addition: when given topics, it also reads a chunk whose chunk index names no message index, since that chunk’s topics are unknown until it is scanned. This function does not prefetch such a chunk, so its bytes are downloaded when it is read.
Parameters
signed_url strResolved download URL of the MCAP file.
start_time Optional[int]Inclusive window lower bound in nanoseconds, or None for unbounded.
end_time Optional[int]Exclusive window upper bound in nanoseconds, or None for unbounded.
topic_name Optional[str]Prefetch only the chunks holding a message on an MCAP channel of this topic, or chunks of every topic when None.
Returns
An HttpRangeReader over the file, primed with the selected chunks’ bytes and positioned at offset 0.
remap_time_fields()
Substitute the decoder’s runtime time-field name into resolution, observed against sample.
A legacy topic’s message paths address a ROS2 time struct’s sub-second leaf as nsec (how it was recorded before the move to wire-true field names), but the shared mcap_codec decoder now materializes that value as a {"sec", "nanosec"} dict. This walks sample along the resolution and rewrites a trailing nsec past any such time value to nanosec, so the built accessor reads the right key.
Returns (remapped, time_resolved). time_resolved is False only when a time-bearing leaf sits past a sequence that is empty in sample — its element cannot be observed, so the runtime names stay a guess and the caller should re-resolve against a later, non-empty message. A resolution with no time component is returned unchanged with True. Paths that already name the leaf nanosec (ROS2 wire-true), ROS1 nsec, and JSON nsec values are all no-ops.
Parameters
Return type
sequence_resolution()
A resolution that crosses the sequence at pre_path, applying sub per element.
Parameters
pre_path collections.sub ResolutionReturn type
simple_resolution()
A resolution for a straight attribute chain (no sequence crossing).
Parameters
path collections.Return type