@arfuhad/react-native-smart-camera 0.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.
- package/ARCHITECTURE.md +341 -0
- package/README.md +154 -0
- package/android/build.gradle +89 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/smartcamera/ImageLoader.kt +106 -0
- package/android/src/main/java/expo/modules/smartcamera/MLKitFaceDetector.kt +273 -0
- package/android/src/main/java/expo/modules/smartcamera/SmartCameraModule.kt +205 -0
- package/android/src/main/java/expo/modules/smartcamera/SmartCameraView.kt +153 -0
- package/android/src/main/java/expo/modules/smartcamera/WebRTCFrameBridge.kt +184 -0
- package/app.plugin.js +17 -0
- package/build/SmartCamera.d.ts +17 -0
- package/build/SmartCamera.d.ts.map +1 -0
- package/build/SmartCamera.js +270 -0
- package/build/SmartCamera.js.map +1 -0
- package/build/SmartCameraModule.d.ts +112 -0
- package/build/SmartCameraModule.d.ts.map +1 -0
- package/build/SmartCameraModule.js +121 -0
- package/build/SmartCameraModule.js.map +1 -0
- package/build/SmartCameraView.d.ts +8 -0
- package/build/SmartCameraView.d.ts.map +1 -0
- package/build/SmartCameraView.js +7 -0
- package/build/SmartCameraView.js.map +1 -0
- package/build/detection/blinkProcessor.d.ts +23 -0
- package/build/detection/blinkProcessor.d.ts.map +1 -0
- package/build/detection/blinkProcessor.js +90 -0
- package/build/detection/blinkProcessor.js.map +1 -0
- package/build/detection/faceDetector.d.ts +16 -0
- package/build/detection/faceDetector.d.ts.map +1 -0
- package/build/detection/faceDetector.js +46 -0
- package/build/detection/faceDetector.js.map +1 -0
- package/build/detection/index.d.ts +4 -0
- package/build/detection/index.d.ts.map +1 -0
- package/build/detection/index.js +4 -0
- package/build/detection/index.js.map +1 -0
- package/build/detection/staticImageDetector.d.ts +25 -0
- package/build/detection/staticImageDetector.d.ts.map +1 -0
- package/build/detection/staticImageDetector.js +48 -0
- package/build/detection/staticImageDetector.js.map +1 -0
- package/build/hooks/index.d.ts +5 -0
- package/build/hooks/index.d.ts.map +1 -0
- package/build/hooks/index.js +5 -0
- package/build/hooks/index.js.map +1 -0
- package/build/hooks/useBlinkDetection.d.ts +39 -0
- package/build/hooks/useBlinkDetection.d.ts.map +1 -0
- package/build/hooks/useBlinkDetection.js +67 -0
- package/build/hooks/useBlinkDetection.js.map +1 -0
- package/build/hooks/useFaceDetection.d.ts +46 -0
- package/build/hooks/useFaceDetection.d.ts.map +1 -0
- package/build/hooks/useFaceDetection.js +80 -0
- package/build/hooks/useFaceDetection.js.map +1 -0
- package/build/hooks/useSmartCamera.d.ts +31 -0
- package/build/hooks/useSmartCamera.d.ts.map +1 -0
- package/build/hooks/useSmartCamera.js +75 -0
- package/build/hooks/useSmartCamera.js.map +1 -0
- package/build/hooks/useSmartCameraWebRTC.d.ts +58 -0
- package/build/hooks/useSmartCameraWebRTC.d.ts.map +1 -0
- package/build/hooks/useSmartCameraWebRTC.js +160 -0
- package/build/hooks/useSmartCameraWebRTC.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +20 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +478 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/index.d.ts +98 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +276 -0
- package/build/utils/index.js.map +1 -0
- package/build/webrtc/WebRTCBridge.d.ts +55 -0
- package/build/webrtc/WebRTCBridge.d.ts.map +1 -0
- package/build/webrtc/WebRTCBridge.js +113 -0
- package/build/webrtc/WebRTCBridge.js.map +1 -0
- package/build/webrtc/index.d.ts +3 -0
- package/build/webrtc/index.d.ts.map +1 -0
- package/build/webrtc/index.js +2 -0
- package/build/webrtc/index.js.map +1 -0
- package/build/webrtc/types.d.ts +64 -0
- package/build/webrtc/types.d.ts.map +1 -0
- package/build/webrtc/types.js +5 -0
- package/build/webrtc/types.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/MLKitFaceDetector.swift +310 -0
- package/ios/SmartCamera.podspec +33 -0
- package/ios/SmartCameraModule.swift +225 -0
- package/ios/SmartCameraView.swift +146 -0
- package/ios/WebRTCFrameBridge.swift +150 -0
- package/package.json +91 -0
- package/plugin/build/index.d.ts +28 -0
- package/plugin/build/index.js +33 -0
- package/plugin/build/withSmartCameraAndroid.d.ts +9 -0
- package/plugin/build/withSmartCameraAndroid.js +108 -0
- package/plugin/build/withSmartCameraIOS.d.ts +11 -0
- package/plugin/build/withSmartCameraIOS.js +92 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartCamera utility functions
|
|
3
|
+
*/
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// FPS Limiter
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Creates an FPS limiter for frame processing
|
|
9
|
+
*
|
|
10
|
+
* @param targetFps - Target frames per second
|
|
11
|
+
* @returns Object with shouldProcess function
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const limiter = createFpsLimiter(15);
|
|
16
|
+
*
|
|
17
|
+
* const frameProcessor = useFrameProcessor((frame) => {
|
|
18
|
+
* if (!limiter.shouldProcess()) return;
|
|
19
|
+
* // Process frame...
|
|
20
|
+
* }, []);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function createFpsLimiter(targetFps) {
|
|
24
|
+
const frameInterval = 1000 / targetFps;
|
|
25
|
+
let lastFrameTime = 0;
|
|
26
|
+
return {
|
|
27
|
+
shouldProcess: () => {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
if (now - lastFrameTime >= frameInterval) {
|
|
30
|
+
lastFrameTime = now;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
},
|
|
35
|
+
reset: () => {
|
|
36
|
+
lastFrameTime = 0;
|
|
37
|
+
},
|
|
38
|
+
setTargetFps: (fps) => {
|
|
39
|
+
// Note: This creates a new interval but doesn't update the closure
|
|
40
|
+
// Consider using a ref if dynamic FPS changes are needed
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Debounce
|
|
46
|
+
// ============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Creates a debounced function
|
|
49
|
+
*
|
|
50
|
+
* @param fn - Function to debounce
|
|
51
|
+
* @param delay - Delay in milliseconds
|
|
52
|
+
* @returns Debounced function
|
|
53
|
+
*/
|
|
54
|
+
export function debounce(fn, delay) {
|
|
55
|
+
let timeoutId = null;
|
|
56
|
+
return (...args) => {
|
|
57
|
+
if (timeoutId) {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
}
|
|
60
|
+
timeoutId = setTimeout(() => {
|
|
61
|
+
fn(...args);
|
|
62
|
+
timeoutId = null;
|
|
63
|
+
}, delay);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Throttle
|
|
68
|
+
// ============================================================================
|
|
69
|
+
/**
|
|
70
|
+
* Creates a throttled function
|
|
71
|
+
*
|
|
72
|
+
* @param fn - Function to throttle
|
|
73
|
+
* @param limit - Minimum time between calls in milliseconds
|
|
74
|
+
* @returns Throttled function
|
|
75
|
+
*/
|
|
76
|
+
export function throttle(fn, limit) {
|
|
77
|
+
let lastCall = 0;
|
|
78
|
+
return (...args) => {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
if (now - lastCall >= limit) {
|
|
81
|
+
lastCall = now;
|
|
82
|
+
fn(...args);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates an object pool for reducing garbage collection
|
|
88
|
+
*
|
|
89
|
+
* @param factory - Factory function to create new objects
|
|
90
|
+
* @param initialSize - Initial pool size
|
|
91
|
+
* @param maxSize - Maximum pool size
|
|
92
|
+
*/
|
|
93
|
+
export function createObjectPool(factory, initialSize = 10, maxSize = 100) {
|
|
94
|
+
const pool = [];
|
|
95
|
+
// Pre-populate pool
|
|
96
|
+
for (let i = 0; i < initialSize; i++) {
|
|
97
|
+
pool.push({ data: factory(), inUse: false });
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
acquire: () => {
|
|
101
|
+
// Find an available object
|
|
102
|
+
const available = pool.find((obj) => !obj.inUse);
|
|
103
|
+
if (available) {
|
|
104
|
+
available.inUse = true;
|
|
105
|
+
return available.data;
|
|
106
|
+
}
|
|
107
|
+
// Create new if pool not at max
|
|
108
|
+
if (pool.length < maxSize) {
|
|
109
|
+
const newObj = { data: factory(), inUse: true };
|
|
110
|
+
pool.push(newObj);
|
|
111
|
+
return newObj.data;
|
|
112
|
+
}
|
|
113
|
+
// Pool exhausted, create temporary object
|
|
114
|
+
return factory();
|
|
115
|
+
},
|
|
116
|
+
release: (obj) => {
|
|
117
|
+
const pooledObj = pool.find((p) => p.data === obj);
|
|
118
|
+
if (pooledObj) {
|
|
119
|
+
pooledObj.inUse = false;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
clear: () => {
|
|
123
|
+
pool.length = 0;
|
|
124
|
+
},
|
|
125
|
+
getStats: () => ({
|
|
126
|
+
size: pool.length,
|
|
127
|
+
inUse: pool.filter((p) => p.inUse).length,
|
|
128
|
+
available: pool.filter((p) => !p.inUse).length,
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Creates a SmartCameraError from an unknown error
|
|
134
|
+
*
|
|
135
|
+
* @param error - The original error
|
|
136
|
+
* @param defaultCode - Default error code if not determinable
|
|
137
|
+
* @returns SmartCameraError
|
|
138
|
+
*/
|
|
139
|
+
export function createSmartCameraError(error, defaultCode = 'UNKNOWN_ERROR') {
|
|
140
|
+
if (error instanceof Error) {
|
|
141
|
+
// Check for specific error types
|
|
142
|
+
const message = error.message.toLowerCase();
|
|
143
|
+
let code = defaultCode;
|
|
144
|
+
if (message.includes('permission')) {
|
|
145
|
+
code = 'PERMISSION_DENIED';
|
|
146
|
+
}
|
|
147
|
+
else if (message.includes('camera') && message.includes('unavailable')) {
|
|
148
|
+
code = 'CAMERA_UNAVAILABLE';
|
|
149
|
+
}
|
|
150
|
+
else if (message.includes('webrtc')) {
|
|
151
|
+
code = 'WEBRTC_ERROR';
|
|
152
|
+
}
|
|
153
|
+
else if (message.includes('mlkit') || message.includes('face detection')) {
|
|
154
|
+
code = 'ML_KIT_ERROR';
|
|
155
|
+
}
|
|
156
|
+
else if (message.includes('frame processor')) {
|
|
157
|
+
code = 'FRAME_PROCESSOR_ERROR';
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
code,
|
|
161
|
+
message: error.message,
|
|
162
|
+
nativeError: error,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
code: defaultCode,
|
|
167
|
+
message: String(error),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Safely execute a function and return a SmartCameraError on failure
|
|
172
|
+
*/
|
|
173
|
+
export async function safeExecute(fn, errorCode = 'UNKNOWN_ERROR') {
|
|
174
|
+
try {
|
|
175
|
+
const data = await fn();
|
|
176
|
+
return { data, error: null };
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
return { data: null, error: createSmartCameraError(error, errorCode) };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Validation Utilities
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/**
|
|
186
|
+
* Validates face detection options
|
|
187
|
+
*/
|
|
188
|
+
export function validateFaceDetectionOptions(options) {
|
|
189
|
+
const errors = [];
|
|
190
|
+
if (options.minFaceSize !== undefined) {
|
|
191
|
+
const minFaceSize = options.minFaceSize;
|
|
192
|
+
if (typeof minFaceSize !== 'number' || minFaceSize < 0 || minFaceSize > 1) {
|
|
193
|
+
errors.push('minFaceSize must be a number between 0 and 1');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (options.performanceMode !== undefined) {
|
|
197
|
+
const mode = options.performanceMode;
|
|
198
|
+
if (mode !== 'fast' && mode !== 'accurate') {
|
|
199
|
+
errors.push('performanceMode must be "fast" or "accurate"');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (options.landmarkMode !== undefined) {
|
|
203
|
+
const mode = options.landmarkMode;
|
|
204
|
+
if (mode !== 'none' && mode !== 'all') {
|
|
205
|
+
errors.push('landmarkMode must be "none" or "all"');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (options.contourMode !== undefined) {
|
|
209
|
+
const mode = options.contourMode;
|
|
210
|
+
if (mode !== 'none' && mode !== 'all') {
|
|
211
|
+
errors.push('contourMode must be "none" or "all"');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (options.classificationMode !== undefined) {
|
|
215
|
+
const mode = options.classificationMode;
|
|
216
|
+
if (mode !== 'none' && mode !== 'all') {
|
|
217
|
+
errors.push('classificationMode must be "none" or "all"');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Warn about contourMode + trackingEnabled combination
|
|
221
|
+
if (options.contourMode === 'all' && options.trackingEnabled === true) {
|
|
222
|
+
errors.push('Warning: Using contourMode="all" with trackingEnabled=true is not recommended. ' +
|
|
223
|
+
'Contour detection only works on the most prominent face, making tracking less useful.');
|
|
224
|
+
}
|
|
225
|
+
return errors;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Creates a performance monitor for frame processing
|
|
229
|
+
*/
|
|
230
|
+
export function createPerformanceMonitor(windowSize = 30) {
|
|
231
|
+
const processingTimes = [];
|
|
232
|
+
let frameCount = 0;
|
|
233
|
+
let droppedFrames = 0;
|
|
234
|
+
let lastFrameTime = 0;
|
|
235
|
+
const targetFrameTime = 1000 / 30; // 30 FPS target
|
|
236
|
+
return {
|
|
237
|
+
startFrame: () => {
|
|
238
|
+
return Date.now();
|
|
239
|
+
},
|
|
240
|
+
endFrame: (startTime) => {
|
|
241
|
+
const processingTime = Date.now() - startTime;
|
|
242
|
+
processingTimes.push(processingTime);
|
|
243
|
+
// Keep only last N samples
|
|
244
|
+
if (processingTimes.length > windowSize) {
|
|
245
|
+
processingTimes.shift();
|
|
246
|
+
}
|
|
247
|
+
frameCount++;
|
|
248
|
+
// Check for dropped frames
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
if (lastFrameTime > 0) {
|
|
251
|
+
const frameGap = now - lastFrameTime;
|
|
252
|
+
if (frameGap > targetFrameTime * 2) {
|
|
253
|
+
droppedFrames += Math.floor(frameGap / targetFrameTime) - 1;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
lastFrameTime = now;
|
|
257
|
+
},
|
|
258
|
+
getMetrics: () => {
|
|
259
|
+
const times = processingTimes.length > 0 ? processingTimes : [0];
|
|
260
|
+
return {
|
|
261
|
+
frameCount,
|
|
262
|
+
averageProcessingTime: times.reduce((a, b) => a + b, 0) / times.length,
|
|
263
|
+
minProcessingTime: Math.min(...times),
|
|
264
|
+
maxProcessingTime: Math.max(...times),
|
|
265
|
+
droppedFrames,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
reset: () => {
|
|
269
|
+
processingTimes.length = 0;
|
|
270
|
+
frameCount = 0;
|
|
271
|
+
droppedFrames = 0;
|
|
272
|
+
lastFrameTime = 0;
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SmartCamera utility functions\n */\n\n// ============================================================================\n// FPS Limiter\n// ============================================================================\n\n/**\n * Creates an FPS limiter for frame processing\n * \n * @param targetFps - Target frames per second\n * @returns Object with shouldProcess function\n * \n * @example\n * ```ts\n * const limiter = createFpsLimiter(15);\n * \n * const frameProcessor = useFrameProcessor((frame) => {\n * if (!limiter.shouldProcess()) return;\n * // Process frame...\n * }, []);\n * ```\n */\nexport function createFpsLimiter(targetFps: number) {\n const frameInterval = 1000 / targetFps;\n let lastFrameTime = 0;\n\n return {\n shouldProcess: (): boolean => {\n const now = Date.now();\n if (now - lastFrameTime >= frameInterval) {\n lastFrameTime = now;\n return true;\n }\n return false;\n },\n reset: () => {\n lastFrameTime = 0;\n },\n setTargetFps: (fps: number) => {\n // Note: This creates a new interval but doesn't update the closure\n // Consider using a ref if dynamic FPS changes are needed\n },\n };\n}\n\n// ============================================================================\n// Debounce\n// ============================================================================\n\n/**\n * Creates a debounced function\n * \n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n delay: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n };\n}\n\n// ============================================================================\n// Throttle\n// ============================================================================\n\n/**\n * Creates a throttled function\n * \n * @param fn - Function to throttle\n * @param limit - Minimum time between calls in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n fn: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let lastCall = 0;\n\n return (...args: Parameters<T>) => {\n const now = Date.now();\n if (now - lastCall >= limit) {\n lastCall = now;\n fn(...args);\n }\n };\n}\n\n// ============================================================================\n// Memory Pool for Face Objects\n// ============================================================================\n\ninterface PooledObject<T> {\n data: T;\n inUse: boolean;\n}\n\n/**\n * Creates an object pool for reducing garbage collection\n * \n * @param factory - Factory function to create new objects\n * @param initialSize - Initial pool size\n * @param maxSize - Maximum pool size\n */\nexport function createObjectPool<T>(\n factory: () => T,\n initialSize: number = 10,\n maxSize: number = 100\n) {\n const pool: PooledObject<T>[] = [];\n\n // Pre-populate pool\n for (let i = 0; i < initialSize; i++) {\n pool.push({ data: factory(), inUse: false });\n }\n\n return {\n acquire: (): T => {\n // Find an available object\n const available = pool.find((obj) => !obj.inUse);\n if (available) {\n available.inUse = true;\n return available.data;\n }\n\n // Create new if pool not at max\n if (pool.length < maxSize) {\n const newObj = { data: factory(), inUse: true };\n pool.push(newObj);\n return newObj.data;\n }\n\n // Pool exhausted, create temporary object\n return factory();\n },\n\n release: (obj: T): void => {\n const pooledObj = pool.find((p) => p.data === obj);\n if (pooledObj) {\n pooledObj.inUse = false;\n }\n },\n\n clear: (): void => {\n pool.length = 0;\n },\n\n getStats: () => ({\n size: pool.length,\n inUse: pool.filter((p) => p.inUse).length,\n available: pool.filter((p) => !p.inUse).length,\n }),\n };\n}\n\n// ============================================================================\n// Error Utilities\n// ============================================================================\n\nimport type { SmartCameraError, SmartCameraErrorCode } from '../types';\n\n/**\n * Creates a SmartCameraError from an unknown error\n * \n * @param error - The original error\n * @param defaultCode - Default error code if not determinable\n * @returns SmartCameraError\n */\nexport function createSmartCameraError(\n error: unknown,\n defaultCode: SmartCameraErrorCode = 'UNKNOWN_ERROR'\n): SmartCameraError {\n if (error instanceof Error) {\n // Check for specific error types\n const message = error.message.toLowerCase();\n\n let code: SmartCameraErrorCode = defaultCode;\n if (message.includes('permission')) {\n code = 'PERMISSION_DENIED';\n } else if (message.includes('camera') && message.includes('unavailable')) {\n code = 'CAMERA_UNAVAILABLE';\n } else if (message.includes('webrtc')) {\n code = 'WEBRTC_ERROR';\n } else if (message.includes('mlkit') || message.includes('face detection')) {\n code = 'ML_KIT_ERROR';\n } else if (message.includes('frame processor')) {\n code = 'FRAME_PROCESSOR_ERROR';\n }\n\n return {\n code,\n message: error.message,\n nativeError: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n };\n}\n\n/**\n * Safely execute a function and return a SmartCameraError on failure\n */\nexport async function safeExecute<T>(\n fn: () => Promise<T>,\n errorCode: SmartCameraErrorCode = 'UNKNOWN_ERROR'\n): Promise<{ data: T; error: null } | { data: null; error: SmartCameraError }> {\n try {\n const data = await fn();\n return { data, error: null };\n } catch (error) {\n return { data: null, error: createSmartCameraError(error, errorCode) };\n }\n}\n\n// ============================================================================\n// Validation Utilities\n// ============================================================================\n\n/**\n * Validates face detection options\n */\nexport function validateFaceDetectionOptions(options: Record<string, unknown>): string[] {\n const errors: string[] = [];\n\n if (options.minFaceSize !== undefined) {\n const minFaceSize = options.minFaceSize as number;\n if (typeof minFaceSize !== 'number' || minFaceSize < 0 || minFaceSize > 1) {\n errors.push('minFaceSize must be a number between 0 and 1');\n }\n }\n\n if (options.performanceMode !== undefined) {\n const mode = options.performanceMode as string;\n if (mode !== 'fast' && mode !== 'accurate') {\n errors.push('performanceMode must be \"fast\" or \"accurate\"');\n }\n }\n\n if (options.landmarkMode !== undefined) {\n const mode = options.landmarkMode as string;\n if (mode !== 'none' && mode !== 'all') {\n errors.push('landmarkMode must be \"none\" or \"all\"');\n }\n }\n\n if (options.contourMode !== undefined) {\n const mode = options.contourMode as string;\n if (mode !== 'none' && mode !== 'all') {\n errors.push('contourMode must be \"none\" or \"all\"');\n }\n }\n\n if (options.classificationMode !== undefined) {\n const mode = options.classificationMode as string;\n if (mode !== 'none' && mode !== 'all') {\n errors.push('classificationMode must be \"none\" or \"all\"');\n }\n }\n\n // Warn about contourMode + trackingEnabled combination\n if (options.contourMode === 'all' && options.trackingEnabled === true) {\n errors.push(\n 'Warning: Using contourMode=\"all\" with trackingEnabled=true is not recommended. ' +\n 'Contour detection only works on the most prominent face, making tracking less useful.'\n );\n }\n\n return errors;\n}\n\n// ============================================================================\n// Performance Monitoring\n// ============================================================================\n\ninterface PerformanceMetrics {\n frameCount: number;\n averageProcessingTime: number;\n minProcessingTime: number;\n maxProcessingTime: number;\n droppedFrames: number;\n}\n\n/**\n * Creates a performance monitor for frame processing\n */\nexport function createPerformanceMonitor(windowSize: number = 30) {\n const processingTimes: number[] = [];\n let frameCount = 0;\n let droppedFrames = 0;\n let lastFrameTime = 0;\n const targetFrameTime = 1000 / 30; // 30 FPS target\n\n return {\n startFrame: (): number => {\n return Date.now();\n },\n\n endFrame: (startTime: number): void => {\n const processingTime = Date.now() - startTime;\n processingTimes.push(processingTime);\n \n // Keep only last N samples\n if (processingTimes.length > windowSize) {\n processingTimes.shift();\n }\n\n frameCount++;\n\n // Check for dropped frames\n const now = Date.now();\n if (lastFrameTime > 0) {\n const frameGap = now - lastFrameTime;\n if (frameGap > targetFrameTime * 2) {\n droppedFrames += Math.floor(frameGap / targetFrameTime) - 1;\n }\n }\n lastFrameTime = now;\n },\n\n getMetrics: (): PerformanceMetrics => {\n const times = processingTimes.length > 0 ? processingTimes : [0];\n return {\n frameCount,\n averageProcessingTime: times.reduce((a, b) => a + b, 0) / times.length,\n minProcessingTime: Math.min(...times),\n maxProcessingTime: Math.max(...times),\n droppedFrames,\n };\n },\n\n reset: (): void => {\n processingTimes.length = 0;\n frameCount = 0;\n droppedFrames = 0;\n lastFrameTime = 0;\n },\n };\n}\n\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { WebRTCVideoSourceConfig, WebRTCStreamStats, WebRTCQualitySettings } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* WebRTC Bridge for SmartCamera
|
|
4
|
+
*
|
|
5
|
+
* This class provides a bridge between VisionCamera frames and WebRTC,
|
|
6
|
+
* allowing camera frames to be streamed via WebRTC peer connections.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WebRTCBridge {
|
|
9
|
+
private isInitialized;
|
|
10
|
+
private isStreaming;
|
|
11
|
+
private config;
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the WebRTC bridge
|
|
14
|
+
*
|
|
15
|
+
* This must be called before starting streaming.
|
|
16
|
+
*/
|
|
17
|
+
initialize(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Start streaming camera frames via WebRTC
|
|
20
|
+
*
|
|
21
|
+
* @param config - Video source configuration
|
|
22
|
+
*/
|
|
23
|
+
startStreaming(config: WebRTCVideoSourceConfig): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Stop streaming
|
|
26
|
+
*/
|
|
27
|
+
stopStreaming(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if currently streaming
|
|
30
|
+
*/
|
|
31
|
+
getIsStreaming(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Get current configuration
|
|
34
|
+
*/
|
|
35
|
+
getConfig(): WebRTCVideoSourceConfig | null;
|
|
36
|
+
/**
|
|
37
|
+
* Update quality settings (if supported)
|
|
38
|
+
*
|
|
39
|
+
* @param settings - Quality settings to apply
|
|
40
|
+
*/
|
|
41
|
+
updateQuality(settings: WebRTCQualitySettings): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Get current stream statistics
|
|
44
|
+
*/
|
|
45
|
+
getStats(): Promise<WebRTCStreamStats | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Cleanup and release resources
|
|
48
|
+
*/
|
|
49
|
+
destroy(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the shared WebRTC bridge instance
|
|
53
|
+
*/
|
|
54
|
+
export declare function getWebRTCBridge(): WebRTCBridge;
|
|
55
|
+
//# sourceMappingURL=WebRTCBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebRTCBridge.d.ts","sourceRoot":"","sources":["../../src/webrtc/WebRTCBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAwC;IAEtD;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;;;OAIG;IACG,cAAc,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpE;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAUpC;;OAEG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACH,SAAS,IAAI,uBAAuB,GAAG,IAAI;IAI3C;;;;OAIG;IACG,aAAa,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAgBnD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B;AAKD;;GAEG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAK9C"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import SmartCameraModule from '../SmartCameraModule';
|
|
2
|
+
/**
|
|
3
|
+
* WebRTC Bridge for SmartCamera
|
|
4
|
+
*
|
|
5
|
+
* This class provides a bridge between VisionCamera frames and WebRTC,
|
|
6
|
+
* allowing camera frames to be streamed via WebRTC peer connections.
|
|
7
|
+
*/
|
|
8
|
+
export class WebRTCBridge {
|
|
9
|
+
isInitialized = false;
|
|
10
|
+
isStreaming = false;
|
|
11
|
+
config = null;
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the WebRTC bridge
|
|
14
|
+
*
|
|
15
|
+
* This must be called before starting streaming.
|
|
16
|
+
*/
|
|
17
|
+
async initialize() {
|
|
18
|
+
if (this.isInitialized) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
await SmartCameraModule.initializeWebRTC();
|
|
22
|
+
this.isInitialized = true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start streaming camera frames via WebRTC
|
|
26
|
+
*
|
|
27
|
+
* @param config - Video source configuration
|
|
28
|
+
*/
|
|
29
|
+
async startStreaming(config) {
|
|
30
|
+
if (!this.isInitialized) {
|
|
31
|
+
await this.initialize();
|
|
32
|
+
}
|
|
33
|
+
if (this.isStreaming) {
|
|
34
|
+
console.warn('[WebRTCBridge] Already streaming, stopping first');
|
|
35
|
+
await this.stopStreaming();
|
|
36
|
+
}
|
|
37
|
+
this.config = config;
|
|
38
|
+
await SmartCameraModule.startWebRTCStream({
|
|
39
|
+
width: config.width,
|
|
40
|
+
height: config.height,
|
|
41
|
+
frameRate: config.frameRate,
|
|
42
|
+
});
|
|
43
|
+
this.isStreaming = true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Stop streaming
|
|
47
|
+
*/
|
|
48
|
+
async stopStreaming() {
|
|
49
|
+
if (!this.isStreaming) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await SmartCameraModule.stopWebRTCStream();
|
|
53
|
+
this.isStreaming = false;
|
|
54
|
+
this.config = null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if currently streaming
|
|
58
|
+
*/
|
|
59
|
+
getIsStreaming() {
|
|
60
|
+
return this.isStreaming;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get current configuration
|
|
64
|
+
*/
|
|
65
|
+
getConfig() {
|
|
66
|
+
return this.config;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Update quality settings (if supported)
|
|
70
|
+
*
|
|
71
|
+
* @param settings - Quality settings to apply
|
|
72
|
+
*/
|
|
73
|
+
async updateQuality(settings) {
|
|
74
|
+
// This would call a native method to adjust encoder settings
|
|
75
|
+
console.log('[WebRTCBridge] Updating quality:', settings);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get current stream statistics
|
|
79
|
+
*/
|
|
80
|
+
async getStats() {
|
|
81
|
+
if (!this.isStreaming) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
// This would call a native method to get stats
|
|
85
|
+
// For now, return placeholder
|
|
86
|
+
return {
|
|
87
|
+
frameRate: this.config?.frameRate ?? 0,
|
|
88
|
+
framesSent: 0,
|
|
89
|
+
framesDropped: 0,
|
|
90
|
+
bytesSent: 0,
|
|
91
|
+
bitrate: 0,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Cleanup and release resources
|
|
96
|
+
*/
|
|
97
|
+
async destroy() {
|
|
98
|
+
await this.stopStreaming();
|
|
99
|
+
this.isInitialized = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Singleton instance for convenience
|
|
103
|
+
let bridgeInstance = null;
|
|
104
|
+
/**
|
|
105
|
+
* Get the shared WebRTC bridge instance
|
|
106
|
+
*/
|
|
107
|
+
export function getWebRTCBridge() {
|
|
108
|
+
if (!bridgeInstance) {
|
|
109
|
+
bridgeInstance = new WebRTCBridge();
|
|
110
|
+
}
|
|
111
|
+
return bridgeInstance;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=WebRTCBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebRTCBridge.js","sourceRoot":"","sources":["../../src/webrtc/WebRTCBridge.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,sBAAsB,CAAC;AAOrD;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IACf,aAAa,GAAG,KAAK,CAAC;IACtB,WAAW,GAAG,KAAK,CAAC;IACpB,MAAM,GAAmC,IAAI,CAAC;IAEtD;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,MAAM,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,MAA+B;QAClD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;YACjE,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,iBAAiB,CAAC,iBAAiB,CAAC;YACxC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,MAAM,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,QAA+B;QACjD,6DAA6D;QAC7D,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,QAAQ,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,+CAA+C;QAC/C,8BAA8B;QAC9B,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC;YACtC,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;SACX,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3B,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;IAC7B,CAAC;CACF;AAED,qCAAqC;AACrC,IAAI,cAAc,GAAwB,IAAI,CAAC;AAE/C;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,IAAI,YAAY,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC","sourcesContent":["import SmartCameraModule from '../SmartCameraModule';\nimport type {\n WebRTCVideoSourceConfig,\n WebRTCStreamStats,\n WebRTCQualitySettings,\n} from './types';\n\n/**\n * WebRTC Bridge for SmartCamera\n * \n * This class provides a bridge between VisionCamera frames and WebRTC,\n * allowing camera frames to be streamed via WebRTC peer connections.\n */\nexport class WebRTCBridge {\n private isInitialized = false;\n private isStreaming = false;\n private config: WebRTCVideoSourceConfig | null = null;\n\n /**\n * Initialize the WebRTC bridge\n * \n * This must be called before starting streaming.\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n\n await SmartCameraModule.initializeWebRTC();\n this.isInitialized = true;\n }\n\n /**\n * Start streaming camera frames via WebRTC\n * \n * @param config - Video source configuration\n */\n async startStreaming(config: WebRTCVideoSourceConfig): Promise<void> {\n if (!this.isInitialized) {\n await this.initialize();\n }\n\n if (this.isStreaming) {\n console.warn('[WebRTCBridge] Already streaming, stopping first');\n await this.stopStreaming();\n }\n\n this.config = config;\n await SmartCameraModule.startWebRTCStream({\n width: config.width,\n height: config.height,\n frameRate: config.frameRate,\n });\n this.isStreaming = true;\n }\n\n /**\n * Stop streaming\n */\n async stopStreaming(): Promise<void> {\n if (!this.isStreaming) {\n return;\n }\n\n await SmartCameraModule.stopWebRTCStream();\n this.isStreaming = false;\n this.config = null;\n }\n\n /**\n * Check if currently streaming\n */\n getIsStreaming(): boolean {\n return this.isStreaming;\n }\n\n /**\n * Get current configuration\n */\n getConfig(): WebRTCVideoSourceConfig | null {\n return this.config;\n }\n\n /**\n * Update quality settings (if supported)\n * \n * @param settings - Quality settings to apply\n */\n async updateQuality(settings: WebRTCQualitySettings): Promise<void> {\n // This would call a native method to adjust encoder settings\n console.log('[WebRTCBridge] Updating quality:', settings);\n }\n\n /**\n * Get current stream statistics\n */\n async getStats(): Promise<WebRTCStreamStats | null> {\n if (!this.isStreaming) {\n return null;\n }\n\n // This would call a native method to get stats\n // For now, return placeholder\n return {\n frameRate: this.config?.frameRate ?? 0,\n framesSent: 0,\n framesDropped: 0,\n bytesSent: 0,\n bitrate: 0,\n };\n }\n\n /**\n * Cleanup and release resources\n */\n async destroy(): Promise<void> {\n await this.stopStreaming();\n this.isInitialized = false;\n }\n}\n\n// Singleton instance for convenience\nlet bridgeInstance: WebRTCBridge | null = null;\n\n/**\n * Get the shared WebRTC bridge instance\n */\nexport function getWebRTCBridge(): WebRTCBridge {\n if (!bridgeInstance) {\n bridgeInstance = new WebRTCBridge();\n }\n return bridgeInstance;\n}\n\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/D,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { WebRTCBridge, getWebRTCBridge } from './WebRTCBridge';\nexport type {\n VideoFrameFormat,\n WebRTCVideoSourceConfig,\n WebRTCConnectionState,\n WebRTCStreamStats,\n WebRTCQualitySettings,\n WebRTCStreamEvent,\n} from './types';\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebRTC-related types for the SmartCamera module
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Video frame format for WebRTC
|
|
6
|
+
*/
|
|
7
|
+
export type VideoFrameFormat = 'I420' | 'NV12' | 'NV21' | 'BGRA';
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for the WebRTC video source
|
|
10
|
+
*/
|
|
11
|
+
export interface WebRTCVideoSourceConfig {
|
|
12
|
+
/** Frame width in pixels */
|
|
13
|
+
width: number;
|
|
14
|
+
/** Frame height in pixels */
|
|
15
|
+
height: number;
|
|
16
|
+
/** Target frame rate */
|
|
17
|
+
frameRate: number;
|
|
18
|
+
/** Video frame format. Default: 'I420' */
|
|
19
|
+
format?: VideoFrameFormat;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* WebRTC connection state
|
|
23
|
+
*/
|
|
24
|
+
export type WebRTCConnectionState = 'new' | 'connecting' | 'connected' | 'disconnected' | 'failed' | 'closed';
|
|
25
|
+
/**
|
|
26
|
+
* WebRTC stream statistics
|
|
27
|
+
*/
|
|
28
|
+
export interface WebRTCStreamStats {
|
|
29
|
+
/** Current frame rate */
|
|
30
|
+
frameRate: number;
|
|
31
|
+
/** Frames sent */
|
|
32
|
+
framesSent: number;
|
|
33
|
+
/** Frames dropped */
|
|
34
|
+
framesDropped: number;
|
|
35
|
+
/** Bytes sent */
|
|
36
|
+
bytesSent: number;
|
|
37
|
+
/** Current bitrate in bps */
|
|
38
|
+
bitrate: number;
|
|
39
|
+
/** Round-trip time in ms */
|
|
40
|
+
rtt?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* WebRTC stream quality settings
|
|
44
|
+
*/
|
|
45
|
+
export interface WebRTCQualitySettings {
|
|
46
|
+
/** Maximum bitrate in bps */
|
|
47
|
+
maxBitrate?: number;
|
|
48
|
+
/** Minimum bitrate in bps */
|
|
49
|
+
minBitrate?: number;
|
|
50
|
+
/** Target frame rate */
|
|
51
|
+
targetFrameRate?: number;
|
|
52
|
+
/** Resolution scale (0.0 - 1.0) */
|
|
53
|
+
resolutionScale?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Event emitted when WebRTC stream state changes
|
|
57
|
+
*/
|
|
58
|
+
export interface WebRTCStreamEvent {
|
|
59
|
+
type: 'started' | 'stopped' | 'error' | 'stats';
|
|
60
|
+
timestamp: number;
|
|
61
|
+
error?: Error;
|
|
62
|
+
stats?: WebRTCStreamStats;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webrtc/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IAEf,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;IAElB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,KAAK,GACL,YAAY,GACZ,WAAW,GACX,cAAc,GACd,QAAQ,GACR,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAElB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IAEnB,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;IAEtB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAElB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAEhB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,wBAAwB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,mCAAmC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webrtc/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * WebRTC-related types for the SmartCamera module\n */\n\n/**\n * Video frame format for WebRTC\n */\nexport type VideoFrameFormat = 'I420' | 'NV12' | 'NV21' | 'BGRA';\n\n/**\n * Configuration for the WebRTC video source\n */\nexport interface WebRTCVideoSourceConfig {\n /** Frame width in pixels */\n width: number;\n \n /** Frame height in pixels */\n height: number;\n \n /** Target frame rate */\n frameRate: number;\n \n /** Video frame format. Default: 'I420' */\n format?: VideoFrameFormat;\n}\n\n/**\n * WebRTC connection state\n */\nexport type WebRTCConnectionState =\n | 'new'\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'failed'\n | 'closed';\n\n/**\n * WebRTC stream statistics\n */\nexport interface WebRTCStreamStats {\n /** Current frame rate */\n frameRate: number;\n \n /** Frames sent */\n framesSent: number;\n \n /** Frames dropped */\n framesDropped: number;\n \n /** Bytes sent */\n bytesSent: number;\n \n /** Current bitrate in bps */\n bitrate: number;\n \n /** Round-trip time in ms */\n rtt?: number;\n}\n\n/**\n * WebRTC stream quality settings\n */\nexport interface WebRTCQualitySettings {\n /** Maximum bitrate in bps */\n maxBitrate?: number;\n \n /** Minimum bitrate in bps */\n minBitrate?: number;\n \n /** Target frame rate */\n targetFrameRate?: number;\n \n /** Resolution scale (0.0 - 1.0) */\n resolutionScale?: number;\n}\n\n/**\n * Event emitted when WebRTC stream state changes\n */\nexport interface WebRTCStreamEvent {\n type: 'started' | 'stopped' | 'error' | 'stats';\n timestamp: number;\n error?: Error;\n stats?: WebRTCStreamStats;\n}\n\n"]}
|