@mentra/sdk 2.1.6 → 2.1.8
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/dist/app/session/modules/audio.d.ts +2 -2
- package/dist/app/session/modules/audio.d.ts.map +1 -1
- package/dist/app/session/modules/audio.js +21 -17
- package/dist/app/session/modules/camera.d.ts +4 -4
- package/dist/app/session/modules/camera.d.ts.map +1 -1
- package/dist/app/session/modules/camera.js +28 -22
- package/dist/app/session/modules/location.d.ts +3 -3
- package/dist/app/session/modules/location.d.ts.map +1 -1
- package/dist/app/session/modules/location.js +8 -5
- package/dist/utils/bitmap-utils.js +2 -2
- package/package.json +1 -1
@@ -4,8 +4,8 @@
|
|
4
4
|
* Audio functionality for App Sessions.
|
5
5
|
* Handles audio playback on connected glasses.
|
6
6
|
*/
|
7
|
-
import { AudioPlayResponse } from
|
8
|
-
import { Logger } from
|
7
|
+
import { AudioPlayResponse } from "../../../types";
|
8
|
+
import { Logger } from "pino";
|
9
9
|
/**
|
10
10
|
* Options for audio playback
|
11
11
|
*/
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/audio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,iBAAiB,EAGlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,cAAc,CAAC,EAAE;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAM;IACtB,OAAO,CAAC,MAAM,CAAS;IAEvB,uDAAuD;IACvD,OAAO,CAAC,oBAAoB,
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/audio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,iBAAiB,EAGlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,cAAc,CAAC,EAAE;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAM;IACtB,OAAO,CAAC,MAAM,CAAS;IAEvB,uDAAuD;IACvD,OAAO,CAAC,oBAAoB,CAMxB;IAEJ;;;;;;;;OAQG;gBAED,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,EAC5B,OAAO,CAAC,EAAE,GAAG,EACb,MAAM,CAAC,EAAE,MAAM;IAajB;;;;;;;;;;;;;OAaG;IACG,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqEpE;;;;;;;;OAQG;IACH,SAAS,IAAI,IAAI;IAqBjB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,eAAe,CAAC;IA+C3B;;;;;;;;OAQG;IACH,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAkC1D;;;;OAIG;IACH,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAO9C;;;OAGG;IACH,sBAAsB,IAAI,MAAM;IAIhC;;;OAGG;IACH,oBAAoB,IAAI,MAAM,EAAE;IAIhC;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAW9C;;;OAGG;IACH,sBAAsB,IAAI,MAAM;IAyBhC;;;;OAIG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAK3C;;;;OAIG;IACH,iBAAiB,IAAI;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;CAI/C"}
|
@@ -71,7 +71,7 @@ class AudioManager {
|
|
71
71
|
try {
|
72
72
|
// Validate input
|
73
73
|
if (!options.audioUrl) {
|
74
|
-
reject(
|
74
|
+
reject("audioUrl must be provided");
|
75
75
|
return;
|
76
76
|
}
|
77
77
|
// Generate unique request ID
|
@@ -87,7 +87,7 @@ class AudioManager {
|
|
87
87
|
timestamp: new Date(),
|
88
88
|
audioUrl: options.audioUrl,
|
89
89
|
volume: options.volume ?? 1.0,
|
90
|
-
stopOtherAudio: options.stopOtherAudio ?? true
|
90
|
+
stopOtherAudio: options.stopOtherAudio ?? true,
|
91
91
|
};
|
92
92
|
// Send request to cloud
|
93
93
|
this.send(message);
|
@@ -97,7 +97,9 @@ class AudioManager {
|
|
97
97
|
// Use session's resource tracker for automatic cleanup
|
98
98
|
this.session.resources.setTimeout(() => {
|
99
99
|
if (this.pendingAudioRequests.has(requestId)) {
|
100
|
-
this.pendingAudioRequests
|
100
|
+
this.pendingAudioRequests
|
101
|
+
.get(requestId)
|
102
|
+
.reject("Audio play request timed out");
|
101
103
|
this.pendingAudioRequests.delete(requestId);
|
102
104
|
this.logger.warn({ requestId }, `🔊 Audio play request timed out`);
|
103
105
|
}
|
@@ -107,7 +109,9 @@ class AudioManager {
|
|
107
109
|
// Fallback to regular setTimeout if session not available
|
108
110
|
setTimeout(() => {
|
109
111
|
if (this.pendingAudioRequests.has(requestId)) {
|
110
|
-
this.pendingAudioRequests
|
112
|
+
this.pendingAudioRequests
|
113
|
+
.get(requestId)
|
114
|
+
.reject("Audio play request timed out");
|
111
115
|
this.pendingAudioRequests.delete(requestId);
|
112
116
|
this.logger.warn({ requestId }, `🔊 Audio play request timed out`);
|
113
117
|
}
|
@@ -116,7 +120,7 @@ class AudioManager {
|
|
116
120
|
}
|
117
121
|
catch (error) {
|
118
122
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
119
|
-
reject(
|
123
|
+
reject(`Failed to play audio: ${errorMessage}`);
|
120
124
|
}
|
121
125
|
});
|
122
126
|
}
|
@@ -136,7 +140,7 @@ class AudioManager {
|
|
136
140
|
type: types_1.AppToCloudMessageType.AUDIO_STOP_REQUEST,
|
137
141
|
packageName: this.packageName,
|
138
142
|
sessionId: this.sessionId,
|
139
|
-
timestamp: new Date()
|
143
|
+
timestamp: new Date(),
|
140
144
|
};
|
141
145
|
// Send request to cloud (one-way, no response expected)
|
142
146
|
this.send(message);
|
@@ -172,24 +176,24 @@ class AudioManager {
|
|
172
176
|
async speak(text, options = {}) {
|
173
177
|
// Validate input
|
174
178
|
if (!text) {
|
175
|
-
throw new Error(
|
179
|
+
throw new Error("text must be provided");
|
176
180
|
}
|
177
181
|
// Get the HTTPS server URL from the session
|
178
182
|
const baseUrl = this.session?.getHttpsServerUrl?.();
|
179
183
|
if (!baseUrl) {
|
180
|
-
throw new Error(
|
184
|
+
throw new Error("Cannot determine server URL for TTS endpoint");
|
181
185
|
}
|
182
186
|
// Build query parameters for the TTS endpoint
|
183
187
|
const queryParams = new URLSearchParams();
|
184
|
-
queryParams.append(
|
188
|
+
queryParams.append("text", text);
|
185
189
|
if (options.voice_id) {
|
186
|
-
queryParams.append(
|
190
|
+
queryParams.append("voice_id", options.voice_id);
|
187
191
|
}
|
188
192
|
if (options.model_id) {
|
189
|
-
queryParams.append(
|
193
|
+
queryParams.append("model_id", options.model_id);
|
190
194
|
}
|
191
195
|
if (options.voice_settings) {
|
192
|
-
queryParams.append(
|
196
|
+
queryParams.append("voice_settings", JSON.stringify(options.voice_settings));
|
193
197
|
}
|
194
198
|
// Construct the TTS URL
|
195
199
|
const ttsUrl = `${baseUrl}/api/tts?${queryParams.toString()}`;
|
@@ -197,7 +201,7 @@ class AudioManager {
|
|
197
201
|
// Use the existing playAudio method to play the TTS audio
|
198
202
|
return this.playAudio({
|
199
203
|
audioUrl: ttsUrl,
|
200
|
-
volume: options.volume
|
204
|
+
volume: options.volume,
|
201
205
|
});
|
202
206
|
}
|
203
207
|
// =====================================
|
@@ -219,14 +223,14 @@ class AudioManager {
|
|
219
223
|
pendingRequest.resolve({
|
220
224
|
success: response.success,
|
221
225
|
error: response.error,
|
222
|
-
duration: response.duration
|
226
|
+
duration: response.duration,
|
223
227
|
});
|
224
228
|
// Clean up
|
225
229
|
this.pendingAudioRequests.delete(response.requestId);
|
226
230
|
this.logger.info({
|
227
231
|
requestId: response.requestId,
|
228
232
|
success: response.success,
|
229
|
-
duration: response.duration
|
233
|
+
duration: response.duration,
|
230
234
|
}, `🔊 Audio play response received`);
|
231
235
|
}
|
232
236
|
else {
|
@@ -269,7 +273,7 @@ class AudioManager {
|
|
269
273
|
cancelAudioRequest(requestId) {
|
270
274
|
const pendingRequest = this.pendingAudioRequests.get(requestId);
|
271
275
|
if (pendingRequest) {
|
272
|
-
pendingRequest.reject(
|
276
|
+
pendingRequest.reject("Audio request cancelled");
|
273
277
|
this.pendingAudioRequests.delete(requestId);
|
274
278
|
this.logger.info({ requestId }, `🔊 Audio request cancelled`);
|
275
279
|
return true;
|
@@ -283,7 +287,7 @@ class AudioManager {
|
|
283
287
|
cancelAllAudioRequests() {
|
284
288
|
const count = this.pendingAudioRequests.size;
|
285
289
|
this.pendingAudioRequests.forEach((request, requestId) => {
|
286
|
-
request.reject(
|
290
|
+
request.reject("Audio request cancelled due to cleanup");
|
287
291
|
this.logger.debug({ requestId }, `🔊 Audio request cancelled during cleanup`);
|
288
292
|
});
|
289
293
|
this.pendingAudioRequests.clear();
|
@@ -4,10 +4,10 @@
|
|
4
4
|
* Unified camera functionality for App Sessions.
|
5
5
|
* Handles both photo requests and RTMP streaming from connected glasses.
|
6
6
|
*/
|
7
|
-
import { PhotoData, RtmpStreamStatus, ManagedStreamStatus } from
|
8
|
-
import { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler } from
|
9
|
-
import { Logger } from
|
10
|
-
import { ManagedStreamOptions, ManagedStreamResult } from
|
7
|
+
import { PhotoData, RtmpStreamStatus, ManagedStreamStatus } from "../../../types";
|
8
|
+
import { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler } from "../../../types/rtmp-stream";
|
9
|
+
import { Logger } from "pino";
|
10
|
+
import { ManagedStreamOptions, ManagedStreamResult } from "./camera-managed-extension";
|
11
11
|
/**
|
12
12
|
* Options for photo requests
|
13
13
|
*/
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/camera.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,SAAS,EAIT,gBAAgB,EAEhB,mBAAmB,EAEpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/camera.ts"],"names":[],"mappings":"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"}
|
@@ -90,7 +90,7 @@ class CameraModule {
|
|
90
90
|
sessionId: this.sessionId,
|
91
91
|
requestId,
|
92
92
|
timestamp: new Date(),
|
93
|
-
saveToGallery: options?.saveToGallery || false
|
93
|
+
saveToGallery: options?.saveToGallery || false,
|
94
94
|
};
|
95
95
|
// Send request to cloud
|
96
96
|
this.send(message);
|
@@ -101,7 +101,9 @@ class CameraModule {
|
|
101
101
|
// Use session's resource tracker for automatic cleanup
|
102
102
|
this.session.resources.setTimeout(() => {
|
103
103
|
if (this.pendingPhotoRequests.has(requestId)) {
|
104
|
-
this.pendingPhotoRequests
|
104
|
+
this.pendingPhotoRequests
|
105
|
+
.get(requestId)
|
106
|
+
.reject("Photo request timed out");
|
105
107
|
this.pendingPhotoRequests.delete(requestId);
|
106
108
|
this.logger.warn({ requestId }, `📸 Photo request timed out`);
|
107
109
|
}
|
@@ -111,7 +113,9 @@ class CameraModule {
|
|
111
113
|
// Fallback to regular setTimeout if session not available
|
112
114
|
setTimeout(() => {
|
113
115
|
if (this.pendingPhotoRequests.has(requestId)) {
|
114
|
-
this.pendingPhotoRequests
|
116
|
+
this.pendingPhotoRequests
|
117
|
+
.get(requestId)
|
118
|
+
.reject("Photo request timed out");
|
115
119
|
this.pendingPhotoRequests.delete(requestId);
|
116
120
|
this.logger.warn({ requestId }, `📸 Photo request timed out`);
|
117
121
|
}
|
@@ -120,7 +124,7 @@ class CameraModule {
|
|
120
124
|
}
|
121
125
|
catch (error) {
|
122
126
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
123
|
-
reject(
|
127
|
+
reject(`Failed to request photo: ${errorMessage}`);
|
124
128
|
}
|
125
129
|
});
|
126
130
|
}
|
@@ -181,7 +185,7 @@ class CameraModule {
|
|
181
185
|
cancelPhotoRequest(requestId) {
|
182
186
|
const pendingRequest = this.pendingPhotoRequests.get(requestId);
|
183
187
|
if (pendingRequest) {
|
184
|
-
pendingRequest.reject(
|
188
|
+
pendingRequest.reject("Photo request cancelled");
|
185
189
|
this.pendingPhotoRequests.delete(requestId);
|
186
190
|
this.logger.info({ requestId }, `📸 Photo request cancelled`);
|
187
191
|
return true;
|
@@ -196,7 +200,7 @@ class CameraModule {
|
|
196
200
|
cancelAllPhotoRequests() {
|
197
201
|
const count = this.pendingPhotoRequests.size;
|
198
202
|
for (const [requestId, { reject }] of this.pendingPhotoRequests) {
|
199
|
-
reject(
|
203
|
+
reject("Photo request cancelled - session cleanup");
|
200
204
|
this.logger.info({ requestId }, `📸 Photo request cancelled during cleanup`);
|
201
205
|
}
|
202
206
|
this.pendingPhotoRequests.clear();
|
@@ -223,14 +227,14 @@ class CameraModule {
|
|
223
227
|
async startStream(options) {
|
224
228
|
this.logger.info({ rtmpUrl: options.rtmpUrl }, `📹 RTMP stream request starting`);
|
225
229
|
if (!options.rtmpUrl) {
|
226
|
-
throw new Error(
|
230
|
+
throw new Error("rtmpUrl is required");
|
227
231
|
}
|
228
232
|
if (this.isStreaming) {
|
229
233
|
this.logger.error({
|
230
234
|
currentStreamUrl: this.currentStreamUrl,
|
231
|
-
requestedUrl: options.rtmpUrl
|
235
|
+
requestedUrl: options.rtmpUrl,
|
232
236
|
}, `📹 Already streaming error`);
|
233
|
-
throw new Error(
|
237
|
+
throw new Error("Already streaming. Stop the current stream before starting a new one.");
|
234
238
|
}
|
235
239
|
// Create stream request message
|
236
240
|
const message = {
|
@@ -241,7 +245,7 @@ class CameraModule {
|
|
241
245
|
video: options.video,
|
242
246
|
audio: options.audio,
|
243
247
|
stream: options.stream,
|
244
|
-
timestamp: new Date()
|
248
|
+
timestamp: new Date(),
|
245
249
|
};
|
246
250
|
// Save stream URL for reference
|
247
251
|
this.currentStreamUrl = options.rtmpUrl;
|
@@ -255,7 +259,7 @@ class CameraModule {
|
|
255
259
|
catch (error) {
|
256
260
|
this.logger.error({ error, rtmpUrl: options.rtmpUrl }, `📹 Failed to send RTMP stream request`);
|
257
261
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
258
|
-
return Promise.reject(
|
262
|
+
return Promise.reject(`Failed to request RTMP stream: ${errorMessage}`);
|
259
263
|
}
|
260
264
|
}
|
261
265
|
/**
|
@@ -271,7 +275,7 @@ class CameraModule {
|
|
271
275
|
async stopStream() {
|
272
276
|
this.logger.info({
|
273
277
|
isCurrentlyStreaming: this.isStreaming,
|
274
|
-
currentStreamUrl: this.currentStreamUrl
|
278
|
+
currentStreamUrl: this.currentStreamUrl,
|
275
279
|
}, `📹 RTMP stream stop request`);
|
276
280
|
if (!this.isStreaming) {
|
277
281
|
this.logger.info(`📹 Not streaming - no-op`);
|
@@ -284,7 +288,7 @@ class CameraModule {
|
|
284
288
|
packageName: this.packageName,
|
285
289
|
sessionId: this.sessionId,
|
286
290
|
streamId: this.currentStreamState?.streamId, // Include streamId if available
|
287
|
-
timestamp: new Date()
|
291
|
+
timestamp: new Date(),
|
288
292
|
};
|
289
293
|
// Send the request
|
290
294
|
try {
|
@@ -293,7 +297,7 @@ class CameraModule {
|
|
293
297
|
}
|
294
298
|
catch (error) {
|
295
299
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
296
|
-
return Promise.reject(
|
300
|
+
return Promise.reject(`Failed to stop RTMP stream: ${errorMessage}`);
|
297
301
|
}
|
298
302
|
}
|
299
303
|
/**
|
@@ -329,7 +333,7 @@ class CameraModule {
|
|
329
333
|
this.session.subscribe(streams_1.StreamType.RTMP_STREAM_STATUS);
|
330
334
|
}
|
331
335
|
else {
|
332
|
-
this.logger.error(
|
336
|
+
this.logger.error("Cannot subscribe to status updates: session reference not available");
|
333
337
|
}
|
334
338
|
}
|
335
339
|
/**
|
@@ -360,7 +364,7 @@ class CameraModule {
|
|
360
364
|
*/
|
361
365
|
onStreamStatus(handler) {
|
362
366
|
if (!this.session) {
|
363
|
-
this.logger.error(
|
367
|
+
this.logger.error("Cannot listen for status updates: session reference not available");
|
364
368
|
return () => { };
|
365
369
|
}
|
366
370
|
this.subscribeToStreamStatusUpdates();
|
@@ -376,7 +380,7 @@ class CameraModule {
|
|
376
380
|
this.logger.debug({
|
377
381
|
messageType: message?.type,
|
378
382
|
messageStatus: message?.status,
|
379
|
-
currentIsStreaming: this.isStreaming
|
383
|
+
currentIsStreaming: this.isStreaming,
|
380
384
|
}, `📹 Stream state update`);
|
381
385
|
// Verify this is a valid stream response
|
382
386
|
if (!(0, types_1.isRtmpStreamStatus)(message)) {
|
@@ -391,19 +395,21 @@ class CameraModule {
|
|
391
395
|
errorDetails: message.errorDetails,
|
392
396
|
appId: message.appId,
|
393
397
|
stats: message.stats,
|
394
|
-
timestamp: message.timestamp || new Date()
|
398
|
+
timestamp: message.timestamp || new Date(),
|
395
399
|
};
|
396
400
|
this.logger.info({
|
397
401
|
streamId: status.streamId,
|
398
402
|
oldStatus: this.currentStreamState?.status,
|
399
403
|
newStatus: status.status,
|
400
|
-
wasStreaming: this.isStreaming
|
404
|
+
wasStreaming: this.isStreaming,
|
401
405
|
}, `📹 Stream status processed`);
|
402
406
|
// Update local state based on status
|
403
|
-
if (status.status ===
|
407
|
+
if (status.status === "stopped" ||
|
408
|
+
status.status === "error" ||
|
409
|
+
status.status === "timeout") {
|
404
410
|
this.logger.info({
|
405
411
|
status: status.status,
|
406
|
-
wasStreaming: this.isStreaming
|
412
|
+
wasStreaming: this.isStreaming,
|
407
413
|
}, `📹 Stream stopped - updating local state`);
|
408
414
|
this.isStreaming = false;
|
409
415
|
this.currentStreamUrl = undefined;
|
@@ -500,7 +506,7 @@ class CameraModule {
|
|
500
506
|
// Stop streaming if active
|
501
507
|
if (this.isStreaming) {
|
502
508
|
this.stopStream().catch((error) => {
|
503
|
-
this.logger.error({ error },
|
509
|
+
this.logger.error({ error }, "Error stopping stream during cleanup");
|
504
510
|
});
|
505
511
|
}
|
506
512
|
// Clean up managed extension
|
@@ -1,12 +1,12 @@
|
|
1
|
-
import { AppSession } from
|
2
|
-
import { LocationUpdate } from
|
1
|
+
import { AppSession } from "..";
|
2
|
+
import { LocationUpdate } from "../../../types";
|
3
3
|
export declare class LocationManager {
|
4
4
|
private session;
|
5
5
|
private send;
|
6
6
|
private lastLocationCleanupHandler;
|
7
7
|
constructor(session: AppSession, send: (message: any) => void);
|
8
8
|
subscribeToStream(options: {
|
9
|
-
accuracy:
|
9
|
+
accuracy: "standard" | "high" | "realtime" | "tenMeters" | "hundredMeters" | "kilometer" | "threeKilometers" | "reduced";
|
10
10
|
}, handler: (data: LocationUpdate) => void): () => void;
|
11
11
|
unsubscribeFromStream(): void;
|
12
12
|
getLatestLocation(options: {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"location.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/location.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,
|
1
|
+
{"version":3,"file":"location.d.ts","sourceRoot":"","sources":["../../../../src/app/session/modules/location.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAGL,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAExB,qBAAa,eAAe;IAIxB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,IAAI;IAJd,OAAO,CAAC,0BAA0B,CAAwB;gBAGhD,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI;IAI/B,iBAAiB,CACtB,OAAO,EAAE;QACP,QAAQ,EACJ,UAAU,GACV,MAAM,GACN,UAAU,GACV,WAAW,GACX,eAAe,GACf,WAAW,GACX,iBAAiB,GACjB,SAAS,CAAC;KACf,EACD,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GACtC,MAAM,IAAI;IAWN,qBAAqB,IAAI,IAAI;IAUvB,iBAAiB,CAAC,OAAO,EAAE;QACtC,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,cAAc,CAAC;CA+B5B"}
|
@@ -10,7 +10,10 @@ class LocationManager {
|
|
10
10
|
}
|
11
11
|
// subscribes to the continuous location stream with a specified accuracy tier
|
12
12
|
subscribeToStream(options, handler) {
|
13
|
-
const subscription = {
|
13
|
+
const subscription = {
|
14
|
+
stream: "location_stream",
|
15
|
+
rate: options.accuracy,
|
16
|
+
};
|
14
17
|
this.session.subscribe(subscription);
|
15
18
|
this.lastLocationCleanupHandler = this.session.events.onLocation(handler);
|
16
19
|
return this.lastLocationCleanupHandler;
|
@@ -22,7 +25,7 @@ class LocationManager {
|
|
22
25
|
this.lastLocationCleanupHandler = () => { };
|
23
26
|
}
|
24
27
|
else {
|
25
|
-
this.session.unsubscribe(
|
28
|
+
this.session.unsubscribe("location_stream");
|
26
29
|
}
|
27
30
|
}
|
28
31
|
// performs a one-time, intelligent poll for a location fix
|
@@ -30,7 +33,7 @@ class LocationManager {
|
|
30
33
|
return new Promise((resolve, reject) => {
|
31
34
|
const requestId = `poll_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
32
35
|
// listens for a location update with a matching correlationId
|
33
|
-
const unsubscribe = this.session.events.on(
|
36
|
+
const unsubscribe = this.session.events.on("location_update", (data) => {
|
34
37
|
if (data.correlationId === requestId) {
|
35
38
|
unsubscribe(); // clean up the listener
|
36
39
|
resolve(data);
|
@@ -42,12 +45,12 @@ class LocationManager {
|
|
42
45
|
correlationId: requestId,
|
43
46
|
packageName: this.session.getPackageName(),
|
44
47
|
sessionId: this.session.getSessionId(),
|
45
|
-
accuracy: options.accuracy
|
48
|
+
accuracy: options.accuracy,
|
46
49
|
});
|
47
50
|
// sets a timeout to prevent the promise from hanging indefinitely
|
48
51
|
setTimeout(() => {
|
49
52
|
unsubscribe();
|
50
|
-
reject(
|
53
|
+
reject("Location poll request timed out");
|
51
54
|
}, 15000); // 15 second timeout
|
52
55
|
});
|
53
56
|
}
|
@@ -190,7 +190,7 @@ class BitmapUtils {
|
|
190
190
|
const image = await jimp_1.Jimp.read(bmpData);
|
191
191
|
// Check if we need to add padding
|
192
192
|
if (image.width !== 576 || image.height !== 135) {
|
193
|
-
console.log(`Adding padding to BMP since it isn't 576x135 (current: ${image.width}x${image.height})`);
|
193
|
+
console.log(`Adding padding to BMP since it isn't 576x135 (assuming it's 526x100!)(current: ${image.width}x${image.height})`);
|
194
194
|
// Create a new 576x135 white canvas
|
195
195
|
const paddedImage = new jimp_1.Jimp({
|
196
196
|
width: 576,
|
@@ -198,7 +198,7 @@ class BitmapUtils {
|
|
198
198
|
color: 0xffffffff,
|
199
199
|
});
|
200
200
|
// // Calculate position to place the original image (with padding)
|
201
|
-
const leftPadding =
|
201
|
+
const leftPadding = 50; // 45px padding on left
|
202
202
|
const topPadding = 35; // 35px padding on top
|
203
203
|
// Composite the original image onto the white canvas
|
204
204
|
paddedImage.composite(image, leftPadding, topPadding);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@mentra/sdk",
|
3
|
-
"version": "2.1.
|
3
|
+
"version": "2.1.8",
|
4
4
|
"description": "Build apps for MentraOS smartglasses. This SDK provides everything you need to create real-time smartglasses applications.",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"main": "dist/index.js",
|