@editframe/elements 0.18.3-beta.0 → 0.18.8-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.
Files changed (110) hide show
  1. package/dist/elements/EFAudio.d.ts +1 -2
  2. package/dist/elements/EFAudio.js +6 -9
  3. package/dist/elements/EFMedia/AssetMediaEngine.browsertest.d.ts +0 -0
  4. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +2 -4
  5. package/dist/elements/EFMedia/AssetMediaEngine.js +34 -5
  6. package/dist/elements/EFMedia/BaseMediaEngine.js +20 -1
  7. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +5 -5
  8. package/dist/elements/EFMedia/BufferedSeekingInput.js +27 -7
  9. package/dist/elements/EFMedia/JitMediaEngine.d.ts +1 -1
  10. package/dist/elements/EFMedia/JitMediaEngine.js +22 -3
  11. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +4 -1
  12. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +11 -3
  13. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.d.ts +0 -0
  14. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +17 -4
  15. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +11 -1
  16. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +3 -2
  17. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +4 -1
  18. package/dist/elements/EFMedia/shared/PrecisionUtils.d.ts +28 -0
  19. package/dist/elements/EFMedia/shared/PrecisionUtils.js +29 -0
  20. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +11 -2
  21. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +11 -1
  22. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +3 -2
  23. package/dist/elements/EFMedia.d.ts +0 -12
  24. package/dist/elements/EFMedia.js +4 -30
  25. package/dist/elements/EFTimegroup.js +12 -17
  26. package/dist/elements/EFVideo.d.ts +0 -9
  27. package/dist/elements/EFVideo.js +0 -7
  28. package/dist/elements/SampleBuffer.js +6 -6
  29. package/dist/getRenderInfo.d.ts +2 -2
  30. package/dist/gui/ContextMixin.js +71 -17
  31. package/dist/gui/TWMixin.js +1 -1
  32. package/dist/style.css +1 -1
  33. package/dist/transcoding/types/index.d.ts +9 -9
  34. package/package.json +2 -3
  35. package/src/elements/EFAudio.browsertest.ts +7 -7
  36. package/src/elements/EFAudio.ts +7 -20
  37. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +100 -0
  38. package/src/elements/EFMedia/AssetMediaEngine.ts +72 -7
  39. package/src/elements/EFMedia/BaseMediaEngine.ts +50 -1
  40. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +135 -54
  41. package/src/elements/EFMedia/BufferedSeekingInput.ts +74 -17
  42. package/src/elements/EFMedia/JitMediaEngine.ts +58 -2
  43. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +10 -1
  44. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +16 -8
  45. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +199 -0
  46. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +35 -4
  47. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +12 -1
  48. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +3 -2
  49. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +10 -1
  50. package/src/elements/EFMedia/shared/PrecisionUtils.ts +46 -0
  51. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +27 -3
  52. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +12 -1
  53. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +3 -2
  54. package/src/elements/EFMedia.browsertest.ts +73 -33
  55. package/src/elements/EFMedia.ts +11 -54
  56. package/src/elements/EFTimegroup.ts +21 -26
  57. package/src/elements/EFVideo.browsertest.ts +895 -162
  58. package/src/elements/EFVideo.ts +0 -16
  59. package/src/elements/SampleBuffer.ts +8 -10
  60. package/src/gui/ContextMixin.ts +104 -26
  61. package/src/transcoding/types/index.ts +10 -6
  62. package/test/EFVideo.framegen.browsertest.ts +1 -1
  63. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +3 -3
  64. package/test/__cache__/GET__api_v1_transcode_audio_1_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__9ed2d25c675aa6bb6ff5b3ae23887c71/data.bin +0 -0
  65. package/test/__cache__/GET__api_v1_transcode_audio_1_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__9ed2d25c675aa6bb6ff5b3ae23887c71/metadata.json +22 -0
  66. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +3 -3
  67. package/test/__cache__/GET__api_v1_transcode_audio_2_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__d5a3309a2bf756dd6e304807eb402f56/data.bin +0 -0
  68. package/test/__cache__/GET__api_v1_transcode_audio_2_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__d5a3309a2bf756dd6e304807eb402f56/metadata.json +22 -0
  69. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +3 -3
  70. package/test/__cache__/GET__api_v1_transcode_audio_3_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__773254bb671e3466fca8677139fb239e/data.bin +0 -0
  71. package/test/__cache__/GET__api_v1_transcode_audio_3_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__773254bb671e3466fca8677139fb239e/metadata.json +22 -0
  72. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +3 -3
  73. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +3 -3
  74. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +3 -3
  75. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +3 -3
  76. package/test/__cache__/GET__api_v1_transcode_high_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/data.bin +0 -0
  77. package/test/__cache__/GET__api_v1_transcode_high_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/metadata.json +21 -0
  78. package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/data.bin +0 -0
  79. package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/metadata.json +21 -0
  80. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +3 -3
  81. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -1
  82. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +4 -4
  83. package/test/recordReplayProxyPlugin.js +50 -0
  84. package/types.json +1 -1
  85. package/dist/DecoderResetFrequency.test.d.ts +0 -1
  86. package/dist/DecoderResetRecovery.test.d.ts +0 -1
  87. package/dist/ScrubTrackManager.d.ts +0 -96
  88. package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +0 -1
  89. package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +0 -22
  90. package/dist/elements/EFMedia/services/AudioElementFactory.js +0 -72
  91. package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +0 -1
  92. package/dist/elements/EFMedia/services/MediaSourceService.d.ts +0 -47
  93. package/dist/elements/EFMedia/services/MediaSourceService.js +0 -73
  94. package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +0 -1
  95. package/dist/gui/services/ElementConnectionManager.d.ts +0 -59
  96. package/dist/gui/services/ElementConnectionManager.js +0 -128
  97. package/dist/gui/services/PlaybackController.browsertest.d.ts +0 -1
  98. package/dist/gui/services/PlaybackController.d.ts +0 -103
  99. package/dist/gui/services/PlaybackController.js +0 -290
  100. package/dist/services/MediaSourceManager.d.ts +0 -62
  101. package/dist/services/MediaSourceManager.js +0 -211
  102. package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +0 -325
  103. package/src/elements/EFMedia/services/AudioElementFactory.ts +0 -119
  104. package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +0 -257
  105. package/src/elements/EFMedia/services/MediaSourceService.ts +0 -102
  106. package/src/gui/services/ElementConnectionManager.browsertest.ts +0 -263
  107. package/src/gui/services/ElementConnectionManager.ts +0 -224
  108. package/src/gui/services/PlaybackController.browsertest.ts +0 -437
  109. package/src/gui/services/PlaybackController.ts +0 -521
  110. package/src/services/MediaSourceManager.ts +0 -333
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,96 +0,0 @@
1
- export interface ScrubTrackConfig {
2
- maxScrubCacheSegments?: number;
3
- prefetchCount?: number;
4
- fastSeekThresholdMs?: number;
5
- onLoadingStateChange?: (isLoading: boolean, message?: string) => void;
6
- }
7
- export interface CacheStats {
8
- hits: number;
9
- misses: number;
10
- hitRate: number;
11
- }
12
- export declare class ScrubTrackManager {
13
- readonly videoUrl: string;
14
- private jitClient;
15
- private config;
16
- private scrubCache;
17
- private metadata;
18
- private cacheHits;
19
- private cacheMisses;
20
- private isInitialized;
21
- private preloadingSegments;
22
- constructor(videoUrl: string, jitClient: any, // JitTranscodingClient,
23
- config?: ScrubTrackConfig);
24
- /**
25
- * Initialize scrub track manager and start preloading
26
- */
27
- initialize(): Promise<void>;
28
- /**
29
- * Determine if scrub track should be used instead of normal video
30
- */
31
- shouldUseScrubTrack(seekTimeMs: number): boolean;
32
- /**
33
- * Detect if this is a fast seeking operation
34
- */
35
- isFastSeeking(currentTimeMs: number, seekTimeMs: number): boolean;
36
- /**
37
- * Align seek time to 30s scrub segment boundary
38
- */
39
- alignToScrubBoundary(timeMs: number): number;
40
- /**
41
- * Get scrub frame for the given seek time
42
- */
43
- getScrubFrame(seekTimeMs: number): Promise<VideoFrame | null>;
44
- /**
45
- * Record cache hit for statistics
46
- */
47
- recordCacheHit(): void;
48
- /**
49
- * Record cache miss for statistics
50
- */
51
- recordCacheMiss(): void;
52
- /**
53
- * Get cache performance statistics
54
- */
55
- getCacheStats(): CacheStats;
56
- /**
57
- * Check if scrub segment is cached for the given time
58
- */
59
- isScrubSegmentCached(seekTimeMs: number): boolean;
60
- /**
61
- * Get current scrub cache size
62
- */
63
- getScrubCacheSize(): number;
64
- /**
65
- * Get total number of scrub segments for video
66
- */
67
- getTotalScrubSegments(): number;
68
- /**
69
- * Update metadata (e.g., when video duration changes)
70
- */
71
- updateMetadata(): Promise<void>;
72
- /**
73
- * Preload initial scrub segments
74
- */
75
- private preloadInitialSegments;
76
- /**
77
- * Preload nearby segments around a given time
78
- */
79
- private preloadNearbySegments;
80
- /**
81
- * Preload a single scrub segment (background operation)
82
- */
83
- private preloadSegment;
84
- /**
85
- * Cache a scrub segment with LRU eviction
86
- */
87
- private cacheSegment;
88
- /**
89
- * Clear cache and reset state
90
- */
91
- reset(): void;
92
- /**
93
- * Clean up resources and cancel any pending operations
94
- */
95
- cleanup(): void;
96
- }
@@ -1,22 +0,0 @@
1
- import { MediaSourceService } from './MediaSourceService.js';
2
- /**
3
- * Factory for creating and caching MediaElementAudioSourceNode instances
4
- * Handles the complex lifecycle and caching logic previously embedded in EFMedia
5
- */
6
- export declare class AudioElementFactory {
7
- private cache;
8
- private currentSource;
9
- private currentAudioContext;
10
- /**
11
- * Create or retrieve cached MediaElementAudioSourceNode for the given AudioContext
12
- */
13
- createMediaElementSource(audioContext: AudioContext, mediaSourceService: MediaSourceService): Promise<MediaElementAudioSourceNode>;
14
- /**
15
- * Clear all cached sources (useful for testing or cleanup)
16
- */
17
- clearCache(): void;
18
- /**
19
- * Check if we have a cached source for the given AudioContext
20
- */
21
- hasCachedSource(audioContext: AudioContext): boolean;
22
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * Factory for creating and caching MediaElementAudioSourceNode instances
3
- * Handles the complex lifecycle and caching logic previously embedded in EFMedia
4
- */
5
- var AudioElementFactory = class {
6
- constructor() {
7
- this.cache = /* @__PURE__ */ new WeakMap();
8
- this.currentSource = null;
9
- this.currentAudioContext = null;
10
- }
11
- /**
12
- * Create or retrieve cached MediaElementAudioSourceNode for the given AudioContext
13
- */
14
- async createMediaElementSource(audioContext, mediaSourceService) {
15
- const cached = this.cache.get(audioContext);
16
- if (cached && audioContext.state !== "closed" && this.currentAudioContext === audioContext) return cached;
17
- if (this.currentSource && this.currentAudioContext !== audioContext) {
18
- this.currentSource.disconnect();
19
- if (this.currentAudioContext) this.cache.delete(this.currentAudioContext);
20
- this.currentSource = null;
21
- this.currentAudioContext = null;
22
- }
23
- await mediaSourceService.ensureInitialized();
24
- const audioElement = mediaSourceService.getAudioElement();
25
- if (!audioElement) throw new Error("Audio element not available from MediaSourceService");
26
- let mediaElementSource;
27
- try {
28
- mediaElementSource = audioContext.createMediaElementSource(audioElement);
29
- } catch (error) {
30
- if (error instanceof Error && error.message.includes("already connected")) {
31
- this.clearCache();
32
- try {
33
- mediaElementSource = audioContext.createMediaElementSource(audioElement);
34
- } catch (retryError) {
35
- console.warn("AudioElementFactory: Failed to create MediaElementSource even after clearing cache:", retryError);
36
- throw retryError;
37
- }
38
- } else throw error;
39
- }
40
- this.currentSource = mediaElementSource;
41
- this.currentAudioContext = audioContext;
42
- this.cache.set(audioContext, mediaElementSource);
43
- const cleanup = () => {
44
- if (audioContext.state === "closed") {
45
- this.cache.delete(audioContext);
46
- if (this.currentAudioContext === audioContext) {
47
- this.currentSource = null;
48
- this.currentAudioContext = null;
49
- }
50
- audioContext.removeEventListener("statechange", cleanup);
51
- }
52
- };
53
- audioContext.addEventListener("statechange", cleanup);
54
- return mediaElementSource;
55
- }
56
- /**
57
- * Clear all cached sources (useful for testing or cleanup)
58
- */
59
- clearCache() {
60
- if (this.currentSource) this.currentSource.disconnect();
61
- this.cache = /* @__PURE__ */ new WeakMap();
62
- this.currentSource = null;
63
- this.currentAudioContext = null;
64
- }
65
- /**
66
- * Check if we have a cached source for the given AudioContext
67
- */
68
- hasCachedSource(audioContext) {
69
- return this.cache.has(audioContext) && audioContext.state !== "closed";
70
- }
71
- };
72
- export { AudioElementFactory };
@@ -1,47 +0,0 @@
1
- export interface MediaSourceServiceOptions {
2
- onError?: (error: Error) => void;
3
- onReady?: () => void;
4
- onUpdateEnd?: () => void;
5
- timeout?: number;
6
- }
7
- /**
8
- * Service for managing MediaSource lifecycle and audio element creation
9
- * Extracted from EFMedia to improve separation of concerns and testability
10
- */
11
- export declare class MediaSourceService {
12
- private mediaSourceManager;
13
- private options;
14
- constructor(options?: MediaSourceServiceOptions);
15
- /**
16
- * Initialize MediaSource if not already initialized
17
- */
18
- ensureInitialized(): Promise<void>;
19
- /**
20
- * Initialize fresh MediaSource
21
- */
22
- initialize(): Promise<void>;
23
- /**
24
- * Get audio element for MediaElementSource creation
25
- */
26
- getAudioElement(): HTMLAudioElement | null;
27
- /**
28
- * Feed audio segments to MediaSource
29
- */
30
- feedSegment(segmentBuffer: ArrayBuffer): Promise<void>;
31
- /**
32
- * Check if MediaSource is ready
33
- */
34
- isReady(): boolean;
35
- /**
36
- * Get buffered time ranges
37
- */
38
- getBuffered(): TimeRanges | null;
39
- /**
40
- * Set audio element current time
41
- */
42
- setCurrentTime(timeMs: number): void;
43
- /**
44
- * Clean up MediaSource resources
45
- */
46
- cleanup(): void;
47
- }
@@ -1,73 +0,0 @@
1
- import { MediaSourceManager } from "../../../services/MediaSourceManager.js";
2
- /**
3
- * Service for managing MediaSource lifecycle and audio element creation
4
- * Extracted from EFMedia to improve separation of concerns and testability
5
- */
6
- var MediaSourceService = class {
7
- constructor(options = {}) {
8
- this.mediaSourceManager = null;
9
- this.options = options;
10
- }
11
- /**
12
- * Initialize MediaSource if not already initialized
13
- */
14
- async ensureInitialized() {
15
- if (this.mediaSourceManager?.isReady()) return;
16
- await this.initialize();
17
- }
18
- /**
19
- * Initialize fresh MediaSource
20
- */
21
- async initialize() {
22
- this.cleanup();
23
- const managerOptions = {
24
- onError: this.options.onError,
25
- onReady: this.options.onReady,
26
- onUpdateEnd: this.options.onUpdateEnd,
27
- timeout: this.options.timeout
28
- };
29
- this.mediaSourceManager = new MediaSourceManager(managerOptions);
30
- await this.mediaSourceManager.initialize();
31
- }
32
- /**
33
- * Get audio element for MediaElementSource creation
34
- */
35
- getAudioElement() {
36
- return this.mediaSourceManager?.getAudioElement() || null;
37
- }
38
- /**
39
- * Feed audio segments to MediaSource
40
- */
41
- async feedSegment(segmentBuffer) {
42
- await this.ensureInitialized();
43
- if (this.mediaSourceManager) await this.mediaSourceManager.feedSegment(segmentBuffer);
44
- }
45
- /**
46
- * Check if MediaSource is ready
47
- */
48
- isReady() {
49
- return this.mediaSourceManager?.isReady() ?? false;
50
- }
51
- /**
52
- * Get buffered time ranges
53
- */
54
- getBuffered() {
55
- return this.mediaSourceManager?.getBuffered() || null;
56
- }
57
- /**
58
- * Set audio element current time
59
- */
60
- setCurrentTime(timeMs) {
61
- this.mediaSourceManager?.setCurrentTime(timeMs);
62
- }
63
- /**
64
- * Clean up MediaSource resources
65
- */
66
- cleanup() {
67
- if (this.mediaSourceManager) {
68
- this.mediaSourceManager.cleanup();
69
- this.mediaSourceManager = null;
70
- }
71
- }
72
- };
73
- export { MediaSourceService };
@@ -1,59 +0,0 @@
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
- }
@@ -1,128 +0,0 @@
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 };
@@ -1 +0,0 @@
1
- export {};
@@ -1,103 +0,0 @@
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
- }