@zappar/zappar-cv 0.4.0-beta.2 → 0.4.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -34
- package/README.md +2 -2
- package/lib/camera-source-map.d.ts +10 -0
- package/lib/camera-source-map.js +40 -0
- package/lib/camera-source.d.ts +2 -5
- package/lib/camera-source.js +3 -13
- package/lib/compatibility.js +1 -1
- package/lib/drawcamera.js +2 -1
- package/lib/gen/zappar-client.js +6 -1
- package/lib/gen/zappar-cwrap.js +37 -21
- package/lib/gen/zappar-native.d.ts +11 -0
- package/lib/gen/zappar-native.js +13 -1
- package/lib/gen/zappar-server.js +1 -1
- package/lib/gen/zappar.d.ts +5 -0
- package/lib/gen/zappar.js +5 -3
- package/lib/gfx.d.ts +1 -0
- package/lib/gfx.js +4 -0
- package/lib/html-element-source.d.ts +3 -20
- package/lib/html-element-source.js +19 -263
- package/lib/image-process-gl.d.ts +37 -0
- package/lib/image-process-gl.js +265 -0
- package/lib/imagebitmap-camera-source.d.ts +36 -0
- package/lib/imagebitmap-camera-source.js +277 -0
- package/lib/mstp-camera-source.d.ts +29 -0
- package/lib/mstp-camera-source.js +230 -0
- package/lib/native.js +45 -8
- package/lib/pipeline.d.ts +13 -3
- package/lib/pipeline.js +95 -26
- package/lib/profile.d.ts +2 -0
- package/lib/profile.js +55 -44
- package/lib/sequencerecorder.d.ts +1 -0
- package/lib/sequencerecorder.js +11 -0
- package/lib/sequencesource.d.ts +7 -1
- package/lib/sequencesource.js +89 -47
- package/lib/source.d.ts +6 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/worker-client.js +0 -5
- package/lib/worker-imagebitmap.d.ts +5 -0
- package/lib/worker-imagebitmap.js +63 -0
- package/lib/worker-server.d.ts +0 -5
- package/lib/worker-server.js +120 -30
- package/lib/workerinterface.d.ts +47 -1
- package/lib/zcv.js +117 -135
- package/lib/zcv.wasm +0 -0
- package/package.json +34 -31
- package/umd/fbf9061bc2d2826ecf97c8ba2b51e7a5.wasm +0 -0
- package/umd/zappar-cv.js +1 -9
- package/umd/zappar-cv.worker.js +1 -1
- package/lib/bundleworker-mainthread.d.ts +0 -2
- package/lib/bundleworker-mainthread.js +0 -36
- package/lib/bundleworker-worker.d.ts +0 -3
- package/lib/bundleworker-worker.js +0 -47
- package/umd/5dd5cacd27e6e7da828f.bundleworker.js +0 -1
- package/umd/a20150d17b0290cadaa6cb9911ab9500.wasm +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImageProcessGL = void 0;
|
|
4
|
+
const gl_matrix_1 = require("gl-matrix");
|
|
5
|
+
const profile_1 = require("./profile");
|
|
6
|
+
const shader_1 = require("./shader");
|
|
7
|
+
class ImageProcessGL {
|
|
8
|
+
constructor(_gl) {
|
|
9
|
+
this._gl = _gl;
|
|
10
|
+
this._isPaused = true;
|
|
11
|
+
this._hadFrames = false;
|
|
12
|
+
this._isUserFacing = false;
|
|
13
|
+
this._cameraToScreenRotation = 0;
|
|
14
|
+
this._isUploadFrame = true;
|
|
15
|
+
this._computedTransformRotation = -1;
|
|
16
|
+
this._computedFrontCameraRotation = false;
|
|
17
|
+
this._cameraUvTransform = gl_matrix_1.mat4.create();
|
|
18
|
+
this._framebufferWidth = 0;
|
|
19
|
+
this._framebufferHeight = 0;
|
|
20
|
+
this._framebufferId = null;
|
|
21
|
+
this._renderTexture = null;
|
|
22
|
+
}
|
|
23
|
+
resetGLContext() {
|
|
24
|
+
this._framebufferId = null;
|
|
25
|
+
this._renderTexture = null;
|
|
26
|
+
this._vertexBuffer = undefined;
|
|
27
|
+
this._indexBuffer = undefined;
|
|
28
|
+
this._greyscaleShader = undefined;
|
|
29
|
+
}
|
|
30
|
+
destroy() {
|
|
31
|
+
this.resetGLContext();
|
|
32
|
+
}
|
|
33
|
+
uploadFrame(texture, img, rotation, fc) {
|
|
34
|
+
let gl = this._gl;
|
|
35
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
36
|
+
gl.disable(gl.DEPTH_TEST);
|
|
37
|
+
gl.disable(gl.BLEND);
|
|
38
|
+
gl.disable(gl.CULL_FACE);
|
|
39
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
40
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
41
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
42
|
+
const level = 0;
|
|
43
|
+
const internalFormat = gl.RGBA;
|
|
44
|
+
const srcFormat = gl.RGBA;
|
|
45
|
+
const srcType = gl.UNSIGNED_BYTE;
|
|
46
|
+
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, img);
|
|
47
|
+
let videoWidth = 0;
|
|
48
|
+
let videoHeight = 0;
|
|
49
|
+
if (typeof HTMLVideoElement !== "undefined" && img instanceof HTMLVideoElement) {
|
|
50
|
+
videoWidth = img.videoWidth;
|
|
51
|
+
videoHeight = img.videoHeight;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
videoWidth = img.width;
|
|
55
|
+
videoHeight = img.height;
|
|
56
|
+
}
|
|
57
|
+
if (videoHeight > videoWidth)
|
|
58
|
+
videoHeight = [videoWidth, videoWidth = videoHeight][0];
|
|
59
|
+
this._updateTransforms(rotation, fc);
|
|
60
|
+
let framebuffer = this._getFramebuffer(gl, profile_1.profile.dataWidth / 4, profile_1.profile.dataHeight);
|
|
61
|
+
let vbo = this._getVertexBuffer(gl);
|
|
62
|
+
let ibo = this._getIndexBuffer(gl);
|
|
63
|
+
let shader = this._getGreyscaleShader(gl);
|
|
64
|
+
// // Rendering to the greyscale conversion buffer - bind the framebuffer
|
|
65
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
66
|
+
gl.viewport(0, 0, this._framebufferWidth, this._framebufferHeight);
|
|
67
|
+
// // We'll be replacing all the content - clear is a good hint for this on mobile
|
|
68
|
+
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
|
69
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
70
|
+
// // Set up bindings for vertex attributes
|
|
71
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
72
|
+
gl.vertexAttribPointer(shader.aVertexPositionLoc, 2, gl.FLOAT, false, 4 * 4, 0);
|
|
73
|
+
gl.enableVertexAttribArray(shader.aVertexPositionLoc);
|
|
74
|
+
gl.vertexAttribPointer(shader.aTextureCoordLoc, 2, gl.FLOAT, false, 4 * 4, 2 * 4);
|
|
75
|
+
gl.enableVertexAttribArray(shader.aTextureCoordLoc);
|
|
76
|
+
// Bind the index buffer
|
|
77
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
|
78
|
+
// Tell WebGL to use our program when drawing
|
|
79
|
+
gl.useProgram(shader.program);
|
|
80
|
+
// Specify greyscale width for the correct offsets, and the uv transform
|
|
81
|
+
gl.uniform1f(shader.uTexWidthLoc, profile_1.profile.dataWidth);
|
|
82
|
+
gl.uniformMatrix4fv(shader.uUvTransformLoc, false, this._cameraUvTransform);
|
|
83
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
84
|
+
// Bind the texture to texture unit 0
|
|
85
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
86
|
+
// Tell the shader we bound the texture to texture unit 0
|
|
87
|
+
gl.uniform1i(shader.uSamplerLoc, 0);
|
|
88
|
+
// Do the drawing...
|
|
89
|
+
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
|
|
90
|
+
// Disable attribute arrays
|
|
91
|
+
gl.disableVertexAttribArray(shader.aVertexPositionLoc);
|
|
92
|
+
gl.disableVertexAttribArray(shader.aTextureCoordLoc);
|
|
93
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
94
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
95
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
|
96
|
+
gl.useProgram(null);
|
|
97
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
98
|
+
}
|
|
99
|
+
readFrame(texture, pixels) {
|
|
100
|
+
let gl = this._gl;
|
|
101
|
+
let pixelsView = new Uint8Array(pixels);
|
|
102
|
+
let framebuffer = this._getFramebuffer(gl, profile_1.profile.dataWidth / 4, profile_1.profile.dataHeight);
|
|
103
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
104
|
+
gl.readPixels(0, 0, this._framebufferWidth, this._framebufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixelsView);
|
|
105
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
106
|
+
return {
|
|
107
|
+
uvTransform: this._cameraUvTransform,
|
|
108
|
+
data: pixels,
|
|
109
|
+
texture,
|
|
110
|
+
dataWidth: profile_1.profile.dataWidth,
|
|
111
|
+
dataHeight: profile_1.profile.dataHeight,
|
|
112
|
+
userFacing: this._computedFrontCameraRotation
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
_updateTransforms(rot, fc) {
|
|
116
|
+
if (rot == this._computedTransformRotation && fc == this._computedFrontCameraRotation)
|
|
117
|
+
return;
|
|
118
|
+
this._computedTransformRotation = rot;
|
|
119
|
+
this._computedFrontCameraRotation = fc;
|
|
120
|
+
this._cameraUvTransform = this._getCameraUvTransform();
|
|
121
|
+
}
|
|
122
|
+
_getCameraUvTransform() {
|
|
123
|
+
switch (this._computedTransformRotation) {
|
|
124
|
+
case 270: return new Float32Array([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1]);
|
|
125
|
+
case 180: return new Float32Array([-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1]);
|
|
126
|
+
case 90: return new Float32Array([0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1]);
|
|
127
|
+
}
|
|
128
|
+
return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
129
|
+
}
|
|
130
|
+
_getFramebuffer(gl, fbWidth, fbHeight) {
|
|
131
|
+
if (this._framebufferWidth === fbWidth && this._framebufferHeight === fbHeight && this._framebufferId)
|
|
132
|
+
return this._framebufferId;
|
|
133
|
+
if (this._framebufferId) {
|
|
134
|
+
gl.deleteFramebuffer(this._framebufferId);
|
|
135
|
+
this._framebufferId = null;
|
|
136
|
+
}
|
|
137
|
+
if (this._renderTexture) {
|
|
138
|
+
gl.deleteTexture(this._renderTexture);
|
|
139
|
+
this._renderTexture = null;
|
|
140
|
+
}
|
|
141
|
+
this._framebufferId = gl.createFramebuffer();
|
|
142
|
+
if (!this._framebufferId)
|
|
143
|
+
throw new Error("Unable to create framebuffer");
|
|
144
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebufferId);
|
|
145
|
+
this._renderTexture = gl.createTexture();
|
|
146
|
+
if (!this._renderTexture)
|
|
147
|
+
throw new Error("Unable to create render texture");
|
|
148
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
149
|
+
gl.bindTexture(gl.TEXTURE_2D, this._renderTexture);
|
|
150
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, fbWidth, fbHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
151
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
152
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
153
|
+
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
154
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._renderTexture, 0);
|
|
155
|
+
let fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
156
|
+
if (fbStatus !== gl.FRAMEBUFFER_COMPLETE)
|
|
157
|
+
throw new Error("Framebuffer not complete: " + fbStatus.toString());
|
|
158
|
+
this._framebufferWidth = fbWidth;
|
|
159
|
+
this._framebufferHeight = fbHeight;
|
|
160
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
161
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
162
|
+
return this._framebufferId;
|
|
163
|
+
}
|
|
164
|
+
_getVertexBuffer(gl) {
|
|
165
|
+
if (this._vertexBuffer)
|
|
166
|
+
return this._vertexBuffer;
|
|
167
|
+
this._vertexBuffer = gl.createBuffer();
|
|
168
|
+
if (!this._vertexBuffer)
|
|
169
|
+
throw new Error("Unable to create vertex buffer");
|
|
170
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
|
|
171
|
+
let buffer = new Float32Array([-1.0, -1.0, 0.0, 0.0,
|
|
172
|
+
-1.0, 1.0, 0.0, 1.0,
|
|
173
|
+
1.0, 1.0, 1.0, 1.0,
|
|
174
|
+
1.0, -1.0, 1.0, 0.0]);
|
|
175
|
+
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
|
|
176
|
+
return this._vertexBuffer;
|
|
177
|
+
}
|
|
178
|
+
_getIndexBuffer(gl) {
|
|
179
|
+
if (this._indexBuffer)
|
|
180
|
+
return this._indexBuffer;
|
|
181
|
+
this._indexBuffer = gl.createBuffer();
|
|
182
|
+
if (!this._indexBuffer)
|
|
183
|
+
throw new Error("Unable to create index buffer");
|
|
184
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
|
|
185
|
+
let buffer = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
186
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
|
|
187
|
+
return this._indexBuffer;
|
|
188
|
+
}
|
|
189
|
+
_getGreyscaleShader(gl) {
|
|
190
|
+
if (this._greyscaleShader)
|
|
191
|
+
return this._greyscaleShader;
|
|
192
|
+
let prog = gl.createProgram();
|
|
193
|
+
if (!prog)
|
|
194
|
+
throw new Error("Unable to create program");
|
|
195
|
+
let vertexShader = shader_1.compileShader(gl, gl.VERTEX_SHADER, greyscaleVsSource);
|
|
196
|
+
let fragmentShader = shader_1.compileShader(gl, gl.FRAGMENT_SHADER, greyscaleFsSource);
|
|
197
|
+
gl.attachShader(prog, vertexShader);
|
|
198
|
+
gl.attachShader(prog, fragmentShader);
|
|
199
|
+
shader_1.linkProgram(gl, prog);
|
|
200
|
+
let uTexWidthLoc = gl.getUniformLocation(prog, "uTexWidth");
|
|
201
|
+
if (!uTexWidthLoc)
|
|
202
|
+
throw new Error("Unable to get uniform location uTexWidth");
|
|
203
|
+
let uUvTransformLoc = gl.getUniformLocation(prog, "uUvTransform");
|
|
204
|
+
if (!uUvTransformLoc)
|
|
205
|
+
throw new Error("Unable to get uniform location uUvTransform");
|
|
206
|
+
let uSamplerLoc = gl.getUniformLocation(prog, "uSampler");
|
|
207
|
+
if (!uSamplerLoc)
|
|
208
|
+
throw new Error("Unable to get uniform location uSampler");
|
|
209
|
+
this._greyscaleShader = {
|
|
210
|
+
program: prog,
|
|
211
|
+
aVertexPositionLoc: gl.getAttribLocation(prog, "aVertexPosition"),
|
|
212
|
+
aTextureCoordLoc: gl.getAttribLocation(prog, "aTextureCoord"),
|
|
213
|
+
uTexWidthLoc: uTexWidthLoc,
|
|
214
|
+
uUvTransformLoc: uUvTransformLoc,
|
|
215
|
+
uSamplerLoc: uSamplerLoc
|
|
216
|
+
};
|
|
217
|
+
return this._greyscaleShader;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.ImageProcessGL = ImageProcessGL;
|
|
221
|
+
let greyscaleVsSource = `
|
|
222
|
+
attribute vec4 aVertexPosition;
|
|
223
|
+
attribute vec2 aTextureCoord;
|
|
224
|
+
|
|
225
|
+
varying highp vec2 vTextureCoord1;
|
|
226
|
+
varying highp vec2 vTextureCoord2;
|
|
227
|
+
varying highp vec2 vTextureCoord3;
|
|
228
|
+
varying highp vec2 vTextureCoord4;
|
|
229
|
+
|
|
230
|
+
uniform float uTexWidth;
|
|
231
|
+
uniform mat4 uUvTransform;
|
|
232
|
+
|
|
233
|
+
void main(void) {
|
|
234
|
+
highp vec2 offset1 = vec2(1.5 / uTexWidth, 0);
|
|
235
|
+
highp vec2 offset2 = vec2(0.5 / uTexWidth, 0);
|
|
236
|
+
|
|
237
|
+
gl_Position = aVertexPosition;
|
|
238
|
+
vTextureCoord1 = (uUvTransform * vec4(aTextureCoord - offset1, 0, 1)).xy;
|
|
239
|
+
vTextureCoord2 = (uUvTransform * vec4(aTextureCoord - offset2, 0, 1)).xy;
|
|
240
|
+
vTextureCoord3 = (uUvTransform * vec4(aTextureCoord + offset2, 0, 1)).xy;
|
|
241
|
+
vTextureCoord4 = (uUvTransform * vec4(aTextureCoord + offset1, 0, 1)).xy;
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
// Fragment shader program
|
|
245
|
+
let greyscaleFsSource = `
|
|
246
|
+
varying highp vec2 vTextureCoord1;
|
|
247
|
+
varying highp vec2 vTextureCoord2;
|
|
248
|
+
varying highp vec2 vTextureCoord3;
|
|
249
|
+
varying highp vec2 vTextureCoord4;
|
|
250
|
+
|
|
251
|
+
uniform sampler2D uSampler;
|
|
252
|
+
|
|
253
|
+
const lowp vec3 colorWeights = vec3(77.0 / 256.0, 150.0 / 256.0, 29.0 / 256.0);
|
|
254
|
+
|
|
255
|
+
void main(void) {
|
|
256
|
+
lowp vec4 outpx;
|
|
257
|
+
|
|
258
|
+
outpx.r = dot(colorWeights, texture2D(uSampler, vTextureCoord1).xyz);
|
|
259
|
+
outpx.g = dot(colorWeights, texture2D(uSampler, vTextureCoord2).xyz);
|
|
260
|
+
outpx.b = dot(colorWeights, texture2D(uSampler, vTextureCoord3).xyz);
|
|
261
|
+
outpx.a = dot(colorWeights, texture2D(uSampler, vTextureCoord4).xyz);
|
|
262
|
+
|
|
263
|
+
gl_FragColor = outpx;
|
|
264
|
+
}
|
|
265
|
+
`;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { zappar_camera_source_t, zappar_pipeline_t } from "./gen/zappar";
|
|
2
|
+
import { CameraFrameInfo, Source } from "./source";
|
|
3
|
+
export declare class ImageBitmapCameraSource extends Source {
|
|
4
|
+
private _impl;
|
|
5
|
+
private _pipeline;
|
|
6
|
+
private _deviceId;
|
|
7
|
+
static USER_DEFAULT_DEVICE_ID: string;
|
|
8
|
+
static DEFAULT_DEVICE_ID: string;
|
|
9
|
+
private _currentStream;
|
|
10
|
+
private _activeDeviceId;
|
|
11
|
+
private _isPaused;
|
|
12
|
+
private _isUserFacing;
|
|
13
|
+
private _hadFrames;
|
|
14
|
+
private _canvas;
|
|
15
|
+
private _context;
|
|
16
|
+
private _lastFrameTime;
|
|
17
|
+
private _video;
|
|
18
|
+
private _cameraToDeviceTransform;
|
|
19
|
+
private _cameraToDeviceTransformUserFacing;
|
|
20
|
+
private _cameraModel;
|
|
21
|
+
constructor(_impl: zappar_camera_source_t, _pipeline: zappar_pipeline_t, _deviceId: string);
|
|
22
|
+
destroy(): void;
|
|
23
|
+
private _stop;
|
|
24
|
+
pause(): void;
|
|
25
|
+
start(): void;
|
|
26
|
+
private _getConstraints;
|
|
27
|
+
getFrame(currentlyProcessing: boolean): void;
|
|
28
|
+
private _getUserMedia;
|
|
29
|
+
private _syncCamera;
|
|
30
|
+
private _hasStartedOrientation;
|
|
31
|
+
private _deviceMotionListener;
|
|
32
|
+
private _startDeviceOrientation;
|
|
33
|
+
private _startDeviceMotion;
|
|
34
|
+
private _stopDeviceMotion;
|
|
35
|
+
uploadGL(info: CameraFrameInfo): void;
|
|
36
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ImageBitmapCameraSource = void 0;
|
|
13
|
+
const profile_1 = require("./profile");
|
|
14
|
+
const pipeline_1 = require("./pipeline");
|
|
15
|
+
const source_1 = require("./source");
|
|
16
|
+
const cameramodel_1 = require("./cameramodel");
|
|
17
|
+
const loglevel_1 = require("./loglevel");
|
|
18
|
+
const camera_source_map_1 = require("./camera-source-map");
|
|
19
|
+
const gl_matrix_1 = require("gl-matrix");
|
|
20
|
+
class ImageBitmapCameraSource extends source_1.Source {
|
|
21
|
+
constructor(_impl, _pipeline, _deviceId) {
|
|
22
|
+
super();
|
|
23
|
+
this._impl = _impl;
|
|
24
|
+
this._pipeline = _pipeline;
|
|
25
|
+
this._deviceId = _deviceId;
|
|
26
|
+
this._currentStream = null;
|
|
27
|
+
this._activeDeviceId = null;
|
|
28
|
+
this._isPaused = true;
|
|
29
|
+
this._isUserFacing = false;
|
|
30
|
+
this._hadFrames = false;
|
|
31
|
+
this._canvas = new OffscreenCanvas(640, 480);
|
|
32
|
+
this._context = this._canvas.getContext("2d");
|
|
33
|
+
this._lastFrameTime = -1;
|
|
34
|
+
this._video = document.createElement("video");
|
|
35
|
+
this._cameraToDeviceTransform = gl_matrix_1.mat4.create();
|
|
36
|
+
this._cameraToDeviceTransformUserFacing = gl_matrix_1.mat4.create();
|
|
37
|
+
this._cameraModel = new Float32Array([300, 300, 160, 120, 0, 0]);
|
|
38
|
+
this._hasStartedOrientation = false;
|
|
39
|
+
this._deviceMotionListener = (ev) => {
|
|
40
|
+
let pipeline = pipeline_1.Pipeline.get(this._pipeline);
|
|
41
|
+
if (!pipeline)
|
|
42
|
+
return;
|
|
43
|
+
let timeStamp = (ev.timeStamp !== undefined && ev.timeStamp !== null) ? ev.timeStamp : performance.now();
|
|
44
|
+
if (ev.accelerationIncludingGravity !== null &&
|
|
45
|
+
ev.accelerationIncludingGravity.x !== null &&
|
|
46
|
+
ev.accelerationIncludingGravity.y !== null &&
|
|
47
|
+
ev.accelerationIncludingGravity.z !== null) {
|
|
48
|
+
pipeline.motionAccelerometerSubmit(timeStamp, ev.accelerationIncludingGravity.x * profile_1.profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.y * profile_1.profile.deviceMotionMutliplier, ev.accelerationIncludingGravity.z * profile_1.profile.deviceMotionMutliplier);
|
|
49
|
+
}
|
|
50
|
+
if (ev.rotationRate !== null &&
|
|
51
|
+
ev.rotationRate.alpha !== null &&
|
|
52
|
+
ev.rotationRate.beta !== null &&
|
|
53
|
+
ev.rotationRate.gamma !== null && !this._hasStartedOrientation) {
|
|
54
|
+
ev.timeStamp;
|
|
55
|
+
pipeline.motionRotationRateSubmit(timeStamp, ev.rotationRate.alpha * Math.PI / -180.0, ev.rotationRate.beta * Math.PI / -180.0, ev.rotationRate.gamma * Math.PI / -180.0);
|
|
56
|
+
}
|
|
57
|
+
else if (!this._hasStartedOrientation) {
|
|
58
|
+
this._startDeviceOrientation();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
loglevel_1.zcout("Using ImageBitmap camera source");
|
|
62
|
+
gl_matrix_1.mat4.fromScaling(this._cameraToDeviceTransformUserFacing, [-1, 1, -1]);
|
|
63
|
+
this._video.muted = true;
|
|
64
|
+
this._video.setAttribute('playsinline', '');
|
|
65
|
+
this._video.setAttribute('webkit-playsinline', '');
|
|
66
|
+
if (profile_1.profile.videoElementInDOM) {
|
|
67
|
+
this._video.style.width = "0px";
|
|
68
|
+
this._video.style.height = "0px";
|
|
69
|
+
document.body.appendChild(this._video);
|
|
70
|
+
}
|
|
71
|
+
this._video.addEventListener("loadedmetadata", () => { this._hadFrames = true; });
|
|
72
|
+
}
|
|
73
|
+
destroy() {
|
|
74
|
+
this.pause();
|
|
75
|
+
camera_source_map_1.deleteCameraSource(this._impl);
|
|
76
|
+
}
|
|
77
|
+
_stop() {
|
|
78
|
+
if (!this._currentStream)
|
|
79
|
+
return;
|
|
80
|
+
let tracks = this._currentStream.getTracks();
|
|
81
|
+
tracks.forEach(t => t.stop());
|
|
82
|
+
this._currentStream = null;
|
|
83
|
+
}
|
|
84
|
+
pause() {
|
|
85
|
+
this._isPaused = true;
|
|
86
|
+
let p = pipeline_1.Pipeline.get(this._pipeline);
|
|
87
|
+
if (p && p.currentCameraSource === this)
|
|
88
|
+
p.currentCameraSource = undefined;
|
|
89
|
+
this._stopDeviceMotion();
|
|
90
|
+
this._syncCamera();
|
|
91
|
+
}
|
|
92
|
+
start() {
|
|
93
|
+
var _a;
|
|
94
|
+
let p = pipeline_1.Pipeline.get(this._pipeline);
|
|
95
|
+
if (p && p.currentCameraSource !== this) {
|
|
96
|
+
(_a = p.currentCameraSource) === null || _a === void 0 ? void 0 : _a.pause();
|
|
97
|
+
p.currentCameraSource = this;
|
|
98
|
+
}
|
|
99
|
+
this._isPaused = false;
|
|
100
|
+
this._startDeviceMotion();
|
|
101
|
+
this._syncCamera();
|
|
102
|
+
}
|
|
103
|
+
_getConstraints() {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
let deviceId;
|
|
106
|
+
let facingMode;
|
|
107
|
+
if (this._deviceId !== ImageBitmapCameraSource.DEFAULT_DEVICE_ID &&
|
|
108
|
+
this._deviceId !== ImageBitmapCameraSource.USER_DEFAULT_DEVICE_ID) {
|
|
109
|
+
// Custom device
|
|
110
|
+
deviceId = this._deviceId;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
facingMode = (this._deviceId === ImageBitmapCameraSource.DEFAULT_DEVICE_ID) ? "environment" : "user";
|
|
114
|
+
}
|
|
115
|
+
let constraints = {
|
|
116
|
+
audio: false,
|
|
117
|
+
video: {
|
|
118
|
+
facingMode: facingMode,
|
|
119
|
+
width: profile_1.profile.videoWidth,
|
|
120
|
+
height: profile_1.profile.videoHeight,
|
|
121
|
+
frameRate: profile_1.profile.requestHighFrameRate ? 60 : undefined,
|
|
122
|
+
deviceId: deviceId
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
if (deviceId)
|
|
126
|
+
return constraints;
|
|
127
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices)
|
|
128
|
+
return constraints;
|
|
129
|
+
let devices = yield navigator.mediaDevices.enumerateDevices();
|
|
130
|
+
let hasHadCapabilities = false;
|
|
131
|
+
devices = devices.filter(val => {
|
|
132
|
+
// Remove non-video devices
|
|
133
|
+
if (val.kind !== "videoinput")
|
|
134
|
+
return false;
|
|
135
|
+
// If the media info object contains capabilities, use it to filter to the correct facing cameras
|
|
136
|
+
if (val.getCapabilities) {
|
|
137
|
+
hasHadCapabilities = true;
|
|
138
|
+
let capabilities = val.getCapabilities();
|
|
139
|
+
if (capabilities && capabilities.facingMode && capabilities.facingMode.indexOf(facingMode === "user" ? "user" : "environment") < 0)
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
});
|
|
144
|
+
// If none of the devices had capability info, or we have no devices left, fall back to the standard constraints
|
|
145
|
+
if (!hasHadCapabilities || devices.length === 0) {
|
|
146
|
+
return constraints;
|
|
147
|
+
}
|
|
148
|
+
if (typeof constraints.video === "object") {
|
|
149
|
+
loglevel_1.zcout("choosing device ID", devices[devices.length - 1].deviceId);
|
|
150
|
+
constraints.video.deviceId = devices[devices.length - 1].deviceId;
|
|
151
|
+
}
|
|
152
|
+
return constraints;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
getFrame(currentlyProcessing) {
|
|
156
|
+
var _a, _b;
|
|
157
|
+
if (!this._context)
|
|
158
|
+
return;
|
|
159
|
+
if (!this._hadFrames)
|
|
160
|
+
return;
|
|
161
|
+
if (currentlyProcessing)
|
|
162
|
+
return;
|
|
163
|
+
let currentTime = performance.now();
|
|
164
|
+
if (currentTime < (this._lastFrameTime + 25))
|
|
165
|
+
return;
|
|
166
|
+
this._lastFrameTime = currentTime;
|
|
167
|
+
if (this._canvas.width !== this._video.videoWidth)
|
|
168
|
+
this._canvas.width = this._video.videoWidth;
|
|
169
|
+
if (this._canvas.height !== this._video.videoHeight)
|
|
170
|
+
this._canvas.height = this._video.videoHeight;
|
|
171
|
+
this._context.drawImage(this._video, 0, 0);
|
|
172
|
+
const imageBitmap = this._canvas.transferToImageBitmap();
|
|
173
|
+
let rotation = cameramodel_1.cameraRotationForScreenOrientation(false);
|
|
174
|
+
let pipeline = pipeline_1.Pipeline.get(this._pipeline);
|
|
175
|
+
if (!pipeline)
|
|
176
|
+
return;
|
|
177
|
+
let focalLength = 300.0 * profile_1.profile.dataWidth / 320.0;
|
|
178
|
+
this._cameraModel[0] = focalLength;
|
|
179
|
+
this._cameraModel[1] = focalLength;
|
|
180
|
+
this._cameraModel[2] = profile_1.profile.dataWidth * 0.5;
|
|
181
|
+
this._cameraModel[3] = profile_1.profile.dataHeight * 0.5;
|
|
182
|
+
let token = pipeline.registerToken({
|
|
183
|
+
dataWidth: this._canvas.width,
|
|
184
|
+
dataHeight: this._canvas.height,
|
|
185
|
+
texture: undefined,
|
|
186
|
+
userFacing: this._isUserFacing,
|
|
187
|
+
cameraSource: this,
|
|
188
|
+
cameraModel: this._cameraModel.slice(),
|
|
189
|
+
cameraToDevice: this._isUserFacing ? this._cameraToDeviceTransformUserFacing : this._cameraToDeviceTransform
|
|
190
|
+
});
|
|
191
|
+
(_b = (_a = pipeline_1.Pipeline.get(this._pipeline)) === null || _a === void 0 ? void 0 : _a.sendImageBitmapToWorker) === null || _b === void 0 ? void 0 : _b.call(_a, imageBitmap, rotation, this._isUserFacing, token, this._cameraModel, this._isUserFacing ? this._cameraToDeviceTransformUserFacing : this._cameraToDeviceTransform);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
_getUserMedia() {
|
|
195
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
let constraints = yield this._getConstraints();
|
|
197
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
|
|
198
|
+
return yield navigator.mediaDevices.getUserMedia(constraints);
|
|
199
|
+
return yield new Promise((resolve, reject) => {
|
|
200
|
+
navigator.getUserMedia(constraints, resolve, reject);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
_syncCamera() {
|
|
205
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
if (this._currentStream && this._isPaused) {
|
|
207
|
+
this._stop();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (this._currentStream && this._activeDeviceId !== this._deviceId)
|
|
211
|
+
this._stop();
|
|
212
|
+
if (!this._isPaused) {
|
|
213
|
+
this._activeDeviceId = this._deviceId;
|
|
214
|
+
this._currentStream = yield this._getUserMedia();
|
|
215
|
+
if (this._isPaused) {
|
|
216
|
+
yield this._syncCamera();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
this._isUserFacing = false;
|
|
220
|
+
if (this._currentStream) {
|
|
221
|
+
let videoTracks = this._currentStream.getVideoTracks();
|
|
222
|
+
if (videoTracks.length > 0) {
|
|
223
|
+
this._isUserFacing = videoTracks[0].getSettings().facingMode === "user";
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this._video.src = "";
|
|
227
|
+
this._video.loop = false;
|
|
228
|
+
this._video.muted = true;
|
|
229
|
+
this._video.srcObject = this._currentStream;
|
|
230
|
+
this._video.play();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
_startDeviceOrientation() {
|
|
235
|
+
if (this._hasStartedOrientation)
|
|
236
|
+
return;
|
|
237
|
+
this._hasStartedOrientation = true;
|
|
238
|
+
window.addEventListener("deviceorientation", (ev) => {
|
|
239
|
+
let pipeline = pipeline_1.Pipeline.get(this._pipeline);
|
|
240
|
+
if (!pipeline)
|
|
241
|
+
return;
|
|
242
|
+
let timeStamp = (ev.timeStamp !== undefined && ev.timeStamp !== null) ? ev.timeStamp : performance.now();
|
|
243
|
+
if (ev.alpha === null || ev.beta === null || ev.gamma === null)
|
|
244
|
+
return;
|
|
245
|
+
pipeline.motionAttitudeSubmit(timeStamp, ev.alpha, ev.beta, ev.gamma);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
_startDeviceMotion() {
|
|
249
|
+
window.addEventListener("devicemotion", this._deviceMotionListener, false);
|
|
250
|
+
}
|
|
251
|
+
_stopDeviceMotion() {
|
|
252
|
+
window.removeEventListener("devicemotion", this._deviceMotionListener);
|
|
253
|
+
}
|
|
254
|
+
uploadGL(info) {
|
|
255
|
+
const pipeline = pipeline_1.Pipeline.get(this._pipeline);
|
|
256
|
+
const gl = pipeline === null || pipeline === void 0 ? void 0 : pipeline.glContext;
|
|
257
|
+
if (!info ||
|
|
258
|
+
info.texture ||
|
|
259
|
+
!info.frame ||
|
|
260
|
+
!pipeline ||
|
|
261
|
+
!gl)
|
|
262
|
+
return;
|
|
263
|
+
let texture = pipeline.getVideoTexture();
|
|
264
|
+
if (!texture)
|
|
265
|
+
return;
|
|
266
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
267
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
268
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, info.frame);
|
|
269
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
270
|
+
info.texture = texture;
|
|
271
|
+
info.frame.close();
|
|
272
|
+
delete info.frame;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.ImageBitmapCameraSource = ImageBitmapCameraSource;
|
|
276
|
+
ImageBitmapCameraSource.USER_DEFAULT_DEVICE_ID = "Simulated User Default Device ID: a908df7f-5661-4d20-b227-a1c15d2fdb4b";
|
|
277
|
+
ImageBitmapCameraSource.DEFAULT_DEVICE_ID = "Simulated Default Device ID: a908df7f-5661-4d20-b227-a1c15d2fdb4b";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { zappar_camera_source_t, zappar_pipeline_t } from "./gen/zappar";
|
|
2
|
+
import { CameraFrameInfo, Source } from "./source";
|
|
3
|
+
export declare class MSTPCameraSource extends Source {
|
|
4
|
+
private _impl;
|
|
5
|
+
private _pipeline;
|
|
6
|
+
private _deviceId;
|
|
7
|
+
static USER_DEFAULT_DEVICE_ID: string;
|
|
8
|
+
static DEFAULT_DEVICE_ID: string;
|
|
9
|
+
private _currentStream;
|
|
10
|
+
private _activeDeviceId;
|
|
11
|
+
private _isPaused;
|
|
12
|
+
private _isUserFacing;
|
|
13
|
+
private _cameraToScreenRotation;
|
|
14
|
+
constructor(_impl: zappar_camera_source_t, _pipeline: zappar_pipeline_t, _deviceId: string);
|
|
15
|
+
destroy(): void;
|
|
16
|
+
private _stop;
|
|
17
|
+
pause(): void;
|
|
18
|
+
start(): void;
|
|
19
|
+
private _getConstraints;
|
|
20
|
+
getFrame(allowRead: boolean): void;
|
|
21
|
+
private _getUserMedia;
|
|
22
|
+
private _syncCamera;
|
|
23
|
+
private _hasStartedOrientation;
|
|
24
|
+
private _deviceMotionListener;
|
|
25
|
+
private _startDeviceOrientation;
|
|
26
|
+
private _startDeviceMotion;
|
|
27
|
+
private _stopDeviceMotion;
|
|
28
|
+
uploadGL(info: CameraFrameInfo): void;
|
|
29
|
+
}
|