@eventual-ai/mcap-viewer 0.1.0-alpha.1 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/core/mcap/reader.d.ts +6 -1
- package/dist/core/mcap/reader.js +24 -4
- package/dist/data-loader/loader.js +1 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,9 @@ const recording = await loader.openSpan(result, { signal });
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Each manifest supplies one streamed MCAP URL. Payload requests omit control
|
|
52
|
-
credentials.
|
|
52
|
+
credentials. Playback covers the requested span, which the MCAP records in its
|
|
53
|
+
`mda` metadata, so `McapRecording.open` on a saved file starts at the same time
|
|
54
|
+
(`bounds: null` keeps the keyframe preroll). `discover` maps topic
|
|
53
55
|
names to canonical schemas and actual wire encodings:
|
|
54
56
|
|
|
55
57
|
```json
|
|
@@ -3,6 +3,11 @@ import type { LoadOptions, McapStreamSource } from "./source.js";
|
|
|
3
3
|
import { McapRecording } from "./recording.js";
|
|
4
4
|
import type { TimeBounds } from "./types.js";
|
|
5
5
|
export interface ReadOptions extends LoadOptions {
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Playback bounds. By default, the span MDA records in `mda` metadata
|
|
8
|
+
* (`start_ns`, exclusive `end_ns`), whose file starts at the preceding
|
|
9
|
+
* keyframe; `null` plays every message.
|
|
10
|
+
*/
|
|
11
|
+
bounds?: TimeBounds | null;
|
|
7
12
|
}
|
|
8
13
|
export declare function readMcap(input: Blob | McapStreamSource, options?: ReadOptions): Promise<McapRecording>;
|
package/dist/core/mcap/reader.js
CHANGED
|
@@ -39,13 +39,30 @@ export async function readMcap(input, options = {}) {
|
|
|
39
39
|
const byTopic = new Map();
|
|
40
40
|
const messages = [];
|
|
41
41
|
const metadata = new Map();
|
|
42
|
-
|
|
42
|
+
let recording = new McapRecording(metadata, channels, messages, byTopic, loading, options.bounds ?? undefined);
|
|
43
|
+
let opened = false;
|
|
43
44
|
let resolveReady;
|
|
44
45
|
let rejectReady;
|
|
45
46
|
const ready = new Promise((resolve, reject) => {
|
|
46
47
|
resolveReady = resolve;
|
|
47
48
|
rejectReady = reject;
|
|
48
49
|
});
|
|
50
|
+
const open = () => {
|
|
51
|
+
opened = true;
|
|
52
|
+
resolveReady(recording);
|
|
53
|
+
};
|
|
54
|
+
// MDA writes its metadata before any message, so bounds are set before `open`.
|
|
55
|
+
const applyMdaBounds = (record) => {
|
|
56
|
+
if (options.bounds !== undefined || opened)
|
|
57
|
+
return;
|
|
58
|
+
const start = record.get("start_ns");
|
|
59
|
+
const end = record.get("end_ns");
|
|
60
|
+
if (start !== undefined && end !== undefined)
|
|
61
|
+
recording = recording.withBounds({
|
|
62
|
+
start: BigInt(start),
|
|
63
|
+
end: BigInt(end) - 1n,
|
|
64
|
+
});
|
|
65
|
+
};
|
|
49
66
|
async function consume() {
|
|
50
67
|
let lastTime;
|
|
51
68
|
for await (const bytes of source.stream({ signal })) {
|
|
@@ -58,8 +75,11 @@ export async function readMcap(input, options = {}) {
|
|
|
58
75
|
signal.throwIfAborted();
|
|
59
76
|
if (record.type === "Schema")
|
|
60
77
|
schemas.set(record.id, record);
|
|
61
|
-
else if (record.type === "Metadata")
|
|
78
|
+
else if (record.type === "Metadata") {
|
|
62
79
|
metadata.set(record.name, record.metadata);
|
|
80
|
+
if (record.name === "mda")
|
|
81
|
+
applyMdaBounds(record.metadata);
|
|
82
|
+
}
|
|
63
83
|
else if (record.type === "Channel") {
|
|
64
84
|
// The summary section repeats channels; keep the objects messages reference.
|
|
65
85
|
if (channels.has(record.id))
|
|
@@ -104,14 +124,14 @@ export async function readMcap(input, options = {}) {
|
|
|
104
124
|
if (bufferedEnd !== undefined && bufferedEnd !== loading.bufferedEnd) {
|
|
105
125
|
loading.advance(bufferedEnd);
|
|
106
126
|
if (recording.bufferedEnd !== undefined)
|
|
107
|
-
|
|
127
|
+
open();
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
signal.throwIfAborted();
|
|
111
131
|
if (!reader.done())
|
|
112
132
|
throw new Error("Incomplete MCAP recording");
|
|
113
133
|
loading.finish();
|
|
114
|
-
|
|
134
|
+
open();
|
|
115
135
|
}
|
|
116
136
|
void consume().catch((error) => {
|
|
117
137
|
loading.fail(error);
|
|
@@ -39,14 +39,6 @@ export class DataLoader {
|
|
|
39
39
|
async openSpan(result, options = {}) {
|
|
40
40
|
if ("error" in result)
|
|
41
41
|
throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
42
|
-
|
|
43
|
-
if (options.bounds)
|
|
44
|
-
return recording;
|
|
45
|
-
const metadata = recording.metadata.get("mda");
|
|
46
|
-
const start = metadata?.get("start_ns");
|
|
47
|
-
const end = metadata?.get("end_ns");
|
|
48
|
-
return start !== undefined && end !== undefined
|
|
49
|
-
? recording.withBounds({ start: BigInt(start), end: BigInt(end) - 1n })
|
|
50
|
-
: recording;
|
|
42
|
+
return McapRecording.open(result.manifest.url, options);
|
|
51
43
|
}
|
|
52
44
|
}
|