@mentra/miniapp 3.2.1-beta.319 → 3.2.1-beta.355
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 +16 -0
- package/dist/background/index.d.ts +5 -3
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -0
- package/dist/background/index.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/display.d.ts +33 -0
- package/dist/modules/display.d.ts.map +1 -1
- package/dist/modules/display.js +2 -0
- package/dist/modules/display.js.map +1 -1
- package/dist/modules/meeting.d.ts +66 -6
- package/dist/modules/meeting.d.ts.map +1 -1
- package/dist/modules/meeting.js +72 -19
- package/dist/modules/meeting.js.map +1 -1
- package/dist/modules/stream.d.ts +46 -0
- package/dist/modules/stream.d.ts.map +1 -1
- package/dist/modules/stream.js +107 -0
- package/dist/modules/stream.js.map +1 -1
- package/dist/protocol.d.ts +39 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +36 -0
- package/dist/protocol.js.map +1 -1
- package/dist/react/StreamPreview.d.ts +41 -0
- package/dist/react/StreamPreview.d.ts.map +1 -0
- package/dist/react/StreamPreview.js +144 -0
- package/dist/react/StreamPreview.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/stream-preview/PreviewConnection.d.ts +139 -0
- package/dist/react/stream-preview/PreviewConnection.d.ts.map +1 -0
- package/dist/react/stream-preview/PreviewConnection.js +462 -0
- package/dist/react/stream-preview/PreviewConnection.js.map +1 -0
- package/dist/react/stream-preview/controlChannel.d.ts +75 -0
- package/dist/react/stream-preview/controlChannel.d.ts.map +1 -0
- package/dist/react/stream-preview/controlChannel.js +55 -0
- package/dist/react/stream-preview/controlChannel.js.map +1 -0
- package/dist/react/stream-preview/creditLoop.d.ts +81 -0
- package/dist/react/stream-preview/creditLoop.d.ts.map +1 -0
- package/dist/react/stream-preview/creditLoop.js +146 -0
- package/dist/react/stream-preview/creditLoop.js.map +1 -0
- package/dist/react/stream-preview/protocol.d.ts +113 -0
- package/dist/react/stream-preview/protocol.d.ts.map +1 -0
- package/dist/react/stream-preview/protocol.js +148 -0
- package/dist/react/stream-preview/protocol.js.map +1 -0
- package/dist/react/stream-preview/resizeCoalescer.d.ts +38 -0
- package/dist/react/stream-preview/resizeCoalescer.d.ts.map +1 -0
- package/dist/react/stream-preview/resizeCoalescer.js +71 -0
- package/dist/react/stream-preview/resizeCoalescer.js.map +1 -0
- package/dist/react/stream-preview/testFrames.d.ts +38 -0
- package/dist/react/stream-preview/testFrames.d.ts.map +1 -0
- package/dist/react/stream-preview/testFrames.js +50 -0
- package/dist/react/stream-preview/testFrames.js.map +1 -0
- package/dist/react/stream-preview/tiers.d.ts +21 -0
- package/dist/react/stream-preview/tiers.d.ts.map +1 -0
- package/dist/react/stream-preview/tiers.js +33 -0
- package/dist/react/stream-preview/tiers.js.map +1 -0
- package/dist/react/stream-preview/trace.d.ts +23 -0
- package/dist/react/stream-preview/trace.d.ts.map +1 -0
- package/dist/react/stream-preview/trace.js +71 -0
- package/dist/react/stream-preview/trace.js.map +1 -0
- package/dist/react/stream-preview/transport.d.ts +79 -0
- package/dist/react/stream-preview/transport.d.ts.map +1 -0
- package/dist/react/stream-preview/transport.js +209 -0
- package/dist/react/stream-preview/transport.js.map +1 -0
- package/dist/react/stream-preview/yuvRenderer.d.ts +31 -0
- package/dist/react/stream-preview/yuvRenderer.d.ts.map +1 -0
- package/dist/react/stream-preview/yuvRenderer.js +398 -0
- package/dist/react/stream-preview/yuvRenderer.js.map +1 -0
- package/dist/session.d.ts +5 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +5 -0
- package/dist/session.js.map +1 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/package.json +8 -3
- package/src/background/index.ts +14 -1
- package/src/index.ts +15 -0
- package/src/modules/display.ts +37 -0
- package/src/modules/meeting.ts +133 -23
- package/src/modules/stream.ts +153 -1
- package/src/protocol.ts +75 -0
- package/src/react/StreamPreview.tsx +187 -0
- package/src/react/index.ts +2 -0
- package/src/react/stream-preview/PreviewConnection.ts +552 -0
- package/src/react/stream-preview/controlChannel.ts +119 -0
- package/src/react/stream-preview/creditLoop.ts +189 -0
- package/src/react/stream-preview/protocol.ts +227 -0
- package/src/react/stream-preview/resizeCoalescer.ts +88 -0
- package/src/react/stream-preview/testFrames.ts +86 -0
- package/src/react/stream-preview/tiers.ts +37 -0
- package/src/react/stream-preview/trace.ts +81 -0
- package/src/react/stream-preview/transport.ts +242 -0
- package/src/react/stream-preview/yuvRenderer.ts +453 -0
- package/src/session.ts +8 -0
- package/src/ui/index.ts +2 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reserved `_preview` UI channel, as seen from the page.
|
|
3
|
+
*
|
|
4
|
+
* The Mentra App answers this channel itself; it never reaches the miniapp's background. Requests
|
|
5
|
+
* ride `mentra.request`, and host events arrive on `mentra.on("_preview")`. Every control request
|
|
6
|
+
* carries the page identity (`docGen`, `token`, `mountEpoch`) so the host can drop operations
|
|
7
|
+
* from a document or a component that has since been replaced.
|
|
8
|
+
*/
|
|
9
|
+
/** `injectFault` is honoured only in debug builds or with the host's hidden developer setting. */
|
|
10
|
+
export type PreviewCommand = "handshake" | "configure" | "start" | "stop" | "rendererError" | "injectFault";
|
|
11
|
+
export interface PreviewControlRequest {
|
|
12
|
+
cmd: PreviewCommand;
|
|
13
|
+
/** 0 until the first handshake has told the page which document it is. */
|
|
14
|
+
docGen: number;
|
|
15
|
+
token?: string;
|
|
16
|
+
mountEpoch: number;
|
|
17
|
+
/** `configure` only: rendered box in device pixels, after rotation. */
|
|
18
|
+
boxWidth?: number;
|
|
19
|
+
boxHeight?: number;
|
|
20
|
+
/** `rendererError` only. */
|
|
21
|
+
message?: string;
|
|
22
|
+
/** `injectFault` only: `ack_delay`, `ack_drop`, `transport_close`, `pack_throw`, `sink_throw`, `clear`. */
|
|
23
|
+
kind?: string;
|
|
24
|
+
ms?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface PreviewDocumentConfig {
|
|
27
|
+
t: "config";
|
|
28
|
+
protocolVersion: number;
|
|
29
|
+
transport: "webmessage" | "websocket";
|
|
30
|
+
url?: string;
|
|
31
|
+
portName?: string;
|
|
32
|
+
token: string;
|
|
33
|
+
docGen: number;
|
|
34
|
+
}
|
|
35
|
+
/** `stale`: the host minted a newer document while this handshake was in flight. */
|
|
36
|
+
export type PreviewHandshakeResult = PreviewDocumentConfig | {
|
|
37
|
+
t: "waiting_for_lease";
|
|
38
|
+
docGen: number;
|
|
39
|
+
} | {
|
|
40
|
+
t?: undefined;
|
|
41
|
+
stale: true;
|
|
42
|
+
};
|
|
43
|
+
/** Reply to configure/start/stop. `stale` means the host dropped it; that is not an error. */
|
|
44
|
+
export interface PreviewControlReply {
|
|
45
|
+
applied?: boolean;
|
|
46
|
+
stale?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export type PreviewHostEvent = PreviewDocumentConfig | ({
|
|
49
|
+
t: "status";
|
|
50
|
+
} & Record<string, unknown>) | {
|
|
51
|
+
t: "lease_available";
|
|
52
|
+
} | {
|
|
53
|
+
t: "lease_ended";
|
|
54
|
+
reason: string;
|
|
55
|
+
} | {
|
|
56
|
+
t: "error";
|
|
57
|
+
code: string;
|
|
58
|
+
docGen?: number;
|
|
59
|
+
} | {
|
|
60
|
+
t: "paused_background";
|
|
61
|
+
} | {
|
|
62
|
+
t: "resumed";
|
|
63
|
+
};
|
|
64
|
+
/** A `_preview` request the host refused. `code` is a preview error code. */
|
|
65
|
+
export declare class PreviewControlError extends Error {
|
|
66
|
+
readonly code: string;
|
|
67
|
+
constructor(code: string, message?: string);
|
|
68
|
+
}
|
|
69
|
+
export interface PreviewControlChannel {
|
|
70
|
+
request(request: PreviewControlRequest): Promise<unknown>;
|
|
71
|
+
onEvent(cb: (event: PreviewHostEvent) => void): () => void;
|
|
72
|
+
}
|
|
73
|
+
/** The channel over the injected `window.mentra` bridge. Null outside a Mentra App WebView. */
|
|
74
|
+
export declare function createMentraControlChannel(): PreviewControlChannel | null;
|
|
75
|
+
//# sourceMappingURL=controlChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlChannel.d.ts","sourceRoot":"","sources":["../../../src/react/stream-preview/controlChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,kGAAkG;AAClG,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,eAAe,GAAG,aAAa,CAAA;AAE3G,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,cAAc,CAAA;IACnB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2GAA2G;IAC3G,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,qBAAqB;IACpC,CAAC,EAAE,QAAQ,CAAA;IACX,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAA;IACrC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,oFAAoF;AACpF,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB;IAAC,CAAC,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,GACxC;IAAC,CAAC,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAC,CAAA;AAEhC,8FAA8F;AAC9F,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,MAAM,gBAAgB,GACxB,qBAAqB,GACrB,CAAC;IAAC,CAAC,EAAE,QAAQ,CAAA;CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC;IAAC,CAAC,EAAE,iBAAiB,CAAA;CAAC,GACtB;IAAC,CAAC,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,GAClC;IAAC,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAC,GAC3C;IAAC,CAAC,EAAE,mBAAmB,CAAA;CAAC,GACxB;IAAC,CAAC,EAAE,SAAS,CAAA;CAAC,CAAA;AAElB,6EAA6E;AAC7E,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAET,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAK3C;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACzD,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;CAC3D;AAgBD,+FAA+F;AAC/F,wBAAgB,0BAA0B,IAAI,qBAAqB,GAAG,IAAI,CA0BzE"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reserved `_preview` UI channel, as seen from the page.
|
|
3
|
+
*
|
|
4
|
+
* The Mentra App answers this channel itself; it never reaches the miniapp's background. Requests
|
|
5
|
+
* ride `mentra.request`, and host events arrive on `mentra.on("_preview")`. Every control request
|
|
6
|
+
* carries the page identity (`docGen`, `token`, `mountEpoch`) so the host can drop operations
|
|
7
|
+
* from a document or a component that has since been replaced.
|
|
8
|
+
*/
|
|
9
|
+
import { PREVIEW_UI_CHANNEL } from "../../protocol";
|
|
10
|
+
/** A `_preview` request the host refused. `code` is a preview error code. */
|
|
11
|
+
export class PreviewControlError extends Error {
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message ?? code);
|
|
14
|
+
this.name = "PreviewControlError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Host replies are local and fast; this only bounds a host that never answers. */
|
|
19
|
+
const REQUEST_TIMEOUT_MS = 10000;
|
|
20
|
+
function mentraGlobal() {
|
|
21
|
+
const candidate = globalThis.mentra;
|
|
22
|
+
if (!candidate || typeof candidate.request !== "function" || typeof candidate.on !== "function")
|
|
23
|
+
return null;
|
|
24
|
+
return candidate;
|
|
25
|
+
}
|
|
26
|
+
/** The channel over the injected `window.mentra` bridge. Null outside a Mentra App WebView. */
|
|
27
|
+
export function createMentraControlChannel() {
|
|
28
|
+
const mentra = mentraGlobal();
|
|
29
|
+
if (!mentra)
|
|
30
|
+
return null;
|
|
31
|
+
return {
|
|
32
|
+
async request(request) {
|
|
33
|
+
try {
|
|
34
|
+
return await mentra.request(PREVIEW_UI_CHANNEL, request, { timeout: REQUEST_TIMEOUT_MS });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const value = (error ?? {});
|
|
38
|
+
const code = typeof value.code === "string"
|
|
39
|
+
? value.code
|
|
40
|
+
: value.name === "MentraRpcTimeoutError"
|
|
41
|
+
? "transport_failed"
|
|
42
|
+
: "unsupported";
|
|
43
|
+
throw new PreviewControlError(code, typeof value.message === "string" ? value.message : undefined);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
onEvent(cb) {
|
|
47
|
+
return mentra.on(PREVIEW_UI_CHANNEL, (payload) => {
|
|
48
|
+
if (payload && typeof payload === "object" && typeof payload.t === "string") {
|
|
49
|
+
cb(payload);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=controlChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlChannel.js","sourceRoot":"","sources":["../../../src/react/stream-preview/controlChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,kBAAkB,EAAC,MAAM,gBAAgB,CAAA;AAoDjD,6EAA6E;AAC7E,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAG5C,YAAY,IAAY,EAAE,OAAgB;QACxC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAA;QACtB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF;AAYD,mFAAmF;AACnF,MAAM,kBAAkB,GAAG,KAAM,CAAA;AAEjC,SAAS,YAAY;IACnB,MAAM,SAAS,GAAI,UAAwD,CAAC,MAAM,CAAA;IAClF,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,UAAU;QAAE,OAAO,IAAI,CAAA;IAC5G,OAAO,SAAuB,CAAA;AAChC,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,0BAA0B;IACxC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAC7B,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,OAAO;YACnB,IAAI,CAAC;gBACH,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,OAAO,EAAE,EAAC,OAAO,EAAE,kBAAkB,EAAC,CAAC,CAAA;YACzF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE,CAAwD,CAAA;gBAClF,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;oBAC5B,CAAC,CAAC,KAAK,CAAC,IAAI;oBACZ,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,uBAAuB;wBACtC,CAAC,CAAC,kBAAkB;wBACpB,CAAC,CAAC,aAAa,CAAA;gBACrB,MAAM,IAAI,mBAAmB,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YACpG,CAAC;QACH,CAAC;QACD,OAAO,CAAC,EAAE;YACR,OAAO,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE;gBAC/C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAQ,OAAyB,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;oBAC/F,EAAE,CAAC,OAA2B,CAAC,CAAA;gBACjC,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer side of the one-frame credit scheme.
|
|
3
|
+
*
|
|
4
|
+
* The native sender holds exactly one frame in flight and will not send the next until this side
|
|
5
|
+
* acknowledges the last, so there is no queue anywhere and a slow consumer shows up as a lower
|
|
6
|
+
* frame rate rather than as growing latency. This module is the half that makes that true: parse,
|
|
7
|
+
* draw, ack — and never hold a reference to a frame after its ack.
|
|
8
|
+
*
|
|
9
|
+
* Durations here would be `performance.now()` and describe JavaScript only; the header's
|
|
10
|
+
* `timestampNs` / `sentAtNs` are a native monotonic clock with a different epoch. End-to-end
|
|
11
|
+
* latency is measured natively (it sees both the send and the ack on one clock).
|
|
12
|
+
*/
|
|
13
|
+
import { type ParsedFrame, type PreviewParseErrorReason } from "./protocol";
|
|
14
|
+
export interface CreditLoopFrameSummary {
|
|
15
|
+
gen: number;
|
|
16
|
+
seq: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
pixelFormat: string;
|
|
20
|
+
rotationQuarters: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CreditLoopStats {
|
|
23
|
+
framesReceived: number;
|
|
24
|
+
drawsSubmitted: number;
|
|
25
|
+
acksSent: number;
|
|
26
|
+
parseErrors: number;
|
|
27
|
+
parseErrorsByReason: Partial<Record<PreviewParseErrorReason, number>>;
|
|
28
|
+
/**
|
|
29
|
+
* Frames that arrived while another was still being processed. Native guarantees one at a time,
|
|
30
|
+
* so any value above zero means the credit scheme is broken.
|
|
31
|
+
*/
|
|
32
|
+
overlapped: number;
|
|
33
|
+
/** Frames the sender skipped, counted from gaps in the header sequence. */
|
|
34
|
+
sequenceGaps: number;
|
|
35
|
+
skippedBySender: number;
|
|
36
|
+
lastError: string | null;
|
|
37
|
+
lastFrame: CreditLoopFrameSummary | null;
|
|
38
|
+
}
|
|
39
|
+
export interface CreditLoopOptions {
|
|
40
|
+
sendAck: (gen: number, seq: number) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Submit the frame. Returns the submit duration in ms, or null when there is no renderer (the
|
|
43
|
+
* component is unmounted or its GL context is lost). The frame is acked either way.
|
|
44
|
+
*/
|
|
45
|
+
draw?: (frame: ParsedFrame) => number | null;
|
|
46
|
+
/** Called once per rejected frame, before anything else happens to it. */
|
|
47
|
+
onParseError?: (reason: PreviewParseErrorReason, detail?: string) => void;
|
|
48
|
+
}
|
|
49
|
+
export declare class CreditLoop {
|
|
50
|
+
private readonly options;
|
|
51
|
+
private readonly errorsByReason;
|
|
52
|
+
private idleWaiters;
|
|
53
|
+
private inFlightCount;
|
|
54
|
+
private framesReceived;
|
|
55
|
+
private drawsSubmitted;
|
|
56
|
+
private acksSent;
|
|
57
|
+
private parseErrors;
|
|
58
|
+
private overlapped;
|
|
59
|
+
private sequenceGaps;
|
|
60
|
+
private skippedBySender;
|
|
61
|
+
private lastSeq;
|
|
62
|
+
private lastGen;
|
|
63
|
+
private lastError;
|
|
64
|
+
private lastFrame;
|
|
65
|
+
private draw;
|
|
66
|
+
constructor(options: CreditLoopOptions);
|
|
67
|
+
/** Swap the renderer without touching the credit state. Null means frames are acked undrawn. */
|
|
68
|
+
setDraw(draw: CreditLoopOptions["draw"] | null): void;
|
|
69
|
+
/** Frames currently being processed. The credit scheme means this is 0 or 1. */
|
|
70
|
+
get inFlight(): number;
|
|
71
|
+
/** Entry point for the transport. Deliberately not awaited: the transport must not block. */
|
|
72
|
+
handleBinary(buffer: ArrayBuffer): void;
|
|
73
|
+
/** Resolves once nothing is being processed. */
|
|
74
|
+
idle(): Promise<void>;
|
|
75
|
+
recordError(reason: string): void;
|
|
76
|
+
snapshot(): CreditLoopStats;
|
|
77
|
+
reset(): void;
|
|
78
|
+
private process;
|
|
79
|
+
private trackSequence;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=creditLoop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creditLoop.d.ts","sourceRoot":"","sources":["../../../src/react/stream-preview/creditLoop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAoB,KAAK,WAAW,EAAE,KAAK,uBAAuB,EAAC,MAAM,YAAY,CAAA;AAE5F,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAA;IACrE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAAA;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,GAAG,IAAI,CAAA;IAC5C,0EAA0E;IAC1E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC1E;AAED,qBAAa,UAAU;IAiBT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhBpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6C;IAC5E,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,IAAI,CAA2B;gBAEV,OAAO,EAAE,iBAAiB;IAIvD,gGAAgG;IAChG,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI;IAIrD,gFAAgF;IAChF,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,6FAA6F;IAC7F,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAavC,gDAAgD;IAChD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIjC,QAAQ,IAAI,eAAe;IAiB3B,KAAK,IAAI,IAAI;YAeC,OAAO;IAiCrB,OAAO,CAAC,aAAa;CAetB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer side of the one-frame credit scheme.
|
|
3
|
+
*
|
|
4
|
+
* The native sender holds exactly one frame in flight and will not send the next until this side
|
|
5
|
+
* acknowledges the last, so there is no queue anywhere and a slow consumer shows up as a lower
|
|
6
|
+
* frame rate rather than as growing latency. This module is the half that makes that true: parse,
|
|
7
|
+
* draw, ack — and never hold a reference to a frame after its ack.
|
|
8
|
+
*
|
|
9
|
+
* Durations here would be `performance.now()` and describe JavaScript only; the header's
|
|
10
|
+
* `timestampNs` / `sentAtNs` are a native monotonic clock with a different epoch. End-to-end
|
|
11
|
+
* latency is measured natively (it sees both the send and the ack on one clock).
|
|
12
|
+
*/
|
|
13
|
+
import { parsePreviewFrame } from "./protocol";
|
|
14
|
+
export class CreditLoop {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.errorsByReason = new Map();
|
|
18
|
+
this.idleWaiters = [];
|
|
19
|
+
this.inFlightCount = 0;
|
|
20
|
+
this.framesReceived = 0;
|
|
21
|
+
this.drawsSubmitted = 0;
|
|
22
|
+
this.acksSent = 0;
|
|
23
|
+
this.parseErrors = 0;
|
|
24
|
+
this.overlapped = 0;
|
|
25
|
+
this.sequenceGaps = 0;
|
|
26
|
+
this.skippedBySender = 0;
|
|
27
|
+
this.lastSeq = null;
|
|
28
|
+
this.lastGen = null;
|
|
29
|
+
this.lastError = null;
|
|
30
|
+
this.lastFrame = null;
|
|
31
|
+
this.draw = options.draw;
|
|
32
|
+
}
|
|
33
|
+
/** Swap the renderer without touching the credit state. Null means frames are acked undrawn. */
|
|
34
|
+
setDraw(draw) {
|
|
35
|
+
this.draw = draw ?? undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Frames currently being processed. The credit scheme means this is 0 or 1. */
|
|
38
|
+
get inFlight() {
|
|
39
|
+
return this.inFlightCount;
|
|
40
|
+
}
|
|
41
|
+
/** Entry point for the transport. Deliberately not awaited: the transport must not block. */
|
|
42
|
+
handleBinary(buffer) {
|
|
43
|
+
if (this.inFlightCount > 0)
|
|
44
|
+
this.overlapped += 1;
|
|
45
|
+
this.inFlightCount += 1;
|
|
46
|
+
void this.process(buffer).finally(() => {
|
|
47
|
+
this.inFlightCount -= 1;
|
|
48
|
+
if (this.inFlightCount === 0) {
|
|
49
|
+
const waiters = this.idleWaiters;
|
|
50
|
+
this.idleWaiters = [];
|
|
51
|
+
for (const resolve of waiters)
|
|
52
|
+
resolve();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Resolves once nothing is being processed. */
|
|
57
|
+
idle() {
|
|
58
|
+
if (this.inFlightCount === 0)
|
|
59
|
+
return Promise.resolve();
|
|
60
|
+
return new Promise((resolve) => this.idleWaiters.push(resolve));
|
|
61
|
+
}
|
|
62
|
+
recordError(reason) {
|
|
63
|
+
this.lastError = reason;
|
|
64
|
+
}
|
|
65
|
+
snapshot() {
|
|
66
|
+
const parseErrorsByReason = {};
|
|
67
|
+
for (const [reason, count] of this.errorsByReason)
|
|
68
|
+
parseErrorsByReason[reason] = count;
|
|
69
|
+
return {
|
|
70
|
+
framesReceived: this.framesReceived,
|
|
71
|
+
drawsSubmitted: this.drawsSubmitted,
|
|
72
|
+
acksSent: this.acksSent,
|
|
73
|
+
parseErrors: this.parseErrors,
|
|
74
|
+
parseErrorsByReason,
|
|
75
|
+
overlapped: this.overlapped,
|
|
76
|
+
sequenceGaps: this.sequenceGaps,
|
|
77
|
+
skippedBySender: this.skippedBySender,
|
|
78
|
+
lastError: this.lastError,
|
|
79
|
+
lastFrame: this.lastFrame,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
reset() {
|
|
83
|
+
this.framesReceived = 0;
|
|
84
|
+
this.drawsSubmitted = 0;
|
|
85
|
+
this.acksSent = 0;
|
|
86
|
+
this.parseErrors = 0;
|
|
87
|
+
this.overlapped = 0;
|
|
88
|
+
this.sequenceGaps = 0;
|
|
89
|
+
this.skippedBySender = 0;
|
|
90
|
+
this.lastSeq = null;
|
|
91
|
+
this.lastGen = null;
|
|
92
|
+
this.lastError = null;
|
|
93
|
+
this.lastFrame = null;
|
|
94
|
+
this.errorsByReason.clear();
|
|
95
|
+
}
|
|
96
|
+
async process(buffer) {
|
|
97
|
+
this.framesReceived += 1;
|
|
98
|
+
const parsed = parsePreviewFrame(buffer);
|
|
99
|
+
if (!parsed.ok) {
|
|
100
|
+
// No ack: a frame this side could not read is not a frame it consumed, and acking it would
|
|
101
|
+
// hide the fault behind a healthy-looking rate.
|
|
102
|
+
this.parseErrors += 1;
|
|
103
|
+
this.errorsByReason.set(parsed.reason, (this.errorsByReason.get(parsed.reason) ?? 0) + 1);
|
|
104
|
+
this.lastError = parsed.detail ? `${parsed.reason} (${parsed.detail})` : parsed.reason;
|
|
105
|
+
this.options.onParseError?.(parsed.reason, parsed.detail);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.trackSequence(parsed);
|
|
109
|
+
this.lastFrame = {
|
|
110
|
+
gen: parsed.sessionGen,
|
|
111
|
+
seq: parsed.frameSeq,
|
|
112
|
+
width: parsed.width,
|
|
113
|
+
height: parsed.height,
|
|
114
|
+
pixelFormat: parsed.pixelFormat,
|
|
115
|
+
rotationQuarters: parsed.rotationQuarters,
|
|
116
|
+
};
|
|
117
|
+
if (this.draw) {
|
|
118
|
+
const submitMs = this.draw(parsed);
|
|
119
|
+
if (submitMs === null)
|
|
120
|
+
this.lastError = "draw_failed";
|
|
121
|
+
else
|
|
122
|
+
this.drawsSubmitted += 1;
|
|
123
|
+
}
|
|
124
|
+
// Ack last, after the draw has been submitted, so the credit the sender gets back means "this
|
|
125
|
+
// side is done with that frame" rather than "this side received some bytes". A dead canvas
|
|
126
|
+
// still acks: withholding credit would stall the sender on top of it.
|
|
127
|
+
this.options.sendAck(parsed.sessionGen, parsed.frameSeq);
|
|
128
|
+
this.acksSent += 1;
|
|
129
|
+
}
|
|
130
|
+
trackSequence(parsed) {
|
|
131
|
+
if (this.lastGen !== parsed.sessionGen) {
|
|
132
|
+
this.lastGen = parsed.sessionGen;
|
|
133
|
+
this.lastSeq = parsed.frameSeq;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (this.lastSeq !== null) {
|
|
137
|
+
const delta = parsed.frameSeq - this.lastSeq;
|
|
138
|
+
if (delta > 1) {
|
|
139
|
+
this.sequenceGaps += 1;
|
|
140
|
+
this.skippedBySender += delta - 1;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.lastSeq = parsed.frameSeq;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=creditLoop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creditLoop.js","sourceRoot":"","sources":["../../../src/react/stream-preview/creditLoop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format for raw decoded-frame preview (`MFPV`): a 64-byte little-endian header followed by
|
|
3
|
+
* a tightly packed 8-bit YUV payload.
|
|
4
|
+
*
|
|
5
|
+
* Nothing in this module may copy the payload. `parsePreviewFrame` returns `Uint8Array` views onto
|
|
6
|
+
* the caller's ArrayBuffer and the renderer uploads those views straight to WebGL; a `.slice()`
|
|
7
|
+
* anywhere in this path turns a zero-copy preview into a per-frame memcpy.
|
|
8
|
+
*
|
|
9
|
+
* Bad input is expected — a truncated or stale frame is a measurement, not an exception — so the
|
|
10
|
+
* parser returns a discriminated union and never throws.
|
|
11
|
+
*
|
|
12
|
+
* offset size field
|
|
13
|
+
* 0 4 magic "MFPV"
|
|
14
|
+
* 4 2 version u16
|
|
15
|
+
* 6 2 headerLen u16
|
|
16
|
+
* 8 4 payloadLen u32
|
|
17
|
+
* 12 4 sessionGen u32
|
|
18
|
+
* 16 4 frameSeq u32
|
|
19
|
+
* 20 2 width u16
|
|
20
|
+
* 22 2 height u16
|
|
21
|
+
* 24 1 pixelFormat u8 (1 = I420, 2 = NV12)
|
|
22
|
+
* 25 1 rotation u8 (quarter turns clockwise)
|
|
23
|
+
* 26 1 colorMatrix u8 (0 unknown, 1 BT.601, 2 BT.709)
|
|
24
|
+
* 27 1 colorRange u8 (0 unknown, 1 limited, 2 full)
|
|
25
|
+
* 28 2 flags u16 (bit 0: the sender fell back on default colour metadata)
|
|
26
|
+
* 30 2 reserved u16
|
|
27
|
+
* 32 8 timestampNs i64 (native monotonic decode time, 0 when unknown)
|
|
28
|
+
* 40 8 sentAtNs i64 (native monotonic at the send call)
|
|
29
|
+
* 48 16 reserved
|
|
30
|
+
*/
|
|
31
|
+
/** Fixed header size. A sender that disagrees is rejected rather than guessed at. */
|
|
32
|
+
export declare const PREVIEW_FRAME_HEADER_BYTES = 64;
|
|
33
|
+
export declare const PREVIEW_FRAME_VERSION = 1;
|
|
34
|
+
/** ASCII "MFPV". */
|
|
35
|
+
export declare const PREVIEW_FRAME_MAGIC: readonly [77, 70, 80, 86];
|
|
36
|
+
/** Neither the glasses nor a phone camera reaches this; anything above it is a corrupt header. */
|
|
37
|
+
export declare const PREVIEW_FRAME_MAX_DIMENSION = 4096;
|
|
38
|
+
export declare const PIXEL_FORMAT_I420 = 1;
|
|
39
|
+
export declare const PIXEL_FORMAT_NV12 = 2;
|
|
40
|
+
export type PreviewPixelFormat = "i420" | "nv12";
|
|
41
|
+
export type PreviewColorMatrix = "bt601" | "bt709";
|
|
42
|
+
export type PreviewColorRange = "limited" | "full";
|
|
43
|
+
/**
|
|
44
|
+
* One code per rejected frame, because "the frame was bad" is not a finding. A run that drops
|
|
45
|
+
* frames on `truncated-payload` and one that drops them on `payload-size-mismatch` point at
|
|
46
|
+
* different halves of the native sender.
|
|
47
|
+
*/
|
|
48
|
+
export type PreviewParseErrorReason = "short-buffer" | "bad-magic" | "unsupported-version" | "bad-header-length" | "bad-dimensions" | "unknown-pixel-format" | "payload-size-mismatch" | "truncated-payload";
|
|
49
|
+
export declare const PREVIEW_PARSE_ERROR_REASONS: readonly PreviewParseErrorReason[];
|
|
50
|
+
interface ParsedFrameBase {
|
|
51
|
+
ok: true;
|
|
52
|
+
sessionGen: number;
|
|
53
|
+
frameSeq: number;
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
/** Quarter turns clockwise, normalized to 0..3. */
|
|
57
|
+
rotationQuarters: number;
|
|
58
|
+
colorMatrix: PreviewColorMatrix;
|
|
59
|
+
colorRange: PreviewColorRange;
|
|
60
|
+
/** The header said "unknown" and BT.601 was assumed. */
|
|
61
|
+
colorMatrixAssumed: boolean;
|
|
62
|
+
/** The header said "unknown" and limited range was assumed. */
|
|
63
|
+
colorRangeAssumed: boolean;
|
|
64
|
+
/** Header flag bit 0: the native sender itself had to fall back on default colour metadata. */
|
|
65
|
+
senderColorFallback: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Native monotonic clock, nanoseconds. Never subtract a `performance.now()` value from these:
|
|
68
|
+
* the two clocks share no epoch. Only native-to-native differences mean anything.
|
|
69
|
+
*/
|
|
70
|
+
timestampNs: bigint;
|
|
71
|
+
sentAtNs: bigint;
|
|
72
|
+
yStride: number;
|
|
73
|
+
chromaWidth: number;
|
|
74
|
+
chromaHeight: number;
|
|
75
|
+
chromaStride: number;
|
|
76
|
+
payloadLen: number;
|
|
77
|
+
}
|
|
78
|
+
export interface ParsedI420Frame extends ParsedFrameBase {
|
|
79
|
+
pixelFormat: "i420";
|
|
80
|
+
y: Uint8Array;
|
|
81
|
+
u: Uint8Array;
|
|
82
|
+
v: Uint8Array;
|
|
83
|
+
}
|
|
84
|
+
export interface ParsedNv12Frame extends ParsedFrameBase {
|
|
85
|
+
pixelFormat: "nv12";
|
|
86
|
+
y: Uint8Array;
|
|
87
|
+
/** Interleaved U,V pairs: `chromaStride` is 2 × `chromaWidth` bytes. */
|
|
88
|
+
uv: Uint8Array;
|
|
89
|
+
}
|
|
90
|
+
export type ParsedFrame = ParsedI420Frame | ParsedNv12Frame;
|
|
91
|
+
export interface PreviewParseError {
|
|
92
|
+
ok: false;
|
|
93
|
+
reason: PreviewParseErrorReason;
|
|
94
|
+
/** Numbers the reason alone does not carry, for the log line. */
|
|
95
|
+
detail?: string;
|
|
96
|
+
}
|
|
97
|
+
export type PreviewParseResult = ParsedFrame | PreviewParseError;
|
|
98
|
+
/** Chroma plane dimensions for 4:2:0, which both supported formats are. */
|
|
99
|
+
export declare function chromaDimensions(width: number, height: number): {
|
|
100
|
+
width: number;
|
|
101
|
+
height: number;
|
|
102
|
+
};
|
|
103
|
+
/** Bytes a tightly packed I420 or NV12 frame occupies. Both layouts are the same size. */
|
|
104
|
+
export declare function packedPayloadBytes(width: number, height: number): number;
|
|
105
|
+
/**
|
|
106
|
+
* Validate a frame and expose its planes.
|
|
107
|
+
*
|
|
108
|
+
* The plane fields are views onto `buffer`, so they stay valid only as long as the caller holds
|
|
109
|
+
* that buffer and nothing else writes into it.
|
|
110
|
+
*/
|
|
111
|
+
export declare function parsePreviewFrame(buffer: ArrayBuffer): PreviewParseResult;
|
|
112
|
+
export {};
|
|
113
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/react/stream-preview/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAC5C,eAAO,MAAM,qBAAqB,IAAI,CAAA;AACtC,oBAAoB;AACpB,eAAO,MAAM,mBAAmB,2BAAoC,CAAA;AACpE,kGAAkG;AAClG,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAE/C,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAClC,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAElC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAA;AAChD,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,OAAO,CAAA;AAClD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,CAAA;AAElD;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAC/B,cAAc,GACd,WAAW,GACX,qBAAqB,GACrB,mBAAmB,GACnB,gBAAgB,GAChB,sBAAsB,GACtB,uBAAuB,GACvB,mBAAmB,CAAA;AAEvB,eAAO,MAAM,2BAA2B,EAAE,SAAS,uBAAuB,EASzE,CAAA;AAED,UAAU,eAAe;IACvB,EAAE,EAAE,IAAI,CAAA;IACR,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,kBAAkB,CAAA;IAC/B,UAAU,EAAE,iBAAiB,CAAA;IAC7B,wDAAwD;IACxD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,iBAAiB,EAAE,OAAO,CAAA;IAC1B,+FAA+F;IAC/F,mBAAmB,EAAE,OAAO,CAAA;IAC5B;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB,CAAC,EAAE,UAAU,CAAA;IACb,CAAC,EAAE,UAAU,CAAA;IACb,CAAC,EAAE,UAAU,CAAA;CACd;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB,CAAC,EAAE,UAAU,CAAA;IACb,wEAAwE;IACxE,EAAE,EAAE,UAAU,CAAA;CACf;AAED,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,eAAe,CAAA;AAE3D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,uBAAuB,CAAA;IAC/B,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,iBAAiB,CAAA;AAEhE,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAE/F;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxE;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,kBAAkB,CA+EzE"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format for raw decoded-frame preview (`MFPV`): a 64-byte little-endian header followed by
|
|
3
|
+
* a tightly packed 8-bit YUV payload.
|
|
4
|
+
*
|
|
5
|
+
* Nothing in this module may copy the payload. `parsePreviewFrame` returns `Uint8Array` views onto
|
|
6
|
+
* the caller's ArrayBuffer and the renderer uploads those views straight to WebGL; a `.slice()`
|
|
7
|
+
* anywhere in this path turns a zero-copy preview into a per-frame memcpy.
|
|
8
|
+
*
|
|
9
|
+
* Bad input is expected — a truncated or stale frame is a measurement, not an exception — so the
|
|
10
|
+
* parser returns a discriminated union and never throws.
|
|
11
|
+
*
|
|
12
|
+
* offset size field
|
|
13
|
+
* 0 4 magic "MFPV"
|
|
14
|
+
* 4 2 version u16
|
|
15
|
+
* 6 2 headerLen u16
|
|
16
|
+
* 8 4 payloadLen u32
|
|
17
|
+
* 12 4 sessionGen u32
|
|
18
|
+
* 16 4 frameSeq u32
|
|
19
|
+
* 20 2 width u16
|
|
20
|
+
* 22 2 height u16
|
|
21
|
+
* 24 1 pixelFormat u8 (1 = I420, 2 = NV12)
|
|
22
|
+
* 25 1 rotation u8 (quarter turns clockwise)
|
|
23
|
+
* 26 1 colorMatrix u8 (0 unknown, 1 BT.601, 2 BT.709)
|
|
24
|
+
* 27 1 colorRange u8 (0 unknown, 1 limited, 2 full)
|
|
25
|
+
* 28 2 flags u16 (bit 0: the sender fell back on default colour metadata)
|
|
26
|
+
* 30 2 reserved u16
|
|
27
|
+
* 32 8 timestampNs i64 (native monotonic decode time, 0 when unknown)
|
|
28
|
+
* 40 8 sentAtNs i64 (native monotonic at the send call)
|
|
29
|
+
* 48 16 reserved
|
|
30
|
+
*/
|
|
31
|
+
/** Fixed header size. A sender that disagrees is rejected rather than guessed at. */
|
|
32
|
+
export const PREVIEW_FRAME_HEADER_BYTES = 64;
|
|
33
|
+
export const PREVIEW_FRAME_VERSION = 1;
|
|
34
|
+
/** ASCII "MFPV". */
|
|
35
|
+
export const PREVIEW_FRAME_MAGIC = [0x4d, 0x46, 0x50, 0x56];
|
|
36
|
+
/** Neither the glasses nor a phone camera reaches this; anything above it is a corrupt header. */
|
|
37
|
+
export const PREVIEW_FRAME_MAX_DIMENSION = 4096;
|
|
38
|
+
export const PIXEL_FORMAT_I420 = 1;
|
|
39
|
+
export const PIXEL_FORMAT_NV12 = 2;
|
|
40
|
+
export const PREVIEW_PARSE_ERROR_REASONS = [
|
|
41
|
+
"short-buffer",
|
|
42
|
+
"bad-magic",
|
|
43
|
+
"unsupported-version",
|
|
44
|
+
"bad-header-length",
|
|
45
|
+
"bad-dimensions",
|
|
46
|
+
"unknown-pixel-format",
|
|
47
|
+
"payload-size-mismatch",
|
|
48
|
+
"truncated-payload",
|
|
49
|
+
];
|
|
50
|
+
/** Chroma plane dimensions for 4:2:0, which both supported formats are. */
|
|
51
|
+
export function chromaDimensions(width, height) {
|
|
52
|
+
return { width: Math.ceil(width / 2), height: Math.ceil(height / 2) };
|
|
53
|
+
}
|
|
54
|
+
/** Bytes a tightly packed I420 or NV12 frame occupies. Both layouts are the same size. */
|
|
55
|
+
export function packedPayloadBytes(width, height) {
|
|
56
|
+
const chroma = chromaDimensions(width, height);
|
|
57
|
+
return width * height + 2 * chroma.width * chroma.height;
|
|
58
|
+
}
|
|
59
|
+
function fail(reason, detail) {
|
|
60
|
+
return detail === undefined ? { ok: false, reason } : { ok: false, reason, detail };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate a frame and expose its planes.
|
|
64
|
+
*
|
|
65
|
+
* The plane fields are views onto `buffer`, so they stay valid only as long as the caller holds
|
|
66
|
+
* that buffer and nothing else writes into it.
|
|
67
|
+
*/
|
|
68
|
+
export function parsePreviewFrame(buffer) {
|
|
69
|
+
if (buffer.byteLength < PREVIEW_FRAME_HEADER_BYTES) {
|
|
70
|
+
return fail("short-buffer", `${buffer.byteLength} bytes`);
|
|
71
|
+
}
|
|
72
|
+
const header = new DataView(buffer, 0, PREVIEW_FRAME_HEADER_BYTES);
|
|
73
|
+
const magic = new Uint8Array(buffer, 0, PREVIEW_FRAME_MAGIC.length);
|
|
74
|
+
if (PREVIEW_FRAME_MAGIC.some((byte, index) => magic[index] !== byte)) {
|
|
75
|
+
return fail("bad-magic", Array.from(magic, (byte) => byte.toString(16).padStart(2, "0")).join(" "));
|
|
76
|
+
}
|
|
77
|
+
const version = header.getUint16(4, true);
|
|
78
|
+
if (version !== PREVIEW_FRAME_VERSION)
|
|
79
|
+
return fail("unsupported-version", `v${version}`);
|
|
80
|
+
const headerLen = header.getUint16(6, true);
|
|
81
|
+
if (headerLen !== PREVIEW_FRAME_HEADER_BYTES)
|
|
82
|
+
return fail("bad-header-length", `${headerLen} bytes`);
|
|
83
|
+
const payloadLen = header.getUint32(8, true);
|
|
84
|
+
const sessionGen = header.getUint32(12, true);
|
|
85
|
+
const frameSeq = header.getUint32(16, true);
|
|
86
|
+
const width = header.getUint16(20, true);
|
|
87
|
+
const height = header.getUint16(22, true);
|
|
88
|
+
if (width === 0 || height === 0 || width > PREVIEW_FRAME_MAX_DIMENSION || height > PREVIEW_FRAME_MAX_DIMENSION) {
|
|
89
|
+
return fail("bad-dimensions", `${width}x${height}`);
|
|
90
|
+
}
|
|
91
|
+
const formatCode = header.getUint8(24);
|
|
92
|
+
if (formatCode !== PIXEL_FORMAT_I420 && formatCode !== PIXEL_FORMAT_NV12) {
|
|
93
|
+
return fail("unknown-pixel-format", `code ${formatCode}`);
|
|
94
|
+
}
|
|
95
|
+
const expectedPayload = packedPayloadBytes(width, height);
|
|
96
|
+
if (payloadLen !== expectedPayload) {
|
|
97
|
+
return fail("payload-size-mismatch", `${payloadLen} bytes, expected ${expectedPayload}`);
|
|
98
|
+
}
|
|
99
|
+
if (buffer.byteLength < headerLen + payloadLen) {
|
|
100
|
+
return fail("truncated-payload", `${buffer.byteLength - headerLen} of ${payloadLen} bytes`);
|
|
101
|
+
}
|
|
102
|
+
const matrixCode = header.getUint8(26);
|
|
103
|
+
const rangeCode = header.getUint8(27);
|
|
104
|
+
const flags = header.getUint16(28, true);
|
|
105
|
+
const chroma = chromaDimensions(width, height);
|
|
106
|
+
const luma = width * height;
|
|
107
|
+
const base = {
|
|
108
|
+
ok: true,
|
|
109
|
+
sessionGen,
|
|
110
|
+
frameSeq,
|
|
111
|
+
width,
|
|
112
|
+
height,
|
|
113
|
+
rotationQuarters: header.getUint8(25) % 4,
|
|
114
|
+
// Unknown metadata is not a rejection: BT.601 limited is what an untagged 8-bit camera frame
|
|
115
|
+
// almost always is. The assumption is surfaced so a wrong-looking picture can be traced to it
|
|
116
|
+
// instead of to the shader.
|
|
117
|
+
colorMatrix: matrixCode === 2 ? "bt709" : "bt601",
|
|
118
|
+
colorRange: rangeCode === 2 ? "full" : "limited",
|
|
119
|
+
colorMatrixAssumed: matrixCode !== 1 && matrixCode !== 2,
|
|
120
|
+
colorRangeAssumed: rangeCode !== 1 && rangeCode !== 2,
|
|
121
|
+
senderColorFallback: (flags & 0x0001) !== 0,
|
|
122
|
+
timestampNs: header.getBigInt64(32, true),
|
|
123
|
+
sentAtNs: header.getBigInt64(40, true),
|
|
124
|
+
yStride: width,
|
|
125
|
+
chromaWidth: chroma.width,
|
|
126
|
+
chromaHeight: chroma.height,
|
|
127
|
+
chromaStride: formatCode === PIXEL_FORMAT_NV12 ? chroma.width * 2 : chroma.width,
|
|
128
|
+
payloadLen,
|
|
129
|
+
};
|
|
130
|
+
// Subviews, never copies.
|
|
131
|
+
if (formatCode === PIXEL_FORMAT_NV12) {
|
|
132
|
+
return {
|
|
133
|
+
...base,
|
|
134
|
+
pixelFormat: "nv12",
|
|
135
|
+
y: new Uint8Array(buffer, headerLen, luma),
|
|
136
|
+
uv: new Uint8Array(buffer, headerLen + luma, 2 * chroma.width * chroma.height),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const chromaPlane = chroma.width * chroma.height;
|
|
140
|
+
return {
|
|
141
|
+
...base,
|
|
142
|
+
pixelFormat: "i420",
|
|
143
|
+
y: new Uint8Array(buffer, headerLen, luma),
|
|
144
|
+
u: new Uint8Array(buffer, headerLen + luma, chromaPlane),
|
|
145
|
+
v: new Uint8Array(buffer, headerLen + luma + chromaPlane, chromaPlane),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/react/stream-preview/protocol.ts"],"names":[],"mappings":"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"}
|