@zappar/zappar-cv 3.0.1-beta.8 → 3.1.0-beta.2
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/README.md +2 -2
- package/lib/additional.d.ts +1 -0
- package/lib/android-bridge-message-handler.d.ts +11 -0
- package/lib/android-bridge-message-handler.js +167 -0
- package/lib/array-from-string.d.ts +2 -0
- package/lib/array-from-string.js +48 -0
- package/lib/bridged-camera-source.d.ts +39 -0
- package/lib/bridged-camera-source.js +383 -0
- package/lib/bridged-message-parser.d.ts +12 -0
- package/lib/bridged-message-parser.js +56 -0
- package/lib/bridged-message.d.ts +64 -0
- package/lib/bridged-message.js +1 -0
- package/lib/bridged-world-tracker.d.ts +20 -0
- package/lib/bridged-world-tracker.js +426 -0
- package/lib/camera-source-map.d.ts +2 -1
- package/lib/camera-source-map.js +4 -1
- package/lib/drawmesh.d.ts +14 -0
- package/lib/drawmesh.js +123 -0
- package/lib/gen/zappar-client.js +12 -0
- package/lib/gen/zappar.d.ts +16 -0
- package/lib/index-standalone.js +1 -1
- package/lib/native.js +61 -15
- package/lib/options.d.ts +1 -0
- package/lib/permission.js +6 -0
- package/lib/pipeline.js +2 -1
- package/lib/profile.js +2 -1
- package/lib/source.d.ts +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/yuv-conversion-gl.d.ts +44 -0
- package/lib/yuv-conversion-gl.js +416 -0
- package/lib/zappar-cv.js +1 -1
- package/lib/zappar-cv.wasm +0 -0
- package/package.json +6 -16
- package/umd/287.zappar-cv.js +1 -0
- package/umd/{7fd693559466ec5f412e.wasm → 641e1456d6d56783d581.wasm} +0 -0
- package/umd/867.zappar-cv.js +1 -1
- package/umd/zappar-cv-ceres.worker.js +1 -0
- package/umd/zappar-cv.js +1 -1
- package/umd/zappar-cv.worker.js +1 -1
- package/umd/751.zappar-cv.js +0 -1
package/README.md
CHANGED
|
@@ -18,8 +18,8 @@ npm i @zappar/zappar-cv
|
|
|
18
18
|
|
|
19
19
|
You can use our CDN from within your HTML:
|
|
20
20
|
```
|
|
21
|
-
<script src="https://libs.zappar.com/zappar-cv/3.0
|
|
21
|
+
<script src="https://libs.zappar.com/zappar-cv/3.1.0-beta.2/zappar-cv.js"></script>
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Or you can download and host our standalone JavaScript bundle:
|
|
25
|
-
[https://libs.zappar.com/zappar-cv/3.0
|
|
25
|
+
[https://libs.zappar.com/zappar-cv/3.1.0-beta.2/zappar-cv.zip](https://libs.zappar.com/zappar-cv/3.1.0-beta.2/zappar-cv.zip)
|
package/lib/additional.d.ts
CHANGED
|
@@ -43,4 +43,5 @@ export interface Additional {
|
|
|
43
43
|
sequence_source_time_set: (o: zappar_sequence_source_t, ms: number) => void;
|
|
44
44
|
cookies_permitted: (p: boolean) => void;
|
|
45
45
|
world_tracker_points_data_matrix(wt: zappar_world_tracker_t, screenWidth: number, screenHeight: number, mirror: boolean): Float32Array;
|
|
46
|
+
in_app_clip: () => void;
|
|
46
47
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class AndroidBridgeMessageHandler {
|
|
2
|
+
private _nativePort;
|
|
3
|
+
private _messageId;
|
|
4
|
+
private _replyResolvers;
|
|
5
|
+
static IsSupported(): boolean;
|
|
6
|
+
postMessage(message: string): Promise<any>;
|
|
7
|
+
private _processMessage;
|
|
8
|
+
private _processStringMessage;
|
|
9
|
+
private _processArrayBufferMessage;
|
|
10
|
+
private _initPort;
|
|
11
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { ArrayBufferFromString } from "./array-from-string";
|
|
11
|
+
import { getSharedTracer } from "./tracing/sharedtracer";
|
|
12
|
+
import { BridgeArrayBufferHeader_u32, hasBridgeArrayBufferHeader, replyIdFromBridgeArrayBuffer } from "./bridged-message-parser";
|
|
13
|
+
// Some no-op tracing helpers for concise code and good dead code removal in bundlers
|
|
14
|
+
let tracer = null;
|
|
15
|
+
let traceStart = (name) => { };
|
|
16
|
+
let traceEnd = () => { };
|
|
17
|
+
if (!(typeof Z_TRACING)) {
|
|
18
|
+
tracer = getSharedTracer();
|
|
19
|
+
traceStart = (name) => { tracer.start(name); };
|
|
20
|
+
traceEnd = () => { tracer.end(); };
|
|
21
|
+
}
|
|
22
|
+
// This class exposes the android native bridge through an interface that
|
|
23
|
+
// looks like the iOS webkit.messageHandlers one - postMessage returns a
|
|
24
|
+
// promise that is resolved with the reply from the app side
|
|
25
|
+
// There are two supported formats for the reply messages - a string-based format
|
|
26
|
+
// or a binary format (which can either be sent as a string or an ArrayBuffer).
|
|
27
|
+
// The binary format header is handled by the functions from bridged-message-parser
|
|
28
|
+
// The string format supports these reply message types:
|
|
29
|
+
// zprreply:messageId:0:[optional error string]
|
|
30
|
+
// zprreply:messageId:1:
|
|
31
|
+
// zprreply:messageId:1:stringDataLen:stringData[optional extra binary data]
|
|
32
|
+
// regexp for matching the formats above, allowing 1-12 digits for message ID
|
|
33
|
+
// and string data length
|
|
34
|
+
// Captured groups are messageId, success flag, success string data length
|
|
35
|
+
// Maximum matching prefix is 9 + 12 + 3 + 12 + 1 = 37 characters
|
|
36
|
+
const msgHeaderRegexp = /^zprreply:(\d{1,12}):(?:0:|(1):(?:(\d{1,12}):|$))/;
|
|
37
|
+
export class AndroidBridgeMessageHandler {
|
|
38
|
+
constructor() {
|
|
39
|
+
this._messageId = 0;
|
|
40
|
+
this._replyResolvers = new Map();
|
|
41
|
+
this._processMessage = (msg) => {
|
|
42
|
+
if (typeof msg.data === "string") {
|
|
43
|
+
traceStart("ABMH: processStringMessage");
|
|
44
|
+
this._processStringMessage(msg.data);
|
|
45
|
+
traceEnd();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (typeof msg.data === "object" && (msg.data instanceof ArrayBuffer)) {
|
|
49
|
+
traceStart("ABMH: processArrayBufferMessage");
|
|
50
|
+
this._processArrayBufferMessage(msg.data);
|
|
51
|
+
traceEnd();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
static IsSupported() {
|
|
56
|
+
let hasAndroidInterface = !!(globalThis.zprCameraBridge);
|
|
57
|
+
return hasAndroidInterface;
|
|
58
|
+
}
|
|
59
|
+
postMessage(message) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
traceStart("ABMH: postMessage");
|
|
62
|
+
if (!this._nativePort) {
|
|
63
|
+
let portReady = this._initPort();
|
|
64
|
+
traceEnd();
|
|
65
|
+
yield portReady;
|
|
66
|
+
traceStart("ABMH: postMessage continuation");
|
|
67
|
+
}
|
|
68
|
+
let fullMsg = `zpr:${this._messageId}:${message}`;
|
|
69
|
+
let reply = new Promise((resolve, reject) => {
|
|
70
|
+
this._replyResolvers.set(this._messageId, { resolve, reject });
|
|
71
|
+
});
|
|
72
|
+
this._nativePort.postMessage(fullMsg);
|
|
73
|
+
this._messageId++;
|
|
74
|
+
traceEnd();
|
|
75
|
+
return reply;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
_processStringMessage(msgData) {
|
|
79
|
+
// Check for the string-encoding of the ZPRM array buffer format
|
|
80
|
+
if (hasBridgeArrayBufferHeader(msgData)) {
|
|
81
|
+
let messageId = replyIdFromBridgeArrayBuffer(msgData);
|
|
82
|
+
let replyResolvers = this._replyResolvers.get(messageId);
|
|
83
|
+
if (!replyResolvers)
|
|
84
|
+
return;
|
|
85
|
+
// Can remove the resolvers from the map
|
|
86
|
+
this._replyResolvers.delete(messageId);
|
|
87
|
+
// Resolve the promise with the full string
|
|
88
|
+
replyResolvers.resolve(msgData);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let match = msgData.match(msgHeaderRegexp);
|
|
92
|
+
if (!match)
|
|
93
|
+
return;
|
|
94
|
+
let matchLength = match[0].length;
|
|
95
|
+
let messageId = Number.parseInt(match[1]);
|
|
96
|
+
let replyResolvers = this._replyResolvers.get(messageId);
|
|
97
|
+
if (!replyResolvers)
|
|
98
|
+
return;
|
|
99
|
+
// Regardless of how we resolve the promise, we've had the response
|
|
100
|
+
// for this messageId now, so can remove from the Map
|
|
101
|
+
this._replyResolvers.delete(messageId);
|
|
102
|
+
if (match[2] !== "1") {
|
|
103
|
+
replyResolvers.reject(new Error(msgData.substring(matchLength)));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (match[3] === undefined) {
|
|
107
|
+
// Success without any data to pass back
|
|
108
|
+
replyResolvers.resolve();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let stringDataLength = Number.parseInt(match[3]);
|
|
112
|
+
let stringDataEnd = matchLength + stringDataLength;
|
|
113
|
+
if (stringDataEnd == msgData.length) {
|
|
114
|
+
// Resolve with a string
|
|
115
|
+
replyResolvers.resolve(msgData.substring(matchLength));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let stringData = msgData.substring(matchLength, stringDataEnd);
|
|
119
|
+
// Convert the full string to an array buffer
|
|
120
|
+
// Even though we don't need the first bit of the string for the byte data,
|
|
121
|
+
// this way avoids a large substring or more complex indexing code in
|
|
122
|
+
// ArrayBufferFromString
|
|
123
|
+
traceStart("ABMH: ArrayBufferFromString");
|
|
124
|
+
let arrayBuffer = new ArrayBuffer(msgData.length * 2);
|
|
125
|
+
ArrayBufferFromString(arrayBuffer, msgData);
|
|
126
|
+
traceEnd();
|
|
127
|
+
// Determine the offset of the byte data - rounded up to the nearest multiple
|
|
128
|
+
// of 16 to meet any alignment restrictions of the underlying data types (as
|
|
129
|
+
// the string header may have just been prepended to correctly-aligned data)
|
|
130
|
+
let byteDataOffset = stringDataEnd * 2;
|
|
131
|
+
byteDataOffset = ((byteDataOffset + 15) >> 4) << 4;
|
|
132
|
+
// Resolve the promise with the combined data
|
|
133
|
+
let reply = { stringData, arrayBuffer, byteDataOffset };
|
|
134
|
+
replyResolvers.resolve(reply);
|
|
135
|
+
}
|
|
136
|
+
_processArrayBufferMessage(msgData) {
|
|
137
|
+
if (msgData.byteLength < 32)
|
|
138
|
+
return;
|
|
139
|
+
// Check the ZPRM header
|
|
140
|
+
let header = new Uint32Array(msgData, 0, 2);
|
|
141
|
+
if (header[0] !== BridgeArrayBufferHeader_u32)
|
|
142
|
+
return;
|
|
143
|
+
let messageId = header[1];
|
|
144
|
+
let replyResolvers = this._replyResolvers.get(messageId);
|
|
145
|
+
if (!replyResolvers)
|
|
146
|
+
return;
|
|
147
|
+
// Remove the resolvers from the map
|
|
148
|
+
this._replyResolvers.delete(messageId);
|
|
149
|
+
// Resolve with the full ArrayBuffer
|
|
150
|
+
replyResolvers.resolve(msgData);
|
|
151
|
+
}
|
|
152
|
+
_initPort() {
|
|
153
|
+
return new Promise(resolve => {
|
|
154
|
+
let listener = (msg) => {
|
|
155
|
+
if (msg.data === "zprCameraBridgePort" && msg.ports.length > 0) {
|
|
156
|
+
this._nativePort = msg.ports[0];
|
|
157
|
+
window.removeEventListener("message", listener);
|
|
158
|
+
window.addEventListener("message", this._processMessage);
|
|
159
|
+
resolve();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
window.addEventListener("message", listener);
|
|
163
|
+
let origin = window.origin || document.location.origin;
|
|
164
|
+
window.zprCameraBridge.requestMessageChannel(origin);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function Uint16ArrayFromString(array, str) {
|
|
2
|
+
const len = Math.min(str.length, array.length);
|
|
3
|
+
// Unrolling gave a significant performance improvement in iOS webkit
|
|
4
|
+
let i = 0;
|
|
5
|
+
for (i = 0; i + 31 < len; i += 32) {
|
|
6
|
+
array[i] = str.charCodeAt(i);
|
|
7
|
+
array[i + 1] = str.charCodeAt(i + 1);
|
|
8
|
+
array[i + 2] = str.charCodeAt(i + 2);
|
|
9
|
+
array[i + 3] = str.charCodeAt(i + 3);
|
|
10
|
+
array[i + 4] = str.charCodeAt(i + 4);
|
|
11
|
+
array[i + 5] = str.charCodeAt(i + 5);
|
|
12
|
+
array[i + 6] = str.charCodeAt(i + 6);
|
|
13
|
+
array[i + 7] = str.charCodeAt(i + 7);
|
|
14
|
+
array[i + 8] = str.charCodeAt(i + 8);
|
|
15
|
+
array[i + 9] = str.charCodeAt(i + 9);
|
|
16
|
+
array[i + 10] = str.charCodeAt(i + 10);
|
|
17
|
+
array[i + 11] = str.charCodeAt(i + 11);
|
|
18
|
+
array[i + 12] = str.charCodeAt(i + 12);
|
|
19
|
+
array[i + 13] = str.charCodeAt(i + 13);
|
|
20
|
+
array[i + 14] = str.charCodeAt(i + 14);
|
|
21
|
+
array[i + 15] = str.charCodeAt(i + 15);
|
|
22
|
+
array[i + 16] = str.charCodeAt(i + 16);
|
|
23
|
+
array[i + 17] = str.charCodeAt(i + 17);
|
|
24
|
+
array[i + 18] = str.charCodeAt(i + 18);
|
|
25
|
+
array[i + 19] = str.charCodeAt(i + 19);
|
|
26
|
+
array[i + 20] = str.charCodeAt(i + 20);
|
|
27
|
+
array[i + 21] = str.charCodeAt(i + 21);
|
|
28
|
+
array[i + 22] = str.charCodeAt(i + 22);
|
|
29
|
+
array[i + 23] = str.charCodeAt(i + 23);
|
|
30
|
+
array[i + 24] = str.charCodeAt(i + 24);
|
|
31
|
+
array[i + 25] = str.charCodeAt(i + 25);
|
|
32
|
+
array[i + 26] = str.charCodeAt(i + 26);
|
|
33
|
+
array[i + 27] = str.charCodeAt(i + 27);
|
|
34
|
+
array[i + 28] = str.charCodeAt(i + 28);
|
|
35
|
+
array[i + 29] = str.charCodeAt(i + 29);
|
|
36
|
+
array[i + 30] = str.charCodeAt(i + 30);
|
|
37
|
+
array[i + 31] = str.charCodeAt(i + 31);
|
|
38
|
+
}
|
|
39
|
+
// Final elements
|
|
40
|
+
for (; i < len; i++) {
|
|
41
|
+
array[i] = str.charCodeAt(i);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function ArrayBufferFromString(ab, str) {
|
|
45
|
+
// Wrap a Uint16Array around the buffer
|
|
46
|
+
let array = new Uint16Array(ab);
|
|
47
|
+
Uint16ArrayFromString(array, str);
|
|
48
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { camera_profile_t, zappar_camera_source_t, zappar_pipeline_t } from "./gen/zappar";
|
|
2
|
+
import { CameraFrameInfo, Source } from "./source";
|
|
3
|
+
import { zappar_custom_anchor_t } from "./gen/zappar-native";
|
|
4
|
+
export declare class BridgedCameraSource extends Source {
|
|
5
|
+
private _impl;
|
|
6
|
+
private _pipeline;
|
|
7
|
+
private _deviceId;
|
|
8
|
+
static DEFAULT_DEVICE_ID: string;
|
|
9
|
+
private _messageHandler;
|
|
10
|
+
private _isPaused;
|
|
11
|
+
private _cameraToScreenRotation;
|
|
12
|
+
private _cameraToDeviceTransform;
|
|
13
|
+
private _cameraModel;
|
|
14
|
+
private _profile;
|
|
15
|
+
private _textureSizes;
|
|
16
|
+
private _yuvConversion;
|
|
17
|
+
private _decodeBuffers;
|
|
18
|
+
static IsSupported(): boolean;
|
|
19
|
+
constructor(_impl: zappar_camera_source_t, _pipeline: zappar_pipeline_t, _deviceId: string);
|
|
20
|
+
destroy(): void;
|
|
21
|
+
pause(): void;
|
|
22
|
+
start(): void;
|
|
23
|
+
getFrame(currentlyProcessing: boolean): void;
|
|
24
|
+
private _frameInFlight;
|
|
25
|
+
private _processFrame;
|
|
26
|
+
uploadGL(info: CameraFrameInfo): void;
|
|
27
|
+
recycle(info: CameraFrameInfo): void;
|
|
28
|
+
setProfile(p: camera_profile_t): void;
|
|
29
|
+
private _anchorPoseFloat32;
|
|
30
|
+
private _anchorPoseUint16;
|
|
31
|
+
setCustomAnchorPose(anchor: zappar_custom_anchor_t, pose_version: number, pose: Float32Array): void;
|
|
32
|
+
resetTracking(): void;
|
|
33
|
+
optionsUpdated(options: number): void;
|
|
34
|
+
private _lastTimestamp;
|
|
35
|
+
private _deviceMotionListener;
|
|
36
|
+
private _deviceOrientationListener;
|
|
37
|
+
private _startDeviceMotion;
|
|
38
|
+
private _stopDeviceMotion;
|
|
39
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { camera_profile_t } from "./gen/zappar";
|
|
11
|
+
import { profile } from "./profile";
|
|
12
|
+
import { Pipeline } from "./pipeline";
|
|
13
|
+
import { mat4 } from "gl-matrix";
|
|
14
|
+
import { Source } from "./source";
|
|
15
|
+
import { cameraRotationForScreenOrientation } from "./cameramodel";
|
|
16
|
+
import { zcout } from "./loglevel";
|
|
17
|
+
import { deleteCameraSource } from "./camera-source-map";
|
|
18
|
+
import { ArrayBufferFromString } from "./array-from-string";
|
|
19
|
+
import { UvLayout, YUVConversionGL } from "./yuv-conversion-gl";
|
|
20
|
+
import { AndroidBridgeMessageHandler } from "./android-bridge-message-handler";
|
|
21
|
+
import { getSharedTracer } from "./tracing/sharedtracer";
|
|
22
|
+
import { BridgedWorldTracker } from "./bridged-world-tracker";
|
|
23
|
+
import { hasBridgeArrayBufferHeader, parseBridgeArrayBuffer } from "./bridged-message-parser";
|
|
24
|
+
// Some no-op tracing helpers for concise code and good dead code removal in bundlers
|
|
25
|
+
let tracer = null;
|
|
26
|
+
let traceStart = (name) => { };
|
|
27
|
+
let traceEnd = () => { };
|
|
28
|
+
if (!(typeof Z_TRACING)) {
|
|
29
|
+
tracer = getSharedTracer();
|
|
30
|
+
traceStart = (name) => { tracer.start(name); };
|
|
31
|
+
traceEnd = () => { tracer.end(); };
|
|
32
|
+
}
|
|
33
|
+
export class BridgedCameraSource extends Source {
|
|
34
|
+
constructor(_impl, _pipeline, _deviceId) {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
super();
|
|
37
|
+
this._impl = _impl;
|
|
38
|
+
this._pipeline = _pipeline;
|
|
39
|
+
this._deviceId = _deviceId;
|
|
40
|
+
this._isPaused = true;
|
|
41
|
+
this._cameraToScreenRotation = 0;
|
|
42
|
+
this._cameraToDeviceTransform = mat4.create();
|
|
43
|
+
this._cameraModel = new Float32Array([1200, 1200, 639.5, 359.5, 0, 0]);
|
|
44
|
+
this._profile = camera_profile_t.DEFAULT;
|
|
45
|
+
this._textureSizes = new Map();
|
|
46
|
+
this._decodeBuffers = [];
|
|
47
|
+
this._frameInFlight = false;
|
|
48
|
+
this._anchorPoseFloat32 = new Float32Array(16);
|
|
49
|
+
this._anchorPoseUint16 = new Uint16Array(this._anchorPoseFloat32.buffer);
|
|
50
|
+
// Motion handlers
|
|
51
|
+
this._lastTimestamp = -1;
|
|
52
|
+
this._deviceMotionListener = (ev) => {
|
|
53
|
+
let pipeline = Pipeline.get(this._pipeline);
|
|
54
|
+
if (!pipeline)
|
|
55
|
+
return;
|
|
56
|
+
let timeStamp = (ev.timeStamp !== undefined && ev.timeStamp !== null) ? ev.timeStamp : performance.now();
|
|
57
|
+
const interval = ev.interval * profile.intervalMultiplier;
|
|
58
|
+
if (profile.trustSensorIntervals) {
|
|
59
|
+
if (this._lastTimestamp === -1)
|
|
60
|
+
this._lastTimestamp = timeStamp;
|
|
61
|
+
else {
|
|
62
|
+
this._lastTimestamp += interval;
|
|
63
|
+
timeStamp = this._lastTimestamp;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (ev.acceleration !== null &&
|
|
67
|
+
ev.acceleration.x !== null &&
|
|
68
|
+
ev.acceleration.y !== null &&
|
|
69
|
+
ev.acceleration.z !== null) {
|
|
70
|
+
pipeline.motionAccelerometerWithoutGravitySubmitInt(timeStamp, interval, ev.acceleration.x * profile.deviceMotionMutliplier, ev.acceleration.y * profile.deviceMotionMutliplier, ev.acceleration.z * profile.deviceMotionMutliplier);
|
|
71
|
+
}
|
|
72
|
+
if (ev.accelerationIncludingGravity !== null &&
|
|
73
|
+
ev.accelerationIncludingGravity.x !== null &&
|
|
74
|
+
ev.accelerationIncludingGravity.y !== null &&
|
|
75
|
+
ev.accelerationIncludingGravity.z !== null) {
|
|
76
|
+
pipeline.motionAccelerometerSubmit(timeStamp, ev.accelerationIncludingGravity.x * profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.y * profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.z * profile.deviceMotionMutliplier);
|
|
77
|
+
pipeline.motionAccelerometerWithGravitySubmitInt(timeStamp, interval, ev.accelerationIncludingGravity.x * profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.y * profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.z * profile.deviceMotionMutliplier);
|
|
78
|
+
}
|
|
79
|
+
if (ev.rotationRate !== null &&
|
|
80
|
+
ev.rotationRate.alpha !== null &&
|
|
81
|
+
ev.rotationRate.beta !== null &&
|
|
82
|
+
ev.rotationRate.gamma !== null) {
|
|
83
|
+
pipeline.motionRotationRateSubmit(timeStamp, ev.rotationRate.alpha * Math.PI / -180.0, ev.rotationRate.beta * Math.PI / -180.0, ev.rotationRate.gamma * Math.PI / -180.0);
|
|
84
|
+
pipeline.motionRotationRateSubmitInt(timeStamp, interval, ev.rotationRate.alpha, ev.rotationRate.beta, ev.rotationRate.gamma);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
this._deviceOrientationListener = (ev) => {
|
|
88
|
+
let pipeline = Pipeline.get(this._pipeline);
|
|
89
|
+
if (!pipeline)
|
|
90
|
+
return;
|
|
91
|
+
let timeStamp = (ev.timeStamp !== undefined && ev.timeStamp !== null) ? ev.timeStamp : performance.now();
|
|
92
|
+
if (ev.alpha === null || ev.beta === null || ev.gamma === null)
|
|
93
|
+
return;
|
|
94
|
+
// pipeline.motionAttitudeSubmit(timeStamp, ev.alpha, ev.beta, ev.gamma);
|
|
95
|
+
pipeline.motionAttitudeSubmitInt(timeStamp, 0, ev.alpha, ev.beta, ev.gamma);
|
|
96
|
+
};
|
|
97
|
+
if (AndroidBridgeMessageHandler.IsSupported()) {
|
|
98
|
+
this._messageHandler = new AndroidBridgeMessageHandler();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this._messageHandler = (_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridgedCamera;
|
|
102
|
+
}
|
|
103
|
+
if (!this._messageHandler)
|
|
104
|
+
throw new Error("bridgedCamera message handler not found");
|
|
105
|
+
zcout("Using bridged camera source");
|
|
106
|
+
}
|
|
107
|
+
static IsSupported() {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
let hasWebkitBridge = !!((_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridgedCamera);
|
|
110
|
+
let hasAndroidBridge = AndroidBridgeMessageHandler.IsSupported();
|
|
111
|
+
return hasWebkitBridge || hasAndroidBridge;
|
|
112
|
+
}
|
|
113
|
+
destroy() {
|
|
114
|
+
this.pause();
|
|
115
|
+
deleteCameraSource(this._impl);
|
|
116
|
+
}
|
|
117
|
+
pause() {
|
|
118
|
+
this._isPaused = true;
|
|
119
|
+
let p = Pipeline.get(this._pipeline);
|
|
120
|
+
if (p && p.currentCameraSource === this)
|
|
121
|
+
p.currentCameraSource = undefined;
|
|
122
|
+
this._decodeBuffers = [];
|
|
123
|
+
this._stopDeviceMotion();
|
|
124
|
+
this._messageHandler.postMessage("stop");
|
|
125
|
+
}
|
|
126
|
+
start() {
|
|
127
|
+
var _a;
|
|
128
|
+
let p = Pipeline.get(this._pipeline);
|
|
129
|
+
if (p && p.currentCameraSource !== this) {
|
|
130
|
+
(_a = p.currentCameraSource) === null || _a === void 0 ? void 0 : _a.pause();
|
|
131
|
+
p.currentCameraSource = this;
|
|
132
|
+
}
|
|
133
|
+
this._textureSizes.clear();
|
|
134
|
+
this._isPaused = false;
|
|
135
|
+
this._startDeviceMotion();
|
|
136
|
+
let options = 0;
|
|
137
|
+
if (p)
|
|
138
|
+
options = BridgedWorldTracker.SharedInstance().getCombinedOptionsForPipeline(p);
|
|
139
|
+
this._messageHandler.postMessage(`start${options}:${this._profile}`);
|
|
140
|
+
}
|
|
141
|
+
getFrame(currentlyProcessing) {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
let rotation = cameraRotationForScreenOrientation(false);
|
|
144
|
+
if (rotation != this._cameraToScreenRotation) {
|
|
145
|
+
(_b = (_a = Pipeline.get(this._pipeline)) === null || _a === void 0 ? void 0 : _a.sendCameraToScreenRotationToWorker) === null || _b === void 0 ? void 0 : _b.call(_a, rotation);
|
|
146
|
+
this._cameraToScreenRotation = rotation;
|
|
147
|
+
}
|
|
148
|
+
if (currentlyProcessing) {
|
|
149
|
+
// zcout("BridgedCameraSource: Worker already processing a frame, not requesting another");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this._processFrame();
|
|
153
|
+
}
|
|
154
|
+
_processFrame() {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
if (this._frameInFlight) {
|
|
157
|
+
// zcout("BridgedCameraSource: Frame request already in flight, not requesting another");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
traceStart("BCS: processFrame - request frameData");
|
|
161
|
+
this._frameInFlight = true;
|
|
162
|
+
let msgReply;
|
|
163
|
+
let dataPromise = this._messageHandler.postMessage("frameData");
|
|
164
|
+
traceEnd();
|
|
165
|
+
try {
|
|
166
|
+
msgReply = yield dataPromise;
|
|
167
|
+
}
|
|
168
|
+
catch (ex) {
|
|
169
|
+
this._frameInFlight = false;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this._frameInFlight = false;
|
|
173
|
+
if (!msgReply) {
|
|
174
|
+
// zcout("BridgedCameraSource: No new frame data to process");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
let decodeBuffer;
|
|
178
|
+
let replyArrayBuffer;
|
|
179
|
+
if (typeof msgReply === "object" && (msgReply instanceof ArrayBuffer)) {
|
|
180
|
+
replyArrayBuffer = msgReply;
|
|
181
|
+
}
|
|
182
|
+
else if (typeof msgReply === 'string' && hasBridgeArrayBufferHeader(msgReply)) {
|
|
183
|
+
traceStart("BCS: processFrame - convert string to ArrayBuffer");
|
|
184
|
+
decodeBuffer = this._decodeBuffers.pop();
|
|
185
|
+
while (decodeBuffer) {
|
|
186
|
+
if ((msgReply.length * 2) <= decodeBuffer.byteLength)
|
|
187
|
+
break;
|
|
188
|
+
decodeBuffer = this._decodeBuffers.pop();
|
|
189
|
+
}
|
|
190
|
+
if (!decodeBuffer) {
|
|
191
|
+
// Add some padding so moderate increases in message size (new planes,
|
|
192
|
+
// boundary points, etc) will be able to reuse the same buffer
|
|
193
|
+
decodeBuffer = new ArrayBuffer(msgReply.length * 2 + 1024);
|
|
194
|
+
}
|
|
195
|
+
replyArrayBuffer = decodeBuffer;
|
|
196
|
+
ArrayBufferFromString(replyArrayBuffer, msgReply);
|
|
197
|
+
traceEnd();
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
zcout("BridgedCameraSource: Unexpected reply to frameData message");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (this._isPaused) {
|
|
204
|
+
zcout("BridgedCameraSource: Dropping frame - source paused");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
let pipeline = Pipeline.get(this._pipeline);
|
|
208
|
+
if (!pipeline)
|
|
209
|
+
return;
|
|
210
|
+
traceStart("BCS: processFrame - frameData received");
|
|
211
|
+
traceStart("parseBridgeArrayBuffer");
|
|
212
|
+
let frameData = parseBridgeArrayBuffer(replyArrayBuffer);
|
|
213
|
+
traceEnd();
|
|
214
|
+
if (!frameData) {
|
|
215
|
+
zcout("BridgedCameraSource: Failed to parse frameData reply ArrayBuffer");
|
|
216
|
+
traceEnd(); // BCS: processFrame - frameData received
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const frameInfo = frameData.json;
|
|
220
|
+
if (frameInfo.cameraModel) {
|
|
221
|
+
this._cameraModel.set(frameInfo.cameraModel);
|
|
222
|
+
}
|
|
223
|
+
traceStart(`Prepare CameraFrameInfo ${frameInfo.frameNo || 0}`);
|
|
224
|
+
let numDataBytes = frameInfo.dataWidth * frameInfo.dataHeight;
|
|
225
|
+
let pixels = pipeline.cameraPixelArrays.pop();
|
|
226
|
+
while (pixels) {
|
|
227
|
+
if (pixels.byteLength === numDataBytes)
|
|
228
|
+
break;
|
|
229
|
+
pixels = pipeline.cameraPixelArrays.pop();
|
|
230
|
+
}
|
|
231
|
+
if (!pixels) {
|
|
232
|
+
pixels = new ArrayBuffer(numDataBytes);
|
|
233
|
+
}
|
|
234
|
+
let pixelBytes = new Uint8Array(pixels);
|
|
235
|
+
pixelBytes.set(frameData.uint8ArrayForBinaryBufferOffset(frameInfo.dataBinaryBufferOffset, frameInfo.dataBinaryBufferLength));
|
|
236
|
+
let yData;
|
|
237
|
+
let uvData;
|
|
238
|
+
let vData;
|
|
239
|
+
let uvBinaryBufferOffset = frameInfo.previewBinaryBufferOffset;
|
|
240
|
+
if (frameInfo.dataWidth === frameInfo.yWidth && frameInfo.dataHeight === frameInfo.yHeight) {
|
|
241
|
+
// Y isn't part of the preview bytes, it's shared with the data
|
|
242
|
+
yData = frameData.uint8ArrayForBinaryBufferOffset(frameInfo.dataBinaryBufferOffset, frameInfo.dataBinaryBufferLength);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Common case - previewData has the Y plane first
|
|
246
|
+
const yBytes = frameInfo.yWidth * frameInfo.yHeight;
|
|
247
|
+
yData = frameData.uint8ArrayForBinaryBufferOffset(frameInfo.previewBinaryBufferOffset, yBytes);
|
|
248
|
+
uvBinaryBufferOffset += yBytes;
|
|
249
|
+
}
|
|
250
|
+
if (frameInfo.uvLayout === UvLayout.PLANAR_UV) {
|
|
251
|
+
const uOffset = uvBinaryBufferOffset;
|
|
252
|
+
const uBytes = frameInfo.uvWidth * frameInfo.uvHeight;
|
|
253
|
+
const vOffset = uvBinaryBufferOffset + uBytes;
|
|
254
|
+
uvData = frameData.uint8ArrayForBinaryBufferOffset(uOffset, uBytes);
|
|
255
|
+
vData = frameData.uint8ArrayForBinaryBufferOffset(vOffset, uBytes);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Interleaved
|
|
259
|
+
const uvBytes = frameInfo.uvWidth * frameInfo.uvHeight * 2;
|
|
260
|
+
uvData = frameData.uint8ArrayForBinaryBufferOffset(uvBinaryBufferOffset, uvBytes);
|
|
261
|
+
}
|
|
262
|
+
let info = {
|
|
263
|
+
data: pixels,
|
|
264
|
+
texture: undefined,
|
|
265
|
+
dataWidth: frameInfo.dataWidth,
|
|
266
|
+
dataHeight: frameInfo.dataHeight,
|
|
267
|
+
cameraModel: this._cameraModel,
|
|
268
|
+
cameraToDevice: this._cameraToDeviceTransform,
|
|
269
|
+
cameraSource: this,
|
|
270
|
+
cameraSourceData: {
|
|
271
|
+
decodeBuffer,
|
|
272
|
+
frameData,
|
|
273
|
+
frameInfo,
|
|
274
|
+
yData,
|
|
275
|
+
uvData,
|
|
276
|
+
vData
|
|
277
|
+
},
|
|
278
|
+
userFacing: false
|
|
279
|
+
};
|
|
280
|
+
traceEnd(); // Prepare CameraFrameInfo
|
|
281
|
+
traceStart("Send to worker");
|
|
282
|
+
let token = pipeline.registerToken(info);
|
|
283
|
+
pipeline.sendDataToWorker(info.data, token, info.dataWidth, info.dataHeight, info.userFacing, info.cameraToDevice, info.cameraModel, performance.now() / 1000.0);
|
|
284
|
+
info.data = undefined;
|
|
285
|
+
traceEnd();
|
|
286
|
+
traceEnd(); // BCS: processFrame - frameData received
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
uploadGL(info) {
|
|
290
|
+
if (!info || !(info.data) || !(info.cameraSourceData))
|
|
291
|
+
return;
|
|
292
|
+
const cameraSourceData = info.cameraSourceData;
|
|
293
|
+
if (info.texture)
|
|
294
|
+
return;
|
|
295
|
+
const pipeline = Pipeline.get(this._pipeline);
|
|
296
|
+
if (!pipeline)
|
|
297
|
+
return;
|
|
298
|
+
const gl = pipeline === null || pipeline === void 0 ? void 0 : pipeline.glContext;
|
|
299
|
+
if (!gl)
|
|
300
|
+
return;
|
|
301
|
+
let texture = pipeline.getVideoTexture();
|
|
302
|
+
if (!texture)
|
|
303
|
+
return;
|
|
304
|
+
let { yWidth, yHeight, uvWidth, uvHeight, uvLayout } = cameraSourceData.frameInfo;
|
|
305
|
+
if (!yWidth || !yHeight || !uvWidth || !uvHeight)
|
|
306
|
+
return;
|
|
307
|
+
if (uvLayout === undefined) {
|
|
308
|
+
uvLayout = 0;
|
|
309
|
+
}
|
|
310
|
+
let yPlane = {
|
|
311
|
+
width: yWidth,
|
|
312
|
+
height: yHeight,
|
|
313
|
+
data: cameraSourceData.yData
|
|
314
|
+
};
|
|
315
|
+
let uvPlane = {
|
|
316
|
+
width: uvWidth,
|
|
317
|
+
height: uvHeight,
|
|
318
|
+
data: cameraSourceData.uvData,
|
|
319
|
+
dataVPlane: cameraSourceData.vData
|
|
320
|
+
};
|
|
321
|
+
let knownSize = this._textureSizes.get(texture);
|
|
322
|
+
if (!knownSize || knownSize[0] != yPlane.width || knownSize[1] != yPlane.height) {
|
|
323
|
+
// Resize the texture to match
|
|
324
|
+
traceStart("Resize camera texture");
|
|
325
|
+
const prevTex = gl.getParameter(gl.TEXTURE_BINDING_2D);
|
|
326
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
327
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, yPlane.width, yPlane.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
328
|
+
gl.bindTexture(gl.TEXTURE_2D, prevTex);
|
|
329
|
+
this._textureSizes.set(texture, [yPlane.width, yPlane.height]);
|
|
330
|
+
traceEnd();
|
|
331
|
+
zcout(`BridgedCameraSource: Resized a texture, elements in map ${this._textureSizes.size}`);
|
|
332
|
+
}
|
|
333
|
+
// Run the YUV -> RGBA conversion
|
|
334
|
+
traceStart("YUV to Texture");
|
|
335
|
+
if (!this._yuvConversion)
|
|
336
|
+
this._yuvConversion = new YUVConversionGL(gl);
|
|
337
|
+
this._yuvConversion.yuvToTexture(yPlane, uvPlane, uvLayout, texture);
|
|
338
|
+
traceEnd();
|
|
339
|
+
// Set the texture in the CameraFrameInfo
|
|
340
|
+
info.texture = texture;
|
|
341
|
+
}
|
|
342
|
+
recycle(info) {
|
|
343
|
+
var _a;
|
|
344
|
+
// Only bother keeping decode buffers for reuse if we're not paused
|
|
345
|
+
if (this._isPaused)
|
|
346
|
+
return;
|
|
347
|
+
// Don't want to accumulate too many
|
|
348
|
+
if (this._decodeBuffers.length >= 2)
|
|
349
|
+
return;
|
|
350
|
+
let recycleBuffer = (_a = info.cameraSourceData) === null || _a === void 0 ? void 0 : _a.decodeBuffer;
|
|
351
|
+
if (recycleBuffer)
|
|
352
|
+
this._decodeBuffers.push(recycleBuffer);
|
|
353
|
+
}
|
|
354
|
+
setProfile(p) {
|
|
355
|
+
if (this._profile === p)
|
|
356
|
+
return;
|
|
357
|
+
this._profile = p;
|
|
358
|
+
if (!this._isPaused) {
|
|
359
|
+
this._messageHandler.postMessage(`setProfile${this._profile}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
setCustomAnchorPose(anchor, pose_version, pose) {
|
|
363
|
+
let message = `setCustomAnchorPose${anchor}:${pose_version}:`;
|
|
364
|
+
this._anchorPoseFloat32.set(pose);
|
|
365
|
+
message += String.fromCharCode(...this._anchorPoseUint16);
|
|
366
|
+
this._messageHandler.postMessage(message);
|
|
367
|
+
}
|
|
368
|
+
resetTracking() {
|
|
369
|
+
this._messageHandler.postMessage('resetTracking');
|
|
370
|
+
}
|
|
371
|
+
optionsUpdated(options) {
|
|
372
|
+
this._messageHandler.postMessage(`setOptions${options}`);
|
|
373
|
+
}
|
|
374
|
+
_startDeviceMotion() {
|
|
375
|
+
window.addEventListener("devicemotion", this._deviceMotionListener);
|
|
376
|
+
window.addEventListener("deviceorientation", this._deviceOrientationListener);
|
|
377
|
+
}
|
|
378
|
+
_stopDeviceMotion() {
|
|
379
|
+
window.removeEventListener("devicemotion", this._deviceMotionListener);
|
|
380
|
+
window.removeEventListener("deviceorientation", this._deviceOrientationListener);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
BridgedCameraSource.DEFAULT_DEVICE_ID = "Simulated Default Device ID: a908df7f-5661-4d20-b227-a1c15d2fdb4b";
|