@bvdmitri/gyroview 0.0.0-stage → 0.9.0-cutaway.2
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 +402 -0
- package/LICENSE +21 -0
- package/README.md +313 -2
- package/dist/define.d.ts +3 -0
- package/dist/define.js +4 -0
- package/dist/index.d.ts +1204 -0
- package/dist/index.js +12 -0
- package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +36 -0
- package/dist/packages/adapters/fetch/src/HttpByteStream.js +173 -0
- package/dist/packages/adapters/fetch/src/HttpRangeSource.js +37 -0
- package/dist/packages/adapters/fetch/src/HttpResource.js +227 -0
- package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
- package/dist/packages/adapters/fetch/src/contentRange.js +15 -0
- package/dist/packages/adapters/fetch/src/httpRequest.js +136 -0
- package/dist/packages/adapters/fetch/src/passingFailures.js +23 -0
- package/dist/packages/adapters/fetch/src/rangeFailures.js +16 -0
- package/dist/packages/adapters/fetch/src/recordingVersion.js +37 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyAudioPackager.js +162 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyCodecReader.js +93 -0
- package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +135 -0
- package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
- package/dist/packages/adapters/mediabunny/src/decoderConfigurations.js +43 -0
- package/dist/packages/adapters/mediabunny/src/trackColour.js +20 -0
- package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +138 -0
- package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +263 -0
- package/dist/packages/adapters/mse-audio/src/events.js +26 -0
- package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +81 -0
- package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +215 -0
- package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
- package/dist/packages/adapters/three/src/conversionUniforms.js +60 -0
- package/dist/packages/adapters/three/src/drawSchedules.js +13 -0
- package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
- package/dist/packages/adapters/three/src/glslLiterals.js +10 -0
- package/dist/packages/adapters/three/src/lensTextures.js +34 -0
- package/dist/packages/adapters/three/src/matrixCorrections.js +34 -0
- package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
- package/dist/packages/adapters/three/src/renderInto.js +15 -0
- package/dist/packages/adapters/three/src/rendererParts.js +108 -0
- package/dist/packages/adapters/three/src/rendererUniforms.js +176 -0
- package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
- package/dist/packages/adapters/three/src/scissorBox.js +20 -0
- package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +85 -0
- package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +42 -0
- package/dist/packages/adapters/three/src/shaderPrograms.js +68 -0
- package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/displayConversion.js +4 -0
- package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
- package/dist/packages/adapters/three/src/shaders/header.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
- package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
- package/dist/packages/adapters/three/src/threeMatrix.js +10 -0
- package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
- package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +152 -0
- package/dist/packages/adapters/webcodecs/src/colorSpaceOf.js +32 -0
- package/dist/packages/core/src/application/download/BlockStore.js +97 -0
- package/dist/packages/core/src/application/download/DownloadedAudioSamples.js +50 -0
- package/dist/packages/core/src/application/download/DownloadedVideoTrack.js +122 -0
- package/dist/packages/core/src/application/download/FileDownload.js +208 -0
- package/dist/packages/core/src/application/download/RecordingBuffer.js +22 -0
- package/dist/packages/core/src/application/download/SampleCursor.js +89 -0
- package/dist/packages/core/src/application/download/SourceByteStream.js +46 -0
- package/dist/packages/core/src/application/download/Transfers.js +82 -0
- package/dist/packages/core/src/application/download/startFileDownload.js +37 -0
- package/dist/packages/core/src/application/gainMatching/GainMatching.js +76 -0
- package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +80 -0
- package/dist/packages/core/src/application/gainMatching/referenceLensOf.js +18 -0
- package/dist/packages/core/src/application/playback/Buffering.js +54 -0
- package/dist/packages/core/src/application/playback/ClockStart.js +55 -0
- package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
- package/dist/packages/core/src/application/playback/DecodePipeline.js +234 -0
- package/dist/packages/core/src/application/playback/DecodeRun.js +108 -0
- package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
- package/dist/packages/core/src/application/playback/FramePairer.js +77 -0
- package/dist/packages/core/src/application/playback/PictureOnScreen.js +56 -0
- package/dist/packages/core/src/application/playback/PlaybackSession.js +410 -0
- package/dist/packages/core/src/application/playback/SeekOrder.js +35 -0
- package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
- package/dist/packages/core/src/application/playback/StartGate.js +69 -0
- package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
- package/dist/packages/core/src/application/playback/WallClock.js +46 -0
- package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
- package/dist/packages/core/src/application/playback/probeDecoding.js +182 -0
- package/dist/packages/core/src/application/recording/frameTimesOf.js +21 -0
- package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
- package/dist/packages/core/src/application/recording/inspectRecording.js +125 -0
- package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
- package/dist/packages/core/src/application/recording/motionOf.js +43 -0
- package/dist/packages/core/src/application/recording/readRecording.js +31 -0
- package/dist/packages/core/src/application/recording/readRecordingFiles.js +130 -0
- package/dist/packages/core/src/application/recording/readSampleTable.js +27 -0
- package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
- package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +33 -0
- 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/gainMatch.js +64 -0
- package/dist/packages/core/src/domain/colour/matrixCorrection.js +76 -0
- package/dist/packages/core/src/domain/container/KeyframeRule.js +7 -0
- package/dist/packages/core/src/domain/container/SampleTable.js +29 -0
- package/dist/packages/core/src/domain/container/TrackSampleTable.js +96 -0
- package/dist/packages/core/src/domain/container/presentationOrder.js +10 -0
- package/dist/packages/core/src/domain/download/DownloadPolicy.js +84 -0
- package/dist/packages/core/src/domain/download/isReadyToResume.js +27 -0
- package/dist/packages/core/src/domain/download/planDownloads.js +175 -0
- package/dist/packages/core/src/domain/download/trackNeeds.js +32 -0
- package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
- package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
- package/dist/packages/core/src/domain/format/boxes/boxHeader.js +44 -0
- package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +32 -0
- package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +16 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +46 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +121 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +46 -0
- package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +94 -0
- package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +77 -0
- package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +59 -0
- package/dist/packages/core/src/domain/format/calibration/v6TermReading.js +20 -0
- package/dist/packages/core/src/domain/format/captureOrigin.js +34 -0
- package/dist/packages/core/src/domain/format/constants.js +35 -0
- package/dist/packages/core/src/domain/format/info/calibrationSources.js +27 -0
- package/dist/packages/core/src/domain/format/info/infoFields.js +116 -0
- package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +107 -0
- package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
- package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +133 -0
- package/dist/packages/core/src/domain/format/mp4/hevcCodecString.js +161 -0
- package/dist/packages/core/src/domain/format/mp4/keyframeRules.js +81 -0
- package/dist/packages/core/src/domain/format/mp4/movieBoxes.js +52 -0
- package/dist/packages/core/src/domain/format/mp4/mp4BoxTypes.js +46 -0
- package/dist/packages/core/src/domain/format/mp4/mp4Layouts.js +183 -0
- package/dist/packages/core/src/domain/format/mp4/parseMovie.js +72 -0
- package/dist/packages/core/src/domain/format/mp4/readTable.js +31 -0
- package/dist/packages/core/src/domain/format/mp4/sampleLocations.js +76 -0
- package/dist/packages/core/src/domain/format/mp4/sampleTiming.js +120 -0
- package/dist/packages/core/src/domain/format/mp4/trackHeaders.js +88 -0
- package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +70 -0
- package/dist/packages/core/src/domain/format/records/TrailerRecords.js +100 -0
- package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
- package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +38 -0
- package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
- package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +89 -0
- package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
- package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +138 -0
- package/dist/packages/core/src/domain/format/records/trim/parseTrimRecord.js +24 -0
- package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
- package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
- package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
- package/dist/packages/core/src/domain/format/trailer/readTrailer.js +60 -0
- package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +48 -0
- package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +70 -0
- package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
- package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
- package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +178 -0
- package/dist/packages/core/src/domain/motion/mounting/Mounting.js +131 -0
- package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +142 -0
- package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +72 -0
- package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
- package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
- package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
- package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
- package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +85 -0
- package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +58 -0
- package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
- package/dist/packages/core/src/domain/optics/EquidistantModel.js +34 -0
- package/dist/packages/core/src/domain/optics/MeiDistortion.js +27 -0
- package/dist/packages/core/src/domain/optics/MeiModel.js +27 -0
- package/dist/packages/core/src/domain/optics/PolynomialModel.js +39 -0
- package/dist/packages/core/src/domain/optics/lensPose.js +49 -0
- package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
- package/dist/packages/core/src/domain/optics/scaledProjection.js +30 -0
- package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
- package/dist/packages/core/src/domain/rendition/RenditionSpan.js +70 -0
- package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
- package/dist/packages/core/src/domain/stitching/StitchingSetup.js +87 -0
- package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
- package/dist/packages/core/src/domain/view/Framing.js +37 -0
- package/dist/packages/core/src/domain/view/Picture.js +12 -0
- package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
- package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
- package/dist/packages/core/src/domain/view/ViewState.js +54 -0
- package/dist/packages/core/src/domain/view/lensTilesView.js +64 -0
- package/dist/packages/core/src/domain/view/magnification.js +103 -0
- package/dist/packages/core/src/domain/view/motionLookView.js +100 -0
- package/dist/packages/core/src/domain/view/normalView.js +37 -0
- package/dist/packages/core/src/domain/view/panoramaView.js +95 -0
- package/dist/packages/core/src/domain/view/rectilinear.js +53 -0
- package/dist/packages/core/src/domain/view/screenLayout.js +129 -0
- package/dist/packages/core/src/domain/view/screenLook.js +74 -0
- package/dist/packages/core/src/domain/view/viewGestures.js +104 -0
- package/dist/packages/core/src/domain/view/viewModes.js +31 -0
- package/dist/packages/core/src/ports/FramePair.js +6 -0
- package/dist/packages/core/src/shared/async/Deferred.js +42 -0
- package/dist/packages/core/src/shared/async/RunStop.js +40 -0
- package/dist/packages/core/src/shared/async/iteration.js +24 -0
- package/dist/packages/core/src/shared/binary/ByteRange.js +50 -0
- package/dist/packages/core/src/shared/binary/ByteRangeSet.js +85 -0
- package/dist/packages/core/src/shared/binary/ByteReader.js +104 -0
- package/dist/packages/core/src/shared/binary/concatenated.js +15 -0
- package/dist/packages/core/src/shared/errors/GyroViewError.js +140 -0
- package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
- package/dist/packages/core/src/shared/events/Outbox.js +68 -0
- package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
- package/dist/packages/core/src/shared/keysOf.js +10 -0
- package/dist/packages/core/src/shared/lazy.js +11 -0
- package/dist/packages/core/src/shared/mapRecord.js +11 -0
- package/dist/packages/core/src/shared/math/Matrix3.js +121 -0
- package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
- package/dist/packages/core/src/shared/math/Vector3.js +58 -0
- package/dist/packages/core/src/shared/math/clamp.js +9 -0
- package/dist/packages/core/src/shared/math/countAtOrBelow.js +31 -0
- package/dist/packages/core/src/shared/math/minimum.js +17 -0
- package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +143 -0
- package/dist/packages/core/src/shared/protobuf/wireFormat.js +16 -0
- package/dist/packages/core/src/shared/text/urlPath.js +31 -0
- package/dist/packages/core/src/shared/text/utf8.js +12 -0
- package/dist/packages/core/src/shared/units/angle.js +22 -0
- package/dist/packages/core/src/shared/units/time.js +13 -0
- package/dist/packages/player/src/PlayerSource.js +14 -0
- package/dist/packages/player/src/browserPlayer.js +20 -0
- package/dist/packages/player/src/choices.js +24 -0
- package/dist/packages/player/src/composition/AnimationFrameDraws.js +38 -0
- package/dist/packages/player/src/composition/BrowserAttitudeSensor.js +191 -0
- package/dist/packages/player/src/composition/Disposables.js +31 -0
- package/dist/packages/player/src/composition/DrawingBufferFit.js +119 -0
- package/dist/packages/player/src/composition/animationFrames.js +9 -0
- package/dist/packages/player/src/composition/attitudeSensor.js +18 -0
- package/dist/packages/player/src/composition/browserPorts.js +80 -0
- package/dist/packages/player/src/composition/buildPipeline.js +186 -0
- package/dist/packages/player/src/composition/ensureDecodable.js +62 -0
- package/dist/packages/player/src/composition/openInputs.js +120 -0
- package/dist/packages/player/src/composition/openRecording.js +41 -0
- package/dist/packages/player/src/composition/readInputs.js +25 -0
- package/dist/packages/player/src/composition/renditionFrames.js +160 -0
- package/dist/packages/player/src/controls/ChoiceMenu.js +125 -0
- package/dist/packages/player/src/controls/FullscreenButton.js +29 -0
- package/dist/packages/player/src/controls/MotionLookButton.js +34 -0
- package/dist/packages/player/src/controls/PictureMenus.js +60 -0
- package/dist/packages/player/src/controls/SeekBar.js +126 -0
- package/dist/packages/player/src/controls/SoundControls.js +43 -0
- package/dist/packages/player/src/controls/TransportButtons.js +28 -0
- package/dist/packages/player/src/controls/ViewGestures.js +135 -0
- package/dist/packages/player/src/controls/Wording.js +79 -0
- package/dist/packages/player/src/controls/controlParts.js +38 -0
- package/dist/packages/player/src/controls/controls.js +4 -0
- package/dist/packages/player/src/controls/controlsBar.js +33 -0
- package/dist/packages/player/src/controls/controlsMarkup.js +100 -0
- package/dist/packages/player/src/controls/customControls.js +43 -0
- package/dist/packages/player/src/controls/formatTime.js +19 -0
- package/dist/packages/player/src/controls/icons.js +66 -0
- package/dist/packages/player/src/controls/keyboard.js +120 -0
- package/dist/packages/player/src/controls/messages.js +130 -0
- package/dist/packages/player/src/controls/nativeFullscreen.js +27 -0
- package/dist/packages/player/src/controls/parseMarkup.js +30 -0
- package/dist/packages/player/src/controls/resetViewButton.js +11 -0
- package/dist/packages/player/src/controls/sliderFill.js +15 -0
- package/dist/packages/player/src/element/ElementLoads.js +130 -0
- package/dist/packages/player/src/element/FullscreenToggle.js +93 -0
- package/dist/packages/player/src/element/GyroViewElement.js +358 -0
- package/dist/packages/player/src/element/IdleWatcher.js +71 -0
- package/dist/packages/player/src/element/PlayerCanvas.js +72 -0
- package/dist/packages/player/src/element/TypedEventElement.js +22 -0
- package/dist/packages/player/src/element/accessibleRegion.js +19 -0
- package/dist/packages/player/src/element/attributeNames.js +62 -0
- package/dist/packages/player/src/element/attributes.js +123 -0
- package/dist/packages/player/src/element/defineGyroView.js +20 -0
- package/dist/packages/player/src/element/earlyProperties.js +35 -0
- package/dist/packages/player/src/element/elementProperties.js +34 -0
- package/dist/packages/player/src/element/elementSource.js +39 -0
- package/dist/packages/player/src/element/fetchProperty.js +48 -0
- package/dist/packages/player/src/element/liveSettings.js +137 -0
- package/dist/packages/player/src/element/playbackAttributes.js +52 -0
- package/dist/packages/player/src/element/playerEventMirror.js +33 -0
- package/dist/packages/player/src/element/reflectedProperties.js +96 -0
- package/dist/packages/player/src/element/styles.js +4 -0
- package/dist/packages/player/src/element/template.js +46 -0
- package/dist/packages/player/src/inspectRecording.js +21 -0
- package/dist/packages/player/src/player/FrameLoop.js +35 -0
- package/dist/packages/player/src/player/MotionLook.js +150 -0
- package/dist/packages/player/src/player/PictureSettings.js +59 -0
- package/dist/packages/player/src/player/PlaybackStarts.js +38 -0
- package/dist/packages/player/src/player/Player.js +379 -0
- package/dist/packages/player/src/player/PlayerEvents.js +28 -0
- package/dist/packages/player/src/player/PlayerPhase.js +38 -0
- package/dist/packages/player/src/player/PlayerSound.js +60 -0
- package/dist/packages/player/src/player/PlayerView.js +143 -0
- package/dist/packages/player/src/player/SessionRelay.js +74 -0
- package/dist/packages/player/src/player/StatusAnnouncer.js +29 -0
- package/dist/packages/player/src/player/ensureFinite.js +26 -0
- package/dist/packages/player/src/player/loadRecording.js +47 -0
- package/dist/packages/player/src/player/transportEvents.js +30 -0
- package/dist/packages/player/src/player/viewAngles.js +17 -0
- package/dist/packages/player/src/rendition.js +52 -0
- package/dist/standalone.js +5633 -0
- package/package.json +70 -3
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GyroViewError, hasErrorCode } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
//#region ../../packages/adapters/fetch/src/passingFailures.ts
|
|
3
|
+
/**
|
|
4
|
+
* The failures on the way, which asking again may cure; any other fails the read at once. They
|
|
5
|
+
* are `source-unreadable` errors like any other, so a caller that gets one after the retries
|
|
6
|
+
* cannot tell, and need not: only the retry loop asks.
|
|
7
|
+
*/
|
|
8
|
+
var passingFailures = /* @__PURE__ */ new WeakSet();
|
|
9
|
+
function passing(failure) {
|
|
10
|
+
passingFailures.add(failure);
|
|
11
|
+
return failure;
|
|
12
|
+
}
|
|
13
|
+
function isPassing(error) {
|
|
14
|
+
return error instanceof GyroViewError && passingFailures.has(error);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A request that did not get through for want of a network is worth another try.
|
|
18
|
+
*/
|
|
19
|
+
function passingIfUnreachable(error) {
|
|
20
|
+
return hasErrorCode(error, "source-unreadable") && error instanceof Error ? passing(new GyroViewError("source-unreadable", error.message, { cause: error.cause })) : error;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { isPassing, passing, passingIfUnreachable };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GyroViewError } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { passing } from "./passingFailures.js";
|
|
3
|
+
//#region ../../packages/adapters/fetch/src/rangeFailures.ts
|
|
4
|
+
/**
|
|
5
|
+
* A body a dropped connection broke off after the headers: the most common way a network fails
|
|
6
|
+
* during long playback, so it is `source-unreadable` as a refused request is, not a bug or a bad
|
|
7
|
+
* file, and worth another try.
|
|
8
|
+
*/
|
|
9
|
+
function brokenOff(url, range, cause) {
|
|
10
|
+
return passing(new GyroViewError("source-unreadable", `${url} broke off the ${range.length}-byte range at ${range.offset}`, { cause }));
|
|
11
|
+
}
|
|
12
|
+
function truncated(url, received, range) {
|
|
13
|
+
return new GyroViewError("source-truncated", `${url} returned ${received} bytes for a ${range.length}-byte range at ${range.offset}`);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { brokenOff, truncated };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { contentRangeOf } from "./contentRange.js";
|
|
2
|
+
//#region ../../packages/adapters/fetch/src/recordingVersion.ts
|
|
3
|
+
var HTTP_OK = 200;
|
|
4
|
+
var WEAK_ETAG_PREFIX = /^W\//u;
|
|
5
|
+
function versionOf(response) {
|
|
6
|
+
const etag = response.headers.get("etag");
|
|
7
|
+
const lastModified = response.headers.get("last-modified");
|
|
8
|
+
const size = sizeOf(response);
|
|
9
|
+
return {
|
|
10
|
+
...etag !== null && { etag },
|
|
11
|
+
...lastModified !== null && { lastModified },
|
|
12
|
+
...size !== void 0 && { size }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether two answers are of one version of the recording, by the ETag where both tell one,
|
|
17
|
+
* else by the Last-Modified and the size; what either does not tell is taken for the same, so
|
|
18
|
+
* a server that tells nothing is never taken for one that changed its file.
|
|
19
|
+
*/
|
|
20
|
+
function isSameVersion(known, seen) {
|
|
21
|
+
return known.etag !== void 0 && seen.etag !== void 0 ? opaqueTagOf(known.etag) === opaqueTagOf(seen.etag) : isSameWhereTold(known.lastModified, seen.lastModified) && isSameWhereTold(known.size, seen.size);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The tag without its weakness mark: a CDN may weaken a strong ETag it passes on.
|
|
25
|
+
*/
|
|
26
|
+
function opaqueTagOf(etag) {
|
|
27
|
+
return etag.replace(WEAK_ETAG_PREFIX, "");
|
|
28
|
+
}
|
|
29
|
+
function isSameWhereTold(known, seen) {
|
|
30
|
+
return known === void 0 || seen === void 0 || known === seen;
|
|
31
|
+
}
|
|
32
|
+
function sizeOf(response) {
|
|
33
|
+
const size = response.status === HTTP_OK ? Number(response.headers.get("content-length") ?? NaN) : contentRangeOf(response)?.total;
|
|
34
|
+
return size !== void 0 && Number.isSafeInteger(size) && size > 0 ? size : void 0;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { isSameVersion, versionOf };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { GyroViewError, ensureInvariant } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { SegmentChannel } from "./SegmentChannel.js";
|
|
3
|
+
import { EncodedAudioPacketSource, EncodedPacket, Mp4OutputFormat, NullTarget, Output } from "mediabunny";
|
|
4
|
+
//#region ../../packages/adapters/mediabunny/src/MediabunnyAudioPackager.ts
|
|
5
|
+
/**
|
|
6
|
+
* Seconds of audio per media segment: short enough that a seek has sound quickly, long enough
|
|
7
|
+
* that appends stay cheap.
|
|
8
|
+
*/
|
|
9
|
+
var FRAGMENT_DURATION_SECONDS = 1;
|
|
10
|
+
/**
|
|
11
|
+
* Media segments that may wait for the consumer before re-packaging pauses.
|
|
12
|
+
*/
|
|
13
|
+
var SEGMENTS_AHEAD = 2;
|
|
14
|
+
/**
|
|
15
|
+
* The codec strings of the audio the cameras record, AAC (`mp4a.40.2` for AAC-LC), and mediabunny's
|
|
16
|
+
* name for it. Other codecs are not re-packaged: the picture then plays on a silent clock.
|
|
17
|
+
*/
|
|
18
|
+
var AAC_CODEC_STRING_PREFIX = "mp4a.40.";
|
|
19
|
+
var AAC = "aac";
|
|
20
|
+
/**
|
|
21
|
+
* AudioPackager over mediabunny: the samples re-packaged, unchanged, into fragmented MP4.
|
|
22
|
+
* Segments are assembled from the muxer's box callbacks rather than taken from its byte stream:
|
|
23
|
+
* that yields whole segments and leaves out the `mfra` index the muxer appends at the end, which
|
|
24
|
+
* Media Source Extensions do not accept. Fragmented output keeps the track's own timestamps, so
|
|
25
|
+
* segments started mid-track land at their true time.
|
|
26
|
+
*/
|
|
27
|
+
var MediabunnyAudioPackager = class {
|
|
28
|
+
segmentsOf(samples, configuration) {
|
|
29
|
+
const codec = muxedCodecOf(configuration);
|
|
30
|
+
return {
|
|
31
|
+
mimeType: `audio/mp4; codecs="${configuration.codec}"`,
|
|
32
|
+
duration: samples.duration,
|
|
33
|
+
segmentsFrom: (from) => ({ [Symbol.asyncIterator]: () => remux({
|
|
34
|
+
samples,
|
|
35
|
+
configuration,
|
|
36
|
+
codec
|
|
37
|
+
}, from) })
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function muxedCodecOf(configuration) {
|
|
42
|
+
if (configuration.codec.startsWith(AAC_CODEC_STRING_PREFIX)) return AAC;
|
|
43
|
+
throw new GyroViewError("codec-unsupported", `audio in ${configuration.codec} cannot be re-packaged for playback`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Starts re-packaging from `from` into a channel and hands out its consumer's side. When the
|
|
47
|
+
* consumer leaves, the samples are let go of at once, even while one is awaited, and the muxer
|
|
48
|
+
* once the producer has noticed.
|
|
49
|
+
*/
|
|
50
|
+
function remux(parts, from) {
|
|
51
|
+
const channel = new SegmentChannel(SEGMENTS_AHEAD);
|
|
52
|
+
const output = new Output({
|
|
53
|
+
format: fragmentedMp4Into(channel),
|
|
54
|
+
target: new NullTarget()
|
|
55
|
+
});
|
|
56
|
+
const source = new EncodedAudioPacketSource(parts.codec);
|
|
57
|
+
output.addAudioTrack(source);
|
|
58
|
+
const samples = parts.samples.samplesFrom(from)[Symbol.asyncIterator]();
|
|
59
|
+
const production = produce({
|
|
60
|
+
samples,
|
|
61
|
+
configuration: parts.configuration,
|
|
62
|
+
output,
|
|
63
|
+
source,
|
|
64
|
+
channel
|
|
65
|
+
});
|
|
66
|
+
return channel.segments(() => {
|
|
67
|
+
samples.return?.();
|
|
68
|
+
releaseAfter(production, output);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Frees the muxer once the producer has noticed the closed channel, without making a consumer
|
|
73
|
+
* that left early (a seek) wait for a sample still in flight.
|
|
74
|
+
*/
|
|
75
|
+
async function releaseAfter(production, output) {
|
|
76
|
+
await production;
|
|
77
|
+
if (output.state === "finalized") return;
|
|
78
|
+
try {
|
|
79
|
+
await output.cancel();
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Feeds samples to the muxer at the consumer's pace; every failure ends up in the channel.
|
|
84
|
+
*/
|
|
85
|
+
async function produce(run) {
|
|
86
|
+
try {
|
|
87
|
+
await run.output.start();
|
|
88
|
+
let meta = { decoderConfig: decoderConfigOf(run.configuration) };
|
|
89
|
+
for (let next = await run.samples.next(); next.done !== true; next = await run.samples.next()) {
|
|
90
|
+
await run.channel.waitForRoom();
|
|
91
|
+
if (run.channel.isClosed) return;
|
|
92
|
+
await run.source.add(packetOf(next.value), meta);
|
|
93
|
+
meta = void 0;
|
|
94
|
+
}
|
|
95
|
+
if (run.channel.isClosed) return;
|
|
96
|
+
await run.output.finalize();
|
|
97
|
+
run.channel.close();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
run.channel.fail(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function packetOf(sample) {
|
|
103
|
+
return new EncodedPacket(sample.data, "key", sample.timestamp, sample.duration);
|
|
104
|
+
}
|
|
105
|
+
function decoderConfigOf(configuration) {
|
|
106
|
+
return {
|
|
107
|
+
codec: configuration.codec,
|
|
108
|
+
sampleRate: configuration.sampleRate,
|
|
109
|
+
numberOfChannels: configuration.numberOfChannels,
|
|
110
|
+
...configuration.description && { description: configuration.description }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The muxer's boxes handed on as whole segments: the initialization segment (`ftyp` and `moov`)
|
|
115
|
+
* and each media segment (`moof` and its `mdat`). A consumer that stops between two segments, as
|
|
116
|
+
* a seek does, then never leaves a source buffer's parser inside one, where Chromium reads the
|
|
117
|
+
* next segments' bytes as the samples of the `moof` it holds and fails to decode them.
|
|
118
|
+
*/
|
|
119
|
+
function fragmentedMp4Into(channel) {
|
|
120
|
+
const segment = new SegmentAssembly(channel);
|
|
121
|
+
return new Mp4OutputFormat({
|
|
122
|
+
fastStart: "fragmented",
|
|
123
|
+
minimumFragmentDuration: FRAGMENT_DURATION_SECONDS,
|
|
124
|
+
onFtyp: (data) => {
|
|
125
|
+
segment.begin(data);
|
|
126
|
+
},
|
|
127
|
+
onMoov: (data) => {
|
|
128
|
+
segment.end(data);
|
|
129
|
+
},
|
|
130
|
+
onMoof: (data) => {
|
|
131
|
+
segment.begin(data);
|
|
132
|
+
},
|
|
133
|
+
onMdat: (data) => {
|
|
134
|
+
segment.end(data);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* A segment's first box held until the box that ends it arrives, then both pushed as one.
|
|
140
|
+
*/
|
|
141
|
+
var SegmentAssembly = class {
|
|
142
|
+
channel;
|
|
143
|
+
opening;
|
|
144
|
+
constructor(channel) {
|
|
145
|
+
this.channel = channel;
|
|
146
|
+
}
|
|
147
|
+
begin(box) {
|
|
148
|
+
ensureInvariant(this.opening === void 0, "a segment began before the last one ended");
|
|
149
|
+
this.opening = new Uint8Array(box);
|
|
150
|
+
}
|
|
151
|
+
end(box) {
|
|
152
|
+
const { opening } = this;
|
|
153
|
+
ensureInvariant(opening !== void 0, "a segment ended that never began");
|
|
154
|
+
const whole = new Uint8Array(opening.byteLength + box.byteLength);
|
|
155
|
+
whole.set(opening);
|
|
156
|
+
whole.set(box, opening.byteLength);
|
|
157
|
+
this.opening = void 0;
|
|
158
|
+
this.channel.push(whole);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
//#endregion
|
|
162
|
+
export { MediabunnyAudioPackager };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { GyroViewError, isAbortError } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { audioConfigurationOf, codecStringOf, videoConfigurationOf } from "./decoderConfigurations.js";
|
|
3
|
+
import { trackColourOf } from "./trackColour.js";
|
|
4
|
+
import { BufferSource, Input, MP4, QTFF } from "mediabunny";
|
|
5
|
+
//#region ../../packages/adapters/mediabunny/src/MediabunnyCodecReader.ts
|
|
6
|
+
/**
|
|
7
|
+
* The movie bytes are an ISO BMFF file's type and movie boxes, whatever its brand: with only
|
|
8
|
+
* these two formats named, a page's bundle leaves out mediabunny's other demuxers, a third of
|
|
9
|
+
* its weight.
|
|
10
|
+
*/
|
|
11
|
+
var MOVIE_FORMATS = [MP4, QTFF];
|
|
12
|
+
/**
|
|
13
|
+
* CodecReader over mediabunny: opens the file type and movie boxes as a file of their own, in
|
|
14
|
+
* memory, and tells each track's decoder configuration. mediabunny reads a sample only for a
|
|
15
|
+
* video codec without its configuration box; the samples are not there, so that track is
|
|
16
|
+
* refused as the file's failing.
|
|
17
|
+
*/
|
|
18
|
+
var MediabunnyCodecReader = class {
|
|
19
|
+
async read(movieBytes) {
|
|
20
|
+
const input = new Input({
|
|
21
|
+
formats: MOVIE_FORMATS,
|
|
22
|
+
source: new BufferSource(movieBytes)
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
const [videoTracks, audioTracks] = await Promise.all([input.getVideoTracks(), input.getAudioTracks()]);
|
|
26
|
+
return {
|
|
27
|
+
video: await Promise.all(videoTracks.map((track, index) => videoCodecOf(track, index))),
|
|
28
|
+
audio: (await Promise.all(audioTracks.map((track) => audioCodecOf(track)))).flat()
|
|
29
|
+
};
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw failureOf(error);
|
|
32
|
+
} finally {
|
|
33
|
+
input.dispose();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
async function videoCodecOf(track, trackIndex) {
|
|
38
|
+
const [config, codedWidth, codedHeight] = await Promise.all([
|
|
39
|
+
configurationOf(track, trackIndex),
|
|
40
|
+
track.getCodedWidth(),
|
|
41
|
+
track.getCodedHeight()
|
|
42
|
+
]);
|
|
43
|
+
const description = {
|
|
44
|
+
trackIndex,
|
|
45
|
+
codedWidth,
|
|
46
|
+
codedHeight,
|
|
47
|
+
codec: await codecStringOf(track, config),
|
|
48
|
+
colour: trackColourOf(config.colorSpace)
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
trackId: track.id,
|
|
52
|
+
description,
|
|
53
|
+
configuration: videoConfigurationOf(config, description)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The configuration of a codec that carries it in the movie box. A sample entry without one, or
|
|
58
|
+
* with one mediabunny cannot read, fails as the file's: no browser could configure a decoder from
|
|
59
|
+
* it, and the movie bytes hold no sample to find it in.
|
|
60
|
+
*/
|
|
61
|
+
async function configurationOf(track, trackIndex) {
|
|
62
|
+
let config;
|
|
63
|
+
try {
|
|
64
|
+
config = await track.getDecoderConfig();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
throw noConfiguration(trackIndex, { cause: error });
|
|
67
|
+
}
|
|
68
|
+
if (config === null) throw noConfiguration(trackIndex);
|
|
69
|
+
return config;
|
|
70
|
+
}
|
|
71
|
+
function noConfiguration(trackIndex, options) {
|
|
72
|
+
return new GyroViewError("unsupported-container", `video track ${trackIndex} has no decoder configuration in its sample entry`, options);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* An audio track mediabunny cannot configure is left out: the picture then plays without sound
|
|
76
|
+
* rather than not at all.
|
|
77
|
+
*/
|
|
78
|
+
async function audioCodecOf(track) {
|
|
79
|
+
const config = await track.getDecoderConfig();
|
|
80
|
+
return config === null ? [] : [{
|
|
81
|
+
trackId: track.id,
|
|
82
|
+
configuration: audioConfigurationOf(config)
|
|
83
|
+
}];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A caller's abort and the core's own failures pass as they are; anything else mediabunny
|
|
87
|
+
* throws means the bytes are no movie it can read.
|
|
88
|
+
*/
|
|
89
|
+
function failureOf(error) {
|
|
90
|
+
return isAbortError(error) || error instanceof GyroViewError ? error : new GyroViewError("unsupported-container", "the movie box is not a readable movie", { cause: error });
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { MediabunnyCodecReader };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { GyroViewError, ensureInvariant } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { ITERATION_END } from "../../../core/src/shared/async/iteration.js";
|
|
3
|
+
//#region ../../packages/adapters/mediabunny/src/SegmentChannel.ts
|
|
4
|
+
/**
|
|
5
|
+
* Hands segments from the muxer's synchronous callbacks to one asynchronous consumer, with a
|
|
6
|
+
* bound on how many may wait unconsumed so re-packaging never runs far ahead of playback.
|
|
7
|
+
* Closing from either side ends the exchange; a producer failure reaches the consumer as a
|
|
8
|
+
* thrown error once the segments before it are drained. A second concurrent consumer is a
|
|
9
|
+
* programming error.
|
|
10
|
+
*/
|
|
11
|
+
var SegmentChannel = class {
|
|
12
|
+
capacity;
|
|
13
|
+
queue = [];
|
|
14
|
+
roomWaiters = [];
|
|
15
|
+
takeWaiter;
|
|
16
|
+
isClosedNow = false;
|
|
17
|
+
failure;
|
|
18
|
+
constructor(capacity) {
|
|
19
|
+
this.capacity = capacity;
|
|
20
|
+
}
|
|
21
|
+
get isClosed() {
|
|
22
|
+
return this.isClosedNow;
|
|
23
|
+
}
|
|
24
|
+
push(segment) {
|
|
25
|
+
if (this.isClosedNow) return;
|
|
26
|
+
this.queue.push(segment);
|
|
27
|
+
this.wakeTaker();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolves once fewer than `capacity` segments wait, or at once when the channel is closed.
|
|
31
|
+
*/
|
|
32
|
+
waitForRoom() {
|
|
33
|
+
return this.queue.length < this.capacity || this.isClosedNow ? Promise.resolve() : new Promise((resolve) => {
|
|
34
|
+
this.roomWaiters.push(resolve);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
close() {
|
|
38
|
+
this.isClosedNow = true;
|
|
39
|
+
this.wakeTaker();
|
|
40
|
+
this.wakeRoomWaiters();
|
|
41
|
+
}
|
|
42
|
+
fail(error) {
|
|
43
|
+
this.failure ??= error instanceof Error ? error : new GyroViewError("decode", "audio re-packaging failed", { cause: error });
|
|
44
|
+
this.close();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The consumer's side: the segments in order until the channel is closed and drained, then
|
|
48
|
+
* the producer's failure if it had one. Returning closes the channel at once, even while a
|
|
49
|
+
* segment is awaited, which then comes as the end. `onEnd` hears once of the end, however it
|
|
50
|
+
* came: the producer's resources may go.
|
|
51
|
+
*/
|
|
52
|
+
segments(onEnd = doNothing) {
|
|
53
|
+
return new SegmentReading({
|
|
54
|
+
take: () => this.take(),
|
|
55
|
+
close: () => {
|
|
56
|
+
this.close();
|
|
57
|
+
},
|
|
58
|
+
onEnd
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The next segment; undefined once closed and drained; the producer's failure, if it had one.
|
|
63
|
+
*/
|
|
64
|
+
async take() {
|
|
65
|
+
while (this.queue.length === 0 && !this.isClosedNow) await this.nextPush();
|
|
66
|
+
const next = this.queue.shift();
|
|
67
|
+
if (next) {
|
|
68
|
+
this.wakeRoomWaiters();
|
|
69
|
+
return next;
|
|
70
|
+
}
|
|
71
|
+
if (this.failure) throw this.failure;
|
|
72
|
+
}
|
|
73
|
+
nextPush() {
|
|
74
|
+
ensureInvariant(this.takeWaiter === void 0, "a segment channel has one consumer");
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
this.takeWaiter = resolve;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
wakeTaker() {
|
|
80
|
+
const waiter = this.takeWaiter;
|
|
81
|
+
this.takeWaiter = void 0;
|
|
82
|
+
waiter?.();
|
|
83
|
+
}
|
|
84
|
+
wakeRoomWaiters() {
|
|
85
|
+
if (this.queue.length >= this.capacity && !this.isClosedNow) return;
|
|
86
|
+
const waiters = this.roomWaiters;
|
|
87
|
+
this.roomWaiters = [];
|
|
88
|
+
for (const resolve of waiters) resolve();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Iterates a channel's segments. A class, not a generator: a generator's return waits behind the
|
|
93
|
+
* segment it awaits, and the producer would re-package another fragment for nobody.
|
|
94
|
+
*/
|
|
95
|
+
var SegmentReading = class {
|
|
96
|
+
parts;
|
|
97
|
+
hasEnded = false;
|
|
98
|
+
constructor(parts) {
|
|
99
|
+
this.parts = parts;
|
|
100
|
+
}
|
|
101
|
+
async next() {
|
|
102
|
+
if (this.hasEnded) return ITERATION_END;
|
|
103
|
+
try {
|
|
104
|
+
const segment = await this.parts.take();
|
|
105
|
+
return segment === void 0 || this.wasEnded() ? this.end() : {
|
|
106
|
+
done: false,
|
|
107
|
+
value: segment
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.end();
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return() {
|
|
115
|
+
this.parts.close();
|
|
116
|
+
return Promise.resolve(this.end());
|
|
117
|
+
}
|
|
118
|
+
[Symbol.asyncIterator]() {
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Asked afresh after the wait: the consumer may have returned meanwhile.
|
|
123
|
+
*/
|
|
124
|
+
wasEnded() {
|
|
125
|
+
return this.hasEnded;
|
|
126
|
+
}
|
|
127
|
+
end() {
|
|
128
|
+
if (!this.hasEnded) this.parts.onEnd();
|
|
129
|
+
this.hasEnded = true;
|
|
130
|
+
return ITERATION_END;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
function doNothing() {}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { SegmentChannel };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region ../../packages/adapters/mediabunny/src/bufferSources.ts
|
|
2
|
+
/**
|
|
3
|
+
* A copy of a buffer source as a plain Uint8Array. Decoder descriptions are small; the copy keeps
|
|
4
|
+
* them independent of mediabunny's own buffers and never on shared memory. SharedArrayBuffer is
|
|
5
|
+
* not named, since browsers define it only under cross-origin isolation.
|
|
6
|
+
*/
|
|
7
|
+
function copyOfBytes(source) {
|
|
8
|
+
const view = ArrayBuffer.isView(source) ? new Uint8Array(source.buffer, source.byteOffset, source.byteLength) : new Uint8Array(source);
|
|
9
|
+
return Uint8Array.from(view);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { copyOfBytes };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { hevcCodecStringOf } from "../../../core/src/domain/format/mp4/hevcCodecString.js";
|
|
2
|
+
import { copyOfBytes } from "./bufferSources.js";
|
|
3
|
+
//#region ../../packages/adapters/mediabunny/src/decoderConfigurations.ts
|
|
4
|
+
/**
|
|
5
|
+
* The sample entry types an HEVC track's codec string begins with (ISO/IEC 14496-15 §8.4.1,
|
|
6
|
+
* Annex E.3). An encrypted track's entry, `encv`, names its protection instead.
|
|
7
|
+
*/
|
|
8
|
+
var HEVC_SAMPLE_ENTRY_TYPES = /* @__PURE__ */ new Set(["hvc1", "hev1"]);
|
|
9
|
+
/**
|
|
10
|
+
* A video track's codec string: mediabunny's, but for an HEVC track, whose codec mediabunny
|
|
11
|
+
* names from the configuration's header alone, the core's, which reads a blank header's SPS.
|
|
12
|
+
*/
|
|
13
|
+
async function codecStringOf(track, config) {
|
|
14
|
+
const [codec, sampleEntryType] = await Promise.all([track.getCodec(), track.getInternalCodecId()]);
|
|
15
|
+
return codec === "hevc" && typeof sampleEntryType === "string" && HEVC_SAMPLE_ENTRY_TYPES.has(sampleEntryType) && config.description !== void 0 ? hevcCodecStringOf(sampleEntryType, copyOfBytes(config.description)) : config.codec;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A WebCodecs video decoder configuration in the core's terms; the codec string and the colour
|
|
19
|
+
* come from the track's description, the coded size from the track where the configuration
|
|
20
|
+
* leaves it out.
|
|
21
|
+
*/
|
|
22
|
+
function videoConfigurationOf(config, description) {
|
|
23
|
+
return {
|
|
24
|
+
codec: description.codec,
|
|
25
|
+
codedWidth: config.codedWidth ?? description.codedWidth,
|
|
26
|
+
codedHeight: config.codedHeight ?? description.codedHeight,
|
|
27
|
+
description: config.description === void 0 ? void 0 : copyOfBytes(config.description),
|
|
28
|
+
colour: description.colour
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A WebCodecs audio decoder configuration in the core's terms.
|
|
33
|
+
*/
|
|
34
|
+
function audioConfigurationOf(config) {
|
|
35
|
+
return {
|
|
36
|
+
codec: config.codec,
|
|
37
|
+
sampleRate: config.sampleRate,
|
|
38
|
+
numberOfChannels: config.numberOfChannels,
|
|
39
|
+
description: config.description === void 0 ? void 0 : copyOfBytes(config.description)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { audioConfigurationOf, codecStringOf, videoConfigurationOf };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { COLOUR_PRIMARIES, MATRIX_COEFFICIENTS, TRANSFER_CHARACTERISTICS, namedOrUnspecified } from "../../../core/src/domain/colour/TrackColour.js";
|
|
2
|
+
//#region ../../packages/adapters/mediabunny/src/trackColour.ts
|
|
3
|
+
/**
|
|
4
|
+
* A track's colour in the core's terms, from the colour space mediabunny reads off its sample
|
|
5
|
+
* entry or, without a `colr` box, its SPS: a value the core does not name is unspecified.
|
|
6
|
+
*/
|
|
7
|
+
function trackColourOf(colorSpace) {
|
|
8
|
+
return {
|
|
9
|
+
primaries: namedOrUnspecified(COLOUR_PRIMARIES, colorSpace?.primaries),
|
|
10
|
+
transfer: namedOrUnspecified(TRANSFER_CHARACTERISTICS, colorSpace?.transfer),
|
|
11
|
+
matrix: namedOrUnspecified(MATRIX_COEFFICIENTS, colorSpace?.matrix),
|
|
12
|
+
range: rangeOf(colorSpace?.fullRange)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function rangeOf(fullRange) {
|
|
16
|
+
if (fullRange === true) return "full";
|
|
17
|
+
return fullRange === false ? "limited" : "unspecified";
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { trackColourOf };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { GyroViewError, isAbortError, messageOf } from "../../../core/src/shared/errors/GyroViewError.js";
|
|
2
|
+
import { seconds } from "../../../core/src/shared/units/time.js";
|
|
3
|
+
import { attachMediaSource, isMediaSourceTypeSupported, mediaSourceConstructor } from "./mediaSourceSupport.js";
|
|
4
|
+
import { SourceBufferFeeder } from "./SourceBufferFeeder.js";
|
|
5
|
+
//#region ../../packages/adapters/mse-audio/src/MediaSourceAudioClock.ts
|
|
6
|
+
/**
|
|
7
|
+
* PlaybackClock over an audio element fed through Media Source Extensions with the recording's
|
|
8
|
+
* own audio track, so the picture follows the sound and volume and mute are the element's.
|
|
9
|
+
* The element is borrowed from the host; disposing detaches the media source from it.
|
|
10
|
+
* See ADR 0007.
|
|
11
|
+
*/
|
|
12
|
+
var MediaSourceAudioClock = class MediaSourceAudioClock {
|
|
13
|
+
element;
|
|
14
|
+
feeder;
|
|
15
|
+
attached;
|
|
16
|
+
/**
|
|
17
|
+
* The end the clock stands at since a seek there, the element left where it was.
|
|
18
|
+
*/
|
|
19
|
+
standingEnd;
|
|
20
|
+
constructor(element, feeder, attached) {
|
|
21
|
+
this.element = element;
|
|
22
|
+
this.feeder = feeder;
|
|
23
|
+
this.attached = attached;
|
|
24
|
+
}
|
|
25
|
+
static isSupported(source) {
|
|
26
|
+
return isMediaSourceTypeSupported(source.mimeType);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Attaches a media source to the element and starts buffering from the beginning.
|
|
30
|
+
*/
|
|
31
|
+
static async open(element, source) {
|
|
32
|
+
const mediaSourceClass = mediaSourceConstructor();
|
|
33
|
+
if (!mediaSourceClass?.isTypeSupported(source.mimeType)) throw new GyroViewError("codec-unsupported", `${source.mimeType} cannot play through Media Source Extensions here`);
|
|
34
|
+
const attached = await attachMediaSource(element, mediaSourceClass);
|
|
35
|
+
try {
|
|
36
|
+
const sourceBuffer = attached.mediaSource.addSourceBuffer(source.mimeType);
|
|
37
|
+
attached.mediaSource.duration = source.duration;
|
|
38
|
+
const feeder = new SourceBufferFeeder({
|
|
39
|
+
element,
|
|
40
|
+
mediaSource: attached.mediaSource,
|
|
41
|
+
sourceBuffer,
|
|
42
|
+
source
|
|
43
|
+
});
|
|
44
|
+
feeder.restartFrom(seconds(0));
|
|
45
|
+
return new MediaSourceAudioClock(element, feeder, attached);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
attached.detach();
|
|
48
|
+
throw asClockFailure(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
get currentTime() {
|
|
52
|
+
return this.standingEnd ?? seconds(this.element.currentTime);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whoever paused the element (the session, media keys, an audio interruption): `play()` clears
|
|
56
|
+
* `paused` at once, so a start in flight counts as running.
|
|
57
|
+
*/
|
|
58
|
+
get isRunning() {
|
|
59
|
+
return !this.element.paused;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Standing at the end since a seek there, or the element `ended`: it played to its end.
|
|
63
|
+
*/
|
|
64
|
+
get hasEnded() {
|
|
65
|
+
return this.standingEnd !== void 0 || this.element.ended;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* What the feeder met, or the element's own error: sound that buffered but would not decode,
|
|
69
|
+
* which stalls the element without the feeder ever hearing of it.
|
|
70
|
+
*/
|
|
71
|
+
get failure() {
|
|
72
|
+
return this.feeder.failure ?? failureOfElement(this.element);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolves once playback has started. A `pause` that interrupts the start is not an error;
|
|
76
|
+
* the autoplay policy refusing to start is reported as `playback-blocked`. A clock at its end
|
|
77
|
+
* stays there: `play()` would play the element from the beginning, or from where a seek to the
|
|
78
|
+
* end left it, under the last picture.
|
|
79
|
+
*/
|
|
80
|
+
async start() {
|
|
81
|
+
if (this.hasEnded) return;
|
|
82
|
+
try {
|
|
83
|
+
await this.element.play();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (isAbortError(error)) return;
|
|
86
|
+
const isBlocked = isNamed(error, "NotAllowedError");
|
|
87
|
+
throw new GyroViewError(isBlocked ? "playback-blocked" : "decode", isBlocked ? "the browser refused to start audio playback; a user gesture is needed" : "audio playback failed to start", { cause: error });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
pause() {
|
|
91
|
+
this.element.pause();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A seek to the end stands the clock there and leaves the element paused where it was: an
|
|
95
|
+
* element at its end has nothing left to play, and engines differ right at the end. WebKit on
|
|
96
|
+
* Linux, under load, fails an element whose seek to the end of its sound is still pending when
|
|
97
|
+
* the stream ends there (media error 4); macOS WebKit loses a seek just short of the end.
|
|
98
|
+
*/
|
|
99
|
+
seek(time) {
|
|
100
|
+
const end = this.attached.mediaSource.duration;
|
|
101
|
+
if (time >= end) {
|
|
102
|
+
this.standAtEnd(seconds(end));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.standingEnd = void 0;
|
|
106
|
+
this.element.currentTime = time;
|
|
107
|
+
this.feeder.restartFrom(time);
|
|
108
|
+
}
|
|
109
|
+
dispose() {
|
|
110
|
+
this.feeder.dispose();
|
|
111
|
+
this.element.pause();
|
|
112
|
+
this.attached.detach();
|
|
113
|
+
}
|
|
114
|
+
standAtEnd(end) {
|
|
115
|
+
this.element.pause();
|
|
116
|
+
this.standingEnd = end;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function isNamed(error, name) {
|
|
120
|
+
return error instanceof Error && error.name === name;
|
|
121
|
+
}
|
|
122
|
+
function failureOfElement(element) {
|
|
123
|
+
const { error } = element;
|
|
124
|
+
if (error === null) return void 0;
|
|
125
|
+
const message = `the audio element failed (media error ${error.code})`;
|
|
126
|
+
return new GyroViewError("decode", message, { cause: error });
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The media source's own exceptions cross the port as typed failures: a type it will not buffer
|
|
130
|
+
* is `codec-unsupported`, anything else `decode`.
|
|
131
|
+
*/
|
|
132
|
+
function asClockFailure(error) {
|
|
133
|
+
if (error instanceof GyroViewError) return error;
|
|
134
|
+
const isUnsupported = isNamed(error, "NotSupportedError");
|
|
135
|
+
return new GyroViewError(isUnsupported ? "codec-unsupported" : "decode", `the audio element refused the recording's sound: ${messageOf(error)}`, { cause: error });
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
export { MediaSourceAudioClock };
|