@pose-tracker/react-native-pose-estimation-light 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/LICENSE +45 -0
- package/PoseTrackerVision.podspec +28 -0
- package/README.md +161 -0
- package/THIRD_PARTY_NOTICES.md +19 -0
- package/ios/PoseTrackerVision/PoseTrackerBodyPosePlugin.m +18 -0
- package/ios/PoseTrackerVision/PoseTrackerBodyPosePlugin.swift +108 -0
- package/lib/PoseTrackerProvider.d.ts +102 -0
- package/lib/PoseTrackerProvider.js +204 -0
- package/lib/api/configure.d.ts +29 -0
- package/lib/api/configure.js +64 -0
- package/lib/api/skeleton.d.ts +17 -0
- package/lib/api/skeleton.js +53 -0
- package/lib/api/track.d.ts +61 -0
- package/lib/api/track.js +146 -0
- package/lib/backends/PoseBackend.d.ts +50 -0
- package/lib/backends/PoseBackend.js +11 -0
- package/lib/backends/vision/VisionPoseBackend.d.ts +72 -0
- package/lib/backends/vision/VisionPoseBackend.js +177 -0
- package/lib/backends/vision/mapVisionJoints.d.ts +35 -0
- package/lib/backends/vision/mapVisionJoints.js +162 -0
- package/lib/backends/vision/optionalVision.d.ts +32 -0
- package/lib/backends/vision/optionalVision.js +76 -0
- package/lib/backends/webview/WebViewPoseBackend.d.ts +223 -0
- package/lib/backends/webview/WebViewPoseBackend.js +382 -0
- package/lib/backends/webview/brandAssets.d.ts +5 -0
- package/lib/backends/webview/brandAssets.js +8 -0
- package/lib/backends/webview/onlineRuntime.d.ts +45 -0
- package/lib/backends/webview/onlineRuntime.js +57 -0
- package/lib/backends/webview/poseHtml.d.ts +71 -0
- package/lib/backends/webview/poseHtml.js +188 -0
- package/lib/backends/webview/poseRuntimeSource.d.ts +3 -0
- package/lib/backends/webview/poseRuntimeSource.js +4 -0
- package/lib/cache/obfuscate.d.ts +18 -0
- package/lib/cache/obfuscate.js +90 -0
- package/lib/camera/PoseCameraView.d.ts +51 -0
- package/lib/camera/PoseCameraView.js +99 -0
- package/lib/camera/WebViewPoseView.d.ts +81 -0
- package/lib/camera/WebViewPoseView.js +312 -0
- package/lib/client.d.ts +361 -0
- package/lib/client.js +1046 -0
- package/lib/diagnostics/logReport.d.ts +23 -0
- package/lib/diagnostics/logReport.js +102 -0
- package/lib/engine/EngineLoader.d.ts +95 -0
- package/lib/engine/EngineLoader.js +347 -0
- package/lib/engine/types.d.ts +83 -0
- package/lib/engine/types.js +11 -0
- package/lib/events/classicMessage.d.ts +31 -0
- package/lib/events/classicMessage.js +259 -0
- package/lib/exercises/aliases.d.ts +18 -0
- package/lib/exercises/aliases.js +48 -0
- package/lib/index.d.ts +72 -0
- package/lib/index.js +170 -0
- package/lib/models/poseModels.d.ts +39 -0
- package/lib/models/poseModels.js +63 -0
- package/lib/quality/AdaptiveQualityController.d.ts +120 -0
- package/lib/quality/AdaptiveQualityController.js +423 -0
- package/lib/quality/RuntimeGuard.d.ts +34 -0
- package/lib/quality/RuntimeGuard.js +105 -0
- package/lib/quality/captureMode.d.ts +82 -0
- package/lib/quality/captureMode.js +75 -0
- package/lib/quality/deviceCapability.d.ts +37 -0
- package/lib/quality/deviceCapability.js +177 -0
- package/lib/quality/profiles.d.ts +125 -0
- package/lib/quality/profiles.js +202 -0
- package/lib/runtime/RuntimeCache.d.ts +75 -0
- package/lib/runtime/RuntimeCache.js +230 -0
- package/lib/sdkVersion.d.ts +2 -0
- package/lib/sdkVersion.js +5 -0
- package/lib/support/optionalModules.d.ts +46 -0
- package/lib/support/optionalModules.js +82 -0
- package/lib/types/acceleration.d.ts +86 -0
- package/lib/types/acceleration.js +15 -0
- package/lib/types/events.d.ts +345 -0
- package/lib/types/events.js +10 -0
- package/lib/types/features.d.ts +90 -0
- package/lib/types/features.js +122 -0
- package/lib/types/manifest.d.ts +197 -0
- package/lib/types/manifest.js +10 -0
- package/lib/types/pose.d.ts +36 -0
- package/lib/types/pose.js +41 -0
- package/lib/types/preload.d.ts +17 -0
- package/lib/types/preload.js +10 -0
- package/lib/types/skeleton.d.ts +29 -0
- package/lib/types/skeleton.js +52 -0
- package/package.json +92 -0
- package/react-native.config.js +12 -0
- package/src/PoseTrackerProvider.tsx +332 -0
- package/src/api/configure.ts +82 -0
- package/src/api/skeleton.ts +60 -0
- package/src/api/track.ts +182 -0
- package/src/backends/PoseBackend.ts +59 -0
- package/src/backends/vision/VisionPoseBackend.ts +238 -0
- package/src/backends/vision/mapVisionJoints.ts +185 -0
- package/src/backends/vision/optionalVision.ts +94 -0
- package/src/backends/webview/WebViewPoseBackend.ts +582 -0
- package/src/backends/webview/brandAssets.ts +5 -0
- package/src/backends/webview/onlineRuntime.ts +94 -0
- package/src/backends/webview/poseHtml.ts +259 -0
- package/src/backends/webview/poseRuntimeSource.d.ts +3 -0
- package/src/cache/obfuscate.ts +94 -0
- package/src/camera/PoseCameraView.tsx +223 -0
- package/src/camera/WebViewPoseView.tsx +466 -0
- package/src/client.ts +1295 -0
- package/src/diagnostics/logReport.ts +131 -0
- package/src/engine/EngineLoader.ts +428 -0
- package/src/engine/types.ts +93 -0
- package/src/events/classicMessage.ts +271 -0
- package/src/exercises/aliases.ts +50 -0
- package/src/index.ts +243 -0
- package/src/models/poseModels.ts +93 -0
- package/src/quality/AdaptiveQualityController.ts +579 -0
- package/src/quality/RuntimeGuard.ts +119 -0
- package/src/quality/captureMode.ts +97 -0
- package/src/quality/deviceCapability.ts +187 -0
- package/src/quality/profiles.ts +253 -0
- package/src/runtime/RuntimeCache.ts +300 -0
- package/src/sdkVersion.ts +2 -0
- package/src/support/optionalModules.ts +114 -0
- package/src/types/acceleration.ts +94 -0
- package/src/types/events.ts +437 -0
- package/src/types/features.ts +176 -0
- package/src/types/manifest.ts +230 -0
- package/src/types/pose.ts +85 -0
- package/src/types/preload.ts +19 -0
- package/src/types/skeleton.ts +77 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebView inference backend — the SDK's base runtime on BOTH platforms.
|
|
3
|
+
*
|
|
4
|
+
* Runs MoveNet SinglePose Lightning (17 keypoints, 192×192) inside a real
|
|
5
|
+
* browser WebGL context (Chromium ANGLE on Android, WKWebView on iOS) — the
|
|
6
|
+
* same approach the PoseTracker WebView product uses in production, and the
|
|
7
|
+
* only one real-time across the whole Android park in Expo Go. Fully
|
|
8
|
+
* offline: TF.js and the model ship inside the npm package (poseHtml.ts).
|
|
9
|
+
*
|
|
10
|
+
* The WebView owns the camera (`getUserMedia`). Frames are NOT pushed from
|
|
11
|
+
* React Native — `estimatePose()` returns the latest pose posted by the page.
|
|
12
|
+
* Mount {@link WebViewPoseView} (or a 1×1 warmer) to attach the runtime.
|
|
13
|
+
*/
|
|
14
|
+
import type { PoseBackend, PoseBackendInitOptions, PoseInputFrame } from '../PoseBackend';
|
|
15
|
+
import type { Pose } from '../../types/pose';
|
|
16
|
+
import type { AccelerationDiagnostics, AccelerationState, DiagnosticListener } from '../../types/acceleration';
|
|
17
|
+
import type { PoseTrackerEvent } from '../../types/events';
|
|
18
|
+
export type WebViewPoseMessage = {
|
|
19
|
+
type: 'ready';
|
|
20
|
+
backend: string;
|
|
21
|
+
medianInferenceMs: number | null;
|
|
22
|
+
warmUpRunsMs: number[];
|
|
23
|
+
estimatedFps?: number | null;
|
|
24
|
+
profileId?: string;
|
|
25
|
+
/** false after basic (model-only) cold-start; true once getUserMedia ran. */
|
|
26
|
+
cameraOpened?: boolean;
|
|
27
|
+
coldStart?: 'basic' | 'full';
|
|
28
|
+
gl: {
|
|
29
|
+
renderer: string | null;
|
|
30
|
+
vendor: string | null;
|
|
31
|
+
version: string | null;
|
|
32
|
+
} | null;
|
|
33
|
+
note?: string | null;
|
|
34
|
+
} | {
|
|
35
|
+
type: 'warmup_estimate';
|
|
36
|
+
medianInferenceMs: number | null;
|
|
37
|
+
estimatedFps: number | null;
|
|
38
|
+
/** Platform floor (iOS 30 / Android 15). */
|
|
39
|
+
minTargetFps: number;
|
|
40
|
+
/** @deprecated Use {@link minTargetFps}. */
|
|
41
|
+
targetFps?: number;
|
|
42
|
+
profileId: string;
|
|
43
|
+
previousProfileId?: string;
|
|
44
|
+
idealWidth?: number;
|
|
45
|
+
idealHeight?: number;
|
|
46
|
+
gl?: {
|
|
47
|
+
renderer: string | null;
|
|
48
|
+
vendor: string | null;
|
|
49
|
+
version: string | null;
|
|
50
|
+
} | null;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'pose';
|
|
53
|
+
keypoints: Array<{
|
|
54
|
+
name: string;
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
score: number;
|
|
58
|
+
}>;
|
|
59
|
+
score: number;
|
|
60
|
+
inferenceTimeMs: number;
|
|
61
|
+
timestampMs: number;
|
|
62
|
+
} | {
|
|
63
|
+
type: 'stats';
|
|
64
|
+
fps: number;
|
|
65
|
+
medianInferenceMs: number | null;
|
|
66
|
+
estimatedFpsFromMedian?: number | null;
|
|
67
|
+
frames: number;
|
|
68
|
+
backend: string;
|
|
69
|
+
breakdown?: {
|
|
70
|
+
drawMs: number | null;
|
|
71
|
+
fromPixelsMs: number | null;
|
|
72
|
+
executeMs: number | null;
|
|
73
|
+
totalPipelineMs?: number | null;
|
|
74
|
+
};
|
|
75
|
+
windowCounts?: {
|
|
76
|
+
inferred: number;
|
|
77
|
+
skipped: number;
|
|
78
|
+
};
|
|
79
|
+
experiments?: Record<string, unknown>;
|
|
80
|
+
leverHints?: {
|
|
81
|
+
dominantStage?: string;
|
|
82
|
+
dominantMs?: number;
|
|
83
|
+
bitmapShare?: number;
|
|
84
|
+
fromPixelsShare?: number;
|
|
85
|
+
executeShare?: number;
|
|
86
|
+
hints?: string[];
|
|
87
|
+
} | null;
|
|
88
|
+
videoSize?: string;
|
|
89
|
+
mode?: string;
|
|
90
|
+
} | {
|
|
91
|
+
type: 'initialization';
|
|
92
|
+
message: string;
|
|
93
|
+
ready?: boolean;
|
|
94
|
+
step?: 'accessing_webcam' | 'loading_pose_model' | 'ready';
|
|
95
|
+
} | {
|
|
96
|
+
type: 'warning';
|
|
97
|
+
message: string;
|
|
98
|
+
code?: string;
|
|
99
|
+
} | {
|
|
100
|
+
type: 'diag';
|
|
101
|
+
message: string;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'error';
|
|
104
|
+
message: string;
|
|
105
|
+
code?: string;
|
|
106
|
+
};
|
|
107
|
+
export interface WebViewPoseStats {
|
|
108
|
+
fps: number;
|
|
109
|
+
medianInferenceMs: number | null;
|
|
110
|
+
estimatedFpsFromMedian?: number | null;
|
|
111
|
+
frames: number;
|
|
112
|
+
backend: string;
|
|
113
|
+
videoSize?: string;
|
|
114
|
+
mode?: string;
|
|
115
|
+
breakdown?: {
|
|
116
|
+
drawMs: number | null;
|
|
117
|
+
fromPixelsMs: number | null;
|
|
118
|
+
executeMs: number | null;
|
|
119
|
+
totalPipelineMs?: number | null;
|
|
120
|
+
};
|
|
121
|
+
windowCounts?: {
|
|
122
|
+
inferred: number;
|
|
123
|
+
skipped: number;
|
|
124
|
+
};
|
|
125
|
+
experiments?: Record<string, unknown>;
|
|
126
|
+
leverHints?: {
|
|
127
|
+
dominantStage?: string;
|
|
128
|
+
dominantMs?: number;
|
|
129
|
+
bitmapShare?: number;
|
|
130
|
+
fromPixelsShare?: number;
|
|
131
|
+
executeShare?: number;
|
|
132
|
+
hints?: string[];
|
|
133
|
+
} | null;
|
|
134
|
+
}
|
|
135
|
+
export interface WebViewPoseBackendOptions {
|
|
136
|
+
onDiagnostic?: DiagnosticListener;
|
|
137
|
+
onAccelerationChange?: (state: AccelerationState) => void;
|
|
138
|
+
/** Fired on every pose from the WebView (used by WebViewPoseView / ingest). */
|
|
139
|
+
onPose?: (pose: Pose, inferenceTimeMs: number) => void;
|
|
140
|
+
/** Fired when the Chromium page reports ready (model + backend warmed). */
|
|
141
|
+
onReady?: (info: {
|
|
142
|
+
backend: string;
|
|
143
|
+
medianInferenceMs: number | null;
|
|
144
|
+
estimatedFps?: number | null;
|
|
145
|
+
profileId?: string | null;
|
|
146
|
+
glRenderer: string | null;
|
|
147
|
+
}) => void;
|
|
148
|
+
/**
|
|
149
|
+
* Fired after zeros warm-up, typically *before* getUserMedia — used to pick
|
|
150
|
+
* a capture profile targeting ≥15 FPS.
|
|
151
|
+
*/
|
|
152
|
+
onWarmupEstimate?: (info: {
|
|
153
|
+
medianInferenceMs: number | null;
|
|
154
|
+
estimatedFps: number | null;
|
|
155
|
+
profileId: string;
|
|
156
|
+
glRenderer: string | null;
|
|
157
|
+
}) => void;
|
|
158
|
+
/** Fired ~1 Hz with inference FPS / breakdown (adaptive quality input). */
|
|
159
|
+
onStats?: (stats: WebViewPoseStats) => void;
|
|
160
|
+
/**
|
|
161
|
+
* Promotes page-level init / warning / error into typed {@link PoseTrackerEvent}s
|
|
162
|
+
* so the host can listen the same way as the classic PoseTracker WebView.
|
|
163
|
+
*/
|
|
164
|
+
onTrackerEvent?: (event: PoseTrackerEvent) => void;
|
|
165
|
+
}
|
|
166
|
+
export declare class WebViewPoseBackend implements PoseBackend {
|
|
167
|
+
readonly name = "webview-movenet";
|
|
168
|
+
private acceleration;
|
|
169
|
+
private attached;
|
|
170
|
+
private ready;
|
|
171
|
+
private warm;
|
|
172
|
+
/** True after the page successfully opened getUserMedia. */
|
|
173
|
+
private cameraOpened;
|
|
174
|
+
private openCameraHandler;
|
|
175
|
+
private lastPose;
|
|
176
|
+
private inferenceTimesMs;
|
|
177
|
+
private medianInferenceMs;
|
|
178
|
+
private glRenderer;
|
|
179
|
+
private glVendor;
|
|
180
|
+
private glVersion;
|
|
181
|
+
private tfjsBackend;
|
|
182
|
+
private reasons;
|
|
183
|
+
private readyWaiters;
|
|
184
|
+
private cameraWaiters;
|
|
185
|
+
private readonly onDiagnostic;
|
|
186
|
+
private readonly onAccelerationChange;
|
|
187
|
+
private onPose;
|
|
188
|
+
private onReady;
|
|
189
|
+
private onWarmupEstimate;
|
|
190
|
+
private onStats;
|
|
191
|
+
private onTrackerEvent;
|
|
192
|
+
constructor(options?: WebViewPoseBackendOptions);
|
|
193
|
+
/** Host view calls this when the WebView mounts / unmounts. */
|
|
194
|
+
setAttached(attached: boolean): void;
|
|
195
|
+
setOnPose(handler: ((pose: Pose, inferenceTimeMs: number) => void) | undefined): void;
|
|
196
|
+
setOnReady(handler: WebViewPoseBackendOptions['onReady']): void;
|
|
197
|
+
setOnStats(handler: WebViewPoseBackendOptions['onStats']): void;
|
|
198
|
+
/** Feed a message from the WebView `onMessage` handler. */
|
|
199
|
+
handleMessage(raw: string): void;
|
|
200
|
+
init(_options: PoseBackendInitOptions): Promise<void>;
|
|
201
|
+
warmup(): Promise<void>;
|
|
202
|
+
/** Whether getUserMedia has already run in the attached page. */
|
|
203
|
+
isCameraOpened(): boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Host injects `__PT_OPEN_CAMERA` via {@link setOpenCameraHandler}.
|
|
206
|
+
* Used when upgrading basic → full cold-start without remounting the page.
|
|
207
|
+
*/
|
|
208
|
+
setOpenCameraHandler(handler: (() => void) | undefined): void;
|
|
209
|
+
/**
|
|
210
|
+
* Open getUserMedia after a basic (model-only) ready. No-op if the page
|
|
211
|
+
* already opened the camera (coldStart=full boot).
|
|
212
|
+
*/
|
|
213
|
+
openCamera(): Promise<void>;
|
|
214
|
+
estimatePose(_frame: PoseInputFrame): Promise<Pose | null>;
|
|
215
|
+
getAcceleration(): AccelerationDiagnostics | null;
|
|
216
|
+
dispose(): Promise<void>;
|
|
217
|
+
private waitUntilReady;
|
|
218
|
+
private waitUntilCameraOpened;
|
|
219
|
+
private flushReadyWaiters;
|
|
220
|
+
private flushCameraWaiters;
|
|
221
|
+
private setAcceleration;
|
|
222
|
+
}
|
|
223
|
+
export declare function isWebViewPoseBackend(backend: PoseBackend): backend is WebViewPoseBackend;
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WebView inference backend — the SDK's base runtime on BOTH platforms.
|
|
4
|
+
*
|
|
5
|
+
* Runs MoveNet SinglePose Lightning (17 keypoints, 192×192) inside a real
|
|
6
|
+
* browser WebGL context (Chromium ANGLE on Android, WKWebView on iOS) — the
|
|
7
|
+
* same approach the PoseTracker WebView product uses in production, and the
|
|
8
|
+
* only one real-time across the whole Android park in Expo Go. Fully
|
|
9
|
+
* offline: TF.js and the model ship inside the npm package (poseHtml.ts).
|
|
10
|
+
*
|
|
11
|
+
* The WebView owns the camera (`getUserMedia`). Frames are NOT pushed from
|
|
12
|
+
* React Native — `estimatePose()` returns the latest pose posted by the page.
|
|
13
|
+
* Mount {@link WebViewPoseView} (or a 1×1 warmer) to attach the runtime.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.WebViewPoseBackend = void 0;
|
|
17
|
+
exports.isWebViewPoseBackend = isWebViewPoseBackend;
|
|
18
|
+
class WebViewPoseBackend {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.name = 'webview-movenet';
|
|
21
|
+
this.acceleration = 'unknown';
|
|
22
|
+
this.attached = false;
|
|
23
|
+
this.ready = false;
|
|
24
|
+
this.warm = false;
|
|
25
|
+
/** True after the page successfully opened getUserMedia. */
|
|
26
|
+
this.cameraOpened = false;
|
|
27
|
+
this.lastPose = null;
|
|
28
|
+
this.inferenceTimesMs = [];
|
|
29
|
+
this.medianInferenceMs = null;
|
|
30
|
+
this.glRenderer = null;
|
|
31
|
+
this.glVendor = null;
|
|
32
|
+
this.glVersion = null;
|
|
33
|
+
this.tfjsBackend = null;
|
|
34
|
+
this.reasons = [];
|
|
35
|
+
this.readyWaiters = [];
|
|
36
|
+
this.cameraWaiters = [];
|
|
37
|
+
this.onDiagnostic = options.onDiagnostic;
|
|
38
|
+
this.onAccelerationChange = options.onAccelerationChange;
|
|
39
|
+
this.onPose = options.onPose;
|
|
40
|
+
this.onReady = options.onReady;
|
|
41
|
+
this.onWarmupEstimate = options.onWarmupEstimate;
|
|
42
|
+
this.onStats = options.onStats;
|
|
43
|
+
this.onTrackerEvent = options.onTrackerEvent;
|
|
44
|
+
}
|
|
45
|
+
/** Host view calls this when the WebView mounts / unmounts. */
|
|
46
|
+
setAttached(attached) {
|
|
47
|
+
this.attached = attached;
|
|
48
|
+
this.onDiagnostic?.(`[posetracker] WebViewPoseBackend attached=${attached} ready=${this.ready}`);
|
|
49
|
+
if (!attached) {
|
|
50
|
+
this.ready = false;
|
|
51
|
+
this.warm = false;
|
|
52
|
+
this.cameraOpened = false;
|
|
53
|
+
this.openCameraHandler = undefined;
|
|
54
|
+
// Drop stale GPU verdict from a previous warmer/page so the host does
|
|
55
|
+
// not show acceleration=gpu while the next WebView is still booting.
|
|
56
|
+
this.setAcceleration('unknown');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
setOnPose(handler) {
|
|
60
|
+
this.onPose = handler;
|
|
61
|
+
}
|
|
62
|
+
setOnReady(handler) {
|
|
63
|
+
this.onReady = handler;
|
|
64
|
+
}
|
|
65
|
+
setOnStats(handler) {
|
|
66
|
+
this.onStats = handler;
|
|
67
|
+
}
|
|
68
|
+
/** Feed a message from the WebView `onMessage` handler. */
|
|
69
|
+
handleMessage(raw) {
|
|
70
|
+
let msg;
|
|
71
|
+
try {
|
|
72
|
+
msg = JSON.parse(raw);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (msg.type === 'warmup_estimate') {
|
|
78
|
+
this.glRenderer = msg.gl?.renderer ?? this.glRenderer;
|
|
79
|
+
this.medianInferenceMs = msg.medianInferenceMs;
|
|
80
|
+
const minTarget = msg.minTargetFps ?? msg.targetFps;
|
|
81
|
+
this.onDiagnostic?.(`[posetracker] WebView warmup_estimate fps=${msg.estimatedFps != null ? msg.estimatedFps.toFixed(1) : '?'} medianMs=${msg.medianInferenceMs != null ? msg.medianInferenceMs.toFixed(1) : '?'} ` +
|
|
82
|
+
`profile=${msg.profileId} minTarget=${minTarget}`);
|
|
83
|
+
this.onWarmupEstimate?.({
|
|
84
|
+
medianInferenceMs: msg.medianInferenceMs,
|
|
85
|
+
estimatedFps: msg.estimatedFps,
|
|
86
|
+
profileId: msg.profileId,
|
|
87
|
+
glRenderer: this.glRenderer,
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (msg.type === 'ready') {
|
|
92
|
+
this.ready = true;
|
|
93
|
+
this.warm = true;
|
|
94
|
+
if (msg.cameraOpened === true) {
|
|
95
|
+
this.cameraOpened = true;
|
|
96
|
+
this.flushCameraWaiters();
|
|
97
|
+
}
|
|
98
|
+
this.tfjsBackend = msg.backend;
|
|
99
|
+
this.medianInferenceMs = msg.medianInferenceMs;
|
|
100
|
+
this.inferenceTimesMs = msg.warmUpRunsMs ?? [];
|
|
101
|
+
this.glRenderer = msg.gl?.renderer ?? null;
|
|
102
|
+
this.glVendor = msg.gl?.vendor ?? null;
|
|
103
|
+
this.glVersion = msg.gl?.version ?? null;
|
|
104
|
+
const med = msg.medianInferenceMs;
|
|
105
|
+
// Chromium WebGL on Android mid-range is typically 15–80 ms — treat as GPU.
|
|
106
|
+
this.setAcceleration(med != null && med > 200 ? 'cpu-fallback' : 'gpu');
|
|
107
|
+
this.onDiagnostic?.(`[posetracker] WebView Chromium ready backend=${msg.backend} ` +
|
|
108
|
+
`medianMs=${med != null ? med.toFixed(1) : 'n/a'} ` +
|
|
109
|
+
`estFps=${msg.estimatedFps != null ? msg.estimatedFps.toFixed(1) : 'n/a'} ` +
|
|
110
|
+
`profile=${msg.profileId ?? 'n/a'} coldStart=${msg.coldStart ?? '?'} ` +
|
|
111
|
+
`camera=${msg.cameraOpened === true ? 'on' : 'deferred'} ` +
|
|
112
|
+
`renderer=${this.glRenderer ?? 'n/a'}` +
|
|
113
|
+
(msg.note ? ` note=${msg.note}` : ''));
|
|
114
|
+
this.onReady?.({
|
|
115
|
+
backend: msg.backend,
|
|
116
|
+
medianInferenceMs: med,
|
|
117
|
+
estimatedFps: msg.estimatedFps,
|
|
118
|
+
profileId: msg.profileId,
|
|
119
|
+
glRenderer: this.glRenderer,
|
|
120
|
+
});
|
|
121
|
+
this.onTrackerEvent?.({
|
|
122
|
+
type: 'initialization',
|
|
123
|
+
step: 'ready',
|
|
124
|
+
message: 'running',
|
|
125
|
+
ready: true,
|
|
126
|
+
});
|
|
127
|
+
this.flushReadyWaiters();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (msg.type === 'initialization') {
|
|
131
|
+
const step = msg.step ?? (msg.ready ? 'ready' : 'loading_pose_model');
|
|
132
|
+
this.onDiagnostic?.(`[posetracker] WebView init: ${msg.message} ready=${Boolean(msg.ready)}`);
|
|
133
|
+
this.onTrackerEvent?.({
|
|
134
|
+
type: 'initialization',
|
|
135
|
+
step,
|
|
136
|
+
message: msg.message,
|
|
137
|
+
ready: Boolean(msg.ready),
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (msg.type === 'warning') {
|
|
142
|
+
this.onDiagnostic?.(`[posetracker] WebView warning: ${msg.message}`);
|
|
143
|
+
this.onTrackerEvent?.({
|
|
144
|
+
type: 'warning',
|
|
145
|
+
code: 'webview',
|
|
146
|
+
message: msg.message,
|
|
147
|
+
timestampMs: Date.now(),
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (msg.type === 'error') {
|
|
152
|
+
// Mid-boot quality swaps abort getUserMedia; ignore so warmup can finish.
|
|
153
|
+
if (/abort/i.test(msg.message)) {
|
|
154
|
+
this.onDiagnostic?.(`[posetracker] WebView pose error ignored (abort during quality swap): ${msg.message}`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.reasons.push(msg.message);
|
|
158
|
+
this.onDiagnostic?.(`[posetracker] WebView pose error: ${msg.message}`);
|
|
159
|
+
this.onTrackerEvent?.({
|
|
160
|
+
type: 'error',
|
|
161
|
+
code: 'webview_error',
|
|
162
|
+
message: msg.message,
|
|
163
|
+
});
|
|
164
|
+
const err = new Error(msg.message);
|
|
165
|
+
for (const w of this.readyWaiters)
|
|
166
|
+
w.reject(err);
|
|
167
|
+
this.readyWaiters = [];
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (msg.type === 'diag') {
|
|
171
|
+
this.onDiagnostic?.(`[posetracker] WebView diag: ${msg.message}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (msg.type === 'stats') {
|
|
175
|
+
this.medianInferenceMs = msg.medianInferenceMs;
|
|
176
|
+
this.tfjsBackend = msg.backend;
|
|
177
|
+
const b = msg.breakdown;
|
|
178
|
+
const fmt = (v) => (v != null ? v.toFixed(1) : 'n/a');
|
|
179
|
+
const wc = msg.windowCounts;
|
|
180
|
+
const lh = msg.leverHints;
|
|
181
|
+
this.onDiagnostic?.(`[posetracker] WebView stats fps=${msg.fps} medianMs=${msg.medianInferenceMs != null ? Math.round(msg.medianInferenceMs) : 'n/a'}` +
|
|
182
|
+
(msg.estimatedFpsFromMedian != null
|
|
183
|
+
? ` estFps=${msg.estimatedFpsFromMedian.toFixed(1)}`
|
|
184
|
+
: '') +
|
|
185
|
+
` backend=${msg.backend}` +
|
|
186
|
+
(msg.mode ? ` mode=${msg.mode}` : '') +
|
|
187
|
+
(msg.videoSize ? ` video=${msg.videoSize}` : '') +
|
|
188
|
+
(b
|
|
189
|
+
? ` bitmap=${fmt(b.drawMs)} fromPixels=${fmt(b.fromPixelsMs)} execute=${fmt(b.executeMs)}` +
|
|
190
|
+
(b.totalPipelineMs != null ? ` total=${fmt(b.totalPipelineMs)}` : '')
|
|
191
|
+
: '') +
|
|
192
|
+
(wc ? ` inferred/skipped=${wc.inferred}/${wc.skipped}` : ''));
|
|
193
|
+
if (lh?.hints?.length) {
|
|
194
|
+
this.onDiagnostic?.(`[posetracker] WebView leverHints dominant=${lh.dominantStage ?? '?'} ` +
|
|
195
|
+
`shares bmp=${lh.bitmapShare ?? '?'} px=${lh.fromPixelsShare ?? '?'} ex=${lh.executeShare ?? '?'} ` +
|
|
196
|
+
`→ ${lh.hints.join(' | ')}`);
|
|
197
|
+
}
|
|
198
|
+
this.onStats?.({
|
|
199
|
+
fps: msg.fps,
|
|
200
|
+
medianInferenceMs: msg.medianInferenceMs,
|
|
201
|
+
estimatedFpsFromMedian: msg.estimatedFpsFromMedian,
|
|
202
|
+
frames: msg.frames,
|
|
203
|
+
backend: msg.backend,
|
|
204
|
+
videoSize: msg.videoSize,
|
|
205
|
+
mode: msg.mode,
|
|
206
|
+
breakdown: msg.breakdown,
|
|
207
|
+
windowCounts: msg.windowCounts,
|
|
208
|
+
experiments: msg.experiments,
|
|
209
|
+
leverHints: msg.leverHints,
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (msg.type === 'pose') {
|
|
214
|
+
const pose = {
|
|
215
|
+
keypoints: msg.keypoints.map((k) => ({
|
|
216
|
+
name: k.name,
|
|
217
|
+
x: k.x,
|
|
218
|
+
y: k.y,
|
|
219
|
+
score: k.score,
|
|
220
|
+
})),
|
|
221
|
+
score: msg.score,
|
|
222
|
+
timestampMs: msg.timestampMs,
|
|
223
|
+
};
|
|
224
|
+
this.lastPose = pose;
|
|
225
|
+
this.onPose?.(pose, msg.inferenceTimeMs);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async init(_options) {
|
|
229
|
+
this.onDiagnostic?.('[posetracker] WebViewPoseBackend.init — waiting for Chromium WebView to mount (WebViewPoseView)');
|
|
230
|
+
// Actual model load happens inside the HTML page once the view attaches.
|
|
231
|
+
}
|
|
232
|
+
async warmup() {
|
|
233
|
+
if (this.warm)
|
|
234
|
+
return;
|
|
235
|
+
this.onDiagnostic?.('[posetracker] WebViewPoseBackend.warmup — awaiting page ready (model cold-start)…');
|
|
236
|
+
await this.waitUntilReady(60000);
|
|
237
|
+
}
|
|
238
|
+
/** Whether getUserMedia has already run in the attached page. */
|
|
239
|
+
isCameraOpened() {
|
|
240
|
+
return this.cameraOpened;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Host injects `__PT_OPEN_CAMERA` via {@link setOpenCameraHandler}.
|
|
244
|
+
* Used when upgrading basic → full cold-start without remounting the page.
|
|
245
|
+
*/
|
|
246
|
+
setOpenCameraHandler(handler) {
|
|
247
|
+
this.openCameraHandler = handler;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Open getUserMedia after a basic (model-only) ready. No-op if the page
|
|
251
|
+
* already opened the camera (coldStart=full boot).
|
|
252
|
+
*/
|
|
253
|
+
async openCamera() {
|
|
254
|
+
if (this.cameraOpened)
|
|
255
|
+
return;
|
|
256
|
+
if (!this.ready) {
|
|
257
|
+
await this.waitUntilReady(60000);
|
|
258
|
+
}
|
|
259
|
+
if (this.cameraOpened)
|
|
260
|
+
return;
|
|
261
|
+
if (!this.openCameraHandler) {
|
|
262
|
+
throw new Error('WebViewPoseBackend.openCamera: no WebViewPoseView attached to inject __PT_OPEN_CAMERA.');
|
|
263
|
+
}
|
|
264
|
+
this.onDiagnostic?.('[posetracker] WebViewPoseBackend.openCamera — requesting getUserMedia…');
|
|
265
|
+
this.openCameraHandler();
|
|
266
|
+
await this.waitUntilCameraOpened(30000);
|
|
267
|
+
}
|
|
268
|
+
async estimatePose(_frame) {
|
|
269
|
+
// Camera lives inside the WebView; return last pose for API compatibility.
|
|
270
|
+
return this.lastPose;
|
|
271
|
+
}
|
|
272
|
+
getAcceleration() {
|
|
273
|
+
if (this.acceleration === 'unknown' && !this.ready) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
state: this.acceleration,
|
|
278
|
+
tfjsBackend: this.tfjsBackend,
|
|
279
|
+
runtime: 'webview',
|
|
280
|
+
medianInferenceMs: this.medianInferenceMs,
|
|
281
|
+
inferenceTimesMs: [...this.inferenceTimesMs],
|
|
282
|
+
maxAcceptableInferenceMs: 200,
|
|
283
|
+
capabilities: this.glRenderer
|
|
284
|
+
? {
|
|
285
|
+
glVersion: this.glVersion,
|
|
286
|
+
renderer: this.glRenderer,
|
|
287
|
+
vendor: this.glVendor,
|
|
288
|
+
maxTextureSize: null,
|
|
289
|
+
colorBufferFloat: true,
|
|
290
|
+
colorBufferHalfFloat: true,
|
|
291
|
+
textureFloat: true,
|
|
292
|
+
textureHalfFloat: true,
|
|
293
|
+
extensionQueriesShimmed: false,
|
|
294
|
+
}
|
|
295
|
+
: null,
|
|
296
|
+
flags: {
|
|
297
|
+
WEBVIEW_CHROMIUM: true,
|
|
298
|
+
note: this.tfjsBackend === 'wasm'
|
|
299
|
+
? 'Inference runs in WebView WASM (slow fallback)'
|
|
300
|
+
: 'Inference runs in WebView main-thread WebGL MoveNet (camera capped, createImageBitmap 192)',
|
|
301
|
+
},
|
|
302
|
+
contextLossCount: 0,
|
|
303
|
+
reasons: [
|
|
304
|
+
...this.reasons,
|
|
305
|
+
'backend=webview-movenet (Chromium getUserMedia + TF.js WebGL MoveNet Lightning)',
|
|
306
|
+
],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async dispose() {
|
|
310
|
+
this.ready = false;
|
|
311
|
+
this.warm = false;
|
|
312
|
+
this.cameraOpened = false;
|
|
313
|
+
this.attached = false;
|
|
314
|
+
this.openCameraHandler = undefined;
|
|
315
|
+
this.lastPose = null;
|
|
316
|
+
this.acceleration = 'unknown';
|
|
317
|
+
this.readyWaiters = [];
|
|
318
|
+
this.cameraWaiters = [];
|
|
319
|
+
}
|
|
320
|
+
waitUntilReady(timeoutMs) {
|
|
321
|
+
if (this.ready)
|
|
322
|
+
return Promise.resolve();
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
const timer = setTimeout(() => {
|
|
325
|
+
reject(new Error('WebViewPoseBackend: timed out waiting for Chromium page ready. ' +
|
|
326
|
+
'Mount <WebViewPoseView coldStart="basic" /> (or full) so MoveNet can warm up.'));
|
|
327
|
+
}, timeoutMs);
|
|
328
|
+
this.readyWaiters.push({
|
|
329
|
+
resolve: () => {
|
|
330
|
+
clearTimeout(timer);
|
|
331
|
+
resolve();
|
|
332
|
+
},
|
|
333
|
+
reject: (err) => {
|
|
334
|
+
clearTimeout(timer);
|
|
335
|
+
reject(err);
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
waitUntilCameraOpened(timeoutMs) {
|
|
341
|
+
if (this.cameraOpened)
|
|
342
|
+
return Promise.resolve();
|
|
343
|
+
return new Promise((resolve, reject) => {
|
|
344
|
+
const timer = setTimeout(() => {
|
|
345
|
+
reject(new Error('WebViewPoseBackend: timed out waiting for getUserMedia. ' +
|
|
346
|
+
'Ensure the user grants camera permission when prompted.'));
|
|
347
|
+
}, timeoutMs);
|
|
348
|
+
this.cameraWaiters.push({
|
|
349
|
+
resolve: () => {
|
|
350
|
+
clearTimeout(timer);
|
|
351
|
+
resolve();
|
|
352
|
+
},
|
|
353
|
+
reject: (err) => {
|
|
354
|
+
clearTimeout(timer);
|
|
355
|
+
reject(err);
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
flushReadyWaiters() {
|
|
361
|
+
const waiters = this.readyWaiters;
|
|
362
|
+
this.readyWaiters = [];
|
|
363
|
+
for (const w of waiters)
|
|
364
|
+
w.resolve();
|
|
365
|
+
}
|
|
366
|
+
flushCameraWaiters() {
|
|
367
|
+
const waiters = this.cameraWaiters;
|
|
368
|
+
this.cameraWaiters = [];
|
|
369
|
+
for (const w of waiters)
|
|
370
|
+
w.resolve();
|
|
371
|
+
}
|
|
372
|
+
setAcceleration(state) {
|
|
373
|
+
if (this.acceleration !== state) {
|
|
374
|
+
this.acceleration = state;
|
|
375
|
+
this.onAccelerationChange?.(state);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.WebViewPoseBackend = WebViewPoseBackend;
|
|
380
|
+
function isWebViewPoseBackend(backend) {
|
|
381
|
+
return backend.name === 'webview-movenet';
|
|
382
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoseTracker join logo (icon + wordmark) for WebView overlays.
|
|
3
|
+
* Generated from assets/PoseTracker_logo.png — do not edit by hand.
|
|
4
|
+
*/
|
|
5
|
+
export declare const POSETRACKER_LOGO_DATA_URL = "data:image/png;base64,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";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POSETRACKER_LOGO_DATA_URL = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* PoseTracker join logo (icon + wordmark) for WebView overlays.
|
|
6
|
+
* Generated from assets/PoseTracker_logo.png — do not edit by hand.
|
|
7
|
+
*/
|
|
8
|
+
exports.POSETRACKER_LOGO_DATA_URL = "data:image/png;base64,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";
|