@bubo-squared/gyroview 0.4.1 → 0.5.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/CHANGELOG.md +47 -0
- package/README.md +7 -7
- package/dist/packages/adapters/mediabunny/src/MediabunnyCodecReader.js +10 -4
- package/dist/packages/adapters/mediabunny/src/decoderConfigurations.js +20 -4
- package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +32 -13
- package/dist/packages/adapters/three/src/drawSchedules.js +13 -0
- package/dist/packages/adapters/three/src/index.js +1 -0
- package/dist/packages/core/src/application/download/FileDownload.js +17 -6
- package/dist/packages/core/src/application/download/SampleCursor.js +4 -4
- package/dist/packages/core/src/application/download/Transfers.js +7 -0
- package/dist/packages/core/src/application/recording/motionOf.js +5 -1
- package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +4 -2
- package/dist/packages/core/src/domain/download/DownloadPolicy.js +10 -1
- package/dist/packages/core/src/domain/format/mp4/hevcCodecString.js +161 -0
- package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +9 -2
- package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +14 -4
- package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +35 -3
- package/dist/packages/core/src/domain/motion/mounting/Mounting.js +117 -0
- package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +6 -4
- package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +9 -1
- package/dist/packages/core/src/domain/motion/orientation/gravity.js +1 -1
- package/dist/packages/core/src/index.js +4 -1
- package/dist/packages/core/src/shared/binary/ByteReader.js +10 -1
- package/dist/packages/core/src/shared/index.js +1 -0
- package/dist/packages/core/src/shared/math/Matrix3.js +18 -1
- package/dist/packages/core/src/shared/math/Quaternion.js +1 -1
- package/dist/packages/core/src/shared/math/Vector3.js +5 -1
- package/dist/packages/core/src/shared/math/minimum.js +17 -0
- package/dist/packages/player/src/composition/AnimationFrameDraws.js +38 -0
- package/dist/packages/player/src/composition/animationFrames.js +9 -0
- package/dist/packages/player/src/composition/buildPipeline.js +13 -7
- package/dist/packages/player/src/player/FrameLoop.js +1 -6
- package/dist/standalone.js +58 -59
- package/package.json +1 -1
|
@@ -38,14 +38,24 @@ var GyroTrack = class {
|
|
|
38
38
|
}
|
|
39
39
|
sampleAt(index) {
|
|
40
40
|
ensureIndexInRange(index, this.length, "gyro sample");
|
|
41
|
-
const base = index * 3;
|
|
42
41
|
return {
|
|
43
42
|
captureTime: microseconds(this.captureTimeStore[index] ?? 0),
|
|
44
|
-
acceleration: this.vectorAt(this.accelerationStore,
|
|
45
|
-
angularVelocity: this.vectorAt(this.angularVelocityStore,
|
|
43
|
+
acceleration: this.vectorAt(this.accelerationStore, index),
|
|
44
|
+
angularVelocity: this.vectorAt(this.angularVelocityStore, index)
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* A sample's angular velocity alone, for the loops over every sample that need nothing else.
|
|
49
|
+
*/
|
|
50
|
+
angularVelocityAt(index) {
|
|
51
|
+
ensureIndexInRange(index, this.length, "gyro sample");
|
|
52
|
+
return this.vectorAt(this.angularVelocityStore, index);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The vector at an index already checked.
|
|
56
|
+
*/
|
|
57
|
+
vectorAt(store, index) {
|
|
58
|
+
const base = index * 3;
|
|
49
59
|
return [
|
|
50
60
|
store[base] ?? NaN,
|
|
51
61
|
store[base + 1] ?? NaN,
|
|
@@ -107,6 +107,34 @@ var X6_IMU_FRAME = measuredImuFrame("X6", QUARTER_TURN_ABOUT_LATERAL);
|
|
|
107
107
|
*/
|
|
108
108
|
var X4_AIR_IMU_FRAME = measuredImuFrame("X4 Air", QUARTER_TURN_ABOUT_LATERAL);
|
|
109
109
|
/**
|
|
110
|
+
* Measured on one Antigravity A1 recording (ADR 0009): the drone's lens axis is its vertical, the
|
|
111
|
+
* IMU's y, lens 0 facing up; the ranking puts this quarter turn about that axis first, the X5's
|
|
112
|
+
* arrangement fourth.
|
|
113
|
+
*/
|
|
114
|
+
var A1_IMU_FRAME = measuredImuFrame("A1", [
|
|
115
|
+
"-z",
|
|
116
|
+
"-x",
|
|
117
|
+
"y"
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* Measured on one X3 recording (ADR 0009): the X5's arrangement again. The camera stood still on
|
|
121
|
+
* its side, so the levelling ranking decided it rather than the stillness ranking: it alone of
|
|
122
|
+
* the four that put gravity on the body's axis stands the picture as level as Studio's.
|
|
123
|
+
*/
|
|
124
|
+
var X3_IMU_FRAME = measuredImuFrame("X3", QUARTER_TURN_ABOUT_LATERAL);
|
|
125
|
+
/**
|
|
126
|
+
* Assumed for the ONE RS until a recording that turns is ranked (ADR 0009). On one tripod
|
|
127
|
+
* recording of the 1-inch 360 edition, standing upright, gravity lies along the IMU's x, so the
|
|
128
|
+
* body's down is the IMU's x; of the four arrangements that agree, the levelling ranking puts
|
|
129
|
+
* this quarter turn about the lens axis first in both browsers, by too little to decide it. The
|
|
130
|
+
* model name covers the 360 lens module too, assumed alike.
|
|
131
|
+
*/
|
|
132
|
+
var ONE_RS_IMU_FRAME = assumedImuFrame("ONE RS (unverified)", [
|
|
133
|
+
"-y",
|
|
134
|
+
"x",
|
|
135
|
+
"z"
|
|
136
|
+
]);
|
|
137
|
+
/**
|
|
110
138
|
* Until a recording proves otherwise, the IMU is assumed aligned with the body.
|
|
111
139
|
*/
|
|
112
140
|
var ALIGNED_IMU_FRAME = assumedImuFrame("aligned (unverified)", [
|
|
@@ -116,12 +144,16 @@ var ALIGNED_IMU_FRAME = assumedImuFrame("aligned (unverified)", [
|
|
|
116
144
|
]);
|
|
117
145
|
/**
|
|
118
146
|
* Keyed by the whole model name: a name that extends a measured one ("X4 Air" of "X4", an "X5
|
|
119
|
-
* Pro") is another camera, unmeasured until a recording of it is ranked.
|
|
147
|
+
* Pro") is another camera, unmeasured until a recording of it is ranked. Every frame is measured
|
|
148
|
+
* but the ONE RS's, assumed and reported as such.
|
|
120
149
|
*/
|
|
121
150
|
var IMU_FRAMES_BY_MODEL = /* @__PURE__ */ new Map([
|
|
151
|
+
["Antigravity A1", A1_IMU_FRAME],
|
|
152
|
+
["Insta360 X3", X3_IMU_FRAME],
|
|
122
153
|
["Insta360 X4 Air", X4_AIR_IMU_FRAME],
|
|
123
154
|
["Insta360 X5", X5_IMU_FRAME],
|
|
124
|
-
["Insta360 X6", X6_IMU_FRAME]
|
|
155
|
+
["Insta360 X6", X6_IMU_FRAME],
|
|
156
|
+
["Insta360 OneRS", ONE_RS_IMU_FRAME]
|
|
125
157
|
]);
|
|
126
158
|
/**
|
|
127
159
|
* The IMU frame for a recording, chosen from the camera model the file names. A starting point
|
|
@@ -131,4 +163,4 @@ function imuFrameFor(hints) {
|
|
|
131
163
|
return IMU_FRAMES_BY_MODEL.get(hints.model?.trim() ?? "") ?? ALIGNED_IMU_FRAME;
|
|
132
164
|
}
|
|
133
165
|
//#endregion
|
|
134
|
-
export { ALIGNED_IMU_FRAME, X4_AIR_IMU_FRAME, X5_IMU_FRAME, X6_IMU_FRAME, assumedImuFrame, imuFrameFor, isProperRotation, toBodyFrame };
|
|
166
|
+
export { A1_IMU_FRAME, ALIGNED_IMU_FRAME, ONE_RS_IMU_FRAME, X3_IMU_FRAME, X4_AIR_IMU_FRAME, X5_IMU_FRAME, X6_IMU_FRAME, assumedImuFrame, imuFrameFor, isProperRotation, toBodyFrame };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { HALF_TURN, QUARTER_TURN, degrees, degreesToRadians, radians } from "../../../shared/units/angle.js";
|
|
2
|
+
import { ZERO_VECTOR3, addVectors, dotProduct, magnitudeOf } from "../../../shared/math/Vector3.js";
|
|
3
|
+
import { multiplyMatrices, rotationAboutX, rotationAboutY, rotationAboutZ, transformVector, transposeMatrix } from "../../../shared/math/Matrix3.js";
|
|
4
|
+
import { toBodyFrame } from "../imu/ImuFrame.js";
|
|
5
|
+
import { indexOfLeast } from "../../../shared/math/minimum.js";
|
|
6
|
+
import { measuredGravity } from "../orientation/gravity.js";
|
|
7
|
+
//#region ../../packages/core/src/domain/motion/mounting/Mounting.ts
|
|
8
|
+
var QUARTER_TURN_ANGLE = degreesToRadians(QUARTER_TURN);
|
|
9
|
+
var MINUS_QUARTER_TURN_ANGLE = degreesToRadians(degrees(-QUARTER_TURN));
|
|
10
|
+
var HALF_TURN_ANGLE = degreesToRadians(HALF_TURN);
|
|
11
|
+
/**
|
|
12
|
+
* Also how a recording stands whose gravity cannot be read: one without a gyro record, or whose
|
|
13
|
+
* IMU frame is a guess.
|
|
14
|
+
*/
|
|
15
|
+
var UPRIGHT_MOUNTING = {
|
|
16
|
+
name: "upright",
|
|
17
|
+
toBody: lensLevel(radians(0))
|
|
18
|
+
};
|
|
19
|
+
var DOWN = [
|
|
20
|
+
0,
|
|
21
|
+
1,
|
|
22
|
+
0
|
|
23
|
+
];
|
|
24
|
+
var LENS_AXIS = [
|
|
25
|
+
0,
|
|
26
|
+
0,
|
|
27
|
+
1
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* The four ways a camera can stand with its lens axis level, one for each body axis across it
|
|
31
|
+
* that gravity can lie along.
|
|
32
|
+
*/
|
|
33
|
+
var LENS_LEVEL_MOUNTINGS = [
|
|
34
|
+
UPRIGHT_MOUNTING,
|
|
35
|
+
{
|
|
36
|
+
name: "upside down",
|
|
37
|
+
toBody: lensLevel(HALF_TURN_ANGLE)
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "on its right side",
|
|
41
|
+
toBody: lensLevel(MINUS_QUARTER_TURN_ANGLE)
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "on its left side",
|
|
45
|
+
toBody: lensLevel(QUARTER_TURN_ANGLE)
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* The two ways a camera can stand with its lens axis the vertical.
|
|
50
|
+
*/
|
|
51
|
+
var LENS_VERTICAL_MOUNTINGS = [{
|
|
52
|
+
name: "with lens 0 up",
|
|
53
|
+
toBody: lensVertical(MINUS_QUARTER_TURN_ANGLE)
|
|
54
|
+
}, {
|
|
55
|
+
name: "with lens 0 down",
|
|
56
|
+
toBody: lensVertical(QUARTER_TURN_ANGLE)
|
|
57
|
+
}];
|
|
58
|
+
var LENS_VERTICAL_CONE_COSINE = Math.cos(degreesToRadians(degrees(30)));
|
|
59
|
+
/**
|
|
60
|
+
* A half turn about the vertical faces the upright frame away from lens 0, then a turn about the
|
|
61
|
+
* lens axis stands the body as it stood.
|
|
62
|
+
*/
|
|
63
|
+
function lensLevel(roll) {
|
|
64
|
+
return multiplyMatrices(rotationAboutZ(roll), rotationAboutY(HALF_TURN_ANGLE));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A quarter turn about the vertical faces the upright frame along the body's minus x, then a
|
|
68
|
+
* quarter turn about the lateral axis stands lens 0's axis up or down.
|
|
69
|
+
*/
|
|
70
|
+
function lensVertical(tilt) {
|
|
71
|
+
return multiplyMatrices(rotationAboutX(tilt), rotationAboutY(MINUS_QUARTER_TURN_ANGLE));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The mounting whose down lies nearest the gravity the accelerometer measured whenever the camera
|
|
75
|
+
* rested, read through the IMU frame: lens-vertical within its cone of the lens axis, lens-level
|
|
76
|
+
* outside it. Quarter turns only, so the picture keeps every tilt the camera really took.
|
|
77
|
+
* Upright for a camera that never rests, and for one whose IMU frame is a guess: read through a
|
|
78
|
+
* wrong frame, gravity would stand the picture on its side or on its head.
|
|
79
|
+
*/
|
|
80
|
+
function mountingOf(gyro, frame) {
|
|
81
|
+
if (!frame.isVerified) return UPRIGHT_MOUNTING;
|
|
82
|
+
const gravity = restingGravitySumOf(gyro, frame);
|
|
83
|
+
const mountings = isNearLensAxis(gravity) ? LENS_VERTICAL_MOUNTINGS : LENS_LEVEL_MOUNTINGS;
|
|
84
|
+
const misalignments = mountings.map((mounting) => -dotProduct(transformVector(mounting.toBody, DOWN), gravity));
|
|
85
|
+
return mountings[indexOfLeast(misalignments)] ?? UPRIGHT_MOUNTING;
|
|
86
|
+
}
|
|
87
|
+
function isNearLensAxis(gravity) {
|
|
88
|
+
return Math.abs(dotProduct(gravity, LENS_AXIS)) > LENS_VERTICAL_CONE_COSINE * magnitudeOf(gravity);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The IMU frame as it lies in the mounting's upright frame: what the camera's orientation is
|
|
92
|
+
* integrated in, so that levelling and heading start from the camera as it stood. Its `toBody`
|
|
93
|
+
* reads into the upright frame, which stands for the body throughout the integration.
|
|
94
|
+
*/
|
|
95
|
+
function uprightImuFrame(frame, mounting) {
|
|
96
|
+
return {
|
|
97
|
+
...frame,
|
|
98
|
+
toBody: multiplyMatrices(transposeMatrix(mounting.toBody), frame.toBody)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A rotation into the mounting's upright frame, such as a stabilizer's, carried on into the body
|
|
103
|
+
* the lenses are posed in.
|
|
104
|
+
*/
|
|
105
|
+
function rotationIntoBody(mounting, uprightRotation) {
|
|
106
|
+
return multiplyMatrices(mounting.toBody, uprightRotation);
|
|
107
|
+
}
|
|
108
|
+
function restingGravitySumOf(gyro, frame) {
|
|
109
|
+
let sum = ZERO_VECTOR3;
|
|
110
|
+
for (let index = 0; index < gyro.length; index += 1) {
|
|
111
|
+
const gravity = measuredGravity(toBodyFrame(frame, gyro.sampleAt(index).acceleration));
|
|
112
|
+
if (gravity) sum = addVectors(sum, gravity);
|
|
113
|
+
}
|
|
114
|
+
return sum;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { UPRIGHT_MOUNTING, mountingOf, rotationIntoBody, uprightImuFrame };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ensureInvariant } from "../../../shared/errors/GyroViewError.js";
|
|
2
|
-
import { seconds } from "../../../shared/units/time.js";
|
|
2
|
+
import { microseconds, seconds } from "../../../shared/units/time.js";
|
|
3
3
|
import { ZERO_VECTOR3, addVectors, isFiniteVector, scaleVector, subtractVectors } from "../../../shared/math/Vector3.js";
|
|
4
4
|
import { clamp } from "../../../shared/math/clamp.js";
|
|
5
5
|
import { toBodyFrame } from "../imu/ImuFrame.js";
|
|
@@ -24,8 +24,10 @@ var QUATERNION_COMPONENTS = 4;
|
|
|
24
24
|
*/
|
|
25
25
|
var MAX_STEP_SECONDS = .05;
|
|
26
26
|
/**
|
|
27
|
-
* The camera's orientation over the whole recording,
|
|
28
|
-
*
|
|
27
|
+
* The camera's orientation over the whole recording, from the frame its IMU frame reads into (the
|
|
28
|
+
* body, or the upright frame of its mounting, as the player integrates it) to the world, one
|
|
29
|
+
* quaternion per gyro sample. World: y down along gravity, z along that frame's forward axis at
|
|
30
|
+
* the start.
|
|
29
31
|
*/
|
|
30
32
|
var OrientationTrack = class OrientationTrack {
|
|
31
33
|
videoTimes;
|
|
@@ -107,7 +109,7 @@ var OrientationTrack = class OrientationTrack {
|
|
|
107
109
|
* lookup's binary search holds.
|
|
108
110
|
*/
|
|
109
111
|
function sampleVideoTimes(gyro, clock) {
|
|
110
|
-
return
|
|
112
|
+
return gyro.captureTimes.map((captureTime) => clock.gyroVideoTimeOf(microseconds(captureTime)));
|
|
111
113
|
}
|
|
112
114
|
function finiteSample(gyro, index) {
|
|
113
115
|
const sample = gyro.sampleAt(index);
|
|
@@ -18,7 +18,7 @@ var FALLBACK_INTERVAL = secondsToMicroseconds(seconds(1));
|
|
|
18
18
|
function stillestWindow(gyro, frame, length) {
|
|
19
19
|
const interval = microsecondsToSeconds(gyro.meanSampleInterval ?? FALLBACK_INTERVAL);
|
|
20
20
|
const size = Math.max(1, Math.min(gyro.length, Math.round(length / interval)));
|
|
21
|
-
const magnitudes =
|
|
21
|
+
const magnitudes = rateMagnitudesOf(gyro, frame);
|
|
22
22
|
let windowSum = 0;
|
|
23
23
|
for (let index = 0; index < size; index += 1) windowSum += magnitudes[index] ?? 0;
|
|
24
24
|
let best = {
|
|
@@ -38,6 +38,14 @@ function stillestWindow(gyro, frame, length) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
+
* How fast the body turns at each sample, in radians per second.
|
|
42
|
+
*/
|
|
43
|
+
function rateMagnitudesOf(gyro, frame) {
|
|
44
|
+
const magnitudes = new Float64Array(gyro.length);
|
|
45
|
+
for (let index = 0; index < gyro.length; index += 1) magnitudes[index] = magnitudeOf(toBodyFrame(frame, gyro.angularVelocityAt(index)));
|
|
46
|
+
return magnitudes;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
41
49
|
* The mean of one vector quantity over a window of samples, in the body frame.
|
|
42
50
|
*/
|
|
43
51
|
function meanOverWindow({ gyro, frame }, window, quantity) {
|
|
@@ -15,7 +15,9 @@ import { clamp } from "./shared/math/clamp.js";
|
|
|
15
15
|
import "./domain/motion/timing/FrameTimes.js";
|
|
16
16
|
import { IDENTITY_MATRIX3 } from "./shared/math/Matrix3.js";
|
|
17
17
|
import "./domain/motion/imu/ImuFrame.js";
|
|
18
|
+
import "./shared/math/minimum.js";
|
|
18
19
|
import "./shared/math/Quaternion.js";
|
|
20
|
+
import { UPRIGHT_MOUNTING } from "./domain/motion/mounting/Mounting.js";
|
|
19
21
|
import "./domain/motion/orientation/OrientationTrack.js";
|
|
20
22
|
import { timeRecording } from "./application/recording/timeRecording.js";
|
|
21
23
|
import "./domain/format/naming/RecordingFileName.js";
|
|
@@ -26,6 +28,7 @@ import "./domain/container/TrackSampleTable.js";
|
|
|
26
28
|
import { readSampleTable } from "./application/recording/readSampleTable.js";
|
|
27
29
|
import "./domain/stitching/LensLayout.js";
|
|
28
30
|
import { detectLensLayout } from "./domain/format/layout/detectLensLayout.js";
|
|
31
|
+
import { hevcCodecStringOf } from "./domain/format/mp4/hevcCodecString.js";
|
|
29
32
|
import { DEFAULT_STABILIZATION_MODE, STABILIZATION_MODES } from "./domain/motion/stabilization/Stabilizer.js";
|
|
30
33
|
import { stabilizerFor } from "./domain/motion/stabilization/stabilizers.js";
|
|
31
34
|
import "./domain/optics/lensPose.js";
|
|
@@ -68,4 +71,4 @@ import { WallClock } from "./application/playback/WallClock.js";
|
|
|
68
71
|
import { isFlowing } from "./domain/playback/PlayerState.js";
|
|
69
72
|
import { TypedEmitter } from "./shared/events/TypedEmitter.js";
|
|
70
73
|
import { PlaybackSession } from "./application/playback/PlaybackSession.js";
|
|
71
|
-
export { BT709_LUMINANCE, ByteRange, COLOUR_PRIMARIES, DEFAULT_FRAMING, DEFAULT_PICTURE_QUALITY, DEFAULT_STABILIZATION_MODE, DEFAULT_VIEW_MODE, Deferred, Ending, GYRO_VIEW_ERROR_CATEGORIES, GYRO_VIEW_ERROR_CODES, GainMatchingFrameSink, GyroViewError, HLG_OETF, IDENTITY_MATRIX3, ITERATION_END, MATRIX_COEFFICIENTS, MEI_TERM_CAPACITY, Outbox, PICTURE_QUALITIES, PlaybackSession, RecordingBuffer, RunStop, SCREEN_CENTRE, STABILIZATION_MODES, STOPPED, SourceByteStream, StabilizingFrameSink, TRANSFER_CHARACTERISTICS, TypedEmitter, VIEW_MODES, WallClock, asGyroViewError, aspectOf, aspectOfArea, buildStitchingSetup, clamp, clampView, degrees, detectLensLayout, displayConversionsOf, downloadPolicyFor, ensureIndexInRange, ensureInvariant, fileNameOfUrl, hasErrorCode, inspectRecording, isAbortError, isFlowing, isGyroViewErrorCode, isSameFraming, isSameView, keysOf, lazy, lensFrameOrder, locateOtherLensFile, lookAt, matrixCorrectionOf, messageOf, microseconds, microsecondsToSeconds, milliseconds, namedOrUnspecified, pixelRatioCapOf, planeHalfExtentOf, probeDecoding, readRecording, readSampleTable, seconds, secondsToMicroseconds, stabilizerFor, startFileDownload, timeRecording, viewModeRulesFor, zoomStepsForPinch };
|
|
74
|
+
export { BT709_LUMINANCE, ByteRange, COLOUR_PRIMARIES, DEFAULT_FRAMING, DEFAULT_PICTURE_QUALITY, DEFAULT_STABILIZATION_MODE, DEFAULT_VIEW_MODE, Deferred, Ending, GYRO_VIEW_ERROR_CATEGORIES, GYRO_VIEW_ERROR_CODES, GainMatchingFrameSink, GyroViewError, HLG_OETF, IDENTITY_MATRIX3, ITERATION_END, MATRIX_COEFFICIENTS, MEI_TERM_CAPACITY, Outbox, PICTURE_QUALITIES, PlaybackSession, RecordingBuffer, RunStop, SCREEN_CENTRE, STABILIZATION_MODES, STOPPED, SourceByteStream, StabilizingFrameSink, TRANSFER_CHARACTERISTICS, TypedEmitter, UPRIGHT_MOUNTING, VIEW_MODES, WallClock, asGyroViewError, aspectOf, aspectOfArea, buildStitchingSetup, clamp, clampView, degrees, detectLensLayout, displayConversionsOf, downloadPolicyFor, ensureIndexInRange, ensureInvariant, fileNameOfUrl, hasErrorCode, hevcCodecStringOf, inspectRecording, isAbortError, isFlowing, isGyroViewErrorCode, isSameFraming, isSameView, keysOf, lazy, lensFrameOrder, locateOtherLensFile, lookAt, matrixCorrectionOf, messageOf, microseconds, microsecondsToSeconds, milliseconds, namedOrUnspecified, pixelRatioCapOf, planeHalfExtentOf, probeDecoding, readRecording, readSampleTable, seconds, secondsToMicroseconds, stabilizerFor, startFileDownload, timeRecording, viewModeRulesFor, zoomStepsForPinch };
|
|
@@ -10,6 +10,11 @@ var FLOAT64_SIZE = 8;
|
|
|
10
10
|
var IS_LITTLE_ENDIAN = true;
|
|
11
11
|
var IS_BIG_ENDIAN = false;
|
|
12
12
|
/**
|
|
13
|
+
* The safe integer range as big integers, made once: a gyro record reads a stamp per sample.
|
|
14
|
+
*/
|
|
15
|
+
var MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
16
|
+
var MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
17
|
+
/**
|
|
13
18
|
* Bounds-checked reads over a byte array: little-endian for Insta360's records, big-endian for
|
|
14
19
|
* ISO BMFF boxes. Every read is positional, so callers express the layouts they parse as named
|
|
15
20
|
* offsets rather than as a moving cursor.
|
|
@@ -36,6 +41,10 @@ var ByteReader = class {
|
|
|
36
41
|
this.ensureAvailable(offset, UINT32_SIZE);
|
|
37
42
|
return this.view.getUint32(offset, IS_LITTLE_ENDIAN);
|
|
38
43
|
}
|
|
44
|
+
uint16BeAt(offset) {
|
|
45
|
+
this.ensureAvailable(offset, UINT16_SIZE);
|
|
46
|
+
return this.view.getUint16(offset, IS_BIG_ENDIAN);
|
|
47
|
+
}
|
|
39
48
|
uint32BeAt(offset) {
|
|
40
49
|
this.ensureAvailable(offset, UINT32_SIZE);
|
|
41
50
|
return this.view.getUint32(offset, IS_BIG_ENDIAN);
|
|
@@ -84,7 +93,7 @@ var ByteReader = class {
|
|
|
84
93
|
return Array.from(this.bytesAt(offset, length), (byte) => String.fromCodePoint(byte)).join("");
|
|
85
94
|
}
|
|
86
95
|
toSafeNumber(value, offset) {
|
|
87
|
-
if (!(value >=
|
|
96
|
+
if (!(value >= MIN_SAFE_BIGINT && value <= MAX_SAFE_BIGINT)) throw new GyroViewError("binary-unsafe-integer", `64-bit integer at offset ${offset} (${value.toString()}) exceeds the safe integer range`);
|
|
88
97
|
return Number(value);
|
|
89
98
|
}
|
|
90
99
|
ensureAvailable(offset, length) {
|
|
@@ -5,6 +5,7 @@ import { microseconds, microsecondsToSeconds, milliseconds, seconds, secondsToMi
|
|
|
5
5
|
import "./math/Vector3.js";
|
|
6
6
|
import { clamp } from "./math/clamp.js";
|
|
7
7
|
import { IDENTITY_MATRIX3 } from "./math/Matrix3.js";
|
|
8
|
+
import "./math/minimum.js";
|
|
8
9
|
import "./math/Quaternion.js";
|
|
9
10
|
import { keysOf } from "./keysOf.js";
|
|
10
11
|
import { Outbox } from "./events/Outbox.js";
|
|
@@ -73,6 +73,23 @@ function multiplyMatrices(a, b) {
|
|
|
73
73
|
a20 * b02 + a21 * b12 + a22 * b22
|
|
74
74
|
];
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The transpose, which for a rotation is its inverse.
|
|
78
|
+
*/
|
|
79
|
+
function transposeMatrix(m) {
|
|
80
|
+
const [m00, m01, m02, m10, m11, m12, m20, m21, m22] = m;
|
|
81
|
+
return [
|
|
82
|
+
m00,
|
|
83
|
+
m10,
|
|
84
|
+
m20,
|
|
85
|
+
m01,
|
|
86
|
+
m11,
|
|
87
|
+
m21,
|
|
88
|
+
m02,
|
|
89
|
+
m12,
|
|
90
|
+
m22
|
|
91
|
+
];
|
|
92
|
+
}
|
|
76
93
|
function transformVector(m, v) {
|
|
77
94
|
const [m00, m01, m02, m10, m11, m12, m20, m21, m22] = m;
|
|
78
95
|
const [x, y, z] = v;
|
|
@@ -90,4 +107,4 @@ function determinantOf(m) {
|
|
|
90
107
|
return m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) + m02 * (m10 * m21 - m11 * m20);
|
|
91
108
|
}
|
|
92
109
|
//#endregion
|
|
93
|
-
export { IDENTITY_MATRIX3, determinantOf, multiplyMatrices, rotationAboutX, rotationAboutY, rotationAboutZ, transformVector };
|
|
110
|
+
export { IDENTITY_MATRIX3, determinantOf, multiplyMatrices, rotationAboutX, rotationAboutY, rotationAboutZ, transformVector, transposeMatrix };
|
|
@@ -62,7 +62,7 @@ function conjugateQuaternion(q) {
|
|
|
62
62
|
}
|
|
63
63
|
function normalizeQuaternion(q) {
|
|
64
64
|
const [x, y, z, w] = q;
|
|
65
|
-
const length = Math.
|
|
65
|
+
const length = Math.sqrt(x * x + y * y + z * z + w * w);
|
|
66
66
|
return length === 0 ? IDENTITY_QUATERNION : [
|
|
67
67
|
x / length,
|
|
68
68
|
y / length,
|
|
@@ -4,8 +4,12 @@ var ZERO_VECTOR3 = [
|
|
|
4
4
|
0,
|
|
5
5
|
0
|
|
6
6
|
];
|
|
7
|
+
/**
|
|
8
|
+
* The square root of the squares, not `Math.hypot`: several times slower in V8, it guards against
|
|
9
|
+
* overflow no reading or direction here comes near, and gyro integration takes a million of these.
|
|
10
|
+
*/
|
|
7
11
|
function magnitudeOf(vector) {
|
|
8
|
-
return Math.
|
|
12
|
+
return Math.sqrt(dotProduct(vector, vector));
|
|
9
13
|
}
|
|
10
14
|
function dotProduct(a, b) {
|
|
11
15
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region ../../packages/core/src/shared/math/minimum.ts
|
|
2
|
+
/**
|
|
3
|
+
* The index of the least of the values, the first on a tie; values that are not finite never
|
|
4
|
+
* win, so a list of them all gives index 0.
|
|
5
|
+
*/
|
|
6
|
+
function indexOfLeast(values) {
|
|
7
|
+
let best = 0;
|
|
8
|
+
let least = Infinity;
|
|
9
|
+
for (const [index, value] of values.entries()) {
|
|
10
|
+
if (!Number.isFinite(value) || !(value < least)) continue;
|
|
11
|
+
best = index;
|
|
12
|
+
least = value;
|
|
13
|
+
}
|
|
14
|
+
return best;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { indexOfLeast };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ANIMATION_FRAMES } from "./animationFrames.js";
|
|
2
|
+
//#region ../../packages/player/src/composition/AnimationFrameDraws.ts
|
|
3
|
+
/**
|
|
4
|
+
* Draws a renderer's changed settings once at the next animation frame, however many changes
|
|
5
|
+
* come before it (ADR 0035): the screen shows one picture a frame, and WebKit hands the page
|
|
6
|
+
* every pointer move of a drag, thousands a second from a fast mouse, which drawn one by one
|
|
7
|
+
* leave no time for the frames of the recording. One schedule serves one renderer.
|
|
8
|
+
*/
|
|
9
|
+
var AnimationFrameDraws = class {
|
|
10
|
+
frames;
|
|
11
|
+
pendingDraw;
|
|
12
|
+
handle;
|
|
13
|
+
constructor(frames = ANIMATION_FRAMES) {
|
|
14
|
+
this.frames = frames;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The latest draw asked for is the one drawn.
|
|
18
|
+
*/
|
|
19
|
+
request(draw) {
|
|
20
|
+
const isFrameRequested = this.pendingDraw !== void 0;
|
|
21
|
+
this.pendingDraw = draw;
|
|
22
|
+
if (!isFrameRequested) this.handle = this.frames.request(this.drawPending);
|
|
23
|
+
}
|
|
24
|
+
cancel() {
|
|
25
|
+
this.pendingDraw = void 0;
|
|
26
|
+
if (this.handle === void 0) return;
|
|
27
|
+
this.frames.cancel(this.handle);
|
|
28
|
+
this.handle = void 0;
|
|
29
|
+
}
|
|
30
|
+
drawPending = () => {
|
|
31
|
+
const draw = this.pendingDraw;
|
|
32
|
+
this.pendingDraw = void 0;
|
|
33
|
+
this.handle = void 0;
|
|
34
|
+
draw?.();
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { AnimationFrameDraws };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { messageOf } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
2
|
import { milliseconds, seconds } from "../../../core/src/shared/units/time.js";
|
|
3
|
+
import { UPRIGHT_MOUNTING } from "../../../core/src/domain/motion/mounting/Mounting.js";
|
|
3
4
|
import { stabilizerFor } from "../../../core/src/domain/motion/stabilization/stabilizers.js";
|
|
4
5
|
import { buildStitchingSetup } from "../../../core/src/domain/stitching/StitchingSetup.js";
|
|
5
6
|
import { StabilizingFrameSink } from "../../../core/src/application/stabilization/StabilizingFrameSink.js";
|
|
@@ -12,6 +13,7 @@ import { MediaSourceAudioClock } from "../../../adapters/mse-audio/src/MediaSour
|
|
|
12
13
|
import "../../../adapters/mse-audio/src/index.js";
|
|
13
14
|
import { ThreeFrameRenderer } from "../../../adapters/three/src/ThreeFrameRenderer.js";
|
|
14
15
|
import "../../../adapters/three/src/index.js";
|
|
16
|
+
import { AnimationFrameDraws } from "./AnimationFrameDraws.js";
|
|
15
17
|
import { Disposables } from "./Disposables.js";
|
|
16
18
|
var DECODE_PIPELINE_OPTIONS = {
|
|
17
19
|
maxPendingPackets: 4,
|
|
@@ -53,10 +55,11 @@ async function buildPipeline(parts) {
|
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* The GPU stitcher on the host canvas, behind the stabilizing sink when there is a gyro, behind
|
|
56
|
-
* gain matching, which measures what the stitcher drew.
|
|
58
|
+
* gain matching, which measures what the stitcher drew. A change of the view or the gains is
|
|
59
|
+
* drawn once at the next animation frame, where a frame presented in it draws it anyway.
|
|
57
60
|
*/
|
|
58
61
|
function drawingFor(parts, disposables) {
|
|
59
|
-
const renderer = ThreeFrameRenderer.create(parts.host.canvas, stitchingSetupOf(parts.opened));
|
|
62
|
+
const renderer = ThreeFrameRenderer.create(parts.host.canvas, stitchingSetupOf(parts.opened), { drawSchedule: new AnimationFrameDraws() });
|
|
60
63
|
disposables.add(() => {
|
|
61
64
|
renderer.dispose();
|
|
62
65
|
});
|
|
@@ -127,13 +130,16 @@ function stitchingSetupOf(opened) {
|
|
|
127
130
|
});
|
|
128
131
|
}
|
|
129
132
|
function sinkOver(renderer, opened) {
|
|
130
|
-
if (!opened.motion)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
if (!opened.motion) {
|
|
134
|
+
renderer.setStabilization(UPRIGHT_MOUNTING.toBody);
|
|
135
|
+
return {
|
|
136
|
+
sink: renderer,
|
|
137
|
+
stabilizing: void 0
|
|
138
|
+
};
|
|
139
|
+
}
|
|
134
140
|
const stabilizing = new StabilizingFrameSink({
|
|
135
141
|
sink: renderer,
|
|
136
|
-
|
|
142
|
+
motion: opened.motion,
|
|
137
143
|
frameTimes: opened.frameTimes
|
|
138
144
|
});
|
|
139
145
|
return {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
+
import { ANIMATION_FRAMES } from "../composition/animationFrames.js";
|
|
1
2
|
//#region ../../packages/player/src/player/FrameLoop.ts
|
|
2
|
-
var ANIMATION_FRAMES = {
|
|
3
|
-
request: (callback) => requestAnimationFrame(callback),
|
|
4
|
-
cancel: (handle) => {
|
|
5
|
-
cancelAnimationFrame(handle);
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
3
|
/**
|
|
9
4
|
* Calls `tick` once per animation frame between {@link start} and {@link stop}.
|
|
10
5
|
*/
|