@mentra/engine 3.2.0-dev.226 → 3.2.0-dev.227

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.
Files changed (33) hide show
  1. package/build/generated/releaseMetadata.js +5 -5
  2. package/build/generated/releaseMetadata.js.map +1 -1
  3. package/build/services/AcsMeetingService.d.ts +3 -1
  4. package/build/services/AcsMeetingService.d.ts.map +1 -1
  5. package/build/services/AcsMeetingService.js +9 -1
  6. package/build/services/AcsMeetingService.js.map +1 -1
  7. package/build/services/GlassesHotspotLease.d.ts +2 -0
  8. package/build/services/GlassesHotspotLease.d.ts.map +1 -0
  9. package/build/services/GlassesHotspotLease.js +13 -0
  10. package/build/services/GlassesHotspotLease.js.map +1 -0
  11. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  12. package/build/services/LocalMiniappRuntime.js +14 -5
  13. package/build/services/LocalMiniappRuntime.js.map +1 -1
  14. package/build/services/ManagedWebRtcRelay.d.ts +87 -0
  15. package/build/services/ManagedWebRtcRelay.d.ts.map +1 -0
  16. package/build/services/ManagedWebRtcRelay.js +239 -0
  17. package/build/services/ManagedWebRtcRelay.js.map +1 -0
  18. package/build/services/PhoneStreamCoordinator.d.ts +5 -0
  19. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  20. package/build/services/PhoneStreamCoordinator.js +131 -68
  21. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  22. package/build/services/SoftapCallTransport.d.ts +2 -1
  23. package/build/services/SoftapCallTransport.d.ts.map +1 -1
  24. package/build/services/SoftapCallTransport.js +6 -1
  25. package/build/services/SoftapCallTransport.js.map +1 -1
  26. package/package.json +8 -7
  27. package/src/generated/releaseMetadata.ts +5 -5
  28. package/src/services/AcsMeetingService.ts +10 -1
  29. package/src/services/GlassesHotspotLease.ts +11 -0
  30. package/src/services/LocalMiniappRuntime.ts +27 -11
  31. package/src/services/ManagedWebRtcRelay.ts +278 -0
  32. package/src/services/PhoneStreamCoordinator.ts +152 -70
  33. package/src/services/SoftapCallTransport.ts +8 -3
@@ -0,0 +1,87 @@
1
+ import type { StreamStartRequest, StreamStatusEvent } from "@mentra/bluetooth-sdk/internal";
2
+ export interface RelayOptions {
3
+ streamId: string;
4
+ ingestUrl: string;
5
+ video?: StreamStartRequest["video"];
6
+ audio?: StreamStartRequest["audio"];
7
+ captureAudio?: boolean;
8
+ sound?: boolean;
9
+ }
10
+ interface NativeRelayEvent {
11
+ attemptId: string;
12
+ state: string;
13
+ reason: string;
14
+ }
15
+ interface NativeRelay {
16
+ prepare(options: {
17
+ attemptId: string;
18
+ ingestUrl: string;
19
+ ssid: string;
20
+ password: string;
21
+ gatewayAddress?: string;
22
+ captureAudio: boolean;
23
+ bitrate: number;
24
+ }): Promise<string>;
25
+ stop(attemptId: string): Promise<void>;
26
+ addListener(event: "onRelayState", listener: (event: NativeRelayEvent) => void): {
27
+ remove(): void;
28
+ };
29
+ }
30
+ export interface RelayDependencies {
31
+ native: NativeRelay;
32
+ hotspot(enabled: boolean): Promise<{
33
+ state: string;
34
+ ssid?: string;
35
+ password?: string;
36
+ localIp?: string;
37
+ }>;
38
+ startGlasses(request: StreamStartRequest): Promise<StreamStatusEvent | undefined>;
39
+ stopGlasses(): Promise<unknown>;
40
+ deferredStop(): void;
41
+ connected(): boolean;
42
+ sleep(ms: number): Promise<void>;
43
+ now(): number;
44
+ acquire(): () => void;
45
+ }
46
+ export interface ManagedRelay {
47
+ start(): Promise<StreamStatusEvent | undefined>;
48
+ cancel(): void;
49
+ stop(): Promise<void>;
50
+ owns(streamId: string): boolean;
51
+ handleGlassesStatus(event: StreamStatusEvent): void;
52
+ }
53
+ /** One attempt at a time; media stays native. All retries unwind both peers and the hotspot. */
54
+ export declare class ManagedWebRtcRelay implements ManagedRelay {
55
+ private readonly options;
56
+ private readonly onStatus;
57
+ private readonly onFailure;
58
+ private readonly deps;
59
+ private cancelled;
60
+ private started;
61
+ private attempt;
62
+ private attemptId;
63
+ private attemptError;
64
+ private hotspotTouched;
65
+ private nativeTouched;
66
+ private glassesTouched;
67
+ private release;
68
+ private listener;
69
+ private operation;
70
+ private restarting;
71
+ private retries;
72
+ private readyAt;
73
+ private stopping;
74
+ constructor(options: RelayOptions, onStatus: (status: string, reason: string) => void, onFailure: (error: Error) => void, deps: RelayDependencies);
75
+ start(): Promise<StreamStatusEvent | undefined>;
76
+ cancel(): void;
77
+ owns(streamId: string): boolean;
78
+ handleGlassesStatus(event: StreamStatusEvent): void;
79
+ private checkpoint;
80
+ private startAttempt;
81
+ private failed;
82
+ stop(): Promise<void>;
83
+ private cleanupAttempt;
84
+ }
85
+ export declare function createManagedWebRtcRelay(options: RelayOptions, onStatus: (status: string, reason: string) => void, onFailure: (error: Error) => void, connected: () => boolean, deferredStop: () => void): ManagedRelay;
86
+ export {};
87
+ //# sourceMappingURL=ManagedWebRtcRelay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManagedWebRtcRelay.d.ts","sourceRoot":"","sources":["../../src/services/ManagedWebRtcRelay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,gCAAgC,CAAA;AAGzF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AACD,UAAU,WAAW;IACnB,OAAO,CAAC,OAAO,EAAE;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;QAChB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,YAAY,EAAE,OAAO,CAAA;QACrB,OAAO,EAAE,MAAM,CAAA;KAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACnB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG;QAAC,MAAM,IAAI,IAAI,CAAA;KAAC,CAAA;CAClG;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IACvG,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;IACjF,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,YAAY,IAAI,IAAI,CAAA;IACpB,SAAS,IAAI,OAAO,CAAA;IACpB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC,GAAG,IAAI,MAAM,CAAA;IACb,OAAO,IAAI,MAAM,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;IAC/C,MAAM,IAAI,IAAI,CAAA;IACd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/B,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAA;CACpD;AAED,gGAAgG;AAChG,qBAAa,kBAAmB,YAAW,YAAY;IAkBnD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IApBvB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,QAAQ,CAA6B;gBAG1B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,EAClD,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EACjC,IAAI,EAAE,iBAAiB;IAG1C,KAAK,IAAI,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAc/C,MAAM,IAAI,IAAI;IAId,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAK/B,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAOnD,OAAO,CAAC,UAAU;YAKJ,YAAY;IAsC1B,OAAO,CAAC,MAAM;IA6Cd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBP,cAAc;CAoC7B;AAMD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,EAClD,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EACjC,SAAS,EAAE,MAAM,OAAO,EACxB,YAAY,EAAE,MAAM,IAAI,GACvB,YAAY,CAgBd"}
@@ -0,0 +1,239 @@
1
+ import BluetoothSdk from "@mentra/bluetooth-sdk/internal";
2
+ import { acquireGlassesHotspot } from "./GlassesHotspotLease";
3
+ /** One attempt at a time; media stays native. All retries unwind both peers and the hotspot. */
4
+ export class ManagedWebRtcRelay {
5
+ options;
6
+ onStatus;
7
+ onFailure;
8
+ deps;
9
+ cancelled = false;
10
+ started = false;
11
+ attempt = 0;
12
+ attemptId = null;
13
+ attemptError = null;
14
+ hotspotTouched = false;
15
+ nativeTouched = false;
16
+ glassesTouched = false;
17
+ release = null;
18
+ listener = null;
19
+ operation = Promise.resolve();
20
+ restarting = false;
21
+ retries = 0;
22
+ readyAt = null;
23
+ stopping = null;
24
+ constructor(options, onStatus, onFailure, deps) {
25
+ this.options = options;
26
+ this.onStatus = onStatus;
27
+ this.onFailure = onFailure;
28
+ this.deps = deps;
29
+ }
30
+ start() {
31
+ if (this.started)
32
+ return Promise.reject(new Error("Relay already started"));
33
+ this.started = true;
34
+ this.release = this.deps.acquire();
35
+ this.listener = this.deps.native.addListener("onRelayState", (event) => {
36
+ if (event.attemptId !== this.attemptId || this.cancelled)
37
+ return;
38
+ if (event.state === "failed")
39
+ this.failed(new Error(event.reason));
40
+ else
41
+ this.onStatus(event.state, event.reason);
42
+ });
43
+ const start = this.startAttempt();
44
+ this.operation = start;
45
+ return start;
46
+ }
47
+ cancel() {
48
+ this.cancelled = true;
49
+ }
50
+ owns(streamId) {
51
+ // Consume late status from all attempts without allowing it to affect the current attempt.
52
+ return streamId.startsWith(`${this.options.streamId}-relay-`);
53
+ }
54
+ handleGlassesStatus(event) {
55
+ if (event.streamId !== this.attemptId || this.cancelled)
56
+ return;
57
+ if (event.status === "stopped" || event.terminal) {
58
+ this.failed(new Error("Glasses publisher stopped"));
59
+ }
60
+ }
61
+ checkpoint() {
62
+ if (this.cancelled)
63
+ throw new Error("Relay cancelled");
64
+ if (this.attemptError)
65
+ throw this.attemptError;
66
+ }
67
+ async startAttempt() {
68
+ this.attemptId = `${this.options.streamId}-relay-${++this.attempt}`;
69
+ this.attemptError = null;
70
+ this.checkpoint();
71
+ this.hotspotTouched = true; // A timed-out BLE command may still have enabled the AP.
72
+ const hotspot = await this.deps.hotspot(true);
73
+ this.checkpoint();
74
+ if (hotspot.state !== "enabled" || !hotspot.ssid || !hotspot.password)
75
+ throw new Error("Glasses hotspot did not start");
76
+ await this.deps.sleep(3_000); // Same beacon/DHCP startup allowance as ACS.
77
+ this.checkpoint();
78
+ this.nativeTouched = true;
79
+ const url = await this.deps.native.prepare({
80
+ attemptId: this.attemptId,
81
+ ingestUrl: this.options.ingestUrl,
82
+ ssid: hotspot.ssid,
83
+ password: hotspot.password,
84
+ gatewayAddress: hotspot.localIp,
85
+ captureAudio: this.options.captureAudio !== false,
86
+ bitrate: this.options.video?.bitrate ?? 2_000_000,
87
+ });
88
+ this.checkpoint();
89
+ this.glassesTouched = true;
90
+ const result = await this.deps.startGlasses({
91
+ type: "start_stream",
92
+ streamId: this.attemptId,
93
+ streamUrl: url,
94
+ ice: { stun: "" },
95
+ sound: this.options.sound ?? true,
96
+ captureAudio: this.options.captureAudio !== false,
97
+ ...(this.options.video !== undefined ? { video: this.options.video } : {}),
98
+ ...(this.options.audio !== undefined ? { audio: this.options.audio } : {}),
99
+ });
100
+ this.checkpoint();
101
+ this.readyAt = this.deps.now();
102
+ return result;
103
+ }
104
+ failed(error) {
105
+ if (this.cancelled || this.attemptError)
106
+ return;
107
+ this.attemptError = error;
108
+ // Bound consecutive trouble, not the lifetime number of recoveries in a long stream.
109
+ if (this.readyAt !== null && this.deps.now() - this.readyAt >= 60_000)
110
+ this.retries = 0;
111
+ this.readyAt = null;
112
+ if (this.restarting)
113
+ return;
114
+ this.restarting = true;
115
+ // Await startup before cleanup: late native/BLE success still belongs to this attempt.
116
+ this.operation = this.operation
117
+ .catch(() => undefined)
118
+ .then(async () => {
119
+ while (!this.cancelled) {
120
+ try {
121
+ await this.cleanupAttempt();
122
+ if (this.cancelled)
123
+ return;
124
+ if (this.retries >= 3)
125
+ throw error;
126
+ this.onStatus("reconnecting", error.message);
127
+ await this.deps.sleep(1_000 * 2 ** this.retries++);
128
+ if (this.cancelled)
129
+ return;
130
+ await this.startAttempt();
131
+ this.onStatus("reconnected", "Glasses relay restarted");
132
+ return;
133
+ }
134
+ catch (failure) {
135
+ // Cleanup failure retains ownership; never create another stream on uncertain state.
136
+ if (this.hotspotTouched || this.nativeTouched || this.glassesTouched) {
137
+ try {
138
+ await this.cleanupAttempt();
139
+ }
140
+ catch (cleanupError) {
141
+ if (!this.cancelled)
142
+ this.onFailure(asError(cleanupError));
143
+ return;
144
+ }
145
+ }
146
+ if (this.retries >= 3) {
147
+ if (!this.cancelled)
148
+ this.onFailure(asError(failure));
149
+ return;
150
+ }
151
+ }
152
+ }
153
+ })
154
+ .finally(() => {
155
+ this.restarting = false;
156
+ });
157
+ }
158
+ stop() {
159
+ this.cancel();
160
+ if (this.stopping)
161
+ return this.stopping;
162
+ this.listener?.remove();
163
+ this.listener = null;
164
+ this.stopping = this.operation
165
+ .catch(() => undefined)
166
+ .then(async () => {
167
+ await this.cleanupAttempt();
168
+ this.release?.();
169
+ this.release = null;
170
+ })
171
+ .finally(() => {
172
+ this.stopping = null;
173
+ });
174
+ return this.stopping;
175
+ }
176
+ async cleanupAttempt() {
177
+ const failures = [];
178
+ const step = async (action) => {
179
+ try {
180
+ await action();
181
+ }
182
+ catch (error) {
183
+ failures.push(error);
184
+ }
185
+ };
186
+ // Invalidate callbacks before intentionally stopping the glasses or dropping the network.
187
+ const id = this.attemptId;
188
+ this.attemptId = null;
189
+ if (this.glassesTouched)
190
+ await step(async () => {
191
+ if (this.deps.connected())
192
+ await this.deps.stopGlasses();
193
+ else
194
+ this.deps.deferredStop();
195
+ this.glassesTouched = false;
196
+ });
197
+ if (this.nativeTouched && id)
198
+ await step(async () => {
199
+ await this.deps.native.stop(id);
200
+ this.nativeTouched = false;
201
+ });
202
+ if (this.hotspotTouched)
203
+ await step(async () => {
204
+ if (this.deps.connected()) {
205
+ const result = await this.deps.hotspot(false);
206
+ if (result.state !== "disabled")
207
+ throw new Error("Glasses hotspot shutdown was not confirmed");
208
+ }
209
+ else
210
+ this.deps.deferredStop();
211
+ this.hotspotTouched = false;
212
+ });
213
+ if (failures.length) {
214
+ this.attemptId = id; // Retain the native attempt token for a subsequent cleanup retry.
215
+ throw new Error(`Relay cleanup failed: ${failures.map((error) => asError(error).message).join("; ")}`);
216
+ }
217
+ }
218
+ }
219
+ function asError(error) {
220
+ return error instanceof Error ? error : new Error(String(error));
221
+ }
222
+ export function createManagedWebRtcRelay(options, onStatus, onFailure, connected, deferredStop) {
223
+ // Load only for managed WHIP. Hosts using direct SRT/RTMP need no relay module.
224
+ const { requireNativeModule } = require("expo-modules-core");
225
+ const { BgTimer } = require("../utils/timers");
226
+ const native = requireNativeModule("MentraGlassesMediaRelay");
227
+ return new ManagedWebRtcRelay(options, onStatus, onFailure, {
228
+ native,
229
+ hotspot: (enabled) => BluetoothSdk.setHotspotState(enabled),
230
+ startGlasses: (request) => BluetoothSdk.startStream(request),
231
+ stopGlasses: () => BluetoothSdk.stopStream(),
232
+ connected,
233
+ deferredStop,
234
+ now: Date.now,
235
+ sleep: (ms) => new Promise((resolve) => BgTimer.setTimeout(resolve, ms)),
236
+ acquire: acquireGlassesHotspot,
237
+ });
238
+ }
239
+ //# sourceMappingURL=ManagedWebRtcRelay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManagedWebRtcRelay.js","sourceRoot":"","sources":["../../src/services/ManagedWebRtcRelay.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {StreamStartRequest, StreamStatusEvent} from \"@mentra/bluetooth-sdk/internal\"\nimport {acquireGlassesHotspot} from \"./GlassesHotspotLease\"\n\nexport interface RelayOptions {\n streamId: string\n ingestUrl: string\n video?: StreamStartRequest[\"video\"]\n audio?: StreamStartRequest[\"audio\"]\n captureAudio?: boolean\n sound?: boolean\n}\n\ninterface NativeRelayEvent {\n attemptId: string\n state: string\n reason: string\n}\ninterface NativeRelay {\n prepare(options: {\n attemptId: string\n ingestUrl: string\n ssid: string\n password: string\n gatewayAddress?: string\n captureAudio: boolean\n bitrate: number\n }): Promise<string>\n stop(attemptId: string): Promise<void>\n addListener(event: \"onRelayState\", listener: (event: NativeRelayEvent) => void): {remove(): void}\n}\n\nexport interface RelayDependencies {\n native: NativeRelay\n hotspot(enabled: boolean): Promise<{state: string; ssid?: string; password?: string; localIp?: string}>\n startGlasses(request: StreamStartRequest): Promise<StreamStatusEvent | undefined>\n stopGlasses(): Promise<unknown>\n deferredStop(): void\n connected(): boolean\n sleep(ms: number): Promise<void>\n now(): number\n acquire(): () => void\n}\n\nexport interface ManagedRelay {\n start(): Promise<StreamStatusEvent | undefined>\n cancel(): void\n stop(): Promise<void>\n owns(streamId: string): boolean\n handleGlassesStatus(event: StreamStatusEvent): void\n}\n\n/** One attempt at a time; media stays native. All retries unwind both peers and the hotspot. */\nexport class ManagedWebRtcRelay implements ManagedRelay {\n private cancelled = false\n private started = false\n private attempt = 0\n private attemptId: string | null = null\n private attemptError: Error | null = null\n private hotspotTouched = false\n private nativeTouched = false\n private glassesTouched = false\n private release: (() => void) | null = null\n private listener: {remove(): void} | null = null\n private operation: Promise<unknown> = Promise.resolve()\n private restarting = false\n private retries = 0\n private readyAt: number | null = null\n private stopping: Promise<void> | null = null\n\n constructor(\n private readonly options: RelayOptions,\n private readonly onStatus: (status: string, reason: string) => void,\n private readonly onFailure: (error: Error) => void,\n private readonly deps: RelayDependencies,\n ) {}\n\n start(): Promise<StreamStatusEvent | undefined> {\n if (this.started) return Promise.reject(new Error(\"Relay already started\"))\n this.started = true\n this.release = this.deps.acquire()\n this.listener = this.deps.native.addListener(\"onRelayState\", (event) => {\n if (event.attemptId !== this.attemptId || this.cancelled) return\n if (event.state === \"failed\") this.failed(new Error(event.reason))\n else this.onStatus(event.state, event.reason)\n })\n const start = this.startAttempt()\n this.operation = start\n return start\n }\n\n cancel(): void {\n this.cancelled = true\n }\n\n owns(streamId: string): boolean {\n // Consume late status from all attempts without allowing it to affect the current attempt.\n return streamId.startsWith(`${this.options.streamId}-relay-`)\n }\n\n handleGlassesStatus(event: StreamStatusEvent): void {\n if (event.streamId !== this.attemptId || this.cancelled) return\n if (event.status === \"stopped\" || event.terminal) {\n this.failed(new Error(\"Glasses publisher stopped\"))\n }\n }\n\n private checkpoint(): void {\n if (this.cancelled) throw new Error(\"Relay cancelled\")\n if (this.attemptError) throw this.attemptError\n }\n\n private async startAttempt(): Promise<StreamStatusEvent | undefined> {\n this.attemptId = `${this.options.streamId}-relay-${++this.attempt}`\n this.attemptError = null\n this.checkpoint()\n this.hotspotTouched = true // A timed-out BLE command may still have enabled the AP.\n const hotspot = await this.deps.hotspot(true)\n this.checkpoint()\n if (hotspot.state !== \"enabled\" || !hotspot.ssid || !hotspot.password)\n throw new Error(\"Glasses hotspot did not start\")\n await this.deps.sleep(3_000) // Same beacon/DHCP startup allowance as ACS.\n this.checkpoint()\n this.nativeTouched = true\n const url = await this.deps.native.prepare({\n attemptId: this.attemptId,\n ingestUrl: this.options.ingestUrl,\n ssid: hotspot.ssid,\n password: hotspot.password,\n gatewayAddress: hotspot.localIp,\n captureAudio: this.options.captureAudio !== false,\n bitrate: this.options.video?.bitrate ?? 2_000_000,\n })\n this.checkpoint()\n this.glassesTouched = true\n const result = await this.deps.startGlasses({\n type: \"start_stream\",\n streamId: this.attemptId,\n streamUrl: url,\n ice: {stun: \"\"},\n sound: this.options.sound ?? true,\n captureAudio: this.options.captureAudio !== false,\n ...(this.options.video !== undefined ? {video: this.options.video} : {}),\n ...(this.options.audio !== undefined ? {audio: this.options.audio} : {}),\n })\n this.checkpoint()\n this.readyAt = this.deps.now()\n return result\n }\n\n private failed(error: Error): void {\n if (this.cancelled || this.attemptError) return\n this.attemptError = error\n // Bound consecutive trouble, not the lifetime number of recoveries in a long stream.\n if (this.readyAt !== null && this.deps.now() - this.readyAt >= 60_000) this.retries = 0\n this.readyAt = null\n if (this.restarting) return\n this.restarting = true\n // Await startup before cleanup: late native/BLE success still belongs to this attempt.\n this.operation = this.operation\n .catch(() => undefined)\n .then(async () => {\n while (!this.cancelled) {\n try {\n await this.cleanupAttempt()\n if (this.cancelled) return\n if (this.retries >= 3) throw error\n this.onStatus(\"reconnecting\", error.message)\n await this.deps.sleep(1_000 * 2 ** this.retries++)\n if (this.cancelled) return\n await this.startAttempt()\n this.onStatus(\"reconnected\", \"Glasses relay restarted\")\n return\n } catch (failure) {\n // Cleanup failure retains ownership; never create another stream on uncertain state.\n if (this.hotspotTouched || this.nativeTouched || this.glassesTouched) {\n try {\n await this.cleanupAttempt()\n } catch (cleanupError) {\n if (!this.cancelled) this.onFailure(asError(cleanupError))\n return\n }\n }\n if (this.retries >= 3) {\n if (!this.cancelled) this.onFailure(asError(failure))\n return\n }\n }\n }\n })\n .finally(() => {\n this.restarting = false\n })\n }\n\n stop(): Promise<void> {\n this.cancel()\n if (this.stopping) return this.stopping\n this.listener?.remove()\n this.listener = null\n this.stopping = this.operation\n .catch(() => undefined)\n .then(async () => {\n await this.cleanupAttempt()\n this.release?.()\n this.release = null\n })\n .finally(() => {\n this.stopping = null\n })\n return this.stopping\n }\n\n private async cleanupAttempt(): Promise<void> {\n const failures: unknown[] = []\n const step = async (action: () => Promise<void>) => {\n try {\n await action()\n } catch (error) {\n failures.push(error)\n }\n }\n // Invalidate callbacks before intentionally stopping the glasses or dropping the network.\n const id = this.attemptId\n this.attemptId = null\n if (this.glassesTouched)\n await step(async () => {\n if (this.deps.connected()) await this.deps.stopGlasses()\n else this.deps.deferredStop()\n this.glassesTouched = false\n })\n if (this.nativeTouched && id)\n await step(async () => {\n await this.deps.native.stop(id)\n this.nativeTouched = false\n })\n if (this.hotspotTouched)\n await step(async () => {\n if (this.deps.connected()) {\n const result = await this.deps.hotspot(false)\n if (result.state !== \"disabled\") throw new Error(\"Glasses hotspot shutdown was not confirmed\")\n } else this.deps.deferredStop()\n this.hotspotTouched = false\n })\n if (failures.length) {\n this.attemptId = id // Retain the native attempt token for a subsequent cleanup retry.\n throw new Error(`Relay cleanup failed: ${failures.map((error) => asError(error).message).join(\"; \")}`)\n }\n }\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nexport function createManagedWebRtcRelay(\n options: RelayOptions,\n onStatus: (status: string, reason: string) => void,\n onFailure: (error: Error) => void,\n connected: () => boolean,\n deferredStop: () => void,\n): ManagedRelay {\n // Load only for managed WHIP. Hosts using direct SRT/RTMP need no relay module.\n const {requireNativeModule} = require(\"expo-modules-core\") as typeof import(\"expo-modules-core\")\n const {BgTimer} = require(\"../utils/timers\") as typeof import(\"../utils/timers\")\n const native = requireNativeModule<NativeRelay>(\"MentraGlassesMediaRelay\")\n return new ManagedWebRtcRelay(options, onStatus, onFailure, {\n native,\n hotspot: (enabled) => BluetoothSdk.setHotspotState(enabled),\n startGlasses: (request) => BluetoothSdk.startStream(request),\n stopGlasses: () => BluetoothSdk.stopStream(),\n connected,\n deferredStop,\n now: Date.now,\n sleep: (ms) => new Promise((resolve) => BgTimer.setTimeout(resolve, ms)),\n acquire: acquireGlassesHotspot,\n })\n}\n"]}
@@ -33,6 +33,7 @@
33
33
  * An explicit stop that could not reach BLE is sent on the next reconnect.
34
34
  */
35
35
  import type { StreamResolvedConfig, StreamStartRequest, StreamStatusEvent } from "@mentra/bluetooth-sdk/internal";
36
+ import { createManagedWebRtcRelay } from "./ManagedWebRtcRelay";
36
37
  import { type RestreamDestinationInput } from "./cloudStreamApi";
37
38
  /**
38
39
  * Default cadence + thresholds. Exposed via {@link CoordinatorTimings} so tests
@@ -141,6 +142,7 @@ export declare class PhoneStreamCoordinator {
141
142
  private statusSubscriber;
142
143
  private idCounter;
143
144
  private readonly timings;
145
+ private readonly relayFactory;
144
146
  private readonly linkSource;
145
147
  private readonly pendingCameraChanges;
146
148
  private unsubscribeLink;
@@ -168,6 +170,7 @@ export declare class PhoneStreamCoordinator {
168
170
  constructor(timings?: CoordinatorTimings, deps?: {
169
171
  linkSource?: GlassesLinkSource;
170
172
  pendingCameraChanges?: () => Promise<void>;
173
+ relayFactory?: typeof createManagedWebRtcRelay;
171
174
  });
172
175
  /**
173
176
  * Fail fast if glasses aren't connected — BEFORE provisioning. Without this a
@@ -217,6 +220,8 @@ export declare class PhoneStreamCoordinator {
217
220
  * publisher possibly still alive" — miniapps word the two differently.
218
221
  */
219
222
  private failSuspended;
223
+ /** Event/timer failures have no awaiting caller; retain and report cleanup errors locally. */
224
+ private requestTeardown;
220
225
  private flushPendingBleStop;
221
226
  private mintId;
222
227
  /** Observe link/status; native SDK and ASG own controller liveness. */
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneStreamCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/PhoneStreamCoordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAC,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,gCAAgC,CAAA;AAM/G,OAAO,EAML,KAAK,wBAAwB,EAC9B,MAAM,kBAAkB,CAAA;AAEzB;;;GAGG;AACH,QAAA,MAAM,eAAe;;;;;;;;CAcX,CAAA;AAEV,KAAK,YAAY,GAAG;KAAE,CAAC,IAAI,MAAM,OAAO,eAAe,GAAG,MAAM;CAAC,CAAA;AACjE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;AAEtD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,IAAI,OAAO,CAAA;IACtB,wDAAwD;IACxD,SAAS,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;CAC9D;AAOD,sFAAsF;AACtF,eAAO,MAAM,WAAW;;;;CAId,CAAA;AAEV,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;OAIG;IACH,GAAG,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAC/B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,oBAAoB,CAAC,EAAE,wBAAwB,EAAE,CAAA;IACjD,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,oBAAoB,CAAA;CACtC;AAED,MAAM,WAAW,kBAAmB,SAAQ,0BAA0B;IACpE,WAAW,EAAE,MAAM,CAAA;IACnB;;6CAEyC;IACzC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,aAAa,CAAA;IAChD,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;AAoCxF,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,IAAI,EAAE,MAAM;IAE5B,6EAA6E;aAC7D,KAAK,CAAC,EAAE,MAAM;IAC9B,kEAAkE;aAClD,SAAS,CAAC,EAAE,MAAM;gBALlB,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;IACf,6EAA6E;IAC7D,KAAK,CAAC,EAAE,MAAM,YAAA;IAC9B,kEAAkE;IAClD,SAAS,CAAC,EAAE,MAAM,YAAA;CAKrC;AASD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAC1D,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,SAAS,CAA8B;IAC/C;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAkC;IACxD;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAmC;IACnD,+DAA+D;IAC/D,OAAO,CAAC,mBAAmB,CAAsB;IACjD,6DAA6D;IAC7D,OAAO,CAAC,uBAAuB,CAAQ;gBAGrC,OAAO,GAAE,kBAAuB,EAChC,IAAI,GAAE;QAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;QAAC,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAM;IAOzF;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAM9B,oEAAoE;IACpE,WAAW,IAAI,OAAO;IAItB;;;;OAIG;YACW,YAAY;IAY1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI;IAI/C;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI/B,sEAAsE;IACtE,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAyB1C,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAwDrG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsIzF,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjE;;;OAGG;IACH,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IA6CnD,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,cAAc;IAKtB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,MAAM;IAKd,uEAAuE;IACvE,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,yBAAyB;IA4HjC,OAAO,CAAC,qBAAqB;IAsE7B,OAAO,CAAC,MAAM;IAYd;;;;;;OAMG;YACW,cAAc;CA2D7B;AAqDD,eAAO,MAAM,sBAAsB,wBAA+B,CAAA"}
1
+ {"version":3,"file":"PhoneStreamCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/PhoneStreamCoordinator.ts"],"names":[],"mappings":"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"}