MCAP

MCAP (pronounced "em-cap") is an open source container file format for logging and storing multimodal data. It supports multiple channels of timestamped pre-serialized data, and is ideal for use in pub/sub or robotics applications.
Pub/sub logging
Store multiple channels of timestamped log data, such as pub/sub messages or multimodal sensor data.
Serialization-agnostic
Record and replay binary messages in any format – like Protobuf, DDS (CDR), ROS, JSON, and more.
High-performance writing
MCAP uses a row-oriented, append-only design to minimize disk I/O and reduce the risk of data corruption during unclean shutdowns.
Self-contained
MCAP stores message schemas alongside data, so your files remain readable in the future even as your codebase evolves.
Efficient seeking
MCAP files contain an optional index, allowing for fast, efficient reading, even over a low-bandwidth internet connection.
Optional compression
Choose between LZ4 or Zstandard for chunk-based compression, while still supporting efficient indexed reads.
Broad language support
Native reader and writer libraries are available in C++, Go, Python, Rust, Swift, and TypeScript.
Flexible
Configure optional features like chunking, indexing, CRC checksums, and compression to make the right tradeoffs for your application.
Production-grade
MCAP is used in production by a wide range of companies, from autonomous vehicles to drones, and is the default log format in ROS 2.
Write your first MCAP file
MCAP libraries are available in six languages. Pick one to see a minimal end-to-end example.
- Python
- C++
- Go
- Rust
- TypeScript
- Swift
from mcap.writer import Writer
with open("out.mcap", "wb") as f:
writer = Writer(f)
writer.start()
schema_id = writer.register_schema(
name="ExampleMsg",
encoding="jsonschema",
data=b'{"type":"object","properties":{"value":{"type":"number"}}}',
)
channel_id = writer.register_channel(
schema_id=schema_id,
topic="/example",
message_encoding="json",
)
writer.add_message(
channel_id,
log_time=0,
data=b'{"value": 1.0}',
publish_time=0,
)
writer.finish()
#include <mcap/writer.hpp>
int main() {
mcap::McapWriter writer;
mcap::McapWriterOptions options("");
writer.open("out.mcap", options);
mcap::Schema schema("ExampleMsg", "jsonschema",
R"({"type":"object","properties":{"value":{"type":"number"}}})");
writer.addSchema(schema);
mcap::Channel channel("/example", "json", schema.id);
writer.addChannel(channel);
mcap::Message msg;
msg.channelId = channel.id;
msg.sequence = 0;
msg.logTime = 0;
msg.publishTime = 0;
std::string data = R"({"value": 1.0})";
msg.data = reinterpret_cast<const std::byte*>(data.data());
msg.dataSize = data.size();
writer.write(msg);
writer.close();
}
package main
import (
"os"
"github.com/foxglove/mcap/go/mcap"
)
func main() {
f, _ := os.Create("out.mcap")
defer f.Close()
writer, _ := mcap.NewWriter(f, &mcap.WriterOptions{})
writer.WriteHeader(&mcap.Header{})
schema := &mcap.Schema{
ID: 1,
Name: "ExampleMsg",
Encoding: "jsonschema",
Data: []byte(`{"type":"object","properties":{"value":{"type":"number"}}}`),
}
writer.WriteSchema(schema)
channel := &mcap.Channel{
ID: 1,
SchemaID: 1,
Topic: "/example",
MessageEncoding: "json",
}
writer.WriteChannel(channel)
writer.WriteMessage(&mcap.Message{
ChannelID: 1,
LogTime: 0,
PublishTime: 0,
Data: []byte(`{"value": 1.0}`),
})
writer.Close()
}
use mcap::{records::MessageHeader, Writer};
use std::{collections::BTreeMap, fs};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut out = Writer::new(fs::File::create("out.mcap")?)?;
let schema_id = out.add_schema(
"ExampleMsg",
"jsonschema",
br#"{"type":"object","properties":{"value":{"type":"number"}}}"#,
)?;
let channel_id = out.add_channel(schema_id, "/example", "json", &BTreeMap::new())?;
out.write_to_known_channel(
&MessageHeader { channel_id, sequence: 0, log_time: 0, publish_time: 0 },
br#"{"value": 1.0}"#,
)?;
out.finish()?;
Ok(())
}
import { McapWriter } from "@mcap/core";
import { FileHandleWritable } from "@mcap/nodejs";
import { open } from "node:fs/promises";
const writable = new FileHandleWritable(await open("out.mcap", "w"));
const writer = new McapWriter({ writable });
await writer.start({ library: "example", profile: "" });
const schemaId = await writer.registerSchema({
name: "ExampleMsg",
encoding: "jsonschema",
data: new TextEncoder().encode(
JSON.stringify({ type: "object", properties: { value: { type: "number" } } }),
),
});
const channelId = await writer.registerChannel({
topic: "/example",
schemaId,
messageEncoding: "json",
metadata: new Map(),
});
await writer.addMessage({
channelId,
sequence: 0,
logTime: 0n,
publishTime: 0n,
data: new TextEncoder().encode(JSON.stringify({ value: 1.0 })),
});
await writer.end();
import Foundation
import MCAP
final class FileWritable: IWritable {
private let handle: FileHandle
init(path: String) throws {
FileManager.default.createFile(atPath: path, contents: nil)
handle = try FileHandle(forWritingTo: URL(fileURLWithPath: path))
}
func position() -> UInt64 {
handle.offsetInFile
}
func write(_ data: Data) async {
handle.write(data)
}
func close() throws {
try handle.close()
}
}
let sink = try FileWritable(path: "out.mcap")
defer { try? sink.close() }
let writer = MCAPWriter(sink)
await writer.start(library: "example", profile: "")
let schemaId = await writer.addSchema(
name: "ExampleMsg",
encoding: "jsonschema",
data: Data(#"{"type":"object","properties":{"value":{"type":"number"}}}"#.utf8)
)
let channelId = await writer.addChannel(
schemaID: schemaId,
topic: "/example",
messageEncoding: "json",
metadata: [:]
)
await writer.addMessage(
Message(
channelID: channelId,
sequence: 0,
logTime: 0,
publishTime: 0,
data: Data(#"{"value": 1.0}"#.utf8)
)
)
await writer.end()