@meframe/core 0.0.2 → 0.0.3
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/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +2 -1
- package/dist/Meframe.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +2 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +3 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +2 -1
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/types.d.ts +1 -0
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/types.d.ts +2 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +0 -1
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/utils/time-utils.d.ts +3 -2
- package/dist/utils/time-utils.d.ts.map +1 -1
- package/dist/utils/time-utils.js +2 -1
- package/dist/utils/time-utils.js.map +1 -1
- package/dist/vite-plugin.d.ts +5 -3
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +109 -52
- package/dist/vite-plugin.js.map +1 -1
- package/dist/worker/WorkerPool.d.ts +7 -0
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +29 -5
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/{stages/demux → workers}/MP4Demuxer.js +4 -13
- package/dist/workers/MP4Demuxer.js.map +1 -0
- package/dist/workers/WorkerChannel.js +486 -0
- package/dist/workers/WorkerChannel.js.map +1 -0
- package/dist/{assets/video-demux.worker-D019I7GQ.js → workers/mp4box.all.js} +4 -912
- package/dist/workers/mp4box.all.js.map +1 -0
- package/dist/{assets/audio-compose.worker-nGVvHD5Q.js → workers/stages/compose/audio-compose.worker.js} +7 -481
- package/dist/workers/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/{assets/video-compose.worker-DPzsC21d.js → workers/stages/compose/video-compose.worker.js} +7 -481
- package/dist/workers/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/{assets/decode.worker-DpWHsc7R.js → workers/stages/decode/decode.worker.js} +7 -481
- package/dist/workers/stages/decode/decode.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/audio-demux.worker.js +184 -4
- package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/video-demux.worker.js +2 -3
- package/dist/workers/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/encode/encode.worker.js +238 -4
- package/dist/workers/stages/encode/encode.worker.js.map +1 -0
- package/dist/{stages/mux/MP4Muxer.js → workers/stages/mux/mux.worker.js} +244 -5
- package/dist/workers/stages/mux/mux.worker.js.map +1 -0
- package/package.json +21 -21
- package/dist/assets/audio-compose.worker-nGVvHD5Q.js.map +0 -1
- package/dist/assets/audio-demux.worker-xwWBtbAe.js +0 -8299
- package/dist/assets/audio-demux.worker-xwWBtbAe.js.map +0 -1
- package/dist/assets/decode.worker-DpWHsc7R.js.map +0 -1
- package/dist/assets/encode.worker-nfOb3kw6.js +0 -1026
- package/dist/assets/encode.worker-nfOb3kw6.js.map +0 -1
- package/dist/assets/mux.worker-uEMQY066.js +0 -8019
- package/dist/assets/mux.worker-uEMQY066.js.map +0 -1
- package/dist/assets/video-compose.worker-DPzsC21d.js.map +0 -1
- package/dist/assets/video-demux.worker-D019I7GQ.js.map +0 -1
- package/dist/model/types.js +0 -5
- package/dist/model/types.js.map +0 -1
- package/dist/plugins/BackpressureMonitor.js +0 -62
- package/dist/plugins/BackpressureMonitor.js.map +0 -1
- package/dist/stages/compose/AudioDucker.js +0 -161
- package/dist/stages/compose/AudioDucker.js.map +0 -1
- package/dist/stages/compose/AudioMixer.js +0 -373
- package/dist/stages/compose/AudioMixer.js.map +0 -1
- package/dist/stages/compose/FilterProcessor.js +0 -226
- package/dist/stages/compose/FilterProcessor.js.map +0 -1
- package/dist/stages/compose/LayerRenderer.js +0 -215
- package/dist/stages/compose/LayerRenderer.js.map +0 -1
- package/dist/stages/compose/TransitionProcessor.js +0 -189
- package/dist/stages/compose/TransitionProcessor.js.map +0 -1
- package/dist/stages/compose/VideoComposer.js +0 -186
- package/dist/stages/compose/VideoComposer.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker.d.ts +0 -79
- package/dist/stages/compose/audio-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/audio-compose.worker.js +0 -540
- package/dist/stages/compose/audio-compose.worker.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker2.js +0 -5
- package/dist/stages/compose/audio-compose.worker2.js.map +0 -1
- package/dist/stages/compose/video-compose.worker.d.ts +0 -60
- package/dist/stages/compose/video-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/video-compose.worker.js +0 -379
- package/dist/stages/compose/video-compose.worker.js.map +0 -1
- package/dist/stages/compose/video-compose.worker2.js +0 -5
- package/dist/stages/compose/video-compose.worker2.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -82
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/decode/BaseDecoder.js +0 -130
- package/dist/stages/decode/BaseDecoder.js.map +0 -1
- package/dist/stages/decode/VideoChunkDecoder.js +0 -199
- package/dist/stages/decode/VideoChunkDecoder.js.map +0 -1
- package/dist/stages/decode/decode.worker.d.ts +0 -70
- package/dist/stages/decode/decode.worker.d.ts.map +0 -1
- package/dist/stages/decode/decode.worker.js +0 -423
- package/dist/stages/decode/decode.worker.js.map +0 -1
- package/dist/stages/decode/decode.worker2.js +0 -5
- package/dist/stages/decode/decode.worker2.js.map +0 -1
- package/dist/stages/demux/MP3FrameParser.js +0 -186
- package/dist/stages/demux/MP3FrameParser.js.map +0 -1
- package/dist/stages/demux/MP4Demuxer.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker.d.ts +0 -51
- package/dist/stages/demux/audio-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/audio-demux.worker.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker2.js +0 -5
- package/dist/stages/demux/audio-demux.worker2.js.map +0 -1
- package/dist/stages/demux/video-demux.worker.d.ts +0 -51
- package/dist/stages/demux/video-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/video-demux.worker.js.map +0 -1
- package/dist/stages/demux/video-demux.worker2.js +0 -5
- package/dist/stages/demux/video-demux.worker2.js.map +0 -1
- package/dist/stages/encode/AudioChunkEncoder.js +0 -37
- package/dist/stages/encode/AudioChunkEncoder.js.map +0 -1
- package/dist/stages/encode/BaseEncoder.js +0 -164
- package/dist/stages/encode/BaseEncoder.js.map +0 -1
- package/dist/stages/encode/VideoChunkEncoder.js +0 -50
- package/dist/stages/encode/VideoChunkEncoder.js.map +0 -1
- package/dist/stages/encode/encode.worker.d.ts +0 -3
- package/dist/stages/encode/encode.worker.d.ts.map +0 -1
- package/dist/stages/encode/encode.worker.js.map +0 -1
- package/dist/stages/encode/encode.worker2.js +0 -5
- package/dist/stages/encode/encode.worker2.js.map +0 -1
- package/dist/stages/mux/MP4Muxer.js.map +0 -1
- package/dist/stages/mux/mux.worker.d.ts +0 -65
- package/dist/stages/mux/mux.worker.d.ts.map +0 -1
- package/dist/stages/mux/mux.worker.js +0 -219
- package/dist/stages/mux/mux.worker.js.map +0 -1
- package/dist/stages/mux/mux.worker2.js +0 -5
- package/dist/stages/mux/mux.worker2.js.map +0 -1
- package/dist/stages/mux/utils.js +0 -34
- package/dist/stages/mux/utils.js.map +0 -1
- package/dist/worker/worker-registry.d.ts +0 -12
- package/dist/worker/worker-registry.d.ts.map +0 -1
- package/dist/worker/worker-registry.js +0 -20
- package/dist/worker/worker-registry.js.map +0 -1
|
@@ -1,483 +1,3 @@
|
|
|
1
|
-
var WorkerMessageType = /* @__PURE__ */ ((WorkerMessageType2) => {
|
|
2
|
-
WorkerMessageType2["Ready"] = "ready";
|
|
3
|
-
WorkerMessageType2["Error"] = "error";
|
|
4
|
-
WorkerMessageType2["Dispose"] = "dispose";
|
|
5
|
-
WorkerMessageType2["Configure"] = "configure";
|
|
6
|
-
WorkerMessageType2["LoadResource"] = "load_resource";
|
|
7
|
-
WorkerMessageType2["ResourceLoaded"] = "resource_loaded";
|
|
8
|
-
WorkerMessageType2["ResourceProgress"] = "resource_progress";
|
|
9
|
-
WorkerMessageType2["ConfigureDemux"] = "configure_demux";
|
|
10
|
-
WorkerMessageType2["AppendBuffer"] = "append_buffer";
|
|
11
|
-
WorkerMessageType2["DemuxSamples"] = "demux_samples";
|
|
12
|
-
WorkerMessageType2["FlushDemux"] = "flush_demux";
|
|
13
|
-
WorkerMessageType2["ConfigureDecode"] = "configure_decode";
|
|
14
|
-
WorkerMessageType2["DecodeChunk"] = "decode_chunk";
|
|
15
|
-
WorkerMessageType2["DecodedFrame"] = "decoded_frame";
|
|
16
|
-
WorkerMessageType2["SeekGop"] = "seek_gop";
|
|
17
|
-
WorkerMessageType2["SetComposition"] = "set_composition";
|
|
18
|
-
WorkerMessageType2["ApplyPatch"] = "apply_patch";
|
|
19
|
-
WorkerMessageType2["RenderFrame"] = "render_frame";
|
|
20
|
-
WorkerMessageType2["ComposeFrameReady"] = "compose_frame_ready";
|
|
21
|
-
WorkerMessageType2["ConfigureEncode"] = "configure_encode";
|
|
22
|
-
WorkerMessageType2["EncodeFrame"] = "encode_frame";
|
|
23
|
-
WorkerMessageType2["EncodeAudio"] = "encode_audio";
|
|
24
|
-
WorkerMessageType2["EncodedChunk"] = "encoded_chunk";
|
|
25
|
-
WorkerMessageType2["FlushEncode"] = "flush_encode";
|
|
26
|
-
WorkerMessageType2["ConfigureMux"] = "configure_mux";
|
|
27
|
-
WorkerMessageType2["AddChunk"] = "add_chunk";
|
|
28
|
-
WorkerMessageType2["FinishMux"] = "finish_mux";
|
|
29
|
-
WorkerMessageType2["MuxComplete"] = "mux_complete";
|
|
30
|
-
WorkerMessageType2["PerformanceStats"] = "performance_stats";
|
|
31
|
-
WorkerMessageType2["RenderWindow"] = "renderWindow";
|
|
32
|
-
WorkerMessageType2["AudioTrackAdd"] = "audio_track:add";
|
|
33
|
-
WorkerMessageType2["AudioTrackRemove"] = "audio_track:remove";
|
|
34
|
-
WorkerMessageType2["AudioTrackUpdate"] = "audio_track:update";
|
|
35
|
-
return WorkerMessageType2;
|
|
36
|
-
})(WorkerMessageType || {});
|
|
37
|
-
var WorkerState = /* @__PURE__ */ ((WorkerState2) => {
|
|
38
|
-
WorkerState2["Idle"] = "idle";
|
|
39
|
-
WorkerState2["Initializing"] = "initializing";
|
|
40
|
-
WorkerState2["Ready"] = "ready";
|
|
41
|
-
WorkerState2["Processing"] = "processing";
|
|
42
|
-
WorkerState2["Error"] = "error";
|
|
43
|
-
WorkerState2["Disposed"] = "disposed";
|
|
44
|
-
return WorkerState2;
|
|
45
|
-
})(WorkerState || {});
|
|
46
|
-
const defaultRetryConfig = {
|
|
47
|
-
maxRetries: 3,
|
|
48
|
-
initialDelay: 100,
|
|
49
|
-
maxDelay: 5e3,
|
|
50
|
-
backoffFactor: 2,
|
|
51
|
-
retryableErrors: ["TIMEOUT", "NETWORK_ERROR", "WORKER_BUSY"]
|
|
52
|
-
};
|
|
53
|
-
function calculateRetryDelay(attempt, config) {
|
|
54
|
-
const { initialDelay = 100, maxDelay = 5e3, backoffFactor = 2 } = config;
|
|
55
|
-
const delay = initialDelay * Math.pow(backoffFactor, attempt - 1);
|
|
56
|
-
return Math.min(delay, maxDelay);
|
|
57
|
-
}
|
|
58
|
-
function isRetryableError(error, config) {
|
|
59
|
-
const { retryableErrors = defaultRetryConfig.retryableErrors } = config;
|
|
60
|
-
if (!error) return false;
|
|
61
|
-
const errorCode = error.code || error.name;
|
|
62
|
-
if (errorCode && retryableErrors.includes(errorCode)) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
const message = error.message || "";
|
|
66
|
-
if (message.includes("timeout") || message.includes("Timeout")) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
async function withRetry(fn, config) {
|
|
72
|
-
const { maxRetries } = config;
|
|
73
|
-
let lastError;
|
|
74
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
75
|
-
try {
|
|
76
|
-
return await fn();
|
|
77
|
-
} catch (error) {
|
|
78
|
-
lastError = error;
|
|
79
|
-
if (!isRetryableError(error, config)) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
if (attempt === maxRetries) {
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
const delay = calculateRetryDelay(attempt, config);
|
|
86
|
-
await sleep(delay);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
throw lastError || new Error("Retry failed");
|
|
90
|
-
}
|
|
91
|
-
function sleep(ms) {
|
|
92
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
93
|
-
}
|
|
94
|
-
function isTransferable(obj) {
|
|
95
|
-
return obj instanceof ArrayBuffer || obj instanceof MessagePort || typeof ImageBitmap !== "undefined" && obj instanceof ImageBitmap || typeof OffscreenCanvas !== "undefined" && obj instanceof OffscreenCanvas || typeof ReadableStream !== "undefined" && obj instanceof ReadableStream || typeof WritableStream !== "undefined" && obj instanceof WritableStream || typeof TransformStream !== "undefined" && obj instanceof TransformStream;
|
|
96
|
-
}
|
|
97
|
-
function findTransferables(obj, transferables) {
|
|
98
|
-
if (!obj || typeof obj !== "object") {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (isTransferable(obj)) {
|
|
102
|
-
transferables.push(obj);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (obj instanceof VideoFrame) {
|
|
106
|
-
transferables.push(obj);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
if (typeof AudioData !== "undefined" && obj instanceof AudioData) {
|
|
110
|
-
transferables.push(obj);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
if (typeof EncodedVideoChunk !== "undefined" && obj instanceof EncodedVideoChunk || typeof EncodedAudioChunk !== "undefined" && obj instanceof EncodedAudioChunk) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (Array.isArray(obj)) {
|
|
117
|
-
for (const item of obj) {
|
|
118
|
-
findTransferables(item, transferables);
|
|
119
|
-
}
|
|
120
|
-
} else {
|
|
121
|
-
for (const key in obj) {
|
|
122
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
123
|
-
findTransferables(obj[key], transferables);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function extractTransferables(payload) {
|
|
129
|
-
const transferables = [];
|
|
130
|
-
findTransferables(payload, transferables);
|
|
131
|
-
return transferables;
|
|
132
|
-
}
|
|
133
|
-
class WorkerChannel {
|
|
134
|
-
name;
|
|
135
|
-
port;
|
|
136
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
137
|
-
messageHandlers = {};
|
|
138
|
-
state = WorkerState.Idle;
|
|
139
|
-
defaultTimeout;
|
|
140
|
-
defaultMaxRetries;
|
|
141
|
-
constructor(port, config) {
|
|
142
|
-
this.name = config.name;
|
|
143
|
-
this.port = port;
|
|
144
|
-
this.defaultTimeout = config.timeout ?? 3e4;
|
|
145
|
-
this.defaultMaxRetries = config.maxRetries ?? 3;
|
|
146
|
-
this.setupMessageHandler();
|
|
147
|
-
this.state = WorkerState.Ready;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Send a message and wait for response with retry support
|
|
151
|
-
*/
|
|
152
|
-
async send(type, payload, options) {
|
|
153
|
-
const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;
|
|
154
|
-
const retryConfig = {
|
|
155
|
-
...defaultRetryConfig,
|
|
156
|
-
maxRetries,
|
|
157
|
-
...options?.retryConfig
|
|
158
|
-
};
|
|
159
|
-
return withRetry(() => this.sendOnce(type, payload, options), retryConfig);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Send a message once (without retry)
|
|
163
|
-
*/
|
|
164
|
-
async sendOnce(type, payload, options) {
|
|
165
|
-
const id = this.generateMessageId();
|
|
166
|
-
const timeout = options?.timeout ?? this.defaultTimeout;
|
|
167
|
-
const message = {
|
|
168
|
-
type,
|
|
169
|
-
id,
|
|
170
|
-
payload,
|
|
171
|
-
timestamp: Date.now()
|
|
172
|
-
};
|
|
173
|
-
return new Promise((resolve, reject) => {
|
|
174
|
-
const request = {
|
|
175
|
-
id,
|
|
176
|
-
type,
|
|
177
|
-
timestamp: Date.now(),
|
|
178
|
-
timeout,
|
|
179
|
-
resolve,
|
|
180
|
-
reject
|
|
181
|
-
};
|
|
182
|
-
this.pendingRequests.set(id, request);
|
|
183
|
-
const timeoutId = setTimeout(() => {
|
|
184
|
-
const pending = this.pendingRequests.get(id);
|
|
185
|
-
if (pending) {
|
|
186
|
-
this.pendingRequests.delete(id);
|
|
187
|
-
const error = new Error(`Request timeout: ${id} ${type} (${timeout}ms)`);
|
|
188
|
-
error.code = "TIMEOUT";
|
|
189
|
-
pending.reject(error);
|
|
190
|
-
}
|
|
191
|
-
}, timeout);
|
|
192
|
-
request.timeoutId = timeoutId;
|
|
193
|
-
if (options?.transfer) {
|
|
194
|
-
this.port.postMessage(message, options.transfer);
|
|
195
|
-
} else {
|
|
196
|
-
this.port.postMessage(message);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Send a message without waiting for response
|
|
202
|
-
*/
|
|
203
|
-
post(type, payload, transfer) {
|
|
204
|
-
const message = {
|
|
205
|
-
type,
|
|
206
|
-
id: this.generateMessageId(),
|
|
207
|
-
payload,
|
|
208
|
-
timestamp: Date.now()
|
|
209
|
-
};
|
|
210
|
-
if (transfer) {
|
|
211
|
-
this.port.postMessage(message, transfer);
|
|
212
|
-
} else {
|
|
213
|
-
this.port.postMessage(message);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Register a message handler
|
|
218
|
-
*/
|
|
219
|
-
on(type, handler) {
|
|
220
|
-
this.messageHandlers[type] = handler;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Unregister a message handler
|
|
224
|
-
*/
|
|
225
|
-
off(type) {
|
|
226
|
-
delete this.messageHandlers[type];
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Dispose the channel
|
|
230
|
-
*/
|
|
231
|
-
dispose() {
|
|
232
|
-
this.state = WorkerState.Disposed;
|
|
233
|
-
for (const [, request] of this.pendingRequests) {
|
|
234
|
-
if (request.timeoutId) {
|
|
235
|
-
clearTimeout(request.timeoutId);
|
|
236
|
-
}
|
|
237
|
-
request.reject(new Error("Channel disposed"));
|
|
238
|
-
}
|
|
239
|
-
this.pendingRequests.clear();
|
|
240
|
-
this.port.onmessage = null;
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Setup message handler for incoming messages
|
|
244
|
-
*/
|
|
245
|
-
setupMessageHandler() {
|
|
246
|
-
this.port.onmessage = async (event) => {
|
|
247
|
-
const data = event.data;
|
|
248
|
-
if (this.isResponse(data)) {
|
|
249
|
-
this.handleResponse(data);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (this.isRequest(data)) {
|
|
253
|
-
await this.handleRequest(data);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Handle incoming request
|
|
260
|
-
*/
|
|
261
|
-
async handleRequest(message) {
|
|
262
|
-
const handler = this.messageHandlers[message.type];
|
|
263
|
-
if (!handler) {
|
|
264
|
-
this.sendResponse(message.id, false, null, {
|
|
265
|
-
code: "NO_HANDLER",
|
|
266
|
-
message: `No handler registered for message type: ${message.type}`
|
|
267
|
-
});
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
this.state = WorkerState.Processing;
|
|
271
|
-
Promise.resolve().then(() => handler(message.payload, message.transfer)).then((result) => {
|
|
272
|
-
this.sendResponse(message.id, true, result);
|
|
273
|
-
this.state = WorkerState.Ready;
|
|
274
|
-
}).catch((error) => {
|
|
275
|
-
const workerError = {
|
|
276
|
-
code: "HANDLER_ERROR",
|
|
277
|
-
message: error instanceof Error ? error.message : String(error),
|
|
278
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
279
|
-
};
|
|
280
|
-
this.sendResponse(message.id, false, null, workerError);
|
|
281
|
-
this.state = WorkerState.Ready;
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Handle incoming response
|
|
286
|
-
*/
|
|
287
|
-
handleResponse(response) {
|
|
288
|
-
const request = this.pendingRequests.get(response.id);
|
|
289
|
-
if (!request) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
this.pendingRequests.delete(response.id);
|
|
293
|
-
if (request.timeoutId) {
|
|
294
|
-
clearTimeout(request.timeoutId);
|
|
295
|
-
}
|
|
296
|
-
if (response.success) {
|
|
297
|
-
request.resolve(response.result);
|
|
298
|
-
} else {
|
|
299
|
-
const error = new Error(response.error?.message || "Unknown error");
|
|
300
|
-
if (response.error) {
|
|
301
|
-
Object.assign(error, response.error);
|
|
302
|
-
}
|
|
303
|
-
request.reject(error);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Send a response message
|
|
308
|
-
*/
|
|
309
|
-
sendResponse(id, success, result, error) {
|
|
310
|
-
let transfer = [];
|
|
311
|
-
if (isTransferable(result)) {
|
|
312
|
-
transfer.push(result);
|
|
313
|
-
}
|
|
314
|
-
const response = {
|
|
315
|
-
id,
|
|
316
|
-
success,
|
|
317
|
-
result,
|
|
318
|
-
error,
|
|
319
|
-
timestamp: Date.now()
|
|
320
|
-
};
|
|
321
|
-
this.port.postMessage(response, transfer);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Check if message is a response
|
|
325
|
-
*/
|
|
326
|
-
isResponse(data) {
|
|
327
|
-
return data && typeof data === "object" && "id" in data && "success" in data && !("type" in data);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Check if message is a request
|
|
331
|
-
*/
|
|
332
|
-
isRequest(data) {
|
|
333
|
-
return data && typeof data === "object" && "id" in data && "type" in data;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Generate unique message ID
|
|
337
|
-
*/
|
|
338
|
-
generateMessageId() {
|
|
339
|
-
return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Send a notification message without waiting for response
|
|
343
|
-
* Alias for post() method for compatibility
|
|
344
|
-
*/
|
|
345
|
-
notify(type, payload, transfer) {
|
|
346
|
-
this.post(type, payload, transfer);
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Register a message handler
|
|
350
|
-
* Alias for on() method for compatibility
|
|
351
|
-
*/
|
|
352
|
-
registerHandler(type, handler) {
|
|
353
|
-
this.on(type, handler);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Send a ReadableStream to another worker
|
|
357
|
-
* Automatically handles transferable streams vs chunk-by-chunk fallback
|
|
358
|
-
*/
|
|
359
|
-
async sendStream(stream, metadata) {
|
|
360
|
-
const streamId = metadata?.streamId || this.generateMessageId();
|
|
361
|
-
if (isTransferable(stream)) {
|
|
362
|
-
this.port.postMessage(
|
|
363
|
-
{
|
|
364
|
-
type: "stream_transfer",
|
|
365
|
-
...metadata,
|
|
366
|
-
stream,
|
|
367
|
-
streamId
|
|
368
|
-
},
|
|
369
|
-
[stream]
|
|
370
|
-
// Transfer ownership
|
|
371
|
-
);
|
|
372
|
-
} else {
|
|
373
|
-
await this.streamChunks(stream, streamId, metadata);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Stream chunks from a ReadableStream (fallback when transfer is not supported)
|
|
378
|
-
*/
|
|
379
|
-
async streamChunks(stream, streamId, metadata) {
|
|
380
|
-
const reader = stream.getReader();
|
|
381
|
-
this.post("stream_start", {
|
|
382
|
-
streamId,
|
|
383
|
-
...metadata,
|
|
384
|
-
mode: "chunk_transfer"
|
|
385
|
-
});
|
|
386
|
-
try {
|
|
387
|
-
while (true) {
|
|
388
|
-
const { done, value } = await reader.read();
|
|
389
|
-
if (done) {
|
|
390
|
-
this.post("stream_end", {
|
|
391
|
-
streamId,
|
|
392
|
-
...metadata
|
|
393
|
-
});
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
const transfer = [];
|
|
397
|
-
if (value instanceof ArrayBuffer) {
|
|
398
|
-
transfer.push(value);
|
|
399
|
-
} else if (value instanceof Uint8Array) {
|
|
400
|
-
transfer.push(value.buffer);
|
|
401
|
-
} else if (typeof AudioData !== "undefined" && value instanceof AudioData) {
|
|
402
|
-
transfer.push(value);
|
|
403
|
-
} else if (typeof VideoFrame !== "undefined" && value instanceof VideoFrame) {
|
|
404
|
-
transfer.push(value);
|
|
405
|
-
} else if (typeof value === "object" && value !== null) {
|
|
406
|
-
const extracted = extractTransferables(value);
|
|
407
|
-
transfer.push(...extracted);
|
|
408
|
-
}
|
|
409
|
-
this.post(
|
|
410
|
-
"stream_chunk",
|
|
411
|
-
{
|
|
412
|
-
streamId,
|
|
413
|
-
chunk: value,
|
|
414
|
-
...metadata
|
|
415
|
-
},
|
|
416
|
-
transfer
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
} catch (error) {
|
|
420
|
-
this.post("stream_error", {
|
|
421
|
-
streamId,
|
|
422
|
-
error: error instanceof Error ? error.message : String(error),
|
|
423
|
-
...metadata
|
|
424
|
-
});
|
|
425
|
-
throw error;
|
|
426
|
-
} finally {
|
|
427
|
-
reader.releaseLock();
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Receive a stream from another worker
|
|
432
|
-
* Handles both transferable streams and chunk-by-chunk reconstruction
|
|
433
|
-
*/
|
|
434
|
-
async receiveStream(onStream) {
|
|
435
|
-
const chunkedStreams = /* @__PURE__ */ new Map();
|
|
436
|
-
const prev = this.port.onmessage;
|
|
437
|
-
const handler = (event) => {
|
|
438
|
-
const raw = event.data;
|
|
439
|
-
const envelopeType = raw?.type;
|
|
440
|
-
const hasPayload = raw && typeof raw === "object" && "payload" in raw;
|
|
441
|
-
const payload = hasPayload ? raw.payload : raw;
|
|
442
|
-
if (envelopeType === "stream_transfer" && payload?.stream) {
|
|
443
|
-
onStream(payload.stream, payload);
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (envelopeType === "stream_start" && payload?.streamId) {
|
|
447
|
-
const stream = new ReadableStream({
|
|
448
|
-
start(controller) {
|
|
449
|
-
chunkedStreams.set(payload.streamId, { controller, metadata: payload });
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
onStream(stream, payload);
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (envelopeType === "stream_chunk" && payload?.streamId && chunkedStreams.has(payload.streamId)) {
|
|
456
|
-
const s = chunkedStreams.get(payload.streamId);
|
|
457
|
-
if (s) s.controller.enqueue(payload.chunk);
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
if (envelopeType === "stream_end" && payload?.streamId && chunkedStreams.has(payload.streamId)) {
|
|
461
|
-
const s = chunkedStreams.get(payload.streamId);
|
|
462
|
-
if (s) {
|
|
463
|
-
s.controller.close();
|
|
464
|
-
chunkedStreams.delete(payload.streamId);
|
|
465
|
-
}
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
if (envelopeType === "stream_error" && payload?.streamId && chunkedStreams.has(payload.streamId)) {
|
|
469
|
-
const s = chunkedStreams.get(payload.streamId);
|
|
470
|
-
if (s) {
|
|
471
|
-
s.controller.error(new Error(String(payload.error || "stream error")));
|
|
472
|
-
chunkedStreams.delete(payload.streamId);
|
|
473
|
-
}
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
if (typeof prev === "function") prev.call(this.port, event);
|
|
477
|
-
};
|
|
478
|
-
this.port.onmessage = handler;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
1
|
var mp4box_all = {};
|
|
482
2
|
(function(exports) {
|
|
483
3
|
var Log = /* @__PURE__ */ function() {
|
|
@@ -7523,435 +7043,7 @@ var mp4box_all = {};
|
|
|
7523
7043
|
exports.createFile = MP4Box.createFile;
|
|
7524
7044
|
}
|
|
7525
7045
|
})(mp4box_all);
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
*/
|
|
7531
|
-
updateMetrics(stage, desiredSize, queueSize = 0) {
|
|
7532
|
-
const isPaused = desiredSize <= 0;
|
|
7533
|
-
this.metrics.set(stage, {
|
|
7534
|
-
desiredSize,
|
|
7535
|
-
queueSize,
|
|
7536
|
-
isPaused,
|
|
7537
|
-
lastUpdate: Date.now()
|
|
7538
|
-
});
|
|
7539
|
-
}
|
|
7540
|
-
/**
|
|
7541
|
-
* Get current metrics snapshot
|
|
7542
|
-
*/
|
|
7543
|
-
getSnapshot() {
|
|
7544
|
-
const now = Date.now();
|
|
7545
|
-
const snapshot = {};
|
|
7546
|
-
for (const [stage, metrics] of this.metrics) {
|
|
7547
|
-
snapshot[stage] = {
|
|
7548
|
-
...metrics,
|
|
7549
|
-
age: now - metrics.lastUpdate
|
|
7550
|
-
};
|
|
7551
|
-
}
|
|
7552
|
-
return snapshot;
|
|
7553
|
-
}
|
|
7554
|
-
/**
|
|
7555
|
-
* Check if any stage is experiencing backpressure
|
|
7556
|
-
*/
|
|
7557
|
-
hasBackpressure() {
|
|
7558
|
-
for (const metrics of this.metrics.values()) {
|
|
7559
|
-
if (metrics.isPaused) {
|
|
7560
|
-
return true;
|
|
7561
|
-
}
|
|
7562
|
-
}
|
|
7563
|
-
return false;
|
|
7564
|
-
}
|
|
7565
|
-
/**
|
|
7566
|
-
* Get stages currently experiencing backpressure
|
|
7567
|
-
*/
|
|
7568
|
-
getBottlenecks() {
|
|
7569
|
-
const bottlenecks = [];
|
|
7570
|
-
for (const [stage, metrics] of this.metrics) {
|
|
7571
|
-
if (metrics.isPaused) {
|
|
7572
|
-
bottlenecks.push(stage);
|
|
7573
|
-
}
|
|
7574
|
-
}
|
|
7575
|
-
return bottlenecks;
|
|
7576
|
-
}
|
|
7577
|
-
/**
|
|
7578
|
-
* Clear all metrics
|
|
7579
|
-
*/
|
|
7580
|
-
clear() {
|
|
7581
|
-
this.metrics.clear();
|
|
7582
|
-
}
|
|
7583
|
-
}
|
|
7584
|
-
class MP4Demuxer {
|
|
7585
|
-
mp4boxFile;
|
|
7586
|
-
tracks = /* @__PURE__ */ new Map();
|
|
7587
|
-
isReady = false;
|
|
7588
|
-
videoController;
|
|
7589
|
-
audioController;
|
|
7590
|
-
backpressureMonitor;
|
|
7591
|
-
demuxHighWaterMark;
|
|
7592
|
-
onReadyCallback;
|
|
7593
|
-
fileOffset = 0;
|
|
7594
|
-
videoTimestampOffset = null;
|
|
7595
|
-
audioTimestampOffset = null;
|
|
7596
|
-
constructor(config = {}) {
|
|
7597
|
-
this.mp4boxFile = mp4box_all.createFile();
|
|
7598
|
-
this.backpressureMonitor = new BackpressureMonitor();
|
|
7599
|
-
this.onReadyCallback = config.onReady;
|
|
7600
|
-
const DEFAULT_HIGH_WATER_MARK = 10;
|
|
7601
|
-
this.demuxHighWaterMark = config.highWaterMark ?? DEFAULT_HIGH_WATER_MARK;
|
|
7602
|
-
this.setupHandlers();
|
|
7603
|
-
}
|
|
7604
|
-
updateConfig(config) {
|
|
7605
|
-
this.demuxHighWaterMark = config.highWaterMark ?? this.demuxHighWaterMark;
|
|
7606
|
-
}
|
|
7607
|
-
setupHandlers() {
|
|
7608
|
-
this.mp4boxFile.onError = (error) => {
|
|
7609
|
-
console.error("MP4Box error:", error);
|
|
7610
|
-
this.videoController?.error(new Error(error));
|
|
7611
|
-
this.audioController?.error(new Error(error));
|
|
7612
|
-
};
|
|
7613
|
-
this.mp4boxFile.onReady = (info) => {
|
|
7614
|
-
this.processTracks(info.tracks);
|
|
7615
|
-
this.isReady = true;
|
|
7616
|
-
if (this.onReadyCallback) {
|
|
7617
|
-
this.onReadyCallback();
|
|
7618
|
-
}
|
|
7619
|
-
this.mp4boxFile.start();
|
|
7620
|
-
};
|
|
7621
|
-
this.mp4boxFile.onSamples = (trackId, _user, samples) => {
|
|
7622
|
-
this.processSamples(trackId, samples);
|
|
7623
|
-
};
|
|
7624
|
-
}
|
|
7625
|
-
processTracks(tracks) {
|
|
7626
|
-
for (const track of tracks) {
|
|
7627
|
-
const trackInfo = {
|
|
7628
|
-
id: track.id,
|
|
7629
|
-
type: track.type === "video" ? "video" : "audio",
|
|
7630
|
-
codec: track.codec,
|
|
7631
|
-
timescale: track.timescale
|
|
7632
|
-
};
|
|
7633
|
-
if (track.type === "video") {
|
|
7634
|
-
trackInfo.width = track.video?.width;
|
|
7635
|
-
trackInfo.height = track.video?.height;
|
|
7636
|
-
trackInfo.description = this.getVideoDescription(track);
|
|
7637
|
-
} else if (track.type === "audio") {
|
|
7638
|
-
trackInfo.sampleRate = track.audio?.sample_rate;
|
|
7639
|
-
trackInfo.numberOfChannels = track.audio?.channel_count;
|
|
7640
|
-
trackInfo.description = this.getAudioDescription(track);
|
|
7641
|
-
}
|
|
7642
|
-
this.tracks.set(track.id, trackInfo);
|
|
7643
|
-
this.mp4boxFile.setExtractionOptions(track.id, track, {
|
|
7644
|
-
nbSamples: 30
|
|
7645
|
-
// Batch size per callback (balance between latency and overhead)
|
|
7646
|
-
});
|
|
7647
|
-
}
|
|
7648
|
-
}
|
|
7649
|
-
processSamples(trackId, samples) {
|
|
7650
|
-
const track = this.tracks.get(trackId);
|
|
7651
|
-
if (!track) return;
|
|
7652
|
-
const timescale = track.timescale || 9e4;
|
|
7653
|
-
for (const sample of samples) {
|
|
7654
|
-
const rawTimestamp = sample.cts * 1e6 / timescale;
|
|
7655
|
-
const duration = sample.duration * 1e6 / timescale;
|
|
7656
|
-
if (track.type === "video") {
|
|
7657
|
-
if (!this.videoController) {
|
|
7658
|
-
console.error("[MP4Demuxer] videoController is null when trying to output chunk!");
|
|
7659
|
-
return;
|
|
7660
|
-
}
|
|
7661
|
-
if (this.videoTimestampOffset === null) {
|
|
7662
|
-
this.videoTimestampOffset = rawTimestamp;
|
|
7663
|
-
}
|
|
7664
|
-
const timestamp = rawTimestamp - this.videoTimestampOffset;
|
|
7665
|
-
const chunk = new EncodedVideoChunk({
|
|
7666
|
-
type: sample.is_sync ? "key" : "delta",
|
|
7667
|
-
timestamp,
|
|
7668
|
-
duration,
|
|
7669
|
-
data: sample.data
|
|
7670
|
-
});
|
|
7671
|
-
this.videoController.enqueue(chunk);
|
|
7672
|
-
} else if (track.type === "audio" && this.audioController) {
|
|
7673
|
-
if (this.audioTimestampOffset === null) {
|
|
7674
|
-
this.audioTimestampOffset = rawTimestamp;
|
|
7675
|
-
}
|
|
7676
|
-
const timestamp = rawTimestamp - this.audioTimestampOffset;
|
|
7677
|
-
const chunk = new EncodedAudioChunk({
|
|
7678
|
-
type: "key",
|
|
7679
|
-
timestamp,
|
|
7680
|
-
duration,
|
|
7681
|
-
data: sample.data
|
|
7682
|
-
});
|
|
7683
|
-
this.audioController.enqueue(chunk);
|
|
7684
|
-
}
|
|
7685
|
-
}
|
|
7686
|
-
const last = samples[samples.length - 1].number;
|
|
7687
|
-
this.mp4boxFile.releaseUsedSamples(trackId, last + 1);
|
|
7688
|
-
}
|
|
7689
|
-
getVideoDescription(track) {
|
|
7690
|
-
try {
|
|
7691
|
-
const fullTrack = this.mp4boxFile.getTrackById(track.id);
|
|
7692
|
-
for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {
|
|
7693
|
-
const box2 = entry.avcC ?? entry.hvcC ?? entry.av1C ?? entry.vpcC;
|
|
7694
|
-
if (box2) {
|
|
7695
|
-
const stream = new mp4box_all.DataStream(
|
|
7696
|
-
void 0,
|
|
7697
|
-
0,
|
|
7698
|
-
mp4box_all.DataStream.BIG_ENDIAN
|
|
7699
|
-
// IMPORTANT: must be BIG_ENDIAN
|
|
7700
|
-
);
|
|
7701
|
-
box2.write(stream);
|
|
7702
|
-
return new Uint8Array(stream.buffer.slice(8)).buffer;
|
|
7703
|
-
}
|
|
7704
|
-
}
|
|
7705
|
-
} catch (error) {
|
|
7706
|
-
console.error("Failed to get video description:", error);
|
|
7707
|
-
}
|
|
7708
|
-
return void 0;
|
|
7709
|
-
}
|
|
7710
|
-
// private getVideoDescription(track: any): ArrayBuffer | undefined {
|
|
7711
|
-
// if (!this.mp4boxFile) return undefined;
|
|
7712
|
-
// return getVideoDescription(this.mp4boxFile, track);
|
|
7713
|
-
// }
|
|
7714
|
-
getAudioDescription(track) {
|
|
7715
|
-
try {
|
|
7716
|
-
const fullTrack = this.mp4boxFile.getTrackById(track.id);
|
|
7717
|
-
for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {
|
|
7718
|
-
if (entry.esds || entry.dOps) {
|
|
7719
|
-
const stream = new mp4box_all.DataStream();
|
|
7720
|
-
(entry.esds || entry.dOps).write(stream);
|
|
7721
|
-
return new Uint8Array(stream.buffer.slice(8)).buffer;
|
|
7722
|
-
}
|
|
7723
|
-
}
|
|
7724
|
-
} catch (error) {
|
|
7725
|
-
console.error("Failed to get audio description:", error);
|
|
7726
|
-
}
|
|
7727
|
-
return void 0;
|
|
7728
|
-
}
|
|
7729
|
-
/**
|
|
7730
|
-
* Create transform stream for video track
|
|
7731
|
-
*/
|
|
7732
|
-
createVideoStream() {
|
|
7733
|
-
return new TransformStream(
|
|
7734
|
-
{
|
|
7735
|
-
start: (controller) => {
|
|
7736
|
-
this.videoController = controller;
|
|
7737
|
-
},
|
|
7738
|
-
transform: (chunk, controller) => {
|
|
7739
|
-
const desiredSize = controller.desiredSize ?? this.demuxHighWaterMark;
|
|
7740
|
-
this.backpressureMonitor.updateMetrics("demux-video", desiredSize);
|
|
7741
|
-
const chunkData = new Uint8Array(chunk);
|
|
7742
|
-
this.appendBuffer(chunkData);
|
|
7743
|
-
this.mp4boxFile.flush();
|
|
7744
|
-
},
|
|
7745
|
-
flush: async () => {
|
|
7746
|
-
this.mp4boxFile.flush();
|
|
7747
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
7748
|
-
}
|
|
7749
|
-
},
|
|
7750
|
-
// Queuing strategy: use configuration
|
|
7751
|
-
{
|
|
7752
|
-
highWaterMark: this.demuxHighWaterMark,
|
|
7753
|
-
size: () => 1
|
|
7754
|
-
// Count-based
|
|
7755
|
-
}
|
|
7756
|
-
);
|
|
7757
|
-
}
|
|
7758
|
-
/**
|
|
7759
|
-
* Create transform stream for audio track
|
|
7760
|
-
*/
|
|
7761
|
-
createAudioStream() {
|
|
7762
|
-
const hasAudio = Array.from(this.tracks.values()).some((t) => t.type === "audio");
|
|
7763
|
-
if (!hasAudio) return null;
|
|
7764
|
-
return new TransformStream(
|
|
7765
|
-
{
|
|
7766
|
-
start: (controller) => {
|
|
7767
|
-
this.audioController = controller;
|
|
7768
|
-
},
|
|
7769
|
-
transform: (chunk, controller) => {
|
|
7770
|
-
const desiredSize = controller.desiredSize ?? this.demuxHighWaterMark;
|
|
7771
|
-
this.backpressureMonitor.updateMetrics("demux-audio", desiredSize);
|
|
7772
|
-
const chunkData = new Uint8Array(chunk);
|
|
7773
|
-
this.appendBuffer(chunkData);
|
|
7774
|
-
this.mp4boxFile.flush();
|
|
7775
|
-
},
|
|
7776
|
-
flush: () => {
|
|
7777
|
-
this.mp4boxFile.flush();
|
|
7778
|
-
}
|
|
7779
|
-
},
|
|
7780
|
-
// Queuing strategy: use configuration
|
|
7781
|
-
{
|
|
7782
|
-
highWaterMark: this.demuxHighWaterMark,
|
|
7783
|
-
size: () => 1
|
|
7784
|
-
}
|
|
7785
|
-
);
|
|
7786
|
-
}
|
|
7787
|
-
appendBuffer(chunk) {
|
|
7788
|
-
const buffer = chunk.buffer;
|
|
7789
|
-
buffer.fileStart = this.fileOffset;
|
|
7790
|
-
this.mp4boxFile.appendBuffer(buffer);
|
|
7791
|
-
this.fileOffset += chunk.byteLength;
|
|
7792
|
-
}
|
|
7793
|
-
/**
|
|
7794
|
-
* Get video track info if available
|
|
7795
|
-
*/
|
|
7796
|
-
get videoTrackInfo() {
|
|
7797
|
-
return Array.from(this.tracks.values()).find((track) => track.type === "video");
|
|
7798
|
-
}
|
|
7799
|
-
/**
|
|
7800
|
-
* Get audio track info if available
|
|
7801
|
-
*/
|
|
7802
|
-
get audioTrackInfo() {
|
|
7803
|
-
return Array.from(this.tracks.values()).find((track) => track.type === "audio");
|
|
7804
|
-
}
|
|
7805
|
-
destroy() {
|
|
7806
|
-
this.mp4boxFile?.stop();
|
|
7807
|
-
this.mp4boxFile = null;
|
|
7808
|
-
this.tracks.clear();
|
|
7809
|
-
this.backpressureMonitor.clear();
|
|
7810
|
-
this.isReady = false;
|
|
7811
|
-
this.videoTimestampOffset = null;
|
|
7812
|
-
this.audioTimestampOffset = null;
|
|
7813
|
-
}
|
|
7814
|
-
}
|
|
7815
|
-
class VideoDemuxWorker {
|
|
7816
|
-
channel;
|
|
7817
|
-
demuxer = null;
|
|
7818
|
-
clipId = null;
|
|
7819
|
-
downstreamPort = null;
|
|
7820
|
-
constructor() {
|
|
7821
|
-
this.channel = new WorkerChannel(self, {
|
|
7822
|
-
name: "VideoDemuxWorker",
|
|
7823
|
-
timeout: 3e4
|
|
7824
|
-
});
|
|
7825
|
-
this.setupHandlers();
|
|
7826
|
-
}
|
|
7827
|
-
/* @better-ai.mdc For test visibility */
|
|
7828
|
-
setupHandlers() {
|
|
7829
|
-
this.channel.registerHandler("configure", this.handleConfigure.bind(this));
|
|
7830
|
-
this.channel.registerHandler("connect", this.handleConnect.bind(this));
|
|
7831
|
-
this.channel.registerHandler("get_stats", this.handleGetStats.bind(this));
|
|
7832
|
-
this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));
|
|
7833
|
-
this.channel.receiveStream(this.handleReceiveStream.bind(this));
|
|
7834
|
-
}
|
|
7835
|
-
/**
|
|
7836
|
-
* Handle connection from orchestrator
|
|
7837
|
-
*/
|
|
7838
|
-
async handleConnect(payload) {
|
|
7839
|
-
const { port, clipId } = payload;
|
|
7840
|
-
if (!port) {
|
|
7841
|
-
return { success: false };
|
|
7842
|
-
}
|
|
7843
|
-
this.downstreamPort = port;
|
|
7844
|
-
this.clipId = clipId || null;
|
|
7845
|
-
return { success: true };
|
|
7846
|
-
}
|
|
7847
|
-
/**
|
|
7848
|
-
* Configure demuxer with format settings
|
|
7849
|
-
* @param payload.config - Demuxer configuration
|
|
7850
|
-
* @param payload.initial - If true, initialize worker state; otherwise just update config
|
|
7851
|
-
*/
|
|
7852
|
-
async handleConfigure(payload) {
|
|
7853
|
-
const { config, initial = false } = payload;
|
|
7854
|
-
try {
|
|
7855
|
-
if (initial) {
|
|
7856
|
-
this.channel.state = WorkerState.Ready;
|
|
7857
|
-
if (this.demuxer) {
|
|
7858
|
-
this.demuxer.destroy();
|
|
7859
|
-
}
|
|
7860
|
-
this.demuxer = new MP4Demuxer({
|
|
7861
|
-
...config,
|
|
7862
|
-
skipAudio: true,
|
|
7863
|
-
// Video only
|
|
7864
|
-
onReady: () => this.handleDemuxerReady()
|
|
7865
|
-
});
|
|
7866
|
-
this.channel.notify("configured");
|
|
7867
|
-
return { success: true };
|
|
7868
|
-
} else {
|
|
7869
|
-
this.demuxer?.updateConfig(config);
|
|
7870
|
-
return { success: true };
|
|
7871
|
-
}
|
|
7872
|
-
} catch (error) {
|
|
7873
|
-
throw {
|
|
7874
|
-
code: error.code || "CONFIG_ERROR",
|
|
7875
|
-
message: error.message
|
|
7876
|
-
};
|
|
7877
|
-
}
|
|
7878
|
-
}
|
|
7879
|
-
/**
|
|
7880
|
-
* Handle input stream from ResourceLoader (main thread)
|
|
7881
|
-
* Strategy: Stream immediately, send codec info when ready
|
|
7882
|
-
*/
|
|
7883
|
-
async handleReceiveStream(stream, metadata) {
|
|
7884
|
-
this.clipId = metadata?.clipId || this.clipId;
|
|
7885
|
-
if (!this.demuxer) {
|
|
7886
|
-
this.demuxer = new MP4Demuxer({
|
|
7887
|
-
highWaterMark: 10,
|
|
7888
|
-
skipAudio: true,
|
|
7889
|
-
onReady: () => this.handleDemuxerReady()
|
|
7890
|
-
});
|
|
7891
|
-
}
|
|
7892
|
-
if (!this.downstreamPort) {
|
|
7893
|
-
throw new Error("Decoder not connected");
|
|
7894
|
-
}
|
|
7895
|
-
const videoStream = this.demuxer.createVideoStream();
|
|
7896
|
-
const downstreamChannel = new WorkerChannel(this.downstreamPort, {
|
|
7897
|
-
name: "VideoDemux-Decoder",
|
|
7898
|
-
timeout: 3e4
|
|
7899
|
-
});
|
|
7900
|
-
downstreamChannel.sendStream(videoStream.readable, {
|
|
7901
|
-
streamType: "video",
|
|
7902
|
-
clipId: this.clipId
|
|
7903
|
-
});
|
|
7904
|
-
await stream.pipeTo(videoStream.writable);
|
|
7905
|
-
}
|
|
7906
|
-
handleDemuxerReady() {
|
|
7907
|
-
if (!this.demuxer || !this.downstreamPort) {
|
|
7908
|
-
return;
|
|
7909
|
-
}
|
|
7910
|
-
const videoTrackInfo = this.demuxer.videoTrackInfo;
|
|
7911
|
-
if (!videoTrackInfo) {
|
|
7912
|
-
console.error("[VideoDemuxWorker] No video track found after ready");
|
|
7913
|
-
return;
|
|
7914
|
-
}
|
|
7915
|
-
const downstreamChannel = new WorkerChannel(this.downstreamPort, {
|
|
7916
|
-
name: "VideoDemux-Decoder",
|
|
7917
|
-
timeout: 3e4
|
|
7918
|
-
});
|
|
7919
|
-
downstreamChannel.send("configure", {
|
|
7920
|
-
clipId: this.clipId,
|
|
7921
|
-
streamType: "video",
|
|
7922
|
-
codec: videoTrackInfo.codec,
|
|
7923
|
-
width: videoTrackInfo.width,
|
|
7924
|
-
height: videoTrackInfo.height,
|
|
7925
|
-
description: videoTrackInfo.description
|
|
7926
|
-
});
|
|
7927
|
-
}
|
|
7928
|
-
/**
|
|
7929
|
-
* Get demuxer statistics
|
|
7930
|
-
*/
|
|
7931
|
-
async handleGetStats() {
|
|
7932
|
-
if (!this.demuxer) {
|
|
7933
|
-
return { state: this.channel.state };
|
|
7934
|
-
}
|
|
7935
|
-
return {
|
|
7936
|
-
tracksInfo: Array.from(this.demuxer.tracks.values()),
|
|
7937
|
-
state: this.channel.state
|
|
7938
|
-
};
|
|
7939
|
-
}
|
|
7940
|
-
/**
|
|
7941
|
-
* Dispose worker and cleanup resources
|
|
7942
|
-
*/
|
|
7943
|
-
async handleDispose() {
|
|
7944
|
-
this.demuxer?.destroy();
|
|
7945
|
-
this.demuxer = null;
|
|
7946
|
-
this.clipId = null;
|
|
7947
|
-
this.downstreamPort?.close();
|
|
7948
|
-
this.downstreamPort = null;
|
|
7949
|
-
this.channel.state = WorkerState.Disposed;
|
|
7950
|
-
return { success: true };
|
|
7951
|
-
}
|
|
7952
|
-
}
|
|
7953
|
-
const worker = new VideoDemuxWorker();
|
|
7954
|
-
self.addEventListener("beforeunload", () => {
|
|
7955
|
-
worker["handleDispose"]();
|
|
7956
|
-
});
|
|
7957
|
-
//# sourceMappingURL=video-demux.worker-D019I7GQ.js.map
|
|
7046
|
+
export {
|
|
7047
|
+
mp4box_all as m
|
|
7048
|
+
};
|
|
7049
|
+
//# sourceMappingURL=mp4box.all.js.map
|