Example 3/@videodb/robopeek-mcap
Build your own UI
No RoboPeek components and no RoboPeek CSS. openMcap() turns the file into a Recording that fills in as bytes arrive: camera topics with their frames (JPEG/PNG bytes, raw pixels or H.264), signals as numbers against time, text, and metadata. Everything on screen is written in this example. The same reader works in Vue, Svelte, a worker or Node.
3 MB · 3 cameras · H.264 video
Use your own URL or file
Files are read locally. Remote URLs must allow CORS and are fetched as supplied.
import { useEffect, useState } from 'react'
import { openMcap, type Progress, type Recording, type Source } from '@videodb/robopeek-mcap'
/** Everything here is plain @videodb/robopeek-mcap: no React components or styles from RoboPeek. */
export function useMcap(source: Source) {
const [recording, setRecording] = useState<Recording>()
const [version, setVersion] = useState(0)
const [progress, setProgress] = useState<Progress>({ loaded: 0 })
const [state, setState] = useState<'loading' | 'done' | 'error'>('loading')
const [error, setError] = useState<string>()
useEffect(() => {
const ac = new AbortController()
let unsubscribe = () => {}
;(async () => {
try {
// openMcap reads the summary (topics, duration); load() then streams the messages in.
const file = await openMcap(source, { signal: ac.signal })
unsubscribe = file.recording.subscribe(() => setVersion(file.recording.version))
setRecording(file.recording)
await file.load({ signal: ac.signal, onProgress: setProgress })
if (!ac.signal.aborted) setState('done')
} catch (e) {
if (ac.signal.aborted) return
setError((e as Error).message)
setState('error')
}
})()
return () => {
ac.abort()
unsubscribe()
}
}, [source])
// version changes whenever the recording grows, so components re-read its tracks.
return { recording, version, progress, state, error }
}The Code tab contains the source files used by this example.
