Lightweight Python library for reading and writing MCAP files
Project description
small-mcap
Lightweight Python library for reading and writing MCAP files.
Installation
uv add small-mcap
# With compression support
uv add small-mcap[compression] # ZSTD + LZ4
uv add small-mcap[zstd] # ZSTD only
uv add small-mcap[lz4] # LZ4 only
Reader
Basic read
from small_mcap import read_message
with open("input.mcap", "rb") as f:
for schema, channel, message in read_message(f):
print(f"{channel.topic}: {message.data}")
Read multiple inputs
from small_mcap import read_message
with open("recording1.mcap", "rb") as f1, \
open("recording2.mcap", "rb") as f2, \
open("recording3.mcap", "rb") as f3:
for schema, channel, message in read_message([f1, f2, f3]):
print(f"{channel.topic}: {message.log_time}")
Read with topic filtering
from small_mcap import read_message, include_topics
with open("input.mcap", "rb") as f:
topics = ["/camera/image", "/lidar/points"]
for schema, channel, message in read_message(f, should_include=include_topics(topics)):
print(f"{channel.topic}: {len(message.data)} bytes")
Read with time range
from small_mcap import read_message
with open("input.mcap", "rb") as f:
start = 1000000000 # nanoseconds
end = 2000000000
for schema, channel, message in read_message(f, start_time_ns=start, end_time_ns=end):
print(f"{channel.topic} at {message.log_time}")
Read decoded messages
from small_mcap import read_message_decoded
import json
class JsonDecoderFactory:
def decoder_for(self, schema):
if schema.encoding == "json":
return lambda data: json.loads(data)
return None
with open("input.mcap", "rb") as f:
for msg in read_message_decoded(f, decoder_factories=[JsonDecoderFactory()]):
print(f"{msg.channel.topic}: {msg.decoded_message}")
Read summary/metadata
from small_mcap import get_summary, get_header
with open("input.mcap", "rb") as f:
summary = get_summary(f)
print(f"Messages: {summary.statistics.message_count}")
print(f"Duration: {summary.statistics.message_start_time} - {summary.statistics.message_end_time}")
for channel in summary.channels.values():
print(f" {channel.topic}: {channel.message_encoding}")
Writer
Basic write
from small_mcap import McapWriter
with open("output.mcap", "wb") as f:
writer = McapWriter(f)
writer.start(profile="", library="my-app")
# Add schema
schema_id = 1
writer.add_schema(schema_id, "MySchema", "json", b'{"type": "object"}')
# Add channel
channel_id = 1
writer.add_channel(channel_id, "/my/topic", "json", schema_id)
# Add messages
for i in range(100):
writer.add_message(
channel_id,
log_time=i * 1000000, # nanoseconds
data=b'{"value": 42}',
publish_time=i * 1000000
)
writer.finish()
Write with compression
from small_mcap import McapWriter, CompressionType
with open("output.mcap", "wb") as f:
writer = McapWriter(
f,
compression=CompressionType.ZSTD,
chunk_size=1024 * 1024 # 1MB chunks
)
writer.start(profile="", library="my-app")
schema_id = 1
writer.add_schema(schema_id, "MySchema", "json", b"{}")
channel_id = 1
writer.add_channel(channel_id, "/topic", "json", schema_id)
for i in range(1000):
writer.add_message(channel_id, log_time=i*1000, data=b"data", publish_time=i*1000)
writer.finish()
Write with encoder factory
from small_mcap import McapWriter
import json
class JsonEncoderFactory:
"""Implements EncoderFactoryProtocol for JSON messages."""
profile = ""
encoding = "jsonschema"
message_encoding = "json"
def encoder_for(self, schema):
return lambda msg: json.dumps(msg).encode()
with open("output.mcap", "wb") as f:
writer = McapWriter(f, encoder_factory=JsonEncoderFactory())
writer.start(profile="", library="my-app")
schema_id = 1
writer.add_schema(schema_id, "SensorData", "jsonschema", b'{"type": "object"}')
channel_id = 1
writer.add_channel(channel_id, "/sensor/data", "json", schema_id)
for i in range(100):
msg = {"timestamp": i, "value": i * 2}
writer.add_message_encode(channel_id, i * 1000, msg, publish_time=i * 1000)
writer.finish()
Features
- Zero dependencies for core functionality
- Optional compression support (ZSTD, LZ4)
- Lazy chunk loading for efficient memory usage
- Topic and time-range filtering
- Automatic schema/channel registration
- CRC validation
- Fast summary/metadata access
Performance
small-mcap is optimized for high-performance MCAP file reading with zero-copy operations and lazy chunk loading:
Key Optimizations:
- Zero-copy memory access: Uses
memoryviewto avoid unnecessary data copies - Lazy chunk loading: Only decompresses chunks when needed
- Binary search: Efficient time-range filtering using chunk indexes
- Heap-based merging: Optimal multi-file reading with automatic ID remapping
Comparison with other libraries:
| Feature | small-mcap | mcap (official) | rosbags | pybag |
|---|---|---|---|---|
| Performance | Fastest | Fast | Fast | Moderate |
| Zero dependencies | Yes | No | No | No |
| Non-seekable streams | Yes | Yes | No | No |
| Multi-file reading | Yes | No | Yes | Yes |
| ROS1 support | No | No | Yes | No |
| SQLite3 backend | No | No | Yes | No |
Benchmarks
Benchmark results comparing small-mcap against mcap (official), rosbags, and pybag libraries using a nuScenes dataset (30,900 messages, 19.15s duration, 560 zstd chunks).
Full File Read (Seekable)
----------------------------------------------------------------------------------------- benchmark 'full-seekable': 4 tests -----------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_benchmark_read[full-seekable-small_mcap] 442.7987 (1.0) 455.6817 (1.0) 448.6568 (1.0) 4.7916 (1.0) 448.9002 (1.0) 6.6608 (1.0) 2;0 2.2288 (1.0) 5 1
test_benchmark_read[full-seekable-rosbags] 502.2698 (1.13) 523.9009 (1.15) 510.3689 (1.14) 8.6200 (1.80) 506.1880 (1.13) 11.7877 (1.77) 1;0 1.9594 (0.88) 5 1
test_benchmark_read[full-seekable-pybag] 559.9649 (1.26) 596.3682 (1.31) 578.5393 (1.29) 13.1715 (2.75) 581.2666 (1.29) 15.2660 (2.29) 2;0 1.7285 (0.78) 5 1
test_benchmark_read[full-seekable-mcap] 574.9254 (1.30) 614.3929 (1.35) 594.9063 (1.33) 15.2217 (3.18) 593.9697 (1.32) 21.7823 (3.27) 2;0 1.6809 (0.75) 5 1
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Full File Read (Non-seekable Stream)
----------------------------------------------------------------------------------------- benchmark 'full-nonseekable': 2 tests ------------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_benchmark_read[full-nonseekable-small_mcap] 423.7839 (1.0) 454.8048 (1.0) 433.9779 (1.0) 12.7063 (1.0) 428.9654 (1.0) 14.9051 (1.0) 1;0 2.3043 (1.0) 5 1
test_benchmark_read[full-nonseekable-mcap] 595.2403 (1.40) 639.9618 (1.41) 616.2232 (1.42) 19.9259 (1.57) 607.5073 (1.42) 34.9823 (2.35) 2;0 1.6228 (0.70) 5 1
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Note: rosbags and pybag require seekable streams and are skipped for non-seekable tests.
Time-Range Filtered Read (Seekable)
---------------------------------------------------------------------------------------- benchmark 'time-seekable': 4 tests ----------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_benchmark_read[time-seekable-small_mcap] 119.7578 (1.0) 128.1060 (1.0) 123.7998 (1.0) 2.5372 (1.0) 123.7509 (1.0) 2.7058 (1.0) 3;0 8.0776 (1.0) 9 1
test_benchmark_read[time-seekable-pybag] 140.4680 (1.17) 159.2642 (1.24) 146.3533 (1.18) 6.4415 (2.54) 143.5709 (1.16) 6.2359 (2.30) 1;1 6.8328 (0.85) 7 1
test_benchmark_read[time-seekable-mcap] 146.3309 (1.22) 155.6906 (1.22) 150.6005 (1.22) 3.9600 (1.56) 150.2616 (1.21) 7.3775 (2.73) 2;0 6.6401 (0.82) 7 1
test_benchmark_read[time-seekable-rosbags] 509.0745 (4.25) 521.1191 (4.07) 512.3522 (4.14) 4.9971 (1.97) 510.4790 (4.13) 4.5978 (1.70) 1;1 1.9518 (0.24) 5 1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Topic-Filtered Read (Seekable)
----------------------------------------------------------------------------------------- benchmark 'topic-seekable': 4 tests -----------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_benchmark_read[topic-seekable-small_mcap] 442.4237 (1.0) 454.9705 (1.0) 446.8667 (1.0) 4.9801 (1.05) 445.4813 (1.0) 6.3691 (1.0) 1;0 2.2378 (1.0) 5 1
test_benchmark_read[topic-seekable-rosbags] 502.9512 (1.14) 514.8851 (1.13) 508.7413 (1.14) 4.7252 (1.0) 507.7358 (1.14) 7.3330 (1.15) 2;0 1.9656 (0.88) 5 1
test_benchmark_read[topic-seekable-pybag] 507.1222 (1.15) 536.2468 (1.18) 520.2659 (1.16) 12.1789 (2.58) 517.0470 (1.16) 20.4743 (3.21) 2;0 1.9221 (0.86) 5 1
test_benchmark_read[topic-seekable-mcap] 548.7598 (1.24) 560.8708 (1.23) 554.8000 (1.24) 5.2638 (1.11) 554.2846 (1.24) 9.4890 (1.49) 2;0 1.8025 (0.81) 5 1
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Summary:
- 1.13-1.42x faster than mcap (official) across all scenarios
- 1.14-4.14x faster than rosbags (especially for time-range filtering)
- 1.16-1.29x faster than pybag for seekable streams
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file small_mcap-0.1.2.tar.gz.
File metadata
- Download URL: small_mcap-0.1.2.tar.gz
- Upload date:
- Size: 31.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fd4b763c61d6b3f85aa9f3de5cec69a3eb2bc152f15ec2a76065b8a3a3a018d
|
|
| MD5 |
4b94d5538411c6cd750bfa7c24cdb763
|
|
| BLAKE2b-256 |
d7d414f6135370d05c2e78610951a1ca4f540c1abb69fe61ce738890cc40c1ad
|
File details
Details for the file small_mcap-0.1.2-py3-none-any.whl.
File metadata
- Download URL: small_mcap-0.1.2-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87c0fe239a678f925ad383d26c680e3395ca25637fa4a70e152ceac2476c3b58
|
|
| MD5 |
5a492f9bdfbb5bfb5a9c9c8301f2c7e9
|
|
| BLAKE2b-256 |
faf5f8e52d9cfb0e0ae18fb8820f7f8afc851a3cdd2c79b3f955ea90ede6985c
|