@bubo-squared/gyroview 0.3.0 → 0.4.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 +49 -0
- package/README.md +20 -11
- package/dist/index.d.ts +56 -2
- package/dist/index.js +2 -2
- package/dist/packages/adapters/mediabunny/src/MediabunnyAudioPackager.js +49 -15
- package/dist/packages/adapters/mediabunny/src/MediabunnyCodecReader.js +3 -1
- package/dist/packages/adapters/mediabunny/src/decoderConfigurations.js +2 -2
- package/dist/packages/adapters/mediabunny/src/trackColour.js +21 -0
- package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +12 -1
- package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +3 -0
- package/dist/packages/adapters/three/src/conversionUniforms.js +61 -0
- package/dist/packages/adapters/three/src/glslLiterals.js +10 -0
- package/dist/packages/adapters/three/src/matrixCorrections.js +35 -0
- package/dist/packages/adapters/three/src/rendererUniforms.js +33 -12
- package/dist/packages/adapters/three/src/shaderPrograms.js +4 -2
- package/dist/packages/adapters/three/src/shaders/analysis.frag.js +1 -1
- package/dist/packages/adapters/three/src/shaders/displayConversion.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensModels.js +1 -1
- package/dist/packages/adapters/three/src/shaders/lensSampling.js +1 -1
- package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +1 -1
- package/dist/packages/adapters/three/src/shaders/stitch.frag.js +1 -1
- package/dist/packages/adapters/three/src/threeMatrix.js +10 -0
- package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +16 -5
- package/dist/packages/adapters/webcodecs/src/colorSpaceOf.js +32 -0
- package/dist/packages/core/src/application/playback/probeDecoding.js +2 -2
- package/dist/packages/core/src/application/recording/frameTimesOf.js +0 -1
- package/dist/packages/core/src/domain/colour/DisplayConversion.js +137 -0
- package/dist/packages/core/src/domain/colour/TrackColour.js +38 -0
- package/dist/packages/core/src/domain/colour/matrixCorrection.js +76 -0
- package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +7 -4
- package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +1 -1
- package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +1 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +109 -35
- package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +1 -0
- package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +33 -4
- package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +22 -17
- package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +28 -15
- package/dist/packages/core/src/domain/format/calibration/v6TermReading.js +20 -0
- package/dist/packages/core/src/domain/format/info/calibrationSources.js +27 -0
- package/dist/packages/core/src/domain/format/info/infoFields.js +8 -2
- package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +11 -6
- package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +17 -8
- package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +1 -3
- package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +0 -3
- package/dist/packages/core/src/domain/optics/MeiDistortion.js +43 -0
- package/dist/packages/core/src/domain/optics/MeiModel.js +6 -14
- package/dist/packages/core/src/domain/optics/scaledProjection.js +30 -0
- package/dist/packages/core/src/domain/stitching/StitchingSetup.js +13 -4
- package/dist/packages/core/src/index.js +7 -3
- package/dist/packages/core/src/shared/errors/GyroViewError.js +55 -3
- package/dist/packages/core/src/shared/index.js +11 -0
- package/dist/packages/core/src/shared/mapRecord.js +11 -0
- package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +12 -26
- package/dist/packages/core/src/shared/protobuf/wireFormat.js +16 -0
- package/dist/packages/player/src/composition/buildPipeline.js +2 -1
- package/dist/packages/player/src/composition/openInputs.js +4 -1
- package/dist/packages/player/src/controls/messages.js +2 -1
- package/dist/standalone.js +211 -100
- package/package.json +4 -4
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { ensureIndexInRange, ensureInvariant } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { MEI_TERM_CAPACITY } from "../../../core/src/domain/optics/MeiDistortion.js";
|
|
2
3
|
import { IDENTITY_MATRIX3 } from "../../../core/src/shared/math/Matrix3.js";
|
|
3
4
|
import { aspectOfArea, planeHalfExtentOf } from "../../../core/src/domain/view/rectilinear.js";
|
|
4
5
|
import "../../../core/src/index.js";
|
|
6
|
+
import { toThreeMatrix } from "./threeMatrix.js";
|
|
7
|
+
import { CONVERSION_DEFINES, conversionUniforms } from "./conversionUniforms.js";
|
|
5
8
|
import "./samplingStrategies.js";
|
|
6
9
|
import { Matrix3, Vector2, Vector3, Vector4 } from "three";
|
|
7
10
|
var LENS_MEI = 0;
|
|
@@ -14,6 +17,10 @@ var SHADER_DEFINES = Object.fromEntries([
|
|
|
14
17
|
["MAX_LENSES", 2],
|
|
15
18
|
["LENS_MEI", LENS_MEI],
|
|
16
19
|
["LENS_RADIAL_POLYNOMIAL", LENS_RADIAL_POLYNOMIAL],
|
|
20
|
+
["MEI_RADIAL_TERMS", MEI_TERM_CAPACITY.radial],
|
|
21
|
+
["MEI_TANGENTIAL_ORDERS", MEI_TERM_CAPACITY.tangential],
|
|
22
|
+
["MEI_THIN_PRISM_ORDERS", MEI_TERM_CAPACITY.thinPrism],
|
|
23
|
+
...CONVERSION_DEFINES,
|
|
17
24
|
["SAMPLING_BILINEAR", 0],
|
|
18
25
|
["SAMPLING_TRILINEAR", 1],
|
|
19
26
|
["SAMPLING_SUPERSAMPLED", 2]
|
|
@@ -24,6 +31,7 @@ function createRendererUniforms(setup, textures) {
|
|
|
24
31
|
...viewUniforms(setup),
|
|
25
32
|
...projectionUniforms(lenses),
|
|
26
33
|
...samplingUniforms(lenses),
|
|
34
|
+
...conversionUniforms(lenses),
|
|
27
35
|
uTexture0: { value: textures[0] ?? null },
|
|
28
36
|
uTexture1: { value: textures[1] ?? null }
|
|
29
37
|
};
|
|
@@ -47,12 +55,18 @@ function projectionUniforms(lenses) {
|
|
|
47
55
|
uLensPrincipalPoint: { value: lenses.map((lens) => toVector2(lens.projection.principalPoint)) },
|
|
48
56
|
uLensFocal: { value: slots.map((slot) => slot.focal) },
|
|
49
57
|
uLensXi: { value: slots.map((slot) => slot.xi) },
|
|
50
|
-
uLensRadial: { value: slots.
|
|
51
|
-
uLensTangential: { value: slots.
|
|
58
|
+
uLensRadial: { value: slots.flatMap((slot) => slot.radial) },
|
|
59
|
+
uLensTangential: { value: slots.flatMap((slot) => slot.tangential) },
|
|
60
|
+
uLensThinPrism: { value: slots.flatMap((slot) => slot.thinPrism) },
|
|
52
61
|
uLensPolynomial: { value: slots.map((slot) => slot.polynomial) },
|
|
53
62
|
uLensHalfFov: { value: lenses.map((lens) => lens.halfFieldOfView) }
|
|
54
63
|
};
|
|
55
64
|
}
|
|
65
|
+
var NO_MEI_DISTORTION = {
|
|
66
|
+
radial: [],
|
|
67
|
+
tangential: [],
|
|
68
|
+
thinPrism: []
|
|
69
|
+
};
|
|
56
70
|
/**
|
|
57
71
|
* Exhaustive over the projection kinds: a new kind does not compile until it is packed.
|
|
58
72
|
*/
|
|
@@ -62,20 +76,33 @@ function slotOf(projection) {
|
|
|
62
76
|
kind: LENS_MEI,
|
|
63
77
|
focal: new Vector2(...projection.focal),
|
|
64
78
|
xi: projection.xi,
|
|
65
|
-
|
|
66
|
-
tangential: new Vector2(...projection.tangential),
|
|
79
|
+
...meiTermSlots(projection.distortion),
|
|
67
80
|
polynomial: new Vector4()
|
|
68
81
|
};
|
|
69
82
|
case "radial-polynomial": return {
|
|
70
83
|
kind: LENS_RADIAL_POLYNOMIAL,
|
|
71
84
|
focal: new Vector2(),
|
|
72
85
|
xi: 0,
|
|
73
|
-
|
|
74
|
-
tangential: new Vector2(),
|
|
86
|
+
...meiTermSlots(NO_MEI_DISTORTION),
|
|
75
87
|
polynomial: new Vector4(...projection.coefficients)
|
|
76
88
|
};
|
|
77
89
|
}
|
|
78
90
|
}
|
|
91
|
+
function meiTermSlots(distortion) {
|
|
92
|
+
return {
|
|
93
|
+
radial: fittedToCapacity(distortion.radial, MEI_TERM_CAPACITY.radial, () => 0),
|
|
94
|
+
tangential: fittedToCapacity(distortion.tangential.map((pair) => new Vector2(pair.p1, pair.p2)), MEI_TERM_CAPACITY.tangential, () => new Vector2()),
|
|
95
|
+
thinPrism: fittedToCapacity(distortion.thinPrism.map((term) => new Vector2(term.x, term.y)), MEI_TERM_CAPACITY.thinPrism, () => new Vector2())
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The terms in the room the shader holds for their family, the rest of it zero; more terms than
|
|
100
|
+
* it holds are a defect of the model that made them.
|
|
101
|
+
*/
|
|
102
|
+
function fittedToCapacity(terms, capacity, zero) {
|
|
103
|
+
ensureInvariant(terms.length <= capacity, `a Mei lens has ${terms.length} terms of a family the shader holds ${capacity} of`);
|
|
104
|
+
return Array.from({ length: capacity }, (_unused, index) => terms[index] ?? zero());
|
|
105
|
+
}
|
|
79
106
|
function samplingUniforms(lenses) {
|
|
80
107
|
return {
|
|
81
108
|
uLensWindow: { value: lenses.map((lens) => new Vector4(lens.window.x, lens.window.y, lens.window.width, lens.window.height)) },
|
|
@@ -136,12 +163,6 @@ function valuesOf(picture, viewportAspect) {
|
|
|
136
163
|
function toVector4(area) {
|
|
137
164
|
return new Vector4(area.x, area.y, area.width, area.height);
|
|
138
165
|
}
|
|
139
|
-
/**
|
|
140
|
-
* Row-major core matrix into a three matrix, whose `set` takes rows too.
|
|
141
|
-
*/
|
|
142
|
-
function toThreeMatrix(m) {
|
|
143
|
-
return new Matrix3().set(...m);
|
|
144
|
-
}
|
|
145
166
|
function toVector2(point) {
|
|
146
167
|
return new Vector2(point.x, point.y);
|
|
147
168
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import analysis_frag_default from "./shaders/analysis.frag.js";
|
|
2
|
+
import displayConversion_default from "./shaders/displayConversion.js";
|
|
2
3
|
import equirectangularRays_default from "./shaders/equirectangularRays.js";
|
|
3
4
|
import fixedJoin_default from "./shaders/fixedJoin.js";
|
|
4
5
|
import header_default from "./shaders/header.js";
|
|
@@ -14,8 +15,9 @@ import stitch_frag_default from "./shaders/stitch.frag.js";
|
|
|
14
15
|
* The fragment programs, each the GLSL chunks it is made of in the order they must appear: a
|
|
15
16
|
* chunk comes after every chunk whose declarations it uses. The only place that order is known.
|
|
16
17
|
*/
|
|
18
|
+
var LENS_TEXELS = [lensTextures_default, displayConversion_default];
|
|
17
19
|
var LENS_PROJECTION = [
|
|
18
|
-
|
|
20
|
+
...LENS_TEXELS,
|
|
19
21
|
lensModels_default,
|
|
20
22
|
lensSampling_default
|
|
21
23
|
];
|
|
@@ -48,7 +50,7 @@ function pictureProgramsWith(seamJoin) {
|
|
|
48
50
|
"lens-tiles": [
|
|
49
51
|
header_default,
|
|
50
52
|
screenAreas_default,
|
|
51
|
-
|
|
53
|
+
...LENS_TEXELS,
|
|
52
54
|
rawLenses_frag_default
|
|
53
55
|
]
|
|
54
56
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region ../../packages/adapters/three/src/shaders/analysis.frag.glsl?raw
|
|
2
|
-
var analysis_frag_default = "in vec2 vNdc;\nout vec4 outColor;\n\n// One row per lens: what that lens sees along the seam ring, the circle of directions halfway\n// through the blend band around lens 0's optical axis, sampled around the ring along x. Rows\n// share their directions, so their means differ by exposure (and parallax), not by content
|
|
2
|
+
var analysis_frag_default = "in vec2 vNdc;\nout vec4 outColor;\n\n// One row per lens: what that lens sees along the seam ring, the circle of directions halfway\n// through the blend band around lens 0's optical axis, sampled around the ring along x. Rows\n// share their directions, so their means differ by exposure (and parallax), not by content:\n// exposure signals, in which it is a factor (ADR 0033). Alpha marks the texels a lens images.\nvoid main() {\n int lens = int(floor((vNdc.y + 1.0) * 0.5 * float(MAX_LENSES)));\n float phi = (vNdc.x + 1.0) * PI;\n float ring = 0.5 * (uFeather.x + uFeather.y);\n vec3 inLensZero = vec3(sin(ring) * cos(phi), sin(ring) * sin(phi), cos(ring));\n // The lens rotations take body directions into each lens; the transpose goes back.\n vec3 dirBody = transpose(uLensRotation[0]) * inLensZero;\n if (lens >= uLensCount) {\n outColor = vec4(0.0);\n return;\n }\n LensSample seen = sampleLensWith(lens, dirBody, SAMPLING_BILINEAR);\n outColor = seen.isImaged ? vec4(seen.signal, 1.0) : vec4(0.0);\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { analysis_frag_default as default };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
//#region ../../packages/adapters/three/src/shaders/displayConversion.glsl?raw
|
|
2
|
+
var displayConversion_default = "// How lens i's texels are brought to the SDR BT.709 display, as the core's exposureSignalOf and\n// shownOf (ADR 0033): the browser's upload has applied the track's matrix and range; the\n// transfer and primaries are this chunk's. Shown as recorded, or HLG converted to scene light,\n// into BT.709 and toned; gain matching scales the exposure signal between the two (ADR 0012).\nuniform int uLensConversion[MAX_LENSES];\n// R′G′B′ through the matrix a frame names back to the track's own (matrixCorrectionOf).\nuniform mat3 uLensMatrixCorrection[MAX_LENSES];\nuniform mat3 uLensGamut[MAX_LENSES];\n// Exposure, roll-off start, roll-off ceiling, display encoding power.\nuniform vec4 uLensTone[MAX_LENSES];\n\nstruct ToneCurve {\n float exposure;\n float kneeStart;\n float ceiling;\n float exponent;\n};\n\nToneCurve toneOf(int i) {\n vec4 tone = uLensTone[i];\n return ToneCurve(tone.x, tone.y, tone.z, tone.w);\n}\n\nfloat hlgInverseOetf(float signal) {\n return signal <= HLG_SEGMENT_JOIN\n ? signal * signal / HLG_SQUARE_SEGMENT_DIVISOR\n : (exp((signal - HLG_C) / HLG_A) + HLG_B) / HLG_LOG_SEGMENT_DIVISOR;\n}\n\nconst vec3 BT709_LUMINANCE = vec3(BT709_LUMINANCE_RED, BT709_LUMINANCE_GREEN, BT709_LUMINANCE_BLUE);\n\nfloat rolledOff(float luminance, ToneCurve tone) {\n float over = luminance - tone.kneeStart;\n return over <= 0.0\n ? luminance\n : tone.kneeStart + over / (1.0 + over / (tone.ceiling - tone.kneeStart));\n}\n\n// The signal exposed back to light, its luminance rolled off with every channel scaled alike,\n// and encoded for the display.\nvec3 tonedFrom(vec3 signal, ToneCurve tone) {\n vec3 light = tone.exposure * pow(signal, vec3(tone.exponent));\n float luminance = dot(light, BT709_LUMINANCE);\n float scale = luminance > 0.0 ? rolledOff(luminance, tone) / luminance : 1.0;\n return min(pow(light * scale, vec3(1.0 / tone.exponent)), vec3(1.0));\n}\n\nbool isHlg(int i) {\n return uLensConversion[i] == CONVERSION_HLG_TO_SDR_BT709;\n}\n\n// Lens i's texel where its exposure is a factor; as recorded for any kind but HLG.\nvec3 exposureSignalOf(int i, vec3 texel) {\n vec3 recorded = uLensMatrixCorrection[i] * texel;\n if (!isHlg(i)) return recorded;\n // A corrected texel may fall just below black, which the square segment would make light.\n recorded = max(recorded, 0.0);\n vec3 scene = vec3(\n hlgInverseOetf(recorded.r),\n hlgInverseOetf(recorded.g),\n hlgInverseOetf(recorded.b)\n );\n vec3 light = max(uLensGamut[i] * scene, 0.0);\n return pow(light, vec3(1.0 / toneOf(i).exponent));\n}\n\n// An exposure signal of lens i as the display shows it; as recorded for any kind but HLG.\nvec3 shownOf(int i, vec3 signal) {\n if (!isHlg(i)) return signal;\n return tonedFrom(signal, toneOf(i));\n}\n\nvec3 toDisplay(int i, vec3 texel) {\n return shownOf(i, exposureSignalOf(i, texel));\n}\n";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { displayConversion_default as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region ../../packages/adapters/three/src/shaders/lensModels.glsl?raw
|
|
2
|
-
var lensModels_default = "// The depth is positive for every direction inside a lens's field; directions behind it are\n// projected too, for the footprint's derivatives, and must not divide by zero.\nconst float MIN_DEPTH = 1e-3;\n\
|
|
2
|
+
var lensModels_default = "// The depth is positive for every direction inside a lens's field; directions behind it are\n// projected too, for the footprint's derivatives, and must not divide by zero.\nconst float MIN_DEPTH = 1e-3;\n\nuniform vec2 uLensPrincipalPoint[MAX_LENSES];\nuniform vec2 uLensFocal[MAX_LENSES];\nuniform float uLensXi[MAX_LENSES];\n// Each Mei lens's distortion as the core's MeiDistortion holds it, lens after lens, as many terms\n// per lens as each family holds room for, by increasing order: radial terms k1, k2, ... of r²,\n// Brown's tangential pairs (p1, p2) scaled by r^(2j), and thin-prism shifts scaled by r^(2j + 2).\n// The orders a lens does not fill are zero.\nuniform float uLensRadial[MAX_LENSES * MEI_RADIAL_TERMS];\nuniform vec2 uLensTangential[MAX_LENSES * MEI_TANGENTIAL_ORDERS];\nuniform vec2 uLensThinPrism[MAX_LENSES * MEI_THIN_PRISM_ORDERS];\nuniform vec4 uLensPolynomial[MAX_LENSES];\n\n// c0 + c1 r² + c2 r⁴ + ... of each of lens i's families, by Horner's rule, as the core's seriesIn.\nfloat radialSeries(int i, float r2) {\n float sum = 0.0;\n for (int j = MEI_RADIAL_TERMS - 1; j >= 0; j--) {\n sum = sum * r2 + uLensRadial[i * MEI_RADIAL_TERMS + j];\n }\n return sum;\n}\n\nvec2 tangentialSeries(int i, float r2) {\n vec2 sum = vec2(0.0);\n for (int j = MEI_TANGENTIAL_ORDERS - 1; j >= 0; j--) {\n sum = sum * r2 + uLensTangential[i * MEI_TANGENTIAL_ORDERS + j];\n }\n return sum;\n}\n\nvec2 thinPrismSeries(int i, float r2) {\n vec2 sum = vec2(0.0);\n for (int j = MEI_THIN_PRISM_ORDERS - 1; j >= 0; j--) {\n sum = sum * r2 + uLensThinPrism[i * MEI_THIN_PRISM_ORDERS + j];\n }\n return sum;\n}\n\n// A normalised image-plane point distorted by lens i, as the core's distortMei.\nvec2 distortMei(int i, vec2 m) {\n float r2 = dot(m, m);\n float radialFactor = 1.0 + r2 * radialSeries(i, r2);\n vec2 decentering = tangentialSeries(i, r2);\n vec2 tangentialTerm = vec2(\n 2.0 * decentering.x * m.x * m.y + decentering.y * (r2 + 2.0 * m.x * m.x),\n decentering.x * (r2 + 2.0 * m.y * m.y) + 2.0 * decentering.y * m.x * m.y\n );\n return radialFactor * m + tangentialTerm + r2 * thinPrismSeries(i, r2);\n}\n\n// Canvas pixel of a unit direction in lens i's frame under the unified (Mei) model: project onto\n// the unit sphere, then from xi behind its centre onto the image plane, distort, scale.\nvec2 projectMei(int i, vec3 d) {\n float depth = max(d.z + uLensXi[i], MIN_DEPTH);\n return uLensFocal[i] * distortMei(i, d.xy / depth) + uLensPrincipalPoint[i];\n}\n\n// Canvas pixel of a unit direction under lens i's radial polynomial in the angle from its axis.\nvec2 projectRadialPolynomial(int i, vec3 d, float theta) {\n vec4 c = uLensPolynomial[i];\n float radius = theta * (c.x + theta * (c.y + theta * (c.z + theta * c.w)));\n float lateral = length(d.xy);\n vec2 unit = lateral > 0.0 ? d.xy / lateral : vec2(0.0);\n return uLensPrincipalPoint[i] + radius * unit;\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { lensModels_default as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region ../../packages/adapters/three/src/shaders/lensSampling.glsl?raw
|
|
2
|
-
var lensSampling_default = "// How a body direction becomes a texel of one lens, for the stitch and the seam meters: the\n//
|
|
2
|
+
var lensSampling_default = "// How a body direction becomes a texel of one lens, for the stitch and the seam meters: the\n// pose and field as uniforms, and the sampling through each lens model, the texel as its\n// exposure signal. Needs lensTextures.glsl, displayConversion.glsl and lensModels.glsl.\nuniform vec2 uFeather;\nuniform mat3 uLensRotation[MAX_LENSES];\nuniform int uLensKind[MAX_LENSES];\nuniform float uLensHalfFov[MAX_LENSES];\nuniform vec4 uLensWindow[MAX_LENSES];\n\nstruct LensSample {\n bool isImaged;\n // Angle from the lens's optical axis.\n float theta;\n // The texel where the lens's exposure is a factor (displayConversion.glsl).\n vec3 signal;\n};\n\n// Where lens i images direction d on its calibration canvas; the principal point, and not\n// known, for a kind of lens this shader does not know.\nvec2 canvasPixel(int i, vec3 d, float theta, out bool isKnown) {\n isKnown = true;\n if (uLensKind[i] == LENS_MEI) {\n return projectMei(i, d);\n }\n if (uLensKind[i] == LENS_RADIAL_POLYNOMIAL) {\n return projectRadialPolynomial(i, d, theta);\n }\n isKnown = false;\n return uLensPrincipalPoint[i];\n}\n\n// What lens i shows in a body direction, read as `sampling` says, if it images it at all:\n// outside its field, or beyond the window the frame shows, nothing. The footprint is taken in\n// every fragment before the gates, so its derivatives are defined.\nLensSample sampleLensWith(int i, vec3 dirBody, int sampling) {\n vec3 d = uLensRotation[i] * dirBody;\n // atan keeps its precision near the axis where acos loses it.\n float theta = atan(length(d.xy), d.z);\n bool isKnown;\n vec2 pixel = canvasPixel(i, d, theta, isKnown);\n vec2 windowUv = (pixel - uLensWindow[i].xy) / uLensWindow[i].zw;\n Footprint footprint = footprintOf(i, windowUv);\n bool isInWindow =\n all(greaterThanEqual(windowUv, vec2(0.0))) && all(lessThanEqual(windowUv, vec2(1.0)));\n LensSample result;\n result.theta = theta;\n result.isImaged = isKnown && theta < uLensHalfFov[i] && isInWindow;\n result.signal = result.isImaged\n ? exposureSignalOf(i, sampleLens(uLensTexture[i], footprint, sampling).rgb)\n : vec3(0.0);\n return result;\n}\n\n// What lens i shows in a body direction, read as the picture is.\nLensSample sampleLensAt(int i, vec3 dirBody) {\n return sampleLensWith(i, dirBody, uSampling);\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { lensSampling_default as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region ../../packages/adapters/three/src/shaders/rawLenses.frag.glsl?raw
|
|
2
|
-
var rawLenses_frag_default = "// The decoded lens images on their own, unstitched: each lens's region of its frame
|
|
2
|
+
var rawLenses_frag_default = "// The decoded lens images on their own, unstitched: each lens's region of its frame as the\n// decoder delivered it, brought to the display (ADR 0033), in the lens's screen area, with no\n// pose, stabilization or gain. Black around the areas. Every pixel reads every tile, and the area\n// picks one without a branch: a read inside a branch that some pixels of a 2x2 quad skip took a\n// far too coarse footprint on Apple GPUs (Chrome through Metal), a grey line along a tile's edge\n// wherever the edge fell inside a quad.\nin vec2 vNdc;\nout vec4 outColor;\n\nvoid main() {\n vec3 color = vec3(0.0);\n for (int i = 0; i < MAX_LENSES; i++) {\n if (i >= uLensCount) break;\n vec2 point = pointInArea(vNdc, uScreenArea[i]);\n Footprint footprint = footprintOf(i, point);\n vec3 sampled = toDisplay(i, sampleLens(uLensTexture[i], footprint, uSampling).rgb);\n color = mix(color, sampled, float(isInArea(point)));\n }\n outColor = vec4(color, 1.0);\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { rawLenses_frag_default as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region ../../packages/adapters/three/src/shaders/stitch.frag.glsl?raw
|
|
2
|
-
var stitch_frag_default = "// The stitched sphere through the picture's rays: each lens's texel for the ray, read and weighted\n// across the feather band as the seam join says, and matched in exposure. Every pixel samples
|
|
2
|
+
var stitch_frag_default = "// The stitched sphere through the picture's rays: each lens's texel for the ray, read and weighted\n// across the feather band as the seam join says, and matched in exposure. Every pixel samples,\n// even outside the picture's area, so the footprints' derivatives are defined; the area decides\n// what shows.\nuniform mat3 uViewRotation;\nuniform mat3 uStabilization;\nuniform vec3 uLensGain[MAX_LENSES];\n\nin vec2 vNdc;\nout vec4 outColor;\n\nvoid main() {\n vec2 point = pointInArea(vNdc, uScreenArea[0]);\n vec3 dirView = rayThroughPicture(point);\n vec3 dirBody = uStabilization * (uViewRotation * dirView);\n vec3 sum = vec3(0.0);\n float weightSum = 0.0;\n float disparity = disparityAt(dirBody);\n vec2 feather = seamFeather(disparity);\n for (int i = 0; i < MAX_LENSES; i++) {\n if (i >= uLensCount) break;\n // A lens silenced through its gain (ADR 0012) leaves the blend altogether, so the other\n // lens fills the feather band alone and a lens-only render shows that lens as it is.\n if (all(equal(uLensGain[i], vec3(0.0)))) continue;\n LensSample lens = sampleLensAt(i, seamReadDirection(i, dirBody, disparity));\n if (!lens.isImaged) continue;\n // Weighed by the angle from the lens's axis where it is read, which a seam join may move.\n float weight = 1.0 - smoothstep(feather.x, feather.y, lens.theta);\n // Matched in exposure where exposure is a factor, then brought to the display (ADR 0033).\n sum += weight * shownOf(i, uLensGain[i] * lens.signal);\n weightSum += weight;\n }\n bool isShown = isInArea(point) && weightSum > 0.0;\n outColor = isShown ? vec4(sum / weightSum, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { stitch_frag_default as default };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Matrix3 } from "three";
|
|
2
|
+
//#region ../../packages/adapters/three/src/threeMatrix.ts
|
|
3
|
+
/**
|
|
4
|
+
* Row-major core matrix into a three matrix, whose `set` takes rows too.
|
|
5
|
+
*/
|
|
6
|
+
function toThreeMatrix(m) {
|
|
7
|
+
return new Matrix3().set(...m);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { toThreeMatrix };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GyroViewError, asGyroViewError, messageOf } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
2
|
import { microseconds, microsecondsToSeconds, secondsToMicroseconds } from "../../../core/src/shared/units/time.js";
|
|
3
3
|
import "../../../core/src/index.js";
|
|
4
|
+
import { colorSpaceOf } from "./colorSpaceOf.js";
|
|
4
5
|
//#region ../../packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.ts
|
|
5
6
|
/**
|
|
6
7
|
* A hard `prefer-hardware` refuses codecs the browser could decode in software (H.264 recordings
|
|
@@ -15,10 +16,11 @@ var HARDWARE_ACCELERATION = "no-preference";
|
|
|
15
16
|
*/
|
|
16
17
|
var WebCodecsVideoDecoderPort = class {
|
|
17
18
|
/**
|
|
18
|
-
* A configuration WebCodecs finds malformed is one it does not support.
|
|
19
|
+
* A configuration WebCodecs finds malformed is one it does not support. A browser without
|
|
20
|
+
* WebCodecs refuses to answer, since no configuration would do.
|
|
19
21
|
*/
|
|
20
22
|
async isSupported(configuration) {
|
|
21
|
-
if (typeof VideoDecoder === "undefined")
|
|
23
|
+
if (typeof VideoDecoder === "undefined") throw webCodecsUnavailable();
|
|
22
24
|
try {
|
|
23
25
|
return (await VideoDecoder.isConfigSupported(this.toWebCodecsConfig(configuration))).supported === true;
|
|
24
26
|
} catch {
|
|
@@ -33,17 +35,18 @@ var WebCodecsVideoDecoderPort = class {
|
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
configureDecoder(configuration, callbacks) {
|
|
36
|
-
if (typeof VideoDecoder === "undefined") throw
|
|
38
|
+
if (typeof VideoDecoder === "undefined") throw webCodecsUnavailable();
|
|
37
39
|
return new WebCodecsDecoderHandle(this.toWebCodecsConfig(configuration), callbacks);
|
|
38
40
|
}
|
|
39
41
|
toWebCodecsConfig(configuration) {
|
|
40
|
-
const { description
|
|
42
|
+
const { description } = configuration;
|
|
43
|
+
const colorSpace = colorSpaceOf(configuration.colour);
|
|
41
44
|
return {
|
|
42
45
|
codec: configuration.codec,
|
|
43
46
|
codedWidth: configuration.codedWidth,
|
|
44
47
|
codedHeight: configuration.codedHeight,
|
|
45
48
|
...description && { description },
|
|
46
|
-
...
|
|
49
|
+
...colorSpace && { colorSpace },
|
|
47
50
|
hardwareAcceleration: HARDWARE_ACCELERATION,
|
|
48
51
|
optimizeForLatency: false
|
|
49
52
|
};
|
|
@@ -114,6 +117,14 @@ var WebCodecsDecoderHandle = class {
|
|
|
114
117
|
if (this.decoder.state !== "closed") this.decoder.close();
|
|
115
118
|
}
|
|
116
119
|
};
|
|
120
|
+
/**
|
|
121
|
+
* Browsers expose WebCodecs only in secure contexts, so a page served over plain HTTP is the
|
|
122
|
+
* likelier cause than an old browser, and the one its developer can fix.
|
|
123
|
+
*/
|
|
124
|
+
function webCodecsUnavailable() {
|
|
125
|
+
const reason = globalThis.isSecureContext ? "this browser has no WebCodecs VideoDecoder" : "WebCodecs exists only in secure contexts (HTTPS), and this page is not one";
|
|
126
|
+
return new GyroViewError("webcodecs-unavailable", reason);
|
|
127
|
+
}
|
|
117
128
|
function chunkOf(packet) {
|
|
118
129
|
return new EncodedVideoChunk({
|
|
119
130
|
type: packet.isKeyFrame ? "key" : "delta",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region ../../packages/adapters/webcodecs/src/colorSpaceOf.ts
|
|
2
|
+
/**
|
|
3
|
+
* A track's colour as a decoder configuration's colour space: every value the track names, none
|
|
4
|
+
* it leaves unspecified. WebCodecs gives the frames the configured colour space whole ("Output
|
|
5
|
+
* VideoFrames"), so it says all the bitstream says, and the frames carry the colour the display
|
|
6
|
+
* conversion was chosen for (ADR 0033). An engine whose WebCodecs does not know one of the values
|
|
7
|
+
* would refuse the whole configuration, so it is told the range alone, as before. Undefined for a
|
|
8
|
+
* track that names nothing.
|
|
9
|
+
*/
|
|
10
|
+
function colorSpaceOf(colour) {
|
|
11
|
+
const range = colour.range === "unspecified" ? void 0 : { fullRange: colour.range === "full" };
|
|
12
|
+
const named = {
|
|
13
|
+
...colour.primaries !== "unspecified" && { primaries: colour.primaries },
|
|
14
|
+
...colour.transfer !== "unspecified" && { transfer: colour.transfer },
|
|
15
|
+
...colour.matrix !== "unspecified" && { matrix: colour.matrix },
|
|
16
|
+
...range
|
|
17
|
+
};
|
|
18
|
+
return Object.keys(named).length === 0 ? void 0 : understoodHere(named) ?? range;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The colour space as this engine reads it, undefined where it names a value the engine does not
|
|
22
|
+
* know.
|
|
23
|
+
*/
|
|
24
|
+
function understoodHere(colorSpace) {
|
|
25
|
+
try {
|
|
26
|
+
return new VideoColorSpace(colorSpace).toJSON();
|
|
27
|
+
} catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { colorSpaceOf };
|
|
@@ -28,8 +28,8 @@ var KEY_FRAME_LATE = {
|
|
|
28
28
|
* a deadline. The host resolves `deadline` once the probe has taken too long (the core has no
|
|
29
29
|
* timers); sources still undecided then report `timed-out`, or `key-frame-late` while their key
|
|
30
30
|
* frame was still being read, and their decoders are closed. A track that cannot be read rejects
|
|
31
|
-
* the probe with its own failure,
|
|
32
|
-
* deadline.
|
|
31
|
+
* the probe with its own failure, as a platform without decoders does, and the other sources'
|
|
32
|
+
* decoders are closed then, not at the deadline.
|
|
33
33
|
*/
|
|
34
34
|
async function probeDecoding(frameSources, decoderPort, deadline) {
|
|
35
35
|
const probes = frameSources.map((track) => new SourceProbe(track, decoderPort));
|
|
@@ -13,7 +13,6 @@ async function frameTimesOf(recording, frameSource, timeline) {
|
|
|
13
13
|
...timeline,
|
|
14
14
|
frameCount,
|
|
15
15
|
frameRate: info.frameRate,
|
|
16
|
-
readoutTime: info.readoutTime,
|
|
17
16
|
exposureRecord
|
|
18
17
|
}, info.preferredFrameTimeSource);
|
|
19
18
|
return plan.needsTrackTimestamps ? plan.resolveWith(await frameSource.sampleTimestamps()) : plan.resolved;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { IDENTITY_MATRIX3 } from "../../shared/math/Matrix3.js";
|
|
2
|
+
//#region ../../packages/core/src/domain/colour/DisplayConversion.ts
|
|
3
|
+
/**
|
|
4
|
+
* BT.2100 HLG's inverse OETF (ITU-R BT.2100-2, table 5): `E'² / 3` up to half signal, then
|
|
5
|
+
* `(exp((E' - c) / a) + b) / 12`, with a, b (1 - 4a) and c; the shader's defines come from here.
|
|
6
|
+
*/
|
|
7
|
+
var HLG_OETF = {
|
|
8
|
+
a: .17883277,
|
|
9
|
+
b: .28466892,
|
|
10
|
+
c: .55991073,
|
|
11
|
+
segmentJoin: .5,
|
|
12
|
+
squareSegmentDivisor: 3,
|
|
13
|
+
logSegmentDivisor: 12
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Linear BT.2020 to linear BT.709 RGB (ITU-R BT.2087-0, equation 5), to seven decimals as the two
|
|
17
|
+
* sets of primaries and their shared D65 white derive it, so each row sums to one and greys stay
|
|
18
|
+
* grey.
|
|
19
|
+
*/
|
|
20
|
+
var BT2020_TO_BT709 = [
|
|
21
|
+
1.660491,
|
|
22
|
+
-.5876411,
|
|
23
|
+
-.0728499,
|
|
24
|
+
-.1245505,
|
|
25
|
+
1.1328999,
|
|
26
|
+
-.0083494,
|
|
27
|
+
-.0181508,
|
|
28
|
+
-.1005789,
|
|
29
|
+
1.1187297
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* BT.709's luminance of linear red, green and blue (ITU-R BT.709-6, item 3.2): what the highlight
|
|
33
|
+
* roll-off acts on, so a colour keeps its hue as it rolls off.
|
|
34
|
+
*/
|
|
35
|
+
var BT709_LUMINANCE = [
|
|
36
|
+
.2126,
|
|
37
|
+
.7152,
|
|
38
|
+
.0722
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Insta360 Studio's own HLG-to-SDR curve, fitted pixel by pixel to the X6's HLG and Rec.709
|
|
42
|
+
* exports of the same stitch: scene light, no OOTF, brought up by two fifths, its luminance
|
|
43
|
+
* rolled off above 0.2 and encoded with a power of 1/2.2 (ADR 0033).
|
|
44
|
+
*/
|
|
45
|
+
var STUDIO_TONE = {
|
|
46
|
+
exposure: 1.4,
|
|
47
|
+
kneeStart: .2,
|
|
48
|
+
ceiling: 1.5,
|
|
49
|
+
exponent: 2.2
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The primaries SDR is shown as recorded with: BT.709's, and the SD primaries close to them; a
|
|
53
|
+
* track that names none is taken for BT.709.
|
|
54
|
+
*/
|
|
55
|
+
var BT709_LIKE_PRIMARIES = /* @__PURE__ */ new Set([
|
|
56
|
+
"bt709",
|
|
57
|
+
"bt470bg",
|
|
58
|
+
"smpte170m",
|
|
59
|
+
"unspecified"
|
|
60
|
+
]);
|
|
61
|
+
/**
|
|
62
|
+
* The matrix that brings HLG's linear light into BT.709, by the track's primaries: none for
|
|
63
|
+
* BT.709, BT.2087's for BT.2020, which BT.2100 means when a track names none. Undefined for
|
|
64
|
+
* primaries HLG is not recorded with, whose gamut is left as it is.
|
|
65
|
+
*/
|
|
66
|
+
var HLG_GAMUT_BY_PRIMARIES = {
|
|
67
|
+
bt709: IDENTITY_MATRIX3,
|
|
68
|
+
bt2020: BT2020_TO_BT709,
|
|
69
|
+
unspecified: BT2020_TO_BT709,
|
|
70
|
+
bt470bg: void 0,
|
|
71
|
+
smpte170m: void 0,
|
|
72
|
+
smpte432: void 0
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A track's texels shown as recorded, its own matrix still corrected back to: what the player
|
|
76
|
+
* draws of SDR, and the measurements of a recording drawn in its own signal.
|
|
77
|
+
*/
|
|
78
|
+
function asRecordedOf({ matrix }) {
|
|
79
|
+
return {
|
|
80
|
+
kind: "as-recorded",
|
|
81
|
+
matrix
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function asRecordedFrom(colour) {
|
|
85
|
+
const conversion = asRecordedOf(colour);
|
|
86
|
+
return BT709_LIKE_PRIMARIES.has(colour.primaries) ? { conversion } : {
|
|
87
|
+
conversion,
|
|
88
|
+
problem: `${colour.primaries} primaries are shown as BT.709`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function hlgFrom(colour) {
|
|
92
|
+
const gamut = HLG_GAMUT_BY_PRIMARIES[colour.primaries];
|
|
93
|
+
const conversion = {
|
|
94
|
+
kind: "hlg-to-sdr-bt709",
|
|
95
|
+
matrix: colour.matrix,
|
|
96
|
+
gamut: gamut ?? IDENTITY_MATRIX3,
|
|
97
|
+
tone: STUDIO_TONE
|
|
98
|
+
};
|
|
99
|
+
return gamut === void 0 ? {
|
|
100
|
+
conversion,
|
|
101
|
+
problem: `HLG of ${colour.primaries} primaries is shown without their gamut`
|
|
102
|
+
} : { conversion };
|
|
103
|
+
}
|
|
104
|
+
function notShown(transfer) {
|
|
105
|
+
return (colour) => ({
|
|
106
|
+
conversion: asRecordedOf(colour),
|
|
107
|
+
problem: `the ${transfer} transfer cannot be shown yet: drawn as recorded`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The conversion of every transfer, from the track's colour: a transfer the player learns to
|
|
112
|
+
* name does not compile until it is given one.
|
|
113
|
+
*/
|
|
114
|
+
var CONVERSION_BY_TRANSFER = {
|
|
115
|
+
bt709: asRecordedFrom,
|
|
116
|
+
smpte170m: asRecordedFrom,
|
|
117
|
+
"iec61966-2-1": asRecordedFrom,
|
|
118
|
+
unspecified: asRecordedFrom,
|
|
119
|
+
hlg: hlgFrom,
|
|
120
|
+
pq: notShown("pq"),
|
|
121
|
+
linear: notShown("linear")
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Picks a conversion for each track's colour; a colour no conversion shows is drawn as recorded,
|
|
125
|
+
* with a warning, rather than refused: the picture is there, its colours wrong. Tracks that share
|
|
126
|
+
* a problem share its warning.
|
|
127
|
+
*/
|
|
128
|
+
function displayConversionsOf(colours) {
|
|
129
|
+
const choices = colours.map((colour) => CONVERSION_BY_TRANSFER[colour.transfer](colour));
|
|
130
|
+
const problems = choices.flatMap((choice) => choice.problem === void 0 ? [] : [choice.problem]);
|
|
131
|
+
return {
|
|
132
|
+
conversions: choices.map((choice) => choice.conversion),
|
|
133
|
+
warnings: [...new Set(problems)]
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { BT709_LUMINANCE, HLG_OETF, asRecordedOf, displayConversionsOf };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region ../../packages/core/src/domain/colour/TrackColour.ts
|
|
2
|
+
/**
|
|
3
|
+
* The values a video track's colour can name, as its bitstream says: the HEVC or AVC VUI, or the
|
|
4
|
+
* sample entry's `colr` box. Spelled as WebCodecs spells `VideoColorSpaceInit`, so an adapter
|
|
5
|
+
* passes values through; `unspecified` where the track says nothing, or something the player
|
|
6
|
+
* does not know.
|
|
7
|
+
*/
|
|
8
|
+
var COLOUR_PRIMARIES = [
|
|
9
|
+
"bt709",
|
|
10
|
+
"bt470bg",
|
|
11
|
+
"smpte170m",
|
|
12
|
+
"bt2020",
|
|
13
|
+
"smpte432"
|
|
14
|
+
];
|
|
15
|
+
var TRANSFER_CHARACTERISTICS = [
|
|
16
|
+
"bt709",
|
|
17
|
+
"smpte170m",
|
|
18
|
+
"iec61966-2-1",
|
|
19
|
+
"linear",
|
|
20
|
+
"pq",
|
|
21
|
+
"hlg"
|
|
22
|
+
];
|
|
23
|
+
var MATRIX_COEFFICIENTS = [
|
|
24
|
+
"rgb",
|
|
25
|
+
"bt709",
|
|
26
|
+
"bt470bg",
|
|
27
|
+
"smpte170m",
|
|
28
|
+
"bt2020-ncl"
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* A value as WebCodecs spells it, as the core names it: one of `names`, or unspecified for any
|
|
32
|
+
* other value or none.
|
|
33
|
+
*/
|
|
34
|
+
function namedOrUnspecified(names, value) {
|
|
35
|
+
return names.find((name) => name === value) ?? "unspecified";
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { COLOUR_PRIMARIES, MATRIX_COEFFICIENTS, TRANSFER_CHARACTERISTICS, namedOrUnspecified };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { IDENTITY_MATRIX3, multiplyMatrices } from "../../shared/math/Matrix3.js";
|
|
2
|
+
//#region ../../packages/core/src/domain/colour/matrixCorrection.ts
|
|
3
|
+
/**
|
|
4
|
+
* The weights of each matrix (ITU-R BT.709-6 item 3.2, BT.2020-2 table 4, BT.601-7 item 2.5.1,
|
|
5
|
+
* whose SD matrices BT.470BG and SMPTE 170M share); none for RGB or a matrix left unsaid.
|
|
6
|
+
*/
|
|
7
|
+
var LUMA_WEIGHTS = {
|
|
8
|
+
bt709: {
|
|
9
|
+
red: .2126,
|
|
10
|
+
blue: .0722
|
|
11
|
+
},
|
|
12
|
+
"bt2020-ncl": {
|
|
13
|
+
red: .2627,
|
|
14
|
+
blue: .0593
|
|
15
|
+
},
|
|
16
|
+
bt470bg: {
|
|
17
|
+
red: .299,
|
|
18
|
+
blue: .114
|
|
19
|
+
},
|
|
20
|
+
smpte170m: {
|
|
21
|
+
red: .299,
|
|
22
|
+
blue: .114
|
|
23
|
+
},
|
|
24
|
+
rgb: void 0,
|
|
25
|
+
unspecified: void 0
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The matrix that brings R′G′B′ a platform derived from Y′CbCr through `applied` to the R′G′B′
|
|
29
|
+
* `recorded` gives: WebKit's decoders convert with BT.709 whatever a stream says (ADR 0033). No
|
|
30
|
+
* correction when the two agree, or either is not known.
|
|
31
|
+
*/
|
|
32
|
+
function matrixCorrectionOf(recorded, applied) {
|
|
33
|
+
const to = LUMA_WEIGHTS[recorded];
|
|
34
|
+
const from = LUMA_WEIGHTS[applied];
|
|
35
|
+
return to === void 0 || from === void 0 || recorded === applied ? IDENTITY_MATRIX3 : multiplyMatrices(rgbFromYcbcr(to), ycbcrFromRgb(from));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* R′G′B′ of Y′, Pb and Pr.
|
|
39
|
+
*/
|
|
40
|
+
function rgbFromYcbcr({ red, blue }) {
|
|
41
|
+
const green = 1 - red - blue;
|
|
42
|
+
const redChroma = 2 * (1 - red);
|
|
43
|
+
const blueChroma = 2 * (1 - blue);
|
|
44
|
+
return [
|
|
45
|
+
1,
|
|
46
|
+
0,
|
|
47
|
+
redChroma,
|
|
48
|
+
1,
|
|
49
|
+
-blue * blueChroma / green,
|
|
50
|
+
-red * redChroma / green,
|
|
51
|
+
1,
|
|
52
|
+
blueChroma,
|
|
53
|
+
0
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Y′, Pb and Pr of R′G′B′.
|
|
58
|
+
*/
|
|
59
|
+
function ycbcrFromRgb({ red, blue }) {
|
|
60
|
+
const green = 1 - red - blue;
|
|
61
|
+
const blueChroma = 2 * (1 - blue);
|
|
62
|
+
const redChroma = 2 * (1 - red);
|
|
63
|
+
return [
|
|
64
|
+
red,
|
|
65
|
+
green,
|
|
66
|
+
blue,
|
|
67
|
+
-red / blueChroma,
|
|
68
|
+
-green / blueChroma,
|
|
69
|
+
(1 - blue) / blueChroma,
|
|
70
|
+
(1 - red) / redChroma,
|
|
71
|
+
-green / redChroma,
|
|
72
|
+
-blue / redChroma
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { matrixCorrectionOf };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
//#region ../../packages/core/src/domain/format/calibration/CalibrationVersion.ts
|
|
2
2
|
/**
|
|
3
|
-
* Which calibration string a set was read from
|
|
4
|
-
* `
|
|
5
|
-
* their version word (the X5 recordings; insta360-rs docs); the legacy one declares
|
|
3
|
+
* Which calibration string a set was read from: `offset` (1), `offset_v2` (2), `offset_v3` (3)
|
|
4
|
+
* and `offset_v6` (6, the Mei model with more terms). The versioned strings declare theirs in the
|
|
5
|
+
* high bits of their version word (the X5 recordings; insta360-rs docs); the legacy one declares
|
|
6
|
+
* none. The number says nothing of which reading draws the lenses best: that is the calibration
|
|
7
|
+
* preference's (ADR 0023).
|
|
6
8
|
*/
|
|
7
9
|
var CalibrationVersion = {
|
|
8
10
|
Legacy: 1,
|
|
9
11
|
Polynomial: 2,
|
|
10
|
-
Mei: 3
|
|
12
|
+
Mei: 3,
|
|
13
|
+
ExtendedMei: 6
|
|
11
14
|
};
|
|
12
15
|
//#endregion
|
|
13
16
|
export { CalibrationVersion };
|
|
@@ -8,7 +8,7 @@ function eulerDegrees(yaw, pitch, roll) {
|
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* For the versioned strings (v2, v3): what is wrong with a version word that does not declare
|
|
11
|
+
* For the versioned strings (v2, v3, v6): what is wrong with a version word that does not declare
|
|
12
12
|
* `expected` in its high bits, if anything.
|
|
13
13
|
*/
|
|
14
14
|
function versionWordMismatch(versionWord, expected) {
|