@editframe/elements 0.16.8-beta.0 → 0.18.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/DecoderResetFrequency.test.d.ts +1 -0
- package/dist/DecoderResetRecovery.test.d.ts +1 -0
- package/dist/DelayedLoadingState.d.ts +48 -0
- package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
- package/dist/DelayedLoadingState.js +113 -0
- package/dist/DelayedLoadingState.test.d.ts +1 -0
- package/dist/EF_FRAMEGEN.d.ts +10 -1
- package/dist/EF_FRAMEGEN.js +199 -179
- package/dist/EF_INTERACTIVE.js +2 -6
- package/dist/EF_RENDERING.js +1 -3
- package/dist/LoadingDebounce.test.d.ts +1 -0
- package/dist/LoadingIndicator.browsertest.d.ts +0 -0
- package/dist/ManualScrubTest.test.d.ts +1 -0
- package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
- package/dist/ScrubTrackManager.d.ts +96 -0
- package/dist/ScrubTrackManager.test.d.ts +1 -0
- package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
- package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
- package/dist/elements/CrossUpdateController.js +13 -15
- package/dist/elements/EFAudio.browsertest.d.ts +0 -0
- package/dist/elements/EFAudio.d.ts +22 -3
- package/dist/elements/EFAudio.js +60 -43
- package/dist/elements/EFCaptions.js +337 -373
- package/dist/elements/EFImage.d.ts +1 -0
- package/dist/elements/EFImage.js +73 -91
- package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
- package/dist/elements/EFMedia/AssetIdMediaEngine.test.d.ts +1 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
- package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
- package/dist/elements/EFMedia/BaseMediaEngine.test.d.ts +1 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
- package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
- package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
- package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
- package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
- package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
- package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia.d.ts +95 -66
- package/dist/elements/EFMedia.js +204 -683
- package/dist/elements/EFSourceMixin.js +31 -48
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +266 -360
- package/dist/elements/EFTimegroup.d.ts +14 -1
- package/dist/elements/EFTimegroup.js +337 -323
- package/dist/elements/EFVideo.browsertest.d.ts +0 -0
- package/dist/elements/EFVideo.d.ts +123 -4
- package/dist/elements/EFVideo.js +308 -111
- package/dist/elements/EFWaveform.js +375 -411
- package/dist/elements/FetchMixin.js +14 -24
- package/dist/elements/MediaController.d.ts +30 -0
- package/dist/elements/SampleBuffer.d.ts +14 -0
- package/dist/elements/SampleBuffer.js +52 -0
- package/dist/elements/TargetController.js +130 -156
- package/dist/elements/TimegroupController.js +17 -19
- package/dist/elements/durationConverter.js +15 -4
- package/dist/elements/parseTimeToMs.js +4 -10
- package/dist/elements/printTaskStatus.d.ts +2 -0
- package/dist/elements/updateAnimations.js +39 -59
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/getRenderInfo.js +59 -67
- package/dist/gui/ContextMixin.js +150 -288
- package/dist/gui/EFConfiguration.js +27 -43
- package/dist/gui/EFFilmstrip.d.ts +3 -3
- package/dist/gui/EFFilmstrip.js +440 -620
- package/dist/gui/EFFitScale.d.ts +2 -2
- package/dist/gui/EFFitScale.js +112 -135
- package/dist/gui/EFFocusOverlay.js +45 -61
- package/dist/gui/EFPreview.js +30 -49
- package/dist/gui/EFScrubber.js +78 -99
- package/dist/gui/EFTimeDisplay.js +49 -70
- package/dist/gui/EFToggleLoop.js +17 -34
- package/dist/gui/EFTogglePlay.js +37 -58
- package/dist/gui/EFWorkbench.js +66 -88
- package/dist/gui/TWMixin.js +2 -48
- package/dist/gui/TWMixin2.js +31 -0
- package/dist/gui/efContext.js +2 -6
- package/dist/gui/fetchContext.js +1 -3
- package/dist/gui/focusContext.js +1 -3
- package/dist/gui/focusedElementContext.js +2 -6
- package/dist/gui/playingContext.js +1 -4
- package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
- package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
- package/dist/gui/services/ElementConnectionManager.js +128 -0
- package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
- package/dist/gui/services/PlaybackController.d.ts +103 -0
- package/dist/gui/services/PlaybackController.js +290 -0
- package/dist/index.js +5 -30
- package/dist/msToTimeCode.js +11 -13
- package/dist/services/MediaSourceManager.d.ts +62 -0
- package/dist/services/MediaSourceManager.js +211 -0
- package/dist/style.css +2 -1
- package/dist/transcoding/cache/CacheManager.d.ts +73 -0
- package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
- package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
- package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
- package/dist/transcoding/types/index.d.ts +242 -0
- package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
- package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
- package/dist/transcoding/utils/UrlGenerator.js +45 -0
- package/dist/transcoding/utils/constants.d.ts +27 -0
- package/dist/utils/LRUCache.d.ts +34 -0
- package/dist/utils/LRUCache.js +115 -0
- package/package.json +4 -3
- package/src/elements/EFAudio.browsertest.ts +709 -0
- package/src/elements/EFAudio.ts +59 -15
- package/src/elements/EFCaptions.browsertest.ts +0 -1
- package/src/elements/EFImage.browsertest.ts +42 -1
- package/src/elements/EFImage.ts +23 -3
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
- package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
- package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
- package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
- package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
- package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
- package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
- package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia.browsertest.ts +696 -271
- package/src/elements/EFMedia.ts +218 -776
- package/src/elements/EFTemporal.browsertest.ts +0 -1
- package/src/elements/EFTemporal.ts +13 -3
- package/src/elements/EFTimegroup.browsertest.ts +6 -3
- package/src/elements/EFTimegroup.ts +221 -27
- package/src/elements/EFVideo.browsertest.ts +758 -0
- package/src/elements/EFVideo.ts +418 -68
- package/src/elements/EFWaveform.ts +5 -5
- package/src/elements/MediaController.ts +98 -0
- package/src/elements/SampleBuffer.ts +97 -0
- package/src/elements/printTaskStatus.ts +16 -0
- package/src/elements/updateAnimations.ts +6 -0
- package/src/gui/ContextMixin.ts +23 -104
- package/src/gui/TWMixin.ts +10 -3
- package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
- package/src/gui/services/ElementConnectionManager.ts +224 -0
- package/src/gui/services/PlaybackController.browsertest.ts +437 -0
- package/src/gui/services/PlaybackController.ts +521 -0
- package/src/services/MediaSourceManager.ts +333 -0
- package/src/transcoding/cache/CacheManager.ts +208 -0
- package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
- package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
- package/src/transcoding/types/index.ts +265 -0
- package/src/transcoding/utils/MediaUtils.ts +63 -0
- package/src/transcoding/utils/UrlGenerator.ts +68 -0
- package/src/transcoding/utils/constants.ts +36 -0
- package/src/utils/LRUCache.ts +153 -0
- package/test/EFVideo.framegen.browsertest.ts +127 -0
- package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
- package/test/createJitTestClips.ts +425 -0
- package/test/recordReplayProxyPlugin.js +302 -0
- package/test/useAssetMSW.ts +49 -0
- package/test/useMSW.ts +44 -0
- package/types.json +1 -1
- package/dist/gui/TWMixin.css.js +0 -4
- /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
- /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages dynamic connection/disconnection of media elements to AudioContext
|
|
3
|
+
* Extracted from ContextMixin to improve separation of concerns and testability
|
|
4
|
+
*/
|
|
5
|
+
export declare class ElementConnectionManager {
|
|
6
|
+
private connectedMediaSources;
|
|
7
|
+
private lookaheadMs;
|
|
8
|
+
constructor(lookaheadMs?: number);
|
|
9
|
+
/**
|
|
10
|
+
* Update connected media elements based on current playhead position
|
|
11
|
+
* Connects upcoming elements and disconnects past elements
|
|
12
|
+
*/
|
|
13
|
+
updateConnectedElements(audioContext: AudioContext, timegroup: any, // EFTimegroup type
|
|
14
|
+
currentMs: number): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Find elements that should be connected based on timeline position
|
|
17
|
+
*/
|
|
18
|
+
private getElementsToConnect;
|
|
19
|
+
/**
|
|
20
|
+
* Connect new elements that aren't already connected
|
|
21
|
+
*/
|
|
22
|
+
private connectNewElements;
|
|
23
|
+
/**
|
|
24
|
+
* Update connection states for all managed elements
|
|
25
|
+
*/
|
|
26
|
+
private updateElementStates;
|
|
27
|
+
/**
|
|
28
|
+
* Activate an element (connect to destination and start playback)
|
|
29
|
+
*/
|
|
30
|
+
private activateElement;
|
|
31
|
+
/**
|
|
32
|
+
* Deactivate an element (disconnect but keep prepared)
|
|
33
|
+
*/
|
|
34
|
+
private deactivateElement;
|
|
35
|
+
/**
|
|
36
|
+
* Clean up elements that are far in the past
|
|
37
|
+
*/
|
|
38
|
+
private cleanupOldElements;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all connected media sources (for cleanup)
|
|
41
|
+
*/
|
|
42
|
+
clearAll(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Get connection status for testing/debugging
|
|
45
|
+
*/
|
|
46
|
+
getConnectionInfo(): {
|
|
47
|
+
total: number;
|
|
48
|
+
connected: number;
|
|
49
|
+
prepared: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Set lookahead time
|
|
53
|
+
*/
|
|
54
|
+
setLookaheadMs(lookaheadMs: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* Get current lookahead time
|
|
57
|
+
*/
|
|
58
|
+
getLookaheadMs(): number;
|
|
59
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages dynamic connection/disconnection of media elements to AudioContext
|
|
3
|
+
* Extracted from ContextMixin to improve separation of concerns and testability
|
|
4
|
+
*/
|
|
5
|
+
var ElementConnectionManager = class {
|
|
6
|
+
constructor(lookaheadMs = 3e3) {
|
|
7
|
+
this.connectedMediaSources = /* @__PURE__ */ new Map();
|
|
8
|
+
this.lookaheadMs = lookaheadMs;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Update connected media elements based on current playhead position
|
|
12
|
+
* Connects upcoming elements and disconnects past elements
|
|
13
|
+
*/
|
|
14
|
+
async updateConnectedElements(audioContext, timegroup, currentMs) {
|
|
15
|
+
if (!audioContext || audioContext.state === "closed") return;
|
|
16
|
+
const allMediaElements = Array.from(timegroup.querySelectorAll("ef-audio, ef-video"));
|
|
17
|
+
const lookaheadMs = currentMs + this.lookaheadMs;
|
|
18
|
+
const elementsToConnect = this.getElementsToConnect(allMediaElements, currentMs, lookaheadMs);
|
|
19
|
+
await this.connectNewElements(audioContext, elementsToConnect);
|
|
20
|
+
await this.updateElementStates(currentMs);
|
|
21
|
+
this.cleanupOldElements(currentMs);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Find elements that should be connected based on timeline position
|
|
25
|
+
*/
|
|
26
|
+
getElementsToConnect(allElements, currentMs, lookaheadMs) {
|
|
27
|
+
return allElements.filter((mediaElement) => {
|
|
28
|
+
const startTime = mediaElement.startTimeMs;
|
|
29
|
+
const endTime = mediaElement.endTimeMs;
|
|
30
|
+
const isCurrentlyActive = currentMs >= startTime && currentMs < endTime;
|
|
31
|
+
const isStartingSoon = startTime > currentMs && startTime <= lookaheadMs;
|
|
32
|
+
return isCurrentlyActive || isStartingSoon;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Connect new elements that aren't already connected
|
|
37
|
+
*/
|
|
38
|
+
async connectNewElements(audioContext, elementsToConnect) {
|
|
39
|
+
for (const mediaElement of elementsToConnect) if (!this.connectedMediaSources.has(mediaElement)) {
|
|
40
|
+
const mediaElementSource = await mediaElement.getMediaElementSource(audioContext);
|
|
41
|
+
this.connectedMediaSources.set(mediaElement, {
|
|
42
|
+
mediaElementSource,
|
|
43
|
+
connected: false
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Update connection states for all managed elements
|
|
49
|
+
*/
|
|
50
|
+
async updateElementStates(currentMs) {
|
|
51
|
+
for (const [mediaElement, sourceInfo] of this.connectedMediaSources.entries()) {
|
|
52
|
+
const startTime = mediaElement.startTimeMs;
|
|
53
|
+
const endTime = mediaElement.endTimeMs;
|
|
54
|
+
const isCurrentlyActive = currentMs >= startTime && currentMs < endTime;
|
|
55
|
+
if (isCurrentlyActive && !sourceInfo.connected) await this.activateElement(mediaElement, sourceInfo);
|
|
56
|
+
else if (!isCurrentlyActive && sourceInfo.connected) await this.deactivateElement(mediaElement, sourceInfo);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Activate an element (connect to destination and start playback)
|
|
61
|
+
*/
|
|
62
|
+
async activateElement(mediaElement, sourceInfo) {
|
|
63
|
+
sourceInfo.mediaElementSource.connect(sourceInfo.mediaElementSource.context.destination);
|
|
64
|
+
sourceInfo.connected = true;
|
|
65
|
+
if (mediaElement.audioElement) {
|
|
66
|
+
const mediaTimeMs = mediaElement.currentSourceTimeMs;
|
|
67
|
+
mediaElement.audioElement.currentTime = mediaTimeMs / 1e3;
|
|
68
|
+
await mediaElement.audioElement.play();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Deactivate an element (disconnect but keep prepared)
|
|
73
|
+
*/
|
|
74
|
+
async deactivateElement(mediaElement, sourceInfo) {
|
|
75
|
+
sourceInfo.mediaElementSource.disconnect();
|
|
76
|
+
sourceInfo.connected = false;
|
|
77
|
+
if (mediaElement.audioElement) mediaElement.audioElement.pause();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Clean up elements that are far in the past
|
|
81
|
+
*/
|
|
82
|
+
cleanupOldElements(currentMs) {
|
|
83
|
+
const cleanupThresholdMs = currentMs - this.lookaheadMs;
|
|
84
|
+
for (const [mediaElement, sourceInfo] of this.connectedMediaSources.entries()) {
|
|
85
|
+
const endTime = mediaElement.endTimeMs;
|
|
86
|
+
if (endTime < cleanupThresholdMs) {
|
|
87
|
+
if (sourceInfo.connected) sourceInfo.mediaElementSource.disconnect();
|
|
88
|
+
this.connectedMediaSources.delete(mediaElement);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Clear all connected media sources (for cleanup)
|
|
94
|
+
*/
|
|
95
|
+
clearAll() {
|
|
96
|
+
for (const [, sourceInfo] of this.connectedMediaSources.entries()) try {
|
|
97
|
+
if (sourceInfo.connected) sourceInfo.mediaElementSource.disconnect();
|
|
98
|
+
} catch (_error) {}
|
|
99
|
+
this.connectedMediaSources.clear();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get connection status for testing/debugging
|
|
103
|
+
*/
|
|
104
|
+
getConnectionInfo() {
|
|
105
|
+
let connected = 0;
|
|
106
|
+
let prepared = 0;
|
|
107
|
+
for (const [, sourceInfo] of this.connectedMediaSources.entries()) if (sourceInfo.connected) connected++;
|
|
108
|
+
else prepared++;
|
|
109
|
+
return {
|
|
110
|
+
total: this.connectedMediaSources.size,
|
|
111
|
+
connected,
|
|
112
|
+
prepared
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Set lookahead time
|
|
117
|
+
*/
|
|
118
|
+
setLookaheadMs(lookaheadMs) {
|
|
119
|
+
this.lookaheadMs = lookaheadMs;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get current lookahead time
|
|
123
|
+
*/
|
|
124
|
+
getLookaheadMs() {
|
|
125
|
+
return this.lookaheadMs;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
export { ElementConnectionManager };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export interface PlaybackControllerOptions {
|
|
2
|
+
fps?: number;
|
|
3
|
+
onTimeUpdate?: (timeMs: number) => void;
|
|
4
|
+
onPlayStateChange?: (playing: boolean) => void;
|
|
5
|
+
onError?: (error: Error) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Manages playback timing, AudioContext lifecycle, and timeline synchronization
|
|
9
|
+
* Extracted from ContextMixin to improve separation of concerns and testability
|
|
10
|
+
*/
|
|
11
|
+
export declare class PlaybackController {
|
|
12
|
+
private playbackAudioContext;
|
|
13
|
+
private animationFrameRequest;
|
|
14
|
+
private options;
|
|
15
|
+
private playing;
|
|
16
|
+
private currentTimeMs;
|
|
17
|
+
private msPerFrame;
|
|
18
|
+
private audioStartTime;
|
|
19
|
+
private playbackStartTimeMs;
|
|
20
|
+
private activeChunks;
|
|
21
|
+
private chunkDurationMs;
|
|
22
|
+
private lookaheadChunks;
|
|
23
|
+
private currentChunkIndex;
|
|
24
|
+
private renderingChunks;
|
|
25
|
+
constructor(options?: PlaybackControllerOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Start playback for the given timegroup
|
|
28
|
+
*/
|
|
29
|
+
startPlayback(timegroup: any, fromMs?: number): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Stop playback and clean up resources
|
|
32
|
+
*/
|
|
33
|
+
stopPlayback(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Pause playback (can be resumed)
|
|
36
|
+
*/
|
|
37
|
+
pausePlayback(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Resume paused playback
|
|
40
|
+
*/
|
|
41
|
+
resumePlayback(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Seek to a specific time (restarts progressive playback from new position)
|
|
44
|
+
*/
|
|
45
|
+
seekTo(timeMs: number, timegroup?: any): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Internal method to sync playhead with unified audio buffer timing
|
|
48
|
+
*/
|
|
49
|
+
private syncPlayheadToAudioBuffer;
|
|
50
|
+
/**
|
|
51
|
+
* Update playing state and notify observers
|
|
52
|
+
*/
|
|
53
|
+
private setPlaying;
|
|
54
|
+
/**
|
|
55
|
+
* Get current playback state
|
|
56
|
+
*/
|
|
57
|
+
isPlaying(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Get current time
|
|
60
|
+
*/
|
|
61
|
+
getCurrentTime(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get current AudioContext
|
|
64
|
+
*/
|
|
65
|
+
getAudioContext(): AudioContext | null;
|
|
66
|
+
/**
|
|
67
|
+
* Check if AudioContext is ready
|
|
68
|
+
*/
|
|
69
|
+
isAudioContextReady(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Get playback statistics for debugging
|
|
72
|
+
*/
|
|
73
|
+
getPlaybackInfo(): {
|
|
74
|
+
playing: boolean;
|
|
75
|
+
currentTimeMs: number;
|
|
76
|
+
audioContextState: string | null;
|
|
77
|
+
hasElementManager: boolean;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Update playback options
|
|
81
|
+
*/
|
|
82
|
+
updateOptions(options: Partial<PlaybackControllerOptions>): void;
|
|
83
|
+
/**
|
|
84
|
+
* Start progressive chunk rendering and playback
|
|
85
|
+
*/
|
|
86
|
+
private startProgressivePlayback;
|
|
87
|
+
/**
|
|
88
|
+
* Render and schedule a single audio chunk
|
|
89
|
+
*/
|
|
90
|
+
private renderAndScheduleChunk;
|
|
91
|
+
/**
|
|
92
|
+
* Update chunk rendering as playhead advances - now handles all chunk management
|
|
93
|
+
*/
|
|
94
|
+
private updateProgressiveChunks;
|
|
95
|
+
/**
|
|
96
|
+
* Systematically ensure chunks are ready ahead of current playback (synchronous)
|
|
97
|
+
*/
|
|
98
|
+
private ensureChunksAhead;
|
|
99
|
+
/**
|
|
100
|
+
* Clean up chunks that are behind the current playhead
|
|
101
|
+
*/
|
|
102
|
+
private cleanupOldChunks;
|
|
103
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages playback timing, AudioContext lifecycle, and timeline synchronization
|
|
3
|
+
* Extracted from ContextMixin to improve separation of concerns and testability
|
|
4
|
+
*/
|
|
5
|
+
var PlaybackController = class {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.playbackAudioContext = null;
|
|
8
|
+
this.animationFrameRequest = null;
|
|
9
|
+
this.playing = false;
|
|
10
|
+
this.currentTimeMs = 0;
|
|
11
|
+
this.audioStartTime = 0;
|
|
12
|
+
this.playbackStartTimeMs = 0;
|
|
13
|
+
this.activeChunks = /* @__PURE__ */ new Map();
|
|
14
|
+
this.chunkDurationMs = 4e3;
|
|
15
|
+
this.lookaheadChunks = 2;
|
|
16
|
+
this.currentChunkIndex = 0;
|
|
17
|
+
this.renderingChunks = /* @__PURE__ */ new Set();
|
|
18
|
+
this.options = {
|
|
19
|
+
fps: 30,
|
|
20
|
+
onTimeUpdate: () => {},
|
|
21
|
+
onPlayStateChange: () => {},
|
|
22
|
+
onError: () => {},
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
this.msPerFrame = 1e3 / this.options.fps;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Start playback for the given timegroup
|
|
29
|
+
*/
|
|
30
|
+
async startPlayback(timegroup, fromMs) {
|
|
31
|
+
await this.stopPlayback();
|
|
32
|
+
if (!timegroup) {
|
|
33
|
+
this.setPlaying(false);
|
|
34
|
+
this.options.onPlayStateChange(false);
|
|
35
|
+
this.options.onError(/* @__PURE__ */ new Error("No timegroup provided"));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
await timegroup.waitForMediaDurations?.();
|
|
39
|
+
const currentMs = fromMs ?? timegroup.currentTimeMs ?? 0;
|
|
40
|
+
const toMs = timegroup.endTimeMs;
|
|
41
|
+
if (currentMs >= toMs) {
|
|
42
|
+
this.setPlaying(false);
|
|
43
|
+
this.options.onPlayStateChange(false);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
this.playbackAudioContext = new AudioContext({ latencyHint: "playback" });
|
|
48
|
+
if (this.playbackAudioContext.state === "suspended") {
|
|
49
|
+
console.warn("AudioContext is suspended, attempting to resume...");
|
|
50
|
+
try {
|
|
51
|
+
await Promise.race([this.playbackAudioContext.resume(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("AudioContext resume timeout")), 2e3))]);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn("AudioContext resume failed:", error);
|
|
54
|
+
}
|
|
55
|
+
} else await this.playbackAudioContext.resume();
|
|
56
|
+
this.audioStartTime = this.playbackAudioContext.currentTime;
|
|
57
|
+
this.playbackStartTimeMs = currentMs;
|
|
58
|
+
this.currentChunkIndex = Math.floor(currentMs / this.chunkDurationMs);
|
|
59
|
+
await this.startProgressivePlayback(timegroup, currentMs, toMs);
|
|
60
|
+
if (this.isAudioContextReady()) {
|
|
61
|
+
this.setPlaying(true);
|
|
62
|
+
this.currentTimeMs = currentMs;
|
|
63
|
+
this.options.onTimeUpdate(currentMs);
|
|
64
|
+
this.syncPlayheadToAudioBuffer(timegroup, currentMs);
|
|
65
|
+
} else {
|
|
66
|
+
this.setPlaying(false);
|
|
67
|
+
this.options.onPlayStateChange(false);
|
|
68
|
+
console.warn("AudioContext not ready for playback, state:", this.playbackAudioContext?.state);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("🎵 [PLAYBACK_ERROR] Failed to setup progressive audio playback:", error);
|
|
72
|
+
this.setPlaying(false);
|
|
73
|
+
this.options.onPlayStateChange(false);
|
|
74
|
+
this.options.onError(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Stop playback and clean up resources
|
|
79
|
+
*/
|
|
80
|
+
async stopPlayback() {
|
|
81
|
+
for (const [_chunkIndex, bufferSource] of this.activeChunks.entries()) try {
|
|
82
|
+
bufferSource.stop();
|
|
83
|
+
} catch (_error) {}
|
|
84
|
+
this.activeChunks.clear();
|
|
85
|
+
this.renderingChunks.clear();
|
|
86
|
+
if (this.playbackAudioContext) {
|
|
87
|
+
if (this.playbackAudioContext.state !== "closed") await this.playbackAudioContext.close();
|
|
88
|
+
}
|
|
89
|
+
if (this.animationFrameRequest) {
|
|
90
|
+
cancelAnimationFrame(this.animationFrameRequest);
|
|
91
|
+
this.animationFrameRequest = null;
|
|
92
|
+
}
|
|
93
|
+
this.playbackAudioContext = null;
|
|
94
|
+
this.setPlaying(false);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Pause playback (can be resumed)
|
|
98
|
+
*/
|
|
99
|
+
async pausePlayback() {
|
|
100
|
+
if (this.playbackAudioContext && this.playbackAudioContext.state === "running") try {
|
|
101
|
+
await Promise.race([this.playbackAudioContext.suspend(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("AudioContext suspend timeout")), 2e3))]);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.warn("AudioContext suspend failed:", error);
|
|
104
|
+
}
|
|
105
|
+
if (this.animationFrameRequest) {
|
|
106
|
+
cancelAnimationFrame(this.animationFrameRequest);
|
|
107
|
+
this.animationFrameRequest = null;
|
|
108
|
+
}
|
|
109
|
+
this.setPlaying(false);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resume paused playback
|
|
113
|
+
*/
|
|
114
|
+
async resumePlayback() {
|
|
115
|
+
if (this.playbackAudioContext && this.playbackAudioContext.state === "suspended") {
|
|
116
|
+
try {
|
|
117
|
+
await Promise.race([this.playbackAudioContext.resume(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("AudioContext resume timeout")), 2e3))]);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.warn("AudioContext resume failed:", error);
|
|
120
|
+
}
|
|
121
|
+
this.setPlaying(true);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Seek to a specific time (restarts progressive playback from new position)
|
|
126
|
+
*/
|
|
127
|
+
async seekTo(timeMs, timegroup) {
|
|
128
|
+
this.currentTimeMs = timeMs;
|
|
129
|
+
this.options.onTimeUpdate(timeMs);
|
|
130
|
+
if (this.playing && timegroup) {
|
|
131
|
+
for (const bufferSource of this.activeChunks.values()) try {
|
|
132
|
+
bufferSource.stop();
|
|
133
|
+
} catch (_error) {}
|
|
134
|
+
this.activeChunks.clear();
|
|
135
|
+
this.renderingChunks.clear();
|
|
136
|
+
this.audioStartTime = this.playbackAudioContext?.currentTime ?? 0;
|
|
137
|
+
this.playbackStartTimeMs = timeMs;
|
|
138
|
+
this.currentChunkIndex = Math.floor(timeMs / this.chunkDurationMs);
|
|
139
|
+
await this.startProgressivePlayback(timegroup, timeMs, timegroup.endTimeMs);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Internal method to sync playhead with unified audio buffer timing
|
|
144
|
+
*/
|
|
145
|
+
syncPlayheadToAudioBuffer(timegroup, startMs) {
|
|
146
|
+
if (!this.playbackAudioContext || !this.playing) return;
|
|
147
|
+
const elapsedAudioTime = this.playbackAudioContext.currentTime - this.audioStartTime;
|
|
148
|
+
const rawTimeMs = startMs + elapsedAudioTime * 1e3;
|
|
149
|
+
const nextTimeMs = Math.round(rawTimeMs / this.msPerFrame) * this.msPerFrame;
|
|
150
|
+
if (nextTimeMs !== this.currentTimeMs) {
|
|
151
|
+
this.currentTimeMs = nextTimeMs;
|
|
152
|
+
this.options.onTimeUpdate(nextTimeMs);
|
|
153
|
+
if (timegroup && timegroup.currentTimeMs !== nextTimeMs) timegroup.currentTimeMs = nextTimeMs;
|
|
154
|
+
this.updateProgressiveChunks(timegroup, nextTimeMs, timegroup.endTimeMs);
|
|
155
|
+
}
|
|
156
|
+
this.animationFrameRequest = requestAnimationFrame(() => {
|
|
157
|
+
this.syncPlayheadToAudioBuffer(timegroup, startMs);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Update playing state and notify observers
|
|
162
|
+
*/
|
|
163
|
+
setPlaying(playing) {
|
|
164
|
+
if (this.playing !== playing) {
|
|
165
|
+
this.playing = playing;
|
|
166
|
+
this.options.onPlayStateChange(playing);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get current playback state
|
|
171
|
+
*/
|
|
172
|
+
isPlaying() {
|
|
173
|
+
return this.playing;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get current time
|
|
177
|
+
*/
|
|
178
|
+
getCurrentTime() {
|
|
179
|
+
return this.currentTimeMs;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get current AudioContext
|
|
183
|
+
*/
|
|
184
|
+
getAudioContext() {
|
|
185
|
+
return this.playbackAudioContext;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Check if AudioContext is ready
|
|
189
|
+
*/
|
|
190
|
+
isAudioContextReady() {
|
|
191
|
+
return this.playbackAudioContext != null && this.playbackAudioContext.state !== "closed";
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get playback statistics for debugging
|
|
195
|
+
*/
|
|
196
|
+
getPlaybackInfo() {
|
|
197
|
+
return {
|
|
198
|
+
playing: this.playing,
|
|
199
|
+
currentTimeMs: this.currentTimeMs,
|
|
200
|
+
audioContextState: this.playbackAudioContext?.state || null,
|
|
201
|
+
hasElementManager: false
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Update playback options
|
|
206
|
+
*/
|
|
207
|
+
updateOptions(options) {
|
|
208
|
+
Object.assign(this.options, options);
|
|
209
|
+
if (options.fps) this.msPerFrame = 1e3 / options.fps;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Start progressive chunk rendering and playback
|
|
213
|
+
*/
|
|
214
|
+
async startProgressivePlayback(timegroup, fromMs, _toMs) {
|
|
215
|
+
const firstChunkIndex = Math.floor(fromMs / this.chunkDurationMs);
|
|
216
|
+
const firstChunkStart = firstChunkIndex * this.chunkDurationMs;
|
|
217
|
+
const offsetInChunk = fromMs - firstChunkStart;
|
|
218
|
+
await this.renderAndScheduleChunk(timegroup, firstChunkStart, firstChunkIndex, offsetInChunk);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Render and schedule a single audio chunk
|
|
222
|
+
*/
|
|
223
|
+
async renderAndScheduleChunk(timegroup, chunkStartMs, chunkIndex, offsetInChunk = 0) {
|
|
224
|
+
if (this.renderingChunks.has(chunkIndex) || this.activeChunks.has(chunkIndex)) return;
|
|
225
|
+
this.renderingChunks.add(chunkIndex);
|
|
226
|
+
try {
|
|
227
|
+
const chunkEndMs = chunkStartMs + this.chunkDurationMs;
|
|
228
|
+
const chunkBuffer = await timegroup.renderAudio(chunkStartMs, chunkEndMs);
|
|
229
|
+
const bufferSource = this.playbackAudioContext?.createBufferSource();
|
|
230
|
+
if (!bufferSource || !this.playbackAudioContext?.destination) throw new Error("Audio context or buffer source not available");
|
|
231
|
+
bufferSource.buffer = chunkBuffer;
|
|
232
|
+
bufferSource.connect(this.playbackAudioContext.destination);
|
|
233
|
+
const chunkTimelineStartMs = chunkIndex * this.chunkDurationMs;
|
|
234
|
+
const relativeDelayMs = Math.max(0, chunkTimelineStartMs - this.playbackStartTimeMs);
|
|
235
|
+
const chunkStartTime = this.audioStartTime + relativeDelayMs / 1e3;
|
|
236
|
+
const startOffset = offsetInChunk / 1e3;
|
|
237
|
+
const now = this.playbackAudioContext?.currentTime ?? 0;
|
|
238
|
+
if (chunkStartTime <= now) console.warn(`🎵 [CHUNK_TIMING_WARNING] Chunk ${chunkIndex} scheduled in the past! startTime=${chunkStartTime.toFixed(3)}s, currentTime=${now.toFixed(3)}s`);
|
|
239
|
+
bufferSource.start(chunkStartTime, startOffset);
|
|
240
|
+
this.activeChunks.set(chunkIndex, bufferSource);
|
|
241
|
+
bufferSource.onended = () => {
|
|
242
|
+
this.activeChunks.delete(chunkIndex);
|
|
243
|
+
};
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`🎵 [CHUNK_ERROR] Failed to render chunk ${chunkIndex}:`, error);
|
|
246
|
+
} finally {
|
|
247
|
+
this.renderingChunks.delete(chunkIndex);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Update chunk rendering as playhead advances - now handles all chunk management
|
|
252
|
+
*/
|
|
253
|
+
updateProgressiveChunks(timegroup, currentTimeMs, maxTimeMs) {
|
|
254
|
+
const newChunkIndex = Math.floor(currentTimeMs / this.chunkDurationMs);
|
|
255
|
+
if (newChunkIndex !== this.currentChunkIndex) {
|
|
256
|
+
this.currentChunkIndex = newChunkIndex;
|
|
257
|
+
this.cleanupOldChunks();
|
|
258
|
+
}
|
|
259
|
+
this.ensureChunksAhead(timegroup, maxTimeMs);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Systematically ensure chunks are ready ahead of current playback (synchronous)
|
|
263
|
+
*/
|
|
264
|
+
ensureChunksAhead(timegroup, maxTimeMs) {
|
|
265
|
+
for (let i = 1; i <= this.lookaheadChunks; i++) {
|
|
266
|
+
const targetChunkIndex = this.currentChunkIndex + i;
|
|
267
|
+
const targetChunkStartMs = targetChunkIndex * this.chunkDurationMs;
|
|
268
|
+
if (targetChunkStartMs >= maxTimeMs) break;
|
|
269
|
+
if (!this.renderingChunks.has(targetChunkIndex) && !this.activeChunks.has(targetChunkIndex)) {
|
|
270
|
+
const offsetInChunk = 0;
|
|
271
|
+
this.renderAndScheduleChunk(timegroup, targetChunkStartMs, targetChunkIndex, offsetInChunk).catch((error) => {
|
|
272
|
+
console.error(`🎵 [ENSURE_CHUNKS_ERROR] Failed to render chunk ${targetChunkIndex}:`, error);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Clean up chunks that are behind the current playhead
|
|
279
|
+
*/
|
|
280
|
+
cleanupOldChunks() {
|
|
281
|
+
const cutoffChunkIndex = this.currentChunkIndex - 1;
|
|
282
|
+
for (const [chunkIndex, bufferSource] of this.activeChunks.entries()) if (chunkIndex < cutoffChunkIndex) {
|
|
283
|
+
try {
|
|
284
|
+
bufferSource.stop();
|
|
285
|
+
} catch (_error) {}
|
|
286
|
+
this.activeChunks.delete(chunkIndex);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
export { PlaybackController };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/* empty css
|
|
1
|
+
/* empty css */
|
|
2
|
+
import { EFConfiguration } from "./gui/EFConfiguration.js";
|
|
3
|
+
import "./elements/EFMedia.js";
|
|
2
4
|
import { EFTimegroup } from "./elements/EFTimegroup.js";
|
|
3
5
|
import { EFImage } from "./elements/EFImage.js";
|
|
4
|
-
import "./elements/EFMedia.js";
|
|
5
6
|
import { EFAudio } from "./elements/EFAudio.js";
|
|
6
7
|
import { EFVideo } from "./elements/EFVideo.js";
|
|
7
8
|
import { EFCaptions, EFCaptionsActiveWord, EFCaptionsAfterActiveWord, EFCaptionsBeforeActiveWord, EFCaptionsSegment } from "./elements/EFCaptions.js";
|
|
8
9
|
import { EFWaveform } from "./elements/EFWaveform.js";
|
|
9
|
-
import { EFConfiguration } from "./gui/EFConfiguration.js";
|
|
10
10
|
import { EFWorkbench } from "./gui/EFWorkbench.js";
|
|
11
11
|
import { EFPreview } from "./gui/EFPreview.js";
|
|
12
12
|
import { EFFilmstrip } from "./gui/EFFilmstrip.js";
|
|
@@ -18,30 +18,5 @@ import { EFFocusOverlay } from "./gui/EFFocusOverlay.js";
|
|
|
18
18
|
import { EFFitScale } from "./gui/EFFitScale.js";
|
|
19
19
|
import "./EF_FRAMEGEN.js";
|
|
20
20
|
import { RenderInfo, getRenderInfo } from "./getRenderInfo.js";
|
|
21
|
-
if (typeof window !== "undefined")
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
export {
|
|
25
|
-
EFAudio,
|
|
26
|
-
EFCaptions,
|
|
27
|
-
EFCaptionsActiveWord,
|
|
28
|
-
EFCaptionsAfterActiveWord,
|
|
29
|
-
EFCaptionsBeforeActiveWord,
|
|
30
|
-
EFCaptionsSegment,
|
|
31
|
-
EFConfiguration,
|
|
32
|
-
EFFilmstrip,
|
|
33
|
-
EFFitScale,
|
|
34
|
-
EFFocusOverlay,
|
|
35
|
-
EFImage,
|
|
36
|
-
EFPreview,
|
|
37
|
-
EFScrubber,
|
|
38
|
-
EFTimeDisplay,
|
|
39
|
-
EFTimegroup,
|
|
40
|
-
EFToggleLoop,
|
|
41
|
-
EFTogglePlay,
|
|
42
|
-
EFVideo,
|
|
43
|
-
EFWaveform,
|
|
44
|
-
EFWorkbench,
|
|
45
|
-
RenderInfo,
|
|
46
|
-
getRenderInfo
|
|
47
|
-
};
|
|
21
|
+
if (typeof window !== "undefined") window.EF_REGISTERED = true;
|
|
22
|
+
export { EFAudio, EFCaptions, EFCaptionsActiveWord, EFCaptionsAfterActiveWord, EFCaptionsBeforeActiveWord, EFCaptionsSegment, EFConfiguration, EFFilmstrip, EFFitScale, EFFocusOverlay, EFImage, EFPreview, EFScrubber, EFTimeDisplay, EFTimegroup, EFToggleLoop, EFTogglePlay, EFVideo, EFWaveform, EFWorkbench, RenderInfo, getRenderInfo };
|
package/dist/msToTimeCode.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
const msToTimeCode = (ms, subSecond = false) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
export {
|
|
14
|
-
msToTimeCode
|
|
2
|
+
const seconds = Math.floor(ms / 1e3);
|
|
3
|
+
const minutes = Math.floor(seconds / 60);
|
|
4
|
+
const hours = Math.floor(minutes / 60);
|
|
5
|
+
const pad = (num) => num.toString().padStart(2, "0");
|
|
6
|
+
let timecode = `${pad(hours)}:${pad(minutes % 60)}:${pad(seconds % 60)}`;
|
|
7
|
+
if (subSecond) {
|
|
8
|
+
const subSeconds = Math.floor(ms % 1e3 / 10);
|
|
9
|
+
timecode += `.${subSeconds.toString().padStart(2, "0")}`;
|
|
10
|
+
}
|
|
11
|
+
return timecode;
|
|
15
12
|
};
|
|
13
|
+
export { msToTimeCode };
|