@bubo-squared/gyroview 0.1.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.
Files changed (228) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +216 -0
  4. package/dist/define.d.ts +3 -0
  5. package/dist/define.js +4 -0
  6. package/dist/index.d.ts +959 -0
  7. package/dist/index.js +13 -0
  8. package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +37 -0
  9. package/dist/packages/adapters/blob/src/index.js +2 -0
  10. package/dist/packages/adapters/fetch/src/HttpRangeSource.js +194 -0
  11. package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
  12. package/dist/packages/adapters/fetch/src/httpRequest.js +126 -0
  13. package/dist/packages/adapters/fetch/src/index.js +3 -0
  14. package/dist/packages/adapters/mediabunny/src/MediabunnyAudioSegments.js +133 -0
  15. package/dist/packages/adapters/mediabunny/src/MediabunnyDemuxer.js +63 -0
  16. package/dist/packages/adapters/mediabunny/src/MediabunnyVideoTrackReader.js +84 -0
  17. package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +84 -0
  18. package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
  19. package/dist/packages/adapters/mediabunny/src/index.js +2 -0
  20. package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +139 -0
  21. package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +174 -0
  22. package/dist/packages/adapters/mse-audio/src/events.js +26 -0
  23. package/dist/packages/adapters/mse-audio/src/index.js +2 -0
  24. package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +82 -0
  25. package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +274 -0
  26. package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
  27. package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
  28. package/dist/packages/adapters/three/src/index.js +2 -0
  29. package/dist/packages/adapters/three/src/lensTextures.js +25 -0
  30. package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
  31. package/dist/packages/adapters/three/src/renderInto.js +15 -0
  32. package/dist/packages/adapters/three/src/rendererUniforms.js +157 -0
  33. package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
  34. package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +79 -0
  35. package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +38 -0
  36. package/dist/packages/adapters/three/src/shaderPrograms.js +66 -0
  37. package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
  38. package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
  39. package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
  40. package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
  41. package/dist/packages/adapters/three/src/shaders/header.js +4 -0
  42. package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
  43. package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
  44. package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
  45. package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
  46. package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
  47. package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
  48. package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
  49. package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
  50. package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +135 -0
  51. package/dist/packages/adapters/webcodecs/src/index.js +2 -0
  52. package/dist/packages/core/src/application/gainMatching/GainMatching.js +66 -0
  53. package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +62 -0
  54. package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
  55. package/dist/packages/core/src/application/playback/DecodePipeline.js +188 -0
  56. package/dist/packages/core/src/application/playback/DecodeRun.js +93 -0
  57. package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
  58. package/dist/packages/core/src/application/playback/FramePairer.js +60 -0
  59. package/dist/packages/core/src/application/playback/PlaybackSession.js +417 -0
  60. package/dist/packages/core/src/application/playback/SeekOrder.js +36 -0
  61. package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
  62. package/dist/packages/core/src/application/playback/StartGate.js +55 -0
  63. package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
  64. package/dist/packages/core/src/application/playback/WallClock.js +46 -0
  65. package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
  66. package/dist/packages/core/src/application/playback/probeDecoding.js +168 -0
  67. package/dist/packages/core/src/application/recording/frameTimesOf.js +22 -0
  68. package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
  69. package/dist/packages/core/src/application/recording/inspectRecording.js +124 -0
  70. package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
  71. package/dist/packages/core/src/application/recording/motionOf.js +39 -0
  72. package/dist/packages/core/src/application/recording/readRecording.js +31 -0
  73. package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
  74. package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +31 -0
  75. package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
  76. package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
  77. package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +59 -0
  78. package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +13 -0
  79. package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
  80. package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +44 -0
  81. package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +45 -0
  82. package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +44 -0
  83. package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +65 -0
  84. package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +52 -0
  85. package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +46 -0
  86. package/dist/packages/core/src/domain/format/captureOrigin.js +20 -0
  87. package/dist/packages/core/src/domain/format/constants.js +33 -0
  88. package/dist/packages/core/src/domain/format/info/infoFields.js +110 -0
  89. package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +90 -0
  90. package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
  91. package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +122 -0
  92. package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +67 -0
  93. package/dist/packages/core/src/domain/format/records/TrailerRecords.js +83 -0
  94. package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
  95. package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +39 -0
  96. package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
  97. package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +82 -0
  98. package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
  99. package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +129 -0
  100. package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
  101. package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
  102. package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
  103. package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
  104. package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
  105. package/dist/packages/core/src/domain/format/trailer/readTrailer.js +32 -0
  106. package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +53 -0
  107. package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +60 -0
  108. package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
  109. package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
  110. package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +112 -0
  111. package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +146 -0
  112. package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +64 -0
  113. package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
  114. package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
  115. package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
  116. package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
  117. package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +92 -0
  118. package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +61 -0
  119. package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
  120. package/dist/packages/core/src/domain/optics/EquidistantModel.js +39 -0
  121. package/dist/packages/core/src/domain/optics/MeiModel.js +45 -0
  122. package/dist/packages/core/src/domain/optics/PolynomialModel.js +44 -0
  123. package/dist/packages/core/src/domain/optics/gainMatch.js +62 -0
  124. package/dist/packages/core/src/domain/optics/lensGeometry.js +18 -0
  125. package/dist/packages/core/src/domain/optics/lensPose.js +35 -0
  126. package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
  127. package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
  128. package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
  129. package/dist/packages/core/src/domain/stitching/StitchingSetup.js +78 -0
  130. package/dist/packages/core/src/domain/stitching/seamDisparity.js +4 -0
  131. package/dist/packages/core/src/domain/stitching/seamDisparityField.js +5 -0
  132. package/dist/packages/core/src/domain/stitching/seamJoin.js +16 -0
  133. package/dist/packages/core/src/domain/stitching/seamMismatch.js +1 -0
  134. package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
  135. package/dist/packages/core/src/domain/stitching/seamStrip.js +26 -0
  136. package/dist/packages/core/src/domain/view/Framing.js +31 -0
  137. package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
  138. package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
  139. package/dist/packages/core/src/domain/view/ViewState.js +48 -0
  140. package/dist/packages/core/src/domain/view/lensTilesView.js +63 -0
  141. package/dist/packages/core/src/domain/view/magnification.js +103 -0
  142. package/dist/packages/core/src/domain/view/normalView.js +29 -0
  143. package/dist/packages/core/src/domain/view/panoramaView.js +99 -0
  144. package/dist/packages/core/src/domain/view/rectilinear.js +40 -0
  145. package/dist/packages/core/src/domain/view/screenLayout.js +113 -0
  146. package/dist/packages/core/src/domain/view/viewGestures.js +98 -0
  147. package/dist/packages/core/src/domain/view/viewModes.js +14 -0
  148. package/dist/packages/core/src/index.js +59 -0
  149. package/dist/packages/core/src/ports/FramePair.js +6 -0
  150. package/dist/packages/core/src/shared/async/Deferred.js +42 -0
  151. package/dist/packages/core/src/shared/async/RunStop.js +40 -0
  152. package/dist/packages/core/src/shared/binary/ByteRange.js +38 -0
  153. package/dist/packages/core/src/shared/binary/ByteReader.js +77 -0
  154. package/dist/packages/core/src/shared/errors/GyroViewError.js +81 -0
  155. package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
  156. package/dist/packages/core/src/shared/events/Outbox.js +68 -0
  157. package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
  158. package/dist/packages/core/src/shared/keysOf.js +10 -0
  159. package/dist/packages/core/src/shared/lazy.js +11 -0
  160. package/dist/packages/core/src/shared/math/Matrix3.js +93 -0
  161. package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
  162. package/dist/packages/core/src/shared/math/Vector3.js +54 -0
  163. package/dist/packages/core/src/shared/math/clamp.js +9 -0
  164. package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +157 -0
  165. package/dist/packages/core/src/shared/text/urlPath.js +31 -0
  166. package/dist/packages/core/src/shared/text/utf8.js +12 -0
  167. package/dist/packages/core/src/shared/units/angle.js +22 -0
  168. package/dist/packages/core/src/shared/units/time.js +13 -0
  169. package/dist/packages/player/src/PlayerSource.js +15 -0
  170. package/dist/packages/player/src/browserPlayer.js +18 -0
  171. package/dist/packages/player/src/choices.js +25 -0
  172. package/dist/packages/player/src/composition/Disposables.js +31 -0
  173. package/dist/packages/player/src/composition/browserPorts.js +46 -0
  174. package/dist/packages/player/src/composition/buildPipeline.js +153 -0
  175. package/dist/packages/player/src/composition/ensureDecodable.js +63 -0
  176. package/dist/packages/player/src/composition/openInputs.js +179 -0
  177. package/dist/packages/player/src/composition/openRecording.js +42 -0
  178. package/dist/packages/player/src/controls/ChoiceMenu.js +113 -0
  179. package/dist/packages/player/src/controls/PictureMenus.js +47 -0
  180. package/dist/packages/player/src/controls/SeekBar.js +125 -0
  181. package/dist/packages/player/src/controls/SoundControls.js +41 -0
  182. package/dist/packages/player/src/controls/TransportButtons.js +31 -0
  183. package/dist/packages/player/src/controls/ViewGestures.js +136 -0
  184. package/dist/packages/player/src/controls/Wording.js +78 -0
  185. package/dist/packages/player/src/controls/controlParts.js +36 -0
  186. package/dist/packages/player/src/controls/controls.js +4 -0
  187. package/dist/packages/player/src/controls/controlsBar.js +23 -0
  188. package/dist/packages/player/src/controls/controlsMarkup.js +85 -0
  189. package/dist/packages/player/src/controls/customControls.js +59 -0
  190. package/dist/packages/player/src/controls/formatTime.js +19 -0
  191. package/dist/packages/player/src/controls/icons.js +40 -0
  192. package/dist/packages/player/src/controls/keyboard.js +118 -0
  193. package/dist/packages/player/src/controls/messages.js +94 -0
  194. package/dist/packages/player/src/controls/parseMarkup.js +31 -0
  195. package/dist/packages/player/src/controls/viewButtons.js +14 -0
  196. package/dist/packages/player/src/element/ElementLoads.js +119 -0
  197. package/dist/packages/player/src/element/FullscreenToggle.js +39 -0
  198. package/dist/packages/player/src/element/GyroViewElement.js +313 -0
  199. package/dist/packages/player/src/element/IdleWatcher.js +71 -0
  200. package/dist/packages/player/src/element/TypedEventElement.js +22 -0
  201. package/dist/packages/player/src/element/accessibleRegion.js +19 -0
  202. package/dist/packages/player/src/element/attributeNames.js +51 -0
  203. package/dist/packages/player/src/element/attributes.js +101 -0
  204. package/dist/packages/player/src/element/defineGyroView.js +21 -0
  205. package/dist/packages/player/src/element/earlyProperties.js +36 -0
  206. package/dist/packages/player/src/element/elementSource.js +23 -0
  207. package/dist/packages/player/src/element/liveSettings.js +138 -0
  208. package/dist/packages/player/src/element/playbackAttributes.js +53 -0
  209. package/dist/packages/player/src/element/playerEventMirror.js +33 -0
  210. package/dist/packages/player/src/element/reflectedProperties.js +78 -0
  211. package/dist/packages/player/src/element/styles.js +4 -0
  212. package/dist/packages/player/src/element/template.js +47 -0
  213. package/dist/packages/player/src/index.js +10 -0
  214. package/dist/packages/player/src/inspectRecording.js +22 -0
  215. package/dist/packages/player/src/player/DrawingBufferFit.js +77 -0
  216. package/dist/packages/player/src/player/FrameLoop.js +40 -0
  217. package/dist/packages/player/src/player/PictureSettings.js +60 -0
  218. package/dist/packages/player/src/player/Player.js +377 -0
  219. package/dist/packages/player/src/player/PlayerEvents.js +28 -0
  220. package/dist/packages/player/src/player/PlayerPhase.js +21 -0
  221. package/dist/packages/player/src/player/PlayerSound.js +61 -0
  222. package/dist/packages/player/src/player/PlayerView.js +113 -0
  223. package/dist/packages/player/src/player/SessionRelay.js +66 -0
  224. package/dist/packages/player/src/player/ensureFinite.js +26 -0
  225. package/dist/packages/player/src/player/loadRecording.js +65 -0
  226. package/dist/packages/player/src/player/transportEvents.js +29 -0
  227. package/dist/standalone.js +4919 -0
  228. package/package.json +73 -0
@@ -0,0 +1,377 @@
1
+ import { degrees } from "../../../core/src/shared/units/angle.js";
2
+ import { asGyroViewError, hasErrorCode, messageOf } from "../../../core/src/shared/errors/GyroViewError.js";
3
+ import { seconds } from "../../../core/src/shared/units/time.js";
4
+ import { Outbox } from "../../../core/src/shared/events/Outbox.js";
5
+ import { isFlowing } from "../../../core/src/domain/playback/PlayerState.js";
6
+ import { TypedEmitter } from "../../../core/src/shared/events/TypedEmitter.js";
7
+ import "../../../core/src/index.js";
8
+ import { ensureFinite, viewStateOf } from "./ensureFinite.js";
9
+ import { cssSizeOf } from "./DrawingBufferFit.js";
10
+ import { FrameLoop } from "./FrameLoop.js";
11
+ import { loadRecording } from "./loadRecording.js";
12
+ import { IDLE, loadingPhase, statusOf } from "./PlayerPhase.js";
13
+ import { PictureSettings } from "./PictureSettings.js";
14
+ import { PlayerSound } from "./PlayerSound.js";
15
+ import { PlayerView } from "./PlayerView.js";
16
+ import { SessionRelay } from "./SessionRelay.js";
17
+ //#region ../../packages/player/src/player/Player.ts
18
+ /**
19
+ * The player without the element, which `createBrowserPlayer` gives: it plays one recording at a
20
+ * time on the canvas and audio element it was handed, reports in media-element events, and keeps
21
+ * its settings (view, view mode, stabilization, gain matching, sound, loop) from load to load.
22
+ * `<gyro-view>` is a facade over it.
23
+ */
24
+ var Player = class {
25
+ parts;
26
+ events = new TypedEmitter();
27
+ /**
28
+ * What the player and its parts announce, heard once each change is whole (ADR 0021).
29
+ */
30
+ outbox = new Outbox(this.events);
31
+ loop;
32
+ phase = IDLE;
33
+ viewing;
34
+ picture = new PictureSettings(this.outbox);
35
+ /**
36
+ * A seek asked for before a recording was ready, where the next one starts.
37
+ */
38
+ pendingStartTime;
39
+ sound;
40
+ relay = new SessionRelay({
41
+ events: this.outbox,
42
+ onState: () => {
43
+ this.announceStatus();
44
+ },
45
+ onEnded: () => {
46
+ this.onEnded();
47
+ }
48
+ });
49
+ isLoopingValue = false;
50
+ /**
51
+ * Past `dispose`: a listener of the idle it announces must not load again.
52
+ */
53
+ isDisposed = false;
54
+ /**
55
+ * The status listeners heard last.
56
+ */
57
+ lastStatus = "idle";
58
+ /**
59
+ * @internal A page gets its player from `createBrowserPlayer`, which composes the parts.
60
+ */
61
+ constructor(parts) {
62
+ this.parts = parts;
63
+ this.loop = new FrameLoop(this.tick);
64
+ parts.host.audio.addEventListener("ended", this.tick);
65
+ this.sound = new PlayerSound(parts.host.audio, this.outbox);
66
+ const { canvas } = parts.host;
67
+ this.viewing = new PlayerView(this.outbox, () => cssSizeOf(canvas));
68
+ }
69
+ get status() {
70
+ return statusOf(this.phase);
71
+ }
72
+ get metadata() {
73
+ return this.loaded?.opened.metadata;
74
+ }
75
+ get currentTime() {
76
+ return this.loaded?.pipeline.session.currentTime ?? seconds(0);
77
+ }
78
+ get duration() {
79
+ return this.loaded?.opened.duration ?? seconds(0);
80
+ }
81
+ /**
82
+ * False while playing or holding for frames, as a media element's `paused`.
83
+ */
84
+ get isPaused() {
85
+ return !isFlowing(this.loaded?.pipeline.session.state);
86
+ }
87
+ get view() {
88
+ return this.viewing.current;
89
+ }
90
+ get viewMode() {
91
+ return this.viewing.viewMode;
92
+ }
93
+ /**
94
+ * Whether a drag moves the picture as it is framed now; the view mode decides.
95
+ */
96
+ get canPan() {
97
+ return this.viewing.canPan;
98
+ }
99
+ get stabilization() {
100
+ return this.picture.stabilization;
101
+ }
102
+ get quality() {
103
+ return this.picture.quality;
104
+ }
105
+ get isLooping() {
106
+ return this.isLoopingValue;
107
+ }
108
+ get volume() {
109
+ return this.sound.volume;
110
+ }
111
+ get isMuted() {
112
+ return this.sound.isMuted;
113
+ }
114
+ /**
115
+ * Whether `setVolume` changes anything on this platform; iOS leaves media at the device's volume.
116
+ */
117
+ get canSetVolume() {
118
+ return this.sound.canSetVolume;
119
+ }
120
+ /**
121
+ * Replaces whatever was loaded. Resolves once the recording is ready (and started, with
122
+ * `autoplay`); rejects with the failure after reporting it as an `error` event. A load that a
123
+ * newer load or `unload` supersedes resolves quietly.
124
+ */
125
+ async load(source, options = {}) {
126
+ if (this.isDisposed) return;
127
+ const loading = this.outbox.change(() => {
128
+ this.unload();
129
+ const next = loadingPhase();
130
+ this.phase = next;
131
+ this.announceStatus();
132
+ return next;
133
+ });
134
+ try {
135
+ await this.complete(loading, source, options);
136
+ } catch (error) {
137
+ if (loading.controller.signal.aborted) return;
138
+ throw this.failWith(error);
139
+ } finally {
140
+ loading.settled.resolve();
141
+ }
142
+ }
143
+ unload() {
144
+ this.outbox.change(() => {
145
+ this.release();
146
+ this.announceStatus();
147
+ });
148
+ }
149
+ /**
150
+ * Starts playback, after a load in progress is ready, as a media element's `play()` does.
151
+ * Rejects with the load's failure, or with `playback-blocked` when the browser wants a user
152
+ * gesture first; with nothing loaded there is nothing to start.
153
+ */
154
+ async play() {
155
+ await this.loadInProgress();
156
+ if (this.phase.kind === "failed") throw this.phase.failure;
157
+ await this.loaded?.pipeline.session.play();
158
+ }
159
+ pause() {
160
+ this.loaded?.pipeline.session.pause();
161
+ }
162
+ stop() {
163
+ this.loaded?.pipeline.session.stop();
164
+ }
165
+ /**
166
+ * Seeks exactly to `time` seconds; while a recording is loading, before one is, or after its
167
+ * playback failed, the next one starts there, as a media element's default playback start
168
+ * position has it.
169
+ */
170
+ seek(time) {
171
+ ensureFinite(time, "time");
172
+ const session = this.loaded?.pipeline.session;
173
+ if (session && session.state !== "error") session.seek(seconds(time));
174
+ else this.pendingStartTime = seconds(time);
175
+ }
176
+ /**
177
+ * Seeks to the key frame at or before `time` seconds: quick to show while a seek bar is dragged.
178
+ */
179
+ async scrub(time) {
180
+ ensureFinite(time, "time");
181
+ await this.loaded?.pipeline.session.scrub(seconds(time));
182
+ }
183
+ setView(view) {
184
+ this.viewing.set(viewStateOf(view));
185
+ }
186
+ /**
187
+ * Points the normal view at `yaw` and `pitch`, in degrees: yaw positive to the right, pitch
188
+ * positive up.
189
+ */
190
+ lookAt(yaw, pitch) {
191
+ ensureFinite(yaw, "yaw");
192
+ ensureFinite(pitch, "pitch");
193
+ this.viewing.lookAt(degrees(yaw), degrees(pitch));
194
+ }
195
+ /**
196
+ * The viewer dragged the picture by `delta` CSS pixels across the canvas.
197
+ */
198
+ pan(delta) {
199
+ this.viewing.pan(delta);
200
+ }
201
+ /**
202
+ * The viewer turned by the given angles in degrees, as the arrow keys do.
203
+ */
204
+ turn(yawDelta, pitchDelta) {
205
+ ensureFinite(yawDelta, "yaw turn");
206
+ ensureFinite(pitchDelta, "pitch turn");
207
+ this.viewing.turn(degrees(yawDelta), degrees(pitchDelta));
208
+ }
209
+ /**
210
+ * Zooms by `steps` (positive zooms in) toward `focus`, a point of the canvas as fractions of its
211
+ * size; about the centre when none is given.
212
+ */
213
+ zoom(steps, focus) {
214
+ ensureFinite(steps, "zoom steps");
215
+ this.viewing.zoom(steps, focus);
216
+ }
217
+ resetView() {
218
+ this.viewing.reset();
219
+ }
220
+ setViewMode(mode) {
221
+ this.viewing.setMode(mode);
222
+ }
223
+ setStabilization(mode) {
224
+ this.picture.setStabilization(mode);
225
+ }
226
+ /**
227
+ * How finely the lens images are read and how many device pixels are drawn; kept across loads.
228
+ */
229
+ setQuality(quality) {
230
+ this.picture.setQuality(quality);
231
+ }
232
+ setLooping(isLooping) {
233
+ this.isLoopingValue = isLooping;
234
+ }
235
+ /**
236
+ * Whether the lenses' exposure is matched along the seam; kept across loads.
237
+ */
238
+ setGainMatching(isEnabled) {
239
+ this.picture.setGainMatching(isEnabled);
240
+ }
241
+ setVolume(volume) {
242
+ ensureFinite(volume, "volume");
243
+ this.sound.setVolume(volume);
244
+ }
245
+ setMuted(isMuted) {
246
+ this.sound.setMuted(isMuted);
247
+ }
248
+ dispose() {
249
+ this.isDisposed = true;
250
+ this.unload();
251
+ this.parts.host.audio.removeEventListener("ended", this.tick);
252
+ this.sound.dispose();
253
+ this.events.removeAll();
254
+ }
255
+ tick = () => {
256
+ this.loaded?.pipeline.session.tick();
257
+ };
258
+ /**
259
+ * Whatever it ends in: a failure is the load's to report, and `play` reads it from the phase.
260
+ */
261
+ async loadInProgress() {
262
+ if (this.phase.kind === "loading") await this.phase.settled.promise;
263
+ }
264
+ get loaded() {
265
+ return this.phase.kind === "loaded" ? this.phase.loaded : void 0;
266
+ }
267
+ async complete(loading, source, options) {
268
+ const { signal } = loading.controller;
269
+ const loaded = await loadRecording({
270
+ source,
271
+ parts: this.parts,
272
+ signal
273
+ });
274
+ if (signal.aborted) {
275
+ loaded.dispose();
276
+ return;
277
+ }
278
+ this.outbox.change(() => {
279
+ this.attach(loaded, loading.controller);
280
+ this.startAtPendingTime(loaded);
281
+ if (options.preload !== false) loaded.pipeline.session.preload();
282
+ });
283
+ if (options.autoplay && this.loaded === loaded) await this.autoplay();
284
+ }
285
+ startAtPendingTime(loaded) {
286
+ const time = this.pendingStartTime;
287
+ this.pendingStartTime = void 0;
288
+ if (time !== void 0) loaded.pipeline.session.seek(time);
289
+ }
290
+ attach(loaded, controller) {
291
+ this.phase = {
292
+ kind: "loaded",
293
+ loaded,
294
+ controller
295
+ };
296
+ this.viewing.attach(loaded.pipeline.renderer);
297
+ const { session } = loaded.pipeline;
298
+ this.picture.attach(loaded.pipeline);
299
+ this.relay.attach(session);
300
+ this.loop.start();
301
+ this.announceStatus();
302
+ for (const warning of loaded.warnings) this.outbox.emit("warning", warning);
303
+ this.outbox.emit("ready", loaded.opened.metadata);
304
+ }
305
+ /**
306
+ * A refused start leaves the recording loaded and paused: a warning, never a failed load.
307
+ */
308
+ async autoplay() {
309
+ try {
310
+ await this.play();
311
+ } catch (error) {
312
+ this.outbox.emit("warning", hasErrorCode(error, "playback-blocked") ? {
313
+ code: "autoplay-blocked",
314
+ message: "playback waits for a user gesture"
315
+ } : {
316
+ code: "playback-failed",
317
+ message: `autoplay failed: ${messageOf(error)}`
318
+ });
319
+ }
320
+ }
321
+ onEnded() {
322
+ this.outbox.emit("ended", void 0);
323
+ if (this.isLoopingValue) this.replay();
324
+ }
325
+ async replay() {
326
+ try {
327
+ await this.play();
328
+ } catch (error) {
329
+ this.outbox.emit("warning", {
330
+ code: "playback-failed",
331
+ message: `the loop could not restart playback: ${messageOf(error)}`
332
+ });
333
+ }
334
+ }
335
+ /**
336
+ * Lets go of whatever is loading or loaded, announcing nothing.
337
+ */
338
+ release() {
339
+ const previous = this.phase;
340
+ this.phase = IDLE;
341
+ if (previous.kind === "loading") previous.controller.abort();
342
+ this.loop.stop();
343
+ this.relay.detach();
344
+ this.viewing.attach(void 0);
345
+ this.picture.attach(void 0);
346
+ if (previous.kind !== "loaded") return;
347
+ previous.loaded.dispose();
348
+ previous.controller.abort();
349
+ }
350
+ failWith(error) {
351
+ const failure = asGyroViewError(error, "invariant-violation", "the player failed unexpectedly");
352
+ this.outbox.change(() => {
353
+ this.release();
354
+ this.phase = {
355
+ kind: "failed",
356
+ failure
357
+ };
358
+ this.announceStatus();
359
+ this.outbox.emit("error", failure);
360
+ });
361
+ return failure;
362
+ }
363
+ /**
364
+ * Announces the status the phase gives now, heard once for each change: a status announced
365
+ * again before listeners heard the other one in between is heard once.
366
+ */
367
+ announceStatus() {
368
+ const { status } = this;
369
+ this.outbox.post(() => {
370
+ if (status === this.lastStatus) return;
371
+ this.lastStatus = status;
372
+ this.events.emit("statuschange", status);
373
+ });
374
+ }
375
+ };
376
+ //#endregion
377
+ export { Player };
@@ -0,0 +1,28 @@
1
+ import { keysOf } from "../../../core/src/shared/keysOf.js";
2
+ import "../../../core/src/index.js";
3
+ //#region ../../packages/player/src/player/PlayerEvents.ts
4
+ /**
5
+ * Every event name once.
6
+ */
7
+ var PLAYER_EVENT_NAMES = keysOf({
8
+ statuschange: true,
9
+ ready: true,
10
+ play: true,
11
+ playing: true,
12
+ waiting: true,
13
+ pause: true,
14
+ ended: true,
15
+ timeupdate: true,
16
+ seeking: true,
17
+ seeked: true,
18
+ frame: true,
19
+ viewchange: true,
20
+ viewmodechange: true,
21
+ stabilizationchange: true,
22
+ qualitychange: true,
23
+ volumechange: true,
24
+ warning: true,
25
+ error: true
26
+ });
27
+ //#endregion
28
+ export { PLAYER_EVENT_NAMES };
@@ -0,0 +1,21 @@
1
+ import { Deferred } from "../../../core/src/shared/async/Deferred.js";
2
+ import "../../../core/src/index.js";
3
+ //#region ../../packages/player/src/player/PlayerPhase.ts
4
+ var IDLE = { kind: "idle" };
5
+ function loadingPhase() {
6
+ return {
7
+ kind: "loading",
8
+ controller: new AbortController(),
9
+ settled: new Deferred()
10
+ };
11
+ }
12
+ function statusOf(phase) {
13
+ switch (phase.kind) {
14
+ case "idle": return "idle";
15
+ case "loading": return "loading";
16
+ case "loaded": return phase.loaded.pipeline.session.state;
17
+ case "failed": return "error";
18
+ }
19
+ }
20
+ //#endregion
21
+ export { IDLE, loadingPhase, statusOf };
@@ -0,0 +1,61 @@
1
+ import { clamp } from "../../../core/src/shared/math/clamp.js";
2
+ import "../../../core/src/index.js";
3
+ //#region ../../packages/player/src/player/PlayerSound.ts
4
+ /**
5
+ * The sound, on the audio element the host lends: its volume and mute, kept across loads by the
6
+ * element itself and announced whoever changes them (the controls, the keyboard, a script, the
7
+ * browser's own media keys).
8
+ */
9
+ var PlayerSound = class {
10
+ audio;
11
+ events;
12
+ /**
13
+ * Whether the platform lets a page set the volume: iOS keeps media at the device's volume and
14
+ * ignores the setting, where a volume slider would move and change nothing.
15
+ */
16
+ canSetVolume;
17
+ constructor(audio, events) {
18
+ this.audio = audio;
19
+ this.events = events;
20
+ this.canSetVolume = isVolumeSettable(audio.ownerDocument);
21
+ audio.addEventListener("volumechange", this.onVolumeChange);
22
+ }
23
+ get volume() {
24
+ return this.audio.volume;
25
+ }
26
+ get isMuted() {
27
+ return this.audio.muted;
28
+ }
29
+ /**
30
+ * From 0 (silent) to 1 (as recorded); anything outside is clamped.
31
+ */
32
+ setVolume(volume) {
33
+ this.audio.volume = clamp(volume, 0, 1);
34
+ }
35
+ setMuted(isMuted) {
36
+ this.audio.muted = isMuted;
37
+ }
38
+ dispose() {
39
+ this.audio.removeEventListener("volumechange", this.onVolumeChange);
40
+ }
41
+ onVolumeChange = () => {
42
+ this.events.emit("volumechange", {
43
+ volume: this.volume,
44
+ isMuted: this.isMuted
45
+ });
46
+ };
47
+ };
48
+ /**
49
+ * A volume the probe sets, other than the default of 1.
50
+ */
51
+ var PROBE_VOLUME = .5;
52
+ /**
53
+ * Tried on an element of its own, so the one playing hears no volume change.
54
+ */
55
+ function isVolumeSettable(document) {
56
+ const probe = document.createElement("audio");
57
+ probe.volume = PROBE_VOLUME;
58
+ return probe.volume === PROBE_VOLUME;
59
+ }
60
+ //#endregion
61
+ export { PlayerSound };
@@ -0,0 +1,113 @@
1
+ import { clampView, isSameView } from "../../../core/src/domain/view/ViewState.js";
2
+ import { DEFAULT_VIEW_MODE } from "../../../core/src/domain/view/ViewMode.js";
3
+ import { SCREEN_CENTRE } from "../../../core/src/domain/view/screenLayout.js";
4
+ import { lookAt } from "../../../core/src/domain/view/viewGestures.js";
5
+ import { DEFAULT_FRAMING, isSameFraming } from "../../../core/src/domain/view/Framing.js";
6
+ import { viewModeRulesFor } from "../../../core/src/domain/view/viewModes.js";
7
+ import "../../../core/src/index.js";
8
+ //#region ../../packages/player/src/player/PlayerView.ts
9
+ /**
10
+ * Every layout the player accepts has two lenses; a renderer attached says so itself.
11
+ */
12
+ var LENS_COUNT_BEFORE_A_LOAD = 2;
13
+ /**
14
+ * How the picture is framed and which view mode shows it: kept across loads, drawn by whichever
15
+ * renderer is attached, and the normal view announced when it changes. The viewer's gestures go
16
+ * through the mode's rules on the viewport as it is now; a host setting the view directly is
17
+ * obeyed as it is.
18
+ */
19
+ var PlayerView = class {
20
+ events;
21
+ measureViewport;
22
+ framing = DEFAULT_FRAMING;
23
+ mode = DEFAULT_VIEW_MODE;
24
+ surface;
25
+ lensCount = LENS_COUNT_BEFORE_A_LOAD;
26
+ constructor(events, measureViewport) {
27
+ this.events = events;
28
+ this.measureViewport = measureViewport;
29
+ }
30
+ get current() {
31
+ return this.framing.view;
32
+ }
33
+ get viewMode() {
34
+ return this.mode;
35
+ }
36
+ /**
37
+ * Whether a drag moves the picture as it is framed now.
38
+ */
39
+ get canPan() {
40
+ return this.rules().canPan(this.framing);
41
+ }
42
+ /**
43
+ * The renderer of the loaded recording, or nothing between loads. A new renderer draws the
44
+ * framing as it is now, however it changed while the recording was loading; the lens tiles are
45
+ * measured by the lenses it draws from then on.
46
+ */
47
+ attach(surface) {
48
+ this.surface = surface;
49
+ if (surface) this.lensCount = surface.lensCount;
50
+ surface?.setViewMode(this.mode);
51
+ surface?.setFraming(this.framing);
52
+ }
53
+ set(view) {
54
+ this.frame({
55
+ ...this.framing,
56
+ view: clampView(view)
57
+ });
58
+ }
59
+ setMode(mode) {
60
+ if (mode === this.mode) return;
61
+ this.mode = mode;
62
+ this.surface?.setViewMode(mode);
63
+ this.events.emit("viewmodechange", mode);
64
+ }
65
+ lookAt(yaw, pitch) {
66
+ this.frame({
67
+ ...this.framing,
68
+ view: lookAt(this.framing.view, yaw, pitch)
69
+ });
70
+ }
71
+ pan(delta) {
72
+ this.frame(this.rules().pan(this.framing, delta, this.context()));
73
+ }
74
+ turn(yaw, pitch) {
75
+ this.frame(this.rules().turn(this.framing, {
76
+ yaw,
77
+ pitch
78
+ }, this.context()));
79
+ }
80
+ /**
81
+ * Zooms by `steps`, keeping the point of the viewport at `focus` on what it shows.
82
+ */
83
+ zoom(steps, focus = SCREEN_CENTRE) {
84
+ this.frame(this.rules().zoom(this.framing, {
85
+ steps,
86
+ focus
87
+ }, this.context()));
88
+ }
89
+ /**
90
+ * Back to how the current mode starts; the other modes keep their framing.
91
+ */
92
+ reset() {
93
+ this.frame(this.rules().reset(this.framing));
94
+ }
95
+ frame(next) {
96
+ if (isSameFraming(next, this.framing)) return;
97
+ const hasViewChanged = !isSameView(next.view, this.framing.view);
98
+ this.framing = next;
99
+ this.surface?.setFraming(next);
100
+ if (hasViewChanged) this.events.emit("viewchange", next.view);
101
+ }
102
+ context() {
103
+ return {
104
+ viewport: this.measureViewport(),
105
+ lensCount: this.lensCount
106
+ };
107
+ }
108
+ rules() {
109
+ return viewModeRulesFor(this.mode);
110
+ }
111
+ };
112
+ //#endregion
113
+ export { PlayerView };
@@ -0,0 +1,66 @@
1
+ import { transportEventsFor } from "./transportEvents.js";
2
+ //#region ../../packages/player/src/player/SessionRelay.ts
3
+ /**
4
+ * Relays the loaded session's events in media-element terms: transport events for its state
5
+ * changes, time updates, drawn frames, errors and the end. Listens to one session at a time and
6
+ * stops before that session is disposed, so its disposal is never announced. A change is relayed
7
+ * whole, as a media element fires the events it queued before a listener's `pause()`; the
8
+ * session announces a listener's newer change after it (ADR 0021).
9
+ */
10
+ var SessionRelay = class {
11
+ targets;
12
+ stopListening = [];
13
+ lastState;
14
+ /**
15
+ * Counts detachments, so a change whose listener let the session go stops relaying.
16
+ */
17
+ detachments = 0;
18
+ constructor(targets) {
19
+ this.targets = targets;
20
+ }
21
+ attach(session) {
22
+ this.detach();
23
+ this.lastState = session.state;
24
+ const { events } = this.targets;
25
+ this.stopListening = [
26
+ session.events.on("statechange", (state) => {
27
+ this.onStateChange(state, session);
28
+ }),
29
+ session.events.on("timeupdate", (time) => {
30
+ events.emit("timeupdate", time);
31
+ }),
32
+ session.events.on("present", (time) => {
33
+ events.emit("frame", time);
34
+ }),
35
+ session.events.on("ended", () => {
36
+ this.targets.onEnded();
37
+ }),
38
+ session.events.on("error", (error) => {
39
+ events.emit("error", error);
40
+ })
41
+ ];
42
+ }
43
+ detach() {
44
+ for (const stop of this.stopListening) stop();
45
+ this.stopListening = [];
46
+ this.lastState = void 0;
47
+ this.detachments += 1;
48
+ }
49
+ onStateChange(state, session) {
50
+ const previous = this.lastState;
51
+ this.lastState = state;
52
+ const detachments = this.detachments;
53
+ for (const name of transportEventsFor(previous, state)) {
54
+ this.emitTransport(name, session);
55
+ if (this.detachments !== detachments) return;
56
+ }
57
+ this.targets.onState(state);
58
+ }
59
+ emitTransport(name, session) {
60
+ const { events } = this.targets;
61
+ if (name === "seeking" || name === "seeked") events.emit(name, session.currentTime);
62
+ else events.emit(name, void 0);
63
+ }
64
+ };
65
+ //#endregion
66
+ export { SessionRelay };
@@ -0,0 +1,26 @@
1
+ import { degrees } from "../../../core/src/shared/units/angle.js";
2
+ import { GyroViewError } from "../../../core/src/shared/errors/GyroViewError.js";
3
+ import "../../../core/src/index.js";
4
+ //#region ../../packages/player/src/player/ensureFinite.ts
5
+ /**
6
+ * A number from the page, or from another origin through the embed: NaN from an empty field, an
7
+ * undefined value or text would reach the clock and the view, which cannot take it.
8
+ */
9
+ function ensureFinite(value, name) {
10
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new GyroViewError("invalid-argument", `${name} must be a finite number`);
11
+ }
12
+ /**
13
+ * A view from the page, each angle checked and read as degrees.
14
+ */
15
+ function viewStateOf(view) {
16
+ ensureFinite(view.yaw, "yaw");
17
+ ensureFinite(view.pitch, "pitch");
18
+ ensureFinite(view.fieldOfView, "fieldOfView");
19
+ return {
20
+ yaw: degrees(view.yaw),
21
+ pitch: degrees(view.pitch),
22
+ fieldOfView: degrees(view.fieldOfView)
23
+ };
24
+ }
25
+ //#endregion
26
+ export { ensureFinite, viewStateOf };