@ateam-ai-mobile/expo 0.1.23 → 0.1.24
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/dist/aiLive/provider.d.mts +4 -1
- package/dist/aiLive/provider.d.ts +4 -1
- package/dist/aiLive/provider.js +161 -12
- package/dist/aiLive/provider.js.map +1 -1
- package/dist/aiLive/provider.mjs +3 -1
- package/dist/bootstrap.js +159 -12
- package/dist/bootstrap.js.map +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/{chunk-PV2EHH2P.mjs → chunk-PQUTEM3C.mjs} +161 -13
- package/dist/chunk-PQUTEM3C.mjs.map +1 -0
- package/package.json +5 -4
- package/src/aiLive/motionGate.ts +87 -0
- package/src/aiLive/provider.ts +54 -13
- package/src/aiLive/signature.ts +33 -0
- package/dist/chunk-PV2EHH2P.mjs.map +0 -1
package/dist/bootstrap.js
CHANGED
|
@@ -3002,7 +3002,103 @@ function base64ToBytes(b64) {
|
|
|
3002
3002
|
return o === out.length ? out : out.subarray(0, o);
|
|
3003
3003
|
}
|
|
3004
3004
|
|
|
3005
|
+
// src/aiLive/motionGate.ts
|
|
3006
|
+
var SIG_W = 16;
|
|
3007
|
+
var SIG_H = 12;
|
|
3008
|
+
var SLACK_SEC = 0.15;
|
|
3009
|
+
var SENSITIVITY = Object.freeze({ low: 0.06, medium: 0.03, high: 0.015 });
|
|
3010
|
+
var GATE_DEFAULTS = Object.freeze({ enabled: true, sensitivity: "medium", preRollSec: 1, postRollSec: 1, heartbeatSec: 8 });
|
|
3011
|
+
function difference(a, b) {
|
|
3012
|
+
if (!a || !b || a.length !== b.length || !a.length) return 1;
|
|
3013
|
+
let sum = 0;
|
|
3014
|
+
for (let i = 0; i < a.length; i++) sum += Math.abs(a[i] - b[i]);
|
|
3015
|
+
return sum / (a.length * 255);
|
|
3016
|
+
}
|
|
3017
|
+
function gateConfigFrom(d) {
|
|
3018
|
+
const c = { ...GATE_DEFAULTS };
|
|
3019
|
+
if (!d || typeof d !== "object") return c;
|
|
3020
|
+
if (typeof d.enabled === "boolean") c.enabled = d.enabled;
|
|
3021
|
+
if (typeof d.sensitivity === "string" && Object.prototype.hasOwnProperty.call(SENSITIVITY, d.sensitivity)) c.sensitivity = d.sensitivity;
|
|
3022
|
+
for (const k of ["preRollSec", "postRollSec", "heartbeatSec"]) {
|
|
3023
|
+
const v = Number(d[k]);
|
|
3024
|
+
if (Number.isFinite(v) && v >= 0 && v <= 60) c[k] = v;
|
|
3025
|
+
}
|
|
3026
|
+
return c;
|
|
3027
|
+
}
|
|
3028
|
+
function createMotionGate(cfg = {}) {
|
|
3029
|
+
const c = { ...GATE_DEFAULTS, ...cfg };
|
|
3030
|
+
const threshold = SENSITIVITY[c.sensitivity] ?? SENSITIVITY.medium;
|
|
3031
|
+
let prevSig = null;
|
|
3032
|
+
let lastChangeT = -Infinity;
|
|
3033
|
+
let lastSentT = -Infinity;
|
|
3034
|
+
let held = [];
|
|
3035
|
+
const n = { offered: 0, sent: 0, held: 0, heartbeats: 0, changes: 0 };
|
|
3036
|
+
const sendNow = (t, item, reason, withPreRoll = false) => {
|
|
3037
|
+
const out = withPreRoll ? held.filter((h) => t - h.t <= c.preRollSec + SLACK_SEC).map((h) => h.item) : [];
|
|
3038
|
+
held = [];
|
|
3039
|
+
out.push(item);
|
|
3040
|
+
lastSentT = t;
|
|
3041
|
+
n.sent += out.length;
|
|
3042
|
+
return { send: out, reason };
|
|
3043
|
+
};
|
|
3044
|
+
return {
|
|
3045
|
+
offer({ t, sig, item }) {
|
|
3046
|
+
n.offered++;
|
|
3047
|
+
if (!c.enabled) {
|
|
3048
|
+
lastSentT = t;
|
|
3049
|
+
n.sent++;
|
|
3050
|
+
return { send: [item], reason: "off" };
|
|
3051
|
+
}
|
|
3052
|
+
const first = prevSig == null;
|
|
3053
|
+
const d = difference(prevSig, sig);
|
|
3054
|
+
prevSig = sig;
|
|
3055
|
+
if (first) return sendNow(t, item, "first");
|
|
3056
|
+
if (d > threshold) {
|
|
3057
|
+
lastChangeT = t;
|
|
3058
|
+
n.changes++;
|
|
3059
|
+
return sendNow(t, item, "change", true);
|
|
3060
|
+
}
|
|
3061
|
+
if (t - lastChangeT <= c.postRollSec + SLACK_SEC) return sendNow(t, item, "after");
|
|
3062
|
+
if (t - lastSentT >= c.heartbeatSec) {
|
|
3063
|
+
n.heartbeats++;
|
|
3064
|
+
return sendNow(t, item, "heartbeat");
|
|
3065
|
+
}
|
|
3066
|
+
held.push({ t, item });
|
|
3067
|
+
while (held.length && t - held[0].t > c.preRollSec + 1) held.shift();
|
|
3068
|
+
n.held++;
|
|
3069
|
+
return { send: [], reason: "quiet" };
|
|
3070
|
+
},
|
|
3071
|
+
counters: () => ({ ...n }),
|
|
3072
|
+
config: () => ({ ...c })
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
// src/aiLive/signature.ts
|
|
3077
|
+
var import_jpeg_js = require("jpeg-js");
|
|
3078
|
+
var SIG_JPEG_SIDE = 64;
|
|
3079
|
+
function signatureFromJpeg(b64) {
|
|
3080
|
+
const img = (0, import_jpeg_js.decode)(base64ToBytes(b64), { useTArray: true, formatAsRGBA: true, maxResolutionInMP: 1 });
|
|
3081
|
+
const { width: w, height: h, data } = img;
|
|
3082
|
+
if (!w || !h) throw new Error("empty picture");
|
|
3083
|
+
const sum = new Float64Array(SIG_W * SIG_H);
|
|
3084
|
+
const cnt = new Uint32Array(SIG_W * SIG_H);
|
|
3085
|
+
for (let y = 0; y < h; y++) {
|
|
3086
|
+
const cy = Math.min(SIG_H - 1, Math.floor(y * SIG_H / h));
|
|
3087
|
+
for (let x = 0; x < w; x++) {
|
|
3088
|
+
const cx = Math.min(SIG_W - 1, Math.floor(x * SIG_W / w));
|
|
3089
|
+
const i = (y * w + x) * 4;
|
|
3090
|
+
const k = cy * SIG_W + cx;
|
|
3091
|
+
sum[k] += 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
|
|
3092
|
+
cnt[k]++;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
const out = new Uint8Array(SIG_W * SIG_H);
|
|
3096
|
+
for (let k = 0; k < out.length; k++) out[k] = cnt[k] ? Math.round(sum[k] / cnt[k]) : 0;
|
|
3097
|
+
return out;
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3005
3100
|
// src/aiLive/provider.ts
|
|
3101
|
+
var PHONE_MAX_PRE_ROLL_SEC = 5;
|
|
3006
3102
|
var LIVE_KIND = "ai-models.live";
|
|
3007
3103
|
var FRESH_MS2 = 3e3;
|
|
3008
3104
|
var MAX_BUFFERED = 512 * 1024;
|
|
@@ -3016,13 +3112,14 @@ var defaultAiLiveDeps = () => ({
|
|
|
3016
3112
|
token: import_core2.getToken,
|
|
3017
3113
|
tenant: import_core2.getTenant,
|
|
3018
3114
|
jpegById,
|
|
3115
|
+
signatureById: async (id) => signatureFromJpeg(await jpegById(id, SIG_JPEG_SIDE, 0.6)),
|
|
3019
3116
|
now: Date.now,
|
|
3020
3117
|
setInterval: (fn, ms) => setInterval(fn, ms),
|
|
3021
3118
|
clearInterval: (h) => clearInterval(h),
|
|
3022
3119
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
3023
3120
|
clearTimeout: (h) => clearTimeout(h)
|
|
3024
3121
|
});
|
|
3025
|
-
var idle = () => ({ state: "idle", camera: null, sent: 0, skipped: 0, lastEpisode: null, lastError: null });
|
|
3122
|
+
var idle = () => ({ state: "idle", camera: null, sent: 0, skipped: 0, heldBack: 0, lastEpisode: null, lastError: null });
|
|
3026
3123
|
function createAiLiveProvider(overrides = {}) {
|
|
3027
3124
|
const deps = { ...defaultAiLiveDeps(), ...overrides };
|
|
3028
3125
|
let owner = null;
|
|
@@ -3038,9 +3135,11 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3038
3135
|
let closingOnPurpose = false;
|
|
3039
3136
|
let seq = 0;
|
|
3040
3137
|
let busy = false;
|
|
3138
|
+
let owedGaps = 0;
|
|
3041
3139
|
let gen = 0;
|
|
3042
3140
|
let lastFrameId = null;
|
|
3043
3141
|
let camera = DEFAULT_CAMERA;
|
|
3142
|
+
let gate = null;
|
|
3044
3143
|
let lastOwner = null;
|
|
3045
3144
|
const episodeCbs = /* @__PURE__ */ new Map();
|
|
3046
3145
|
const snapCbs = /* @__PURE__ */ new Map();
|
|
@@ -3083,7 +3182,6 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3083
3182
|
}
|
|
3084
3183
|
}
|
|
3085
3184
|
async function slot() {
|
|
3086
|
-
const mySeq = seq++;
|
|
3087
3185
|
const g = gen;
|
|
3088
3186
|
const sock = ws;
|
|
3089
3187
|
const current = () => g === gen && ws === sock && sock?.readyState === 1;
|
|
@@ -3092,13 +3190,22 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3092
3190
|
if (g === gen) void teardown("stopped", import_plugin_runtime3.CONSENT_TAKEN_BACK);
|
|
3093
3191
|
return true;
|
|
3094
3192
|
};
|
|
3193
|
+
const gap = (lastError) => {
|
|
3194
|
+
seq++;
|
|
3195
|
+
set2(lastError ? { skipped: snap.skipped + 1, lastError } : { skipped: snap.skipped + 1 });
|
|
3196
|
+
};
|
|
3095
3197
|
if (consentGone()) return;
|
|
3096
|
-
if (busy
|
|
3198
|
+
if (busy && current()) {
|
|
3199
|
+
owedGaps++;
|
|
3097
3200
|
set2({ skipped: snap.skipped + 1 });
|
|
3098
3201
|
return;
|
|
3099
3202
|
}
|
|
3203
|
+
if (busy || !current()) {
|
|
3204
|
+
gap();
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3100
3207
|
if ((sock.bufferedAmount || 0) > MAX_BUFFERED) {
|
|
3101
|
-
|
|
3208
|
+
gap();
|
|
3102
3209
|
return;
|
|
3103
3210
|
}
|
|
3104
3211
|
busy = true;
|
|
@@ -3109,19 +3216,52 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3109
3216
|
const id = f?.handle?.id;
|
|
3110
3217
|
const t = Number(f?.timestamp);
|
|
3111
3218
|
if (!id || id === lastFrameId || !(deps.now() - t < FRESH_MS2)) {
|
|
3112
|
-
|
|
3219
|
+
gap();
|
|
3113
3220
|
return;
|
|
3114
3221
|
}
|
|
3115
|
-
const b64 = await deps.jpegById(id, camera.maxPx, 0.7);
|
|
3116
|
-
if (!current() || consentGone()) return;
|
|
3117
|
-
const frame = encodeFrame({ seq: mySeq, tCapture: t, payload: base64ToBytes(b64) });
|
|
3118
|
-
sock.send(frame.buffer);
|
|
3119
3222
|
lastFrameId = id;
|
|
3120
|
-
|
|
3223
|
+
let toSend = [{ id, t }];
|
|
3224
|
+
const myGate = gate;
|
|
3225
|
+
if (myGate) {
|
|
3226
|
+
let sig = null;
|
|
3227
|
+
try {
|
|
3228
|
+
sig = await deps.signatureById(id);
|
|
3229
|
+
} catch {
|
|
3230
|
+
sig = null;
|
|
3231
|
+
}
|
|
3232
|
+
if (!current() || consentGone()) return;
|
|
3233
|
+
toSend = myGate.offer({ t: t / 1e3, sig, item: { id, t } }).send;
|
|
3234
|
+
if (!toSend.length) {
|
|
3235
|
+
set2({ heldBack: (snap.heldBack ?? 0) + 1 });
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
for (const it of toSend) {
|
|
3240
|
+
if (!current() || consentGone()) return;
|
|
3241
|
+
if ((sock.bufferedAmount || 0) > MAX_BUFFERED) {
|
|
3242
|
+
gap();
|
|
3243
|
+
continue;
|
|
3244
|
+
}
|
|
3245
|
+
let b64;
|
|
3246
|
+
try {
|
|
3247
|
+
b64 = await deps.jpegById(it.id, camera.maxPx, 0.7);
|
|
3248
|
+
} catch (e) {
|
|
3249
|
+
if (g === gen) gap(String(e?.message || e));
|
|
3250
|
+
continue;
|
|
3251
|
+
}
|
|
3252
|
+
if (!current() || consentGone()) return;
|
|
3253
|
+
const frame = encodeFrame({ seq: seq++, tCapture: it.t, payload: base64ToBytes(b64) });
|
|
3254
|
+
sock.send(frame.buffer);
|
|
3255
|
+
set2({ sent: snap.sent + 1 });
|
|
3256
|
+
}
|
|
3121
3257
|
} catch (e) {
|
|
3122
|
-
if (g === gen)
|
|
3258
|
+
if (g === gen) gap(String(e?.message || e));
|
|
3123
3259
|
} finally {
|
|
3124
|
-
if (g === gen)
|
|
3260
|
+
if (g === gen) {
|
|
3261
|
+
busy = false;
|
|
3262
|
+
seq += owedGaps;
|
|
3263
|
+
owedGaps = 0;
|
|
3264
|
+
}
|
|
3125
3265
|
}
|
|
3126
3266
|
}
|
|
3127
3267
|
function openSocket() {
|
|
@@ -3166,6 +3306,11 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3166
3306
|
camera = c && Number(c.fps) > 0 && Number(c.maxPx) > 0 ? { fps: Number(c.fps), maxPx: Number(c.maxPx) } : DEFAULT_CAMERA;
|
|
3167
3307
|
return;
|
|
3168
3308
|
}
|
|
3309
|
+
if (ev.event === "motion" && !opened) {
|
|
3310
|
+
const c = gateConfigFrom(ev.data);
|
|
3311
|
+
gate = c.enabled ? createMotionGate({ ...c, preRollSec: Math.min(c.preRollSec, PHONE_MAX_PRE_ROLL_SEC) }) : null;
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3169
3314
|
if (ev.event === "opened" && !opened) {
|
|
3170
3315
|
opened = true;
|
|
3171
3316
|
deps.clearTimeout(gaveUp);
|
|
@@ -3272,8 +3417,10 @@ function createAiLiveProvider(overrides = {}) {
|
|
|
3272
3417
|
allowed = typeof ctx.allowed === "function" ? ctx.allowed : () => false;
|
|
3273
3418
|
gen++;
|
|
3274
3419
|
seq = 0;
|
|
3420
|
+
owedGaps = 0;
|
|
3275
3421
|
lastFrameId = null;
|
|
3276
3422
|
busy = false;
|
|
3423
|
+
gate = null;
|
|
3277
3424
|
const g = gen;
|
|
3278
3425
|
const cameraOfScreen = ctx.visual;
|
|
3279
3426
|
snap = { ...idle(), camera: cam };
|