D
DreamLake

Preview

MCAP Preview

MCAP recordings (the open robotics container format from Foxglove). The previewer reads only the footer + summary — message bodies are never fetched.

Extensions: mcap

MCAP format brief

MCAP is a chunked binary format optimized for indexed access:

┌─────────────────────────┐
│ Magic                   │  8 bytes
├─────────────────────────┤
│ Header                  │
├─────────────────────────┤
│ Schemas, channels       │
├─────────────────────────┤
│ Chunks (messages)       │  ← bulk of the file (GBs)
...
├─────────────────────────┤
│ Statistics              │
│ Channel index           │  ← Summary section
│ Schema records          │
├─────────────────────────┤
│ Summary offset          │
├─────────────────────────┤
│ Footer (last 28 bytes)  │
│   Summary start offset  │
│   Summary CRC
│   Magic                 │
└─────────────────────────┘

The footer points to the summary; the summary describes everything in the file (schemas, channels, message counts, time ranges) without referencing message bodies.

IReadable adapter pattern

@mcap/core exposes McapIndexedReader.Initialize, which expects an IReadable:

interface IReadable {
  size(): Promise<bigint>
  read(offset: bigint, length: bigint): Promise<Uint8Array>
}

The previewer adapts a URL into an IReadable backed by Range requests:

const sizeResp = await fetcher(url, { method: 'HEAD' })  // or use prop-passed size
const total = BigInt(sizeResp.headers.get('Content-Length')!)
 
const readable: IReadable = {
  size: async () => total,
  read: async (offset, length) => {
    const start = Number(offset)
    const end = start + Number(length) - 1
    const r = await fetcher(url, { headers: { Range: `bytes=${start}-${end}` } })
    return new Uint8Array(await r.arrayBuffer())
  },
}
 
const reader = await McapIndexedReader.Initialize({ readable })

McapIndexedReader then issues two reads:

read 1:  bytes=<size-8192>-<size-1>     # footer + tail of summary
read 2:  bytes=<summaryStart>-<summaryEnd>  # rest of summary

Two requests, regardless of file size. A 50 GB MCAP previews in a few hundred KB.

What's shown

After initialization, the previewer reads reader.channelsById, reader.schemasById, and reader.statistics:

SectionContent
Header barFilename, size, library/profile from reader.header
StatisticsTotal messages, message-type counts, time range (first → last)
ChannelsTopic name, schema, message count, frequency (msgs/sec)
SchemasName, encoding (ROS, Protobuf, JSON, …), brief def preview

What's NOT shown

Message bodies are not fetched. <FilePreview> is for triage ("does this MCAP have the topics I expect, recorded over the time range I expect?"), not for inspecting payloads. For payload inspection, download and load in Foxglove Studio or the mcap CLI.

Live demo

Loading demo...
import { FilePreview } from '@vuer-ai/vuer-m3u/preview'

export function MyComponent() {
  return (
    <FilePreview url="/vuer-m3u-demo/preview/mcap/sample.mcap" />
  )
}

Cross-links