@mentra/sdk 2.0.3 โ†’ 2.1.0

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.
@@ -0,0 +1,230 @@
1
+ /**
2
+ * ๐Ÿ“ท Camera Module
3
+ *
4
+ * Unified camera functionality for App Sessions.
5
+ * Handles both photo requests and RTMP streaming from connected glasses.
6
+ */
7
+ import { PhotoData, RtmpStreamStatus } from '../../../types';
8
+ import { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler } from '../../../types/rtmp-stream';
9
+ import { Logger } from 'pino';
10
+ /**
11
+ * Options for photo requests
12
+ */
13
+ export interface PhotoRequestOptions {
14
+ /** Whether to save the photo to the device gallery */
15
+ saveToGallery?: boolean;
16
+ }
17
+ /**
18
+ * Configuration options for an RTMP stream
19
+ */
20
+ export interface RtmpStreamOptions {
21
+ /** The RTMP URL to stream to (e.g., rtmp://server.example.com/live/stream-key) */
22
+ rtmpUrl: string;
23
+ /** Optional video configuration settings */
24
+ video?: VideoConfig;
25
+ /** Optional audio configuration settings */
26
+ audio?: AudioConfig;
27
+ /** Optional stream configuration settings */
28
+ stream?: StreamConfig;
29
+ }
30
+ /**
31
+ * ๐Ÿ“ท Camera Module Implementation
32
+ *
33
+ * Unified camera management for App Sessions.
34
+ * Provides methods for:
35
+ * - ๐Ÿ“ธ Requesting photos from glasses
36
+ * - ๐Ÿ“น Starting/stopping RTMP streams
37
+ * - ๐Ÿ” Monitoring photo and stream status
38
+ * - ๐Ÿงน Cleanup and cancellation
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * // Request a photo
43
+ * const photoData = await session.camera.requestPhoto({ saveToGallery: true });
44
+ *
45
+ * // Start streaming
46
+ * await session.camera.startStream({ rtmpUrl: 'rtmp://example.com/live/key' });
47
+ *
48
+ * // Monitor stream status
49
+ * session.camera.onStreamStatus((status) => {
50
+ * console.log('Stream status:', status.status);
51
+ * });
52
+ *
53
+ * // Stop streaming
54
+ * await session.camera.stopStream();
55
+ * ```
56
+ */
57
+ export declare class CameraModule {
58
+ private send;
59
+ private packageName;
60
+ private sessionId;
61
+ private session?;
62
+ private logger;
63
+ /** Map to store pending photo request promises */
64
+ private pendingPhotoRequests;
65
+ private isStreaming;
66
+ private currentStreamUrl?;
67
+ private currentStreamState?;
68
+ /**
69
+ * Create a new CameraModule
70
+ *
71
+ * @param packageName - The App package name
72
+ * @param sessionId - The current session ID
73
+ * @param send - Function to send messages to the cloud
74
+ * @param session - Reference to the parent AppSession (optional)
75
+ * @param logger - Logger instance for debugging
76
+ */
77
+ constructor(packageName: string, sessionId: string, send: (message: any) => void, session?: any, logger?: Logger);
78
+ /**
79
+ * ๐Ÿ“ธ Request a photo from the connected glasses
80
+ *
81
+ * @param options - Optional configuration for the photo request
82
+ * @returns Promise that resolves with the actual photo data
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // Request a photo
87
+ * const photo = await session.camera.requestPhoto();
88
+ * ```
89
+ */
90
+ requestPhoto(options?: PhotoRequestOptions): Promise<PhotoData>;
91
+ /**
92
+ * ๐Ÿ“ฅ Handle photo received from /photo-upload endpoint
93
+ *
94
+ * This method is called internally when a photo response is received.
95
+ * It resolves the corresponding pending promise with the photo data.
96
+ *
97
+ * @param photoData - The photo data received
98
+ * @internal This method is used internally by AppSession
99
+ */
100
+ handlePhotoReceived(photoData: PhotoData): void;
101
+ /**
102
+ * ๐Ÿ” Check if there's a pending photo request for the given request ID
103
+ *
104
+ * @param requestId - The request ID to check
105
+ * @returns true if there's a pending request
106
+ */
107
+ hasPhotoPendingRequest(requestId: string): boolean;
108
+ /**
109
+ * ๐Ÿ“Š Get the number of pending photo requests
110
+ *
111
+ * @returns Number of pending photo requests
112
+ */
113
+ getPhotoPendingRequestCount(): number;
114
+ /**
115
+ * ๐Ÿ“‹ Get all pending photo request IDs
116
+ *
117
+ * @returns Array of pending request IDs
118
+ */
119
+ getPhotoPendingRequestIds(): string[];
120
+ /**
121
+ * โŒ Cancel a pending photo request
122
+ *
123
+ * @param requestId - The request ID to cancel
124
+ * @returns true if the request was cancelled, false if it wasn't found
125
+ */
126
+ cancelPhotoRequest(requestId: string): boolean;
127
+ /**
128
+ * ๐Ÿงน Cancel all pending photo requests
129
+ *
130
+ * @returns Number of requests that were cancelled
131
+ */
132
+ cancelAllPhotoRequests(): number;
133
+ /**
134
+ * ๐Ÿ“น Start an RTMP stream to the specified URL
135
+ *
136
+ * @param options - Configuration options for the stream
137
+ * @returns Promise that resolves when the stream request is sent (not when streaming begins)
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * await session.camera.startStream({
142
+ * rtmpUrl: 'rtmp://live.example.com/stream/key',
143
+ * video: { resolution: '1920x1080', bitrate: 5000 },
144
+ * audio: { bitrate: 128 }
145
+ * });
146
+ * ```
147
+ */
148
+ startStream(options: RtmpStreamOptions): Promise<void>;
149
+ /**
150
+ * ๐Ÿ›‘ Stop the current RTMP stream
151
+ *
152
+ * @returns Promise that resolves when the stop request is sent
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * await session.camera.stopStream();
157
+ * ```
158
+ */
159
+ stopStream(): Promise<void>;
160
+ /**
161
+ * ๐Ÿ” Check if currently streaming
162
+ *
163
+ * @returns True if a stream is active or initializing
164
+ */
165
+ isCurrentlyStreaming(): boolean;
166
+ /**
167
+ * ๐Ÿ“ Get the URL of the current stream (if any)
168
+ *
169
+ * @returns The RTMP URL of the current stream, or undefined if not streaming
170
+ */
171
+ getCurrentStreamUrl(): string | undefined;
172
+ /**
173
+ * ๐Ÿ“Š Get the current stream status
174
+ *
175
+ * @returns The current stream status, or undefined if not available
176
+ */
177
+ getStreamStatus(): RtmpStreamStatus | undefined;
178
+ /**
179
+ * ๐Ÿ“บ Subscribe to RTMP stream status updates
180
+ * This uses the standard stream subscription mechanism
181
+ */
182
+ subscribeToStreamStatusUpdates(): void;
183
+ /**
184
+ * ๐Ÿ“บ Unsubscribe from RTMP stream status updates
185
+ */
186
+ unsubscribeFromStreamStatusUpdates(): void;
187
+ /**
188
+ * ๐Ÿ‘‚ Listen for stream status updates using the standard event system
189
+ * @param handler - Function to call when stream status changes
190
+ * @returns Cleanup function to remove the handler
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * const cleanup = session.camera.onStreamStatus((status) => {
195
+ * console.log('Stream status:', status.status);
196
+ * if (status.status === 'error') {
197
+ * console.error('Stream error:', status.errorDetails);
198
+ * }
199
+ * });
200
+ *
201
+ * // Later, cleanup the listener
202
+ * cleanup();
203
+ * ```
204
+ */
205
+ onStreamStatus(handler: StreamStatusHandler): () => void;
206
+ /**
207
+ * ๐Ÿ”„ Update internal stream state based on a status message
208
+ * For internal use by AppSession
209
+ * @param message - The status message from the cloud
210
+ * @internal This method is used internally by AppSession
211
+ */
212
+ updateStreamState(message: any): void;
213
+ /**
214
+ * ๐Ÿ”ง Update the session ID (used when reconnecting)
215
+ *
216
+ * @param newSessionId - The new session ID
217
+ * @internal This method is used internally by AppSession
218
+ */
219
+ updateSessionId(newSessionId: string): void;
220
+ /**
221
+ * ๐Ÿงน Cancel all pending requests and clean up resources
222
+ *
223
+ * @returns Object with counts of cancelled requests
224
+ */
225
+ cancelAllRequests(): {
226
+ photoRequests: number;
227
+ };
228
+ }
229
+ export { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler };
230
+ //# sourceMappingURL=camera.d.ts.map
@@ -0,0 +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,EAEjB,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;AAE9B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;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;IAGvB,kDAAkD;IAClD,OAAO,CAAC,oBAAoB,CAGvB;IAGL,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,kBAAkB,CAAC,CAAmB;IAE9C;;;;;;;;OAQG;gBACS,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM;IAYhH;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAoDrE;;;;;;;;OAQG;IACH,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAiB/C;;;;;OAKG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlD;;;;OAIG;IACH,2BAA2B,IAAI,MAAM;IAIrC;;;;OAIG;IACH,yBAAyB,IAAI,MAAM,EAAE;IAIrC;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAW9C;;;;OAIG;IACH,sBAAsB,IAAI,MAAM;IAgBhC;;;;;;;;;;;;;;OAcG;IACG,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C5D;;;;;;;;;OASG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BjC;;;;OAIG;IACH,oBAAoB,IAAI,OAAO;IAI/B;;;;OAIG;IACH,mBAAmB,IAAI,MAAM,GAAG,SAAS;IAIzC;;;;OAIG;IACH,eAAe,IAAI,gBAAgB,GAAG,SAAS;IAI/C;;;OAGG;IACH,8BAA8B,IAAI,IAAI;IAQtC;;OAEG;IACH,kCAAkC,IAAI,IAAI;IAM1C;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAUxD;;;;;OAKG;IACH,iBAAiB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IAiDrC;;;;;OAKG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI3C;;;;OAIG;IACH,iBAAiB,IAAI;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;CAY/C;AAGD,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,mBAAmB,EACpB,CAAC"}
@@ -0,0 +1,439 @@
1
+ "use strict";
2
+ /**
3
+ * ๐Ÿ“ท Camera Module
4
+ *
5
+ * Unified camera functionality for App Sessions.
6
+ * Handles both photo requests and RTMP streaming from connected glasses.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CameraModule = void 0;
10
+ const types_1 = require("../../../types");
11
+ const streams_1 = require("../../../types/streams");
12
+ /**
13
+ * ๐Ÿ“ท Camera Module Implementation
14
+ *
15
+ * Unified camera management for App Sessions.
16
+ * Provides methods for:
17
+ * - ๐Ÿ“ธ Requesting photos from glasses
18
+ * - ๐Ÿ“น Starting/stopping RTMP streams
19
+ * - ๐Ÿ” Monitoring photo and stream status
20
+ * - ๐Ÿงน Cleanup and cancellation
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Request a photo
25
+ * const photoData = await session.camera.requestPhoto({ saveToGallery: true });
26
+ *
27
+ * // Start streaming
28
+ * await session.camera.startStream({ rtmpUrl: 'rtmp://example.com/live/key' });
29
+ *
30
+ * // Monitor stream status
31
+ * session.camera.onStreamStatus((status) => {
32
+ * console.log('Stream status:', status.status);
33
+ * });
34
+ *
35
+ * // Stop streaming
36
+ * await session.camera.stopStream();
37
+ * ```
38
+ */
39
+ class CameraModule {
40
+ /**
41
+ * Create a new CameraModule
42
+ *
43
+ * @param packageName - The App package name
44
+ * @param sessionId - The current session ID
45
+ * @param send - Function to send messages to the cloud
46
+ * @param session - Reference to the parent AppSession (optional)
47
+ * @param logger - Logger instance for debugging
48
+ */
49
+ constructor(packageName, sessionId, send, session, logger) {
50
+ // Photo functionality
51
+ /** Map to store pending photo request promises */
52
+ this.pendingPhotoRequests = new Map();
53
+ // Streaming functionality
54
+ this.isStreaming = false;
55
+ this.packageName = packageName;
56
+ this.sessionId = sessionId;
57
+ this.send = send;
58
+ this.session = session;
59
+ this.logger = logger || console;
60
+ }
61
+ // =====================================
62
+ // ๐Ÿ“ธ Photo Functionality
63
+ // =====================================
64
+ /**
65
+ * ๐Ÿ“ธ Request a photo from the connected glasses
66
+ *
67
+ * @param options - Optional configuration for the photo request
68
+ * @returns Promise that resolves with the actual photo data
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Request a photo
73
+ * const photo = await session.camera.requestPhoto();
74
+ * ```
75
+ */
76
+ async requestPhoto(options) {
77
+ return new Promise((resolve, reject) => {
78
+ try {
79
+ // Generate unique request ID
80
+ const requestId = `photo_req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
81
+ // Store promise resolvers for when we get the response
82
+ this.pendingPhotoRequests.set(requestId, { resolve, reject });
83
+ // Create photo request message
84
+ const message = {
85
+ type: types_1.AppToCloudMessageType.PHOTO_REQUEST,
86
+ packageName: this.packageName,
87
+ sessionId: this.sessionId,
88
+ requestId,
89
+ timestamp: new Date(),
90
+ saveToGallery: options?.saveToGallery || false
91
+ };
92
+ // Send request to cloud
93
+ this.send(message);
94
+ this.logger.info({ requestId, saveToGallery: options?.saveToGallery }, `๐Ÿ“ธ Photo request sent`);
95
+ // Set timeout to avoid hanging promises
96
+ const timeoutMs = 30000; // 30 seconds
97
+ if (this.session && this.session.resources) {
98
+ // Use session's resource tracker for automatic cleanup
99
+ this.session.resources.setTimeout(() => {
100
+ if (this.pendingPhotoRequests.has(requestId)) {
101
+ this.pendingPhotoRequests.get(requestId).reject(new Error('Photo request timed out'));
102
+ this.pendingPhotoRequests.delete(requestId);
103
+ this.logger.warn({ requestId }, `๐Ÿ“ธ Photo request timed out`);
104
+ }
105
+ }, timeoutMs);
106
+ }
107
+ else {
108
+ // Fallback to regular setTimeout if session not available
109
+ setTimeout(() => {
110
+ if (this.pendingPhotoRequests.has(requestId)) {
111
+ this.pendingPhotoRequests.get(requestId).reject(new Error('Photo request timed out'));
112
+ this.pendingPhotoRequests.delete(requestId);
113
+ this.logger.warn({ requestId }, `๐Ÿ“ธ Photo request timed out`);
114
+ }
115
+ }, timeoutMs);
116
+ }
117
+ }
118
+ catch (error) {
119
+ const errorMessage = error instanceof Error ? error.message : String(error);
120
+ reject(new Error(`Failed to request photo: ${errorMessage}`));
121
+ }
122
+ });
123
+ }
124
+ /**
125
+ * ๐Ÿ“ฅ Handle photo received from /photo-upload endpoint
126
+ *
127
+ * This method is called internally when a photo response is received.
128
+ * It resolves the corresponding pending promise with the photo data.
129
+ *
130
+ * @param photoData - The photo data received
131
+ * @internal This method is used internally by AppSession
132
+ */
133
+ handlePhotoReceived(photoData) {
134
+ const { requestId } = photoData;
135
+ const pendingRequest = this.pendingPhotoRequests.get(requestId);
136
+ if (pendingRequest) {
137
+ this.logger.info({ requestId }, `๐Ÿ“ธ Photo received for request ${requestId}`);
138
+ // Resolve the promise with the photo data
139
+ pendingRequest.resolve(photoData);
140
+ // Clean up
141
+ this.pendingPhotoRequests.delete(requestId);
142
+ }
143
+ else {
144
+ this.logger.warn({ requestId }, `๐Ÿ“ธ Received photo for unknown request ID: ${requestId}`);
145
+ }
146
+ }
147
+ /**
148
+ * ๐Ÿ” Check if there's a pending photo request for the given request ID
149
+ *
150
+ * @param requestId - The request ID to check
151
+ * @returns true if there's a pending request
152
+ */
153
+ hasPhotoPendingRequest(requestId) {
154
+ return this.pendingPhotoRequests.has(requestId);
155
+ }
156
+ /**
157
+ * ๐Ÿ“Š Get the number of pending photo requests
158
+ *
159
+ * @returns Number of pending photo requests
160
+ */
161
+ getPhotoPendingRequestCount() {
162
+ return this.pendingPhotoRequests.size;
163
+ }
164
+ /**
165
+ * ๐Ÿ“‹ Get all pending photo request IDs
166
+ *
167
+ * @returns Array of pending request IDs
168
+ */
169
+ getPhotoPendingRequestIds() {
170
+ return Array.from(this.pendingPhotoRequests.keys());
171
+ }
172
+ /**
173
+ * โŒ Cancel a pending photo request
174
+ *
175
+ * @param requestId - The request ID to cancel
176
+ * @returns true if the request was cancelled, false if it wasn't found
177
+ */
178
+ cancelPhotoRequest(requestId) {
179
+ const pendingRequest = this.pendingPhotoRequests.get(requestId);
180
+ if (pendingRequest) {
181
+ pendingRequest.reject(new Error('Photo request cancelled'));
182
+ this.pendingPhotoRequests.delete(requestId);
183
+ this.logger.info({ requestId }, `๐Ÿ“ธ Photo request cancelled`);
184
+ return true;
185
+ }
186
+ return false;
187
+ }
188
+ /**
189
+ * ๐Ÿงน Cancel all pending photo requests
190
+ *
191
+ * @returns Number of requests that were cancelled
192
+ */
193
+ cancelAllPhotoRequests() {
194
+ const count = this.pendingPhotoRequests.size;
195
+ for (const [requestId, { reject }] of this.pendingPhotoRequests) {
196
+ reject(new Error('Photo request cancelled - session cleanup'));
197
+ this.logger.info({ requestId }, `๐Ÿ“ธ Photo request cancelled during cleanup`);
198
+ }
199
+ this.pendingPhotoRequests.clear();
200
+ return count;
201
+ }
202
+ // =====================================
203
+ // ๐Ÿ“น Streaming Functionality
204
+ // =====================================
205
+ /**
206
+ * ๐Ÿ“น Start an RTMP stream to the specified URL
207
+ *
208
+ * @param options - Configuration options for the stream
209
+ * @returns Promise that resolves when the stream request is sent (not when streaming begins)
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * await session.camera.startStream({
214
+ * rtmpUrl: 'rtmp://live.example.com/stream/key',
215
+ * video: { resolution: '1920x1080', bitrate: 5000 },
216
+ * audio: { bitrate: 128 }
217
+ * });
218
+ * ```
219
+ */
220
+ async startStream(options) {
221
+ this.logger.info({ rtmpUrl: options.rtmpUrl }, `๐Ÿ“น RTMP stream request starting`);
222
+ if (!options.rtmpUrl) {
223
+ throw new Error('rtmpUrl is required');
224
+ }
225
+ if (this.isStreaming) {
226
+ this.logger.error({
227
+ currentStreamUrl: this.currentStreamUrl,
228
+ requestedUrl: options.rtmpUrl
229
+ }, `๐Ÿ“น Already streaming error`);
230
+ throw new Error('Already streaming. Stop the current stream before starting a new one.');
231
+ }
232
+ // Create stream request message
233
+ const message = {
234
+ type: types_1.AppToCloudMessageType.RTMP_STREAM_REQUEST,
235
+ packageName: this.packageName,
236
+ sessionId: this.sessionId,
237
+ rtmpUrl: options.rtmpUrl,
238
+ video: options.video,
239
+ audio: options.audio,
240
+ stream: options.stream,
241
+ timestamp: new Date()
242
+ };
243
+ // Save stream URL for reference
244
+ this.currentStreamUrl = options.rtmpUrl;
245
+ // Send the request
246
+ try {
247
+ this.send(message);
248
+ this.isStreaming = true;
249
+ this.logger.info({ rtmpUrl: options.rtmpUrl }, `๐Ÿ“น RTMP stream request sent successfully`);
250
+ return Promise.resolve();
251
+ }
252
+ catch (error) {
253
+ this.logger.error({ error, rtmpUrl: options.rtmpUrl }, `๐Ÿ“น Failed to send RTMP stream request`);
254
+ const errorMessage = error instanceof Error ? error.message : String(error);
255
+ return Promise.reject(new Error(`Failed to request RTMP stream: ${errorMessage}`));
256
+ }
257
+ }
258
+ /**
259
+ * ๐Ÿ›‘ Stop the current RTMP stream
260
+ *
261
+ * @returns Promise that resolves when the stop request is sent
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * await session.camera.stopStream();
266
+ * ```
267
+ */
268
+ async stopStream() {
269
+ this.logger.info({
270
+ isCurrentlyStreaming: this.isStreaming,
271
+ currentStreamUrl: this.currentStreamUrl
272
+ }, `๐Ÿ“น RTMP stream stop request`);
273
+ if (!this.isStreaming) {
274
+ this.logger.info(`๐Ÿ“น Not streaming - no-op`);
275
+ // Not an error - just a no-op if not streaming
276
+ return Promise.resolve();
277
+ }
278
+ // Create stop request message
279
+ const message = {
280
+ type: types_1.AppToCloudMessageType.RTMP_STREAM_STOP,
281
+ packageName: this.packageName,
282
+ sessionId: this.sessionId,
283
+ streamId: this.currentStreamState?.streamId, // Include streamId if available
284
+ timestamp: new Date()
285
+ };
286
+ // Send the request
287
+ try {
288
+ this.send(message);
289
+ return Promise.resolve();
290
+ }
291
+ catch (error) {
292
+ const errorMessage = error instanceof Error ? error.message : String(error);
293
+ return Promise.reject(new Error(`Failed to stop RTMP stream: ${errorMessage}`));
294
+ }
295
+ }
296
+ /**
297
+ * ๐Ÿ” Check if currently streaming
298
+ *
299
+ * @returns True if a stream is active or initializing
300
+ */
301
+ isCurrentlyStreaming() {
302
+ return this.isStreaming;
303
+ }
304
+ /**
305
+ * ๐Ÿ“ Get the URL of the current stream (if any)
306
+ *
307
+ * @returns The RTMP URL of the current stream, or undefined if not streaming
308
+ */
309
+ getCurrentStreamUrl() {
310
+ return this.currentStreamUrl;
311
+ }
312
+ /**
313
+ * ๐Ÿ“Š Get the current stream status
314
+ *
315
+ * @returns The current stream status, or undefined if not available
316
+ */
317
+ getStreamStatus() {
318
+ return this.currentStreamState;
319
+ }
320
+ /**
321
+ * ๐Ÿ“บ Subscribe to RTMP stream status updates
322
+ * This uses the standard stream subscription mechanism
323
+ */
324
+ subscribeToStreamStatusUpdates() {
325
+ if (this.session) {
326
+ this.session.subscribe(streams_1.StreamType.RTMP_STREAM_STATUS);
327
+ }
328
+ else {
329
+ this.logger.error('Cannot subscribe to status updates: session reference not available');
330
+ }
331
+ }
332
+ /**
333
+ * ๐Ÿ“บ Unsubscribe from RTMP stream status updates
334
+ */
335
+ unsubscribeFromStreamStatusUpdates() {
336
+ if (this.session) {
337
+ this.session.unsubscribe(streams_1.StreamType.RTMP_STREAM_STATUS);
338
+ }
339
+ }
340
+ /**
341
+ * ๐Ÿ‘‚ Listen for stream status updates using the standard event system
342
+ * @param handler - Function to call when stream status changes
343
+ * @returns Cleanup function to remove the handler
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * const cleanup = session.camera.onStreamStatus((status) => {
348
+ * console.log('Stream status:', status.status);
349
+ * if (status.status === 'error') {
350
+ * console.error('Stream error:', status.errorDetails);
351
+ * }
352
+ * });
353
+ *
354
+ * // Later, cleanup the listener
355
+ * cleanup();
356
+ * ```
357
+ */
358
+ onStreamStatus(handler) {
359
+ if (!this.session) {
360
+ this.logger.error('Cannot listen for status updates: session reference not available');
361
+ return () => { };
362
+ }
363
+ this.subscribeToStreamStatusUpdates();
364
+ return this.session.on(streams_1.StreamType.RTMP_STREAM_STATUS, handler);
365
+ }
366
+ /**
367
+ * ๐Ÿ”„ Update internal stream state based on a status message
368
+ * For internal use by AppSession
369
+ * @param message - The status message from the cloud
370
+ * @internal This method is used internally by AppSession
371
+ */
372
+ updateStreamState(message) {
373
+ this.logger.debug({
374
+ messageType: message?.type,
375
+ messageStatus: message?.status,
376
+ currentIsStreaming: this.isStreaming
377
+ }, `๐Ÿ“น Stream state update`);
378
+ // Verify this is a valid stream response
379
+ if (!(0, types_1.isRtmpStreamStatus)(message)) {
380
+ this.logger.warn({ message }, `๐Ÿ“น Received invalid stream status message`);
381
+ return;
382
+ }
383
+ // Convert to StreamStatus format
384
+ const status = {
385
+ type: message.type,
386
+ streamId: message.streamId,
387
+ status: message.status,
388
+ errorDetails: message.errorDetails,
389
+ appId: message.appId,
390
+ stats: message.stats,
391
+ timestamp: message.timestamp || new Date()
392
+ };
393
+ this.logger.info({
394
+ streamId: status.streamId,
395
+ oldStatus: this.currentStreamState?.status,
396
+ newStatus: status.status,
397
+ wasStreaming: this.isStreaming
398
+ }, `๐Ÿ“น Stream status processed`);
399
+ // Update local state based on status
400
+ if (status.status === 'stopped' || status.status === 'error' || status.status === 'timeout') {
401
+ this.logger.info({
402
+ status: status.status,
403
+ wasStreaming: this.isStreaming
404
+ }, `๐Ÿ“น Stream stopped - updating local state`);
405
+ this.isStreaming = false;
406
+ this.currentStreamUrl = undefined;
407
+ }
408
+ // Save the latest status
409
+ this.currentStreamState = status;
410
+ }
411
+ // =====================================
412
+ // ๐Ÿ”ง General Utilities
413
+ // =====================================
414
+ /**
415
+ * ๐Ÿ”ง Update the session ID (used when reconnecting)
416
+ *
417
+ * @param newSessionId - The new session ID
418
+ * @internal This method is used internally by AppSession
419
+ */
420
+ updateSessionId(newSessionId) {
421
+ this.sessionId = newSessionId;
422
+ }
423
+ /**
424
+ * ๐Ÿงน Cancel all pending requests and clean up resources
425
+ *
426
+ * @returns Object with counts of cancelled requests
427
+ */
428
+ cancelAllRequests() {
429
+ const photoRequests = this.cancelAllPhotoRequests();
430
+ // Stop streaming if active
431
+ if (this.isStreaming) {
432
+ this.stopStream().catch((error) => {
433
+ this.logger.error({ error }, 'Error stopping stream during cleanup');
434
+ });
435
+ }
436
+ return { photoRequests };
437
+ }
438
+ }
439
+ exports.CameraModule = CameraModule;
@@ -31,7 +31,7 @@ async function startApp() {
31
31
  // Set up handler for stream status updates
32
32
  function setupStreamStatusHandler() {
33
33
  // Register a handler for stream status updates
34
- const cleanup = session.streaming.onStatus((status) => {
34
+ const cleanup = session.camera.onStreamStatus((status) => {
35
35
  console.log(`Stream status: ${status.status}`);
36
36
  // Log detailed information if available
37
37
  if (status.stats) {
@@ -69,7 +69,7 @@ function setupStreamStatusHandler() {
69
69
  async function requestStream() {
70
70
  try {
71
71
  // Request a stream with configuration
72
- await session.streaming.requestStream({
72
+ await session.camera.startStream({
73
73
  rtmpUrl: 'rtmp://your-rtmp-server.com/live/stream-key',
74
74
  video: {
75
75
  width: 1280,
@@ -87,7 +87,7 @@ async function requestStream() {
87
87
  // Stop the stream
88
88
  async function stopStream() {
89
89
  try {
90
- await session.streaming.stopStream();
90
+ await session.camera.stopStream();
91
91
  console.log('Stop stream request sent successfully');
92
92
  }
93
93
  catch (error) {