@editframe/elements 0.18.3-beta.0 → 0.18.7-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/dist/elements/EFMedia/AssetMediaEngine.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +2 -4
- package/dist/elements/EFMedia/AssetMediaEngine.js +22 -3
- package/dist/elements/EFMedia/BaseMediaEngine.js +20 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +5 -5
- package/dist/elements/EFMedia/BufferedSeekingInput.js +27 -7
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +22 -3
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +4 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +11 -3
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +10 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +11 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +3 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +4 -1
- package/dist/elements/EFMedia/shared/PrecisionUtils.d.ts +28 -0
- package/dist/elements/EFMedia/shared/PrecisionUtils.js +29 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +11 -2
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +11 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +3 -2
- package/dist/elements/EFMedia.d.ts +0 -12
- package/dist/elements/EFMedia.js +4 -30
- package/dist/elements/EFTimegroup.js +12 -17
- package/dist/elements/EFVideo.d.ts +0 -9
- package/dist/elements/EFVideo.js +0 -7
- package/dist/elements/SampleBuffer.js +6 -6
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +71 -17
- package/dist/gui/TWMixin.js +1 -1
- package/dist/style.css +1 -1
- package/dist/transcoding/types/index.d.ts +9 -9
- package/package.json +2 -3
- package/src/elements/EFAudio.browsertest.ts +7 -7
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +100 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +52 -7
- package/src/elements/EFMedia/BaseMediaEngine.ts +50 -1
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +135 -54
- package/src/elements/EFMedia/BufferedSeekingInput.ts +74 -17
- package/src/elements/EFMedia/JitMediaEngine.ts +58 -2
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +10 -1
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +16 -8
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +199 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +25 -3
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +12 -1
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +3 -2
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +10 -1
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +46 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +27 -3
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +12 -1
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +3 -2
- package/src/elements/EFMedia.browsertest.ts +73 -33
- package/src/elements/EFMedia.ts +11 -54
- package/src/elements/EFTimegroup.ts +21 -26
- package/src/elements/EFVideo.browsertest.ts +895 -162
- package/src/elements/EFVideo.ts +0 -16
- package/src/elements/SampleBuffer.ts +8 -10
- package/src/gui/ContextMixin.ts +104 -26
- package/src/transcoding/types/index.ts +10 -6
- package/test/EFVideo.framegen.browsertest.ts +1 -1
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -1
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +4 -4
- package/test/recordReplayProxyPlugin.js +50 -0
- package/types.json +1 -1
- package/dist/DecoderResetFrequency.test.d.ts +0 -1
- package/dist/DecoderResetRecovery.test.d.ts +0 -1
- package/dist/ScrubTrackManager.d.ts +0 -96
- package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +0 -1
- package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +0 -22
- package/dist/elements/EFMedia/services/AudioElementFactory.js +0 -72
- package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +0 -1
- package/dist/elements/EFMedia/services/MediaSourceService.d.ts +0 -47
- package/dist/elements/EFMedia/services/MediaSourceService.js +0 -73
- package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +0 -1
- package/dist/gui/services/ElementConnectionManager.d.ts +0 -59
- package/dist/gui/services/ElementConnectionManager.js +0 -128
- package/dist/gui/services/PlaybackController.browsertest.d.ts +0 -1
- package/dist/gui/services/PlaybackController.d.ts +0 -103
- package/dist/gui/services/PlaybackController.js +0 -290
- package/dist/services/MediaSourceManager.d.ts +0 -62
- package/dist/services/MediaSourceManager.js +0 -211
- package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +0 -325
- package/src/elements/EFMedia/services/AudioElementFactory.ts +0 -119
- package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +0 -257
- package/src/elements/EFMedia/services/MediaSourceService.ts +0 -102
- package/src/gui/services/ElementConnectionManager.browsertest.ts +0 -263
- package/src/gui/services/ElementConnectionManager.ts +0 -224
- package/src/gui/services/PlaybackController.browsertest.ts +0 -437
- package/src/gui/services/PlaybackController.ts +0 -521
- package/src/services/MediaSourceManager.ts +0 -333
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
export interface MediaSourceManagerOptions {
|
|
2
|
-
onError?: (error: Error) => void;
|
|
3
|
-
onReady?: () => void;
|
|
4
|
-
onUpdateEnd?: () => void;
|
|
5
|
-
timeout?: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Manages MediaSource for audio streaming
|
|
10
|
-
*/
|
|
11
|
-
export class MediaSourceManager {
|
|
12
|
-
private mediaSource: MediaSource | null = null;
|
|
13
|
-
private audioElement: HTMLAudioElement | null = null;
|
|
14
|
-
private sourceBuffer: SourceBuffer | null = null;
|
|
15
|
-
private mediaSourceReady = false;
|
|
16
|
-
private pendingSegments: ArrayBuffer[] = [];
|
|
17
|
-
private options: MediaSourceManagerOptions;
|
|
18
|
-
|
|
19
|
-
constructor(options: MediaSourceManagerOptions = {}) {
|
|
20
|
-
this.options = {
|
|
21
|
-
timeout: 10000,
|
|
22
|
-
...options,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Initialize MediaSource for audio streaming
|
|
28
|
-
*/
|
|
29
|
-
async initialize(): Promise<void> {
|
|
30
|
-
this.cleanup(true);
|
|
31
|
-
|
|
32
|
-
this.mediaSource = new MediaSource();
|
|
33
|
-
this.audioElement = document.createElement("audio");
|
|
34
|
-
|
|
35
|
-
// Add error event listeners to the audio element
|
|
36
|
-
this.audioElement.addEventListener("error", (event) => {
|
|
37
|
-
const error = this.audioElement?.error;
|
|
38
|
-
console.error("🎵 [AUDIO_ELEMENT_ERROR] Audio element error:", {
|
|
39
|
-
code: error?.code,
|
|
40
|
-
message: error?.message,
|
|
41
|
-
event,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (this.options.onError) {
|
|
45
|
-
this.options.onError(
|
|
46
|
-
new Error(`Audio element error: ${error?.message}`),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
this.audioElement.src = URL.createObjectURL(this.mediaSource);
|
|
52
|
-
|
|
53
|
-
return new Promise((resolve, reject) => {
|
|
54
|
-
this.mediaSource?.addEventListener("sourceopen", () => {
|
|
55
|
-
try {
|
|
56
|
-
const sourceBuffer = this.createSourceBuffer();
|
|
57
|
-
if (!sourceBuffer) {
|
|
58
|
-
throw new Error(
|
|
59
|
-
"Failed to create SourceBuffer with any supported codec",
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
this.sourceBuffer = sourceBuffer;
|
|
64
|
-
this.setupSourceBufferListeners();
|
|
65
|
-
|
|
66
|
-
this.mediaSourceReady = true;
|
|
67
|
-
|
|
68
|
-
if (this.options.onReady) {
|
|
69
|
-
this.options.onReady();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
resolve();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(
|
|
75
|
-
"🎵 [MEDIA_SOURCE_ERROR] Failed to create SourceBuffer:",
|
|
76
|
-
error,
|
|
77
|
-
);
|
|
78
|
-
reject(error);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
this.mediaSource?.addEventListener("error", (error) => {
|
|
83
|
-
console.error("🎵 [MEDIA_SOURCE_ERROR] MediaSource error:", error);
|
|
84
|
-
reject(error);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Add timeout for MediaSource opening
|
|
88
|
-
setTimeout(() => {
|
|
89
|
-
if (!this.mediaSourceReady) {
|
|
90
|
-
const timeoutError = new Error(
|
|
91
|
-
"MediaSource failed to open within timeout",
|
|
92
|
-
);
|
|
93
|
-
console.error(
|
|
94
|
-
"🎵 [MEDIA_SOURCE_TIMEOUT] MediaSource initialization timeout",
|
|
95
|
-
);
|
|
96
|
-
reject(timeoutError);
|
|
97
|
-
}
|
|
98
|
-
}, 4000);
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create SourceBuffer with codec fallback
|
|
104
|
-
*/
|
|
105
|
-
private createSourceBuffer(): SourceBuffer | undefined {
|
|
106
|
-
const codecOptions = [
|
|
107
|
-
'audio/mp4; codecs="mp4a.40.2"', // AAC-LC (most common)
|
|
108
|
-
'audio/mp4; codecs="mp4a.40.5"', // AAC-HE
|
|
109
|
-
"audio/mp4", // Generic MP4 audio
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
let sourceBuffer: SourceBuffer | undefined;
|
|
113
|
-
let lastError: Error | undefined;
|
|
114
|
-
|
|
115
|
-
for (const codec of codecOptions) {
|
|
116
|
-
try {
|
|
117
|
-
if (MediaSource.isTypeSupported(codec)) {
|
|
118
|
-
sourceBuffer = this.mediaSource?.addSourceBuffer(codec);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error(
|
|
123
|
-
`🎵 [CODEC_ERROR] Failed to create SourceBuffer with ${codec}:`,
|
|
124
|
-
error,
|
|
125
|
-
);
|
|
126
|
-
lastError = error as Error;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!sourceBuffer && lastError) {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Failed to create SourceBuffer with any supported codec. Last error: ${lastError.message}`,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return sourceBuffer;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Setup SourceBuffer event listeners
|
|
141
|
-
*/
|
|
142
|
-
private setupSourceBufferListeners(): void {
|
|
143
|
-
if (!this.sourceBuffer) return;
|
|
144
|
-
|
|
145
|
-
this.sourceBuffer.addEventListener("updateend", () => {
|
|
146
|
-
this.processPendingSegments();
|
|
147
|
-
|
|
148
|
-
if (this.options.onUpdateEnd) {
|
|
149
|
-
this.options.onUpdateEnd();
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
this.sourceBuffer.addEventListener("error", (event) => {
|
|
154
|
-
console.error(
|
|
155
|
-
"🎵 [SOURCE_BUFFER_EVENT_ERROR] SourceBuffer error event:",
|
|
156
|
-
event,
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
if (this.options.onError) {
|
|
160
|
-
this.options.onError(new Error("SourceBuffer error"));
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Feed audio segments directly to MediaSource SourceBuffer
|
|
167
|
-
*/
|
|
168
|
-
async feedSegment(segmentBuffer: ArrayBuffer): Promise<void> {
|
|
169
|
-
if (!this.mediaSourceReady || !this.sourceBuffer) {
|
|
170
|
-
this.pendingSegments.push(segmentBuffer);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (this.sourceBuffer.updating) {
|
|
175
|
-
this.pendingSegments.push(segmentBuffer);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check for HTMLMediaElement errors before appending
|
|
180
|
-
if (this.audioElement?.error) {
|
|
181
|
-
const error = this.audioElement.error;
|
|
182
|
-
console.error(
|
|
183
|
-
"🎵 [MEDIA_ELEMENT_ERROR] HTMLMediaElement error detected:",
|
|
184
|
-
{
|
|
185
|
-
code: error.code,
|
|
186
|
-
message: error.message,
|
|
187
|
-
MEDIA_ERR_ABORTED: error.code === MediaError.MEDIA_ERR_ABORTED,
|
|
188
|
-
MEDIA_ERR_NETWORK: error.code === MediaError.MEDIA_ERR_NETWORK,
|
|
189
|
-
MEDIA_ERR_DECODE: error.code === MediaError.MEDIA_ERR_DECODE,
|
|
190
|
-
MEDIA_ERR_SRC_NOT_SUPPORTED:
|
|
191
|
-
error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
|
|
192
|
-
},
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// Reset the audio element to try to recover
|
|
196
|
-
this.audioElement.load();
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
this.sourceBuffer.appendBuffer(segmentBuffer);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error(
|
|
204
|
-
"🎵 [SOURCE_BUFFER_ERROR] Failed to append segment:",
|
|
205
|
-
error,
|
|
206
|
-
);
|
|
207
|
-
this.logDebugInfo();
|
|
208
|
-
this.pendingSegments.push(segmentBuffer);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Process any queued segments when SourceBuffer becomes available
|
|
214
|
-
*/
|
|
215
|
-
private processPendingSegments(): void {
|
|
216
|
-
if (
|
|
217
|
-
!this.sourceBuffer ||
|
|
218
|
-
this.sourceBuffer.updating ||
|
|
219
|
-
this.pendingSegments.length === 0
|
|
220
|
-
) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const nextSegment = this.pendingSegments.shift();
|
|
225
|
-
if (nextSegment) {
|
|
226
|
-
this.feedSegment(nextSegment);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Log debug information for troubleshooting
|
|
232
|
-
*/
|
|
233
|
-
private logDebugInfo(): void {
|
|
234
|
-
console.error("🎵 [SOURCE_BUFFER_DEBUG] SourceBuffer state:", {
|
|
235
|
-
updating: this.sourceBuffer?.updating,
|
|
236
|
-
buffered: this.sourceBuffer?.buffered
|
|
237
|
-
? Array.from(
|
|
238
|
-
{ length: this.sourceBuffer.buffered.length },
|
|
239
|
-
(_, i) =>
|
|
240
|
-
`${this.sourceBuffer?.buffered.start(i)}-${this.sourceBuffer?.buffered.end(i)}`,
|
|
241
|
-
)
|
|
242
|
-
: [],
|
|
243
|
-
mode: this.sourceBuffer?.mode,
|
|
244
|
-
timestampOffset: this.sourceBuffer?.timestampOffset,
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
console.error("🎵 [MEDIA_SOURCE_DEBUG] MediaSource state:", {
|
|
248
|
-
readyState: this.mediaSource?.readyState,
|
|
249
|
-
sourceBuffers: this.mediaSource?.sourceBuffers.length,
|
|
250
|
-
duration: this.mediaSource?.duration,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
console.error("🎵 [AUDIO_ELEMENT_DEBUG] Audio element state:", {
|
|
254
|
-
readyState: this.audioElement?.readyState,
|
|
255
|
-
networkState: this.audioElement?.networkState,
|
|
256
|
-
error: this.audioElement?.error?.code,
|
|
257
|
-
src: `${this.audioElement?.src.substring(0, 50)}...`,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Set audio element current time
|
|
263
|
-
*/
|
|
264
|
-
setCurrentTime(timeMs: number): void {
|
|
265
|
-
if (this.audioElement) {
|
|
266
|
-
this.audioElement.currentTime = timeMs / 1000;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get the audio element for MediaElementSource
|
|
272
|
-
*/
|
|
273
|
-
getAudioElement(): HTMLAudioElement | null {
|
|
274
|
-
return this.audioElement;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Check if MediaSource is ready
|
|
279
|
-
*/
|
|
280
|
-
isReady(): boolean {
|
|
281
|
-
return this.mediaSourceReady;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Get buffered time ranges
|
|
286
|
-
*/
|
|
287
|
-
getBuffered(): TimeRanges | null {
|
|
288
|
-
return this.sourceBuffer?.buffered || null;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Clean up MediaSource resources
|
|
293
|
-
*/
|
|
294
|
-
cleanup(_preserveCache = false): void {
|
|
295
|
-
// Clean up existing MediaSource
|
|
296
|
-
if (
|
|
297
|
-
this.sourceBuffer &&
|
|
298
|
-
this.mediaSource &&
|
|
299
|
-
this.mediaSource.readyState === "open"
|
|
300
|
-
) {
|
|
301
|
-
try {
|
|
302
|
-
this.mediaSource.removeSourceBuffer(this.sourceBuffer);
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.warn("🎵 [CLEANUP_ERROR] Error removing SourceBuffer:", error);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (this.mediaSource) {
|
|
309
|
-
try {
|
|
310
|
-
if (this.mediaSource.readyState === "open") {
|
|
311
|
-
this.mediaSource.endOfStream();
|
|
312
|
-
}
|
|
313
|
-
} catch (error) {
|
|
314
|
-
console.warn("🎵 [CLEANUP_ERROR] Error ending MediaSource:", error);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (this.audioElement) {
|
|
319
|
-
try {
|
|
320
|
-
URL.revokeObjectURL(this.audioElement.src);
|
|
321
|
-
} catch (error) {
|
|
322
|
-
console.warn("🎵 [CLEANUP_ERROR] Error revoking URL:", error);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Reset state
|
|
327
|
-
this.mediaSource = null;
|
|
328
|
-
this.audioElement = null;
|
|
329
|
-
this.sourceBuffer = null;
|
|
330
|
-
this.mediaSourceReady = false;
|
|
331
|
-
this.pendingSegments = [];
|
|
332
|
-
}
|
|
333
|
-
}
|