html-video-element-webworker-polyfill 0.0.0-stage → 0.1.1
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/LICENSE +201 -0
- package/README.md +111 -2
- package/dist/auto.d.ts +1 -0
- package/dist/auto.js +6 -0
- package/dist/auto.js.map +1 -0
- package/dist/canvas2d.d.ts +4 -0
- package/dist/canvas2d.js +15 -0
- package/dist/canvas2d.js.map +1 -0
- package/dist/document-GIe-DKmi.js +879 -0
- package/dist/document-GIe-DKmi.js.map +1 -0
- package/dist/environment-De2r8LaA.js +29 -0
- package/dist/environment-De2r8LaA.js.map +1 -0
- package/dist/environment-W_2WcDor.d.ts +35 -0
- package/dist/frame-source-BG--PzqD.js +43 -0
- package/dist/frame-source-BG--PzqD.js.map +1 -0
- package/dist/host-D-rmvsLs.js +423 -0
- package/dist/host-D-rmvsLs.js.map +1 -0
- package/dist/host.d.ts +3 -0
- package/dist/host.js +3 -0
- package/dist/image-bitmap.d.ts +5 -0
- package/dist/image-bitmap.js +40 -0
- package/dist/image-bitmap.js.map +1 -0
- package/dist/index-B9gWpxKE.d.ts +186 -0
- package/dist/index-RizJW9Vb.d.ts +15 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/install-BDVttfHM.js +20 -0
- package/dist/install-BDVttfHM.js.map +1 -0
- package/dist/protocol-Be-N8xdd.js +79 -0
- package/dist/protocol-Be-N8xdd.js.map +1 -0
- package/dist/webgl.d.ts +4 -0
- package/dist/webgl.js +18 -0
- package/dist/webgl.js.map +1 -0
- package/dist/webgpu.d.ts +4 -0
- package/dist/webgpu.js +24 -0
- package/dist/webgpu.js.map +1 -0
- package/dist/worker.d.ts +3 -0
- package/dist/worker.js +3 -0
- package/package.json +109 -4
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
import { a as isProtocolMessage, r as MEDIA_EVENTS, t as CHANNEL } from "./protocol-Be-N8xdd.js";
|
|
2
|
+
import { a as isMessageEndpoint, r as assertWorker } from "./environment-De2r8LaA.js";
|
|
3
|
+
import { t as FRAME_SOURCE } from "./frame-source-BG--PzqD.js";
|
|
4
|
+
//#region src/worker/bridge.ts
|
|
5
|
+
const MISSING_HOST_WARNING_DELAY = 3e3;
|
|
6
|
+
var Bridge = class {
|
|
7
|
+
endpoint;
|
|
8
|
+
timeOffset = 0;
|
|
9
|
+
#seq = 0;
|
|
10
|
+
#nextId = 1;
|
|
11
|
+
#nextCallId = 1;
|
|
12
|
+
#receivers = /* @__PURE__ */ new Map();
|
|
13
|
+
#pinned = /* @__PURE__ */ new Set();
|
|
14
|
+
#calls = /* @__PURE__ */ new Map();
|
|
15
|
+
#canPlayType = /* @__PURE__ */ new Map();
|
|
16
|
+
#registry;
|
|
17
|
+
#welcomed = false;
|
|
18
|
+
#missingHostTimer;
|
|
19
|
+
constructor(endpoint) {
|
|
20
|
+
this.endpoint = endpoint;
|
|
21
|
+
this.#registry = typeof FinalizationRegistry === "function" ? new FinalizationRegistry(({ id, release }) => {
|
|
22
|
+
release();
|
|
23
|
+
this.#receivers.delete(id);
|
|
24
|
+
this.post({
|
|
25
|
+
kind: "dispose",
|
|
26
|
+
id
|
|
27
|
+
});
|
|
28
|
+
}) : null;
|
|
29
|
+
endpoint.addEventListener("message", this.#onMessage);
|
|
30
|
+
endpoint.start?.();
|
|
31
|
+
this.post({ kind: "hello" });
|
|
32
|
+
}
|
|
33
|
+
watchForHost() {
|
|
34
|
+
if (this.#welcomed || this.#missingHostTimer !== void 0) return;
|
|
35
|
+
this.#missingHostTimer = setTimeout(() => {
|
|
36
|
+
if (this.#welcomed) return;
|
|
37
|
+
console.warn("html-video-element-webworker-polyfill: no VideoHost answered this worker. Call createVideoHost(worker) on the main thread right after creating the worker, otherwise videos never load.");
|
|
38
|
+
}, MISSING_HOST_WARNING_DELAY);
|
|
39
|
+
}
|
|
40
|
+
nextId() {
|
|
41
|
+
return this.#nextId++;
|
|
42
|
+
}
|
|
43
|
+
post(message, transfer = []) {
|
|
44
|
+
const seq = ++this.#seq;
|
|
45
|
+
this.endpoint.postMessage({
|
|
46
|
+
...message,
|
|
47
|
+
[CHANNEL]: 1,
|
|
48
|
+
seq
|
|
49
|
+
}, transfer);
|
|
50
|
+
return seq;
|
|
51
|
+
}
|
|
52
|
+
call(message, transfer) {
|
|
53
|
+
const callId = this.#nextCallId++;
|
|
54
|
+
const promise = new Promise((resolve, reject) => this.#calls.set(callId, {
|
|
55
|
+
resolve,
|
|
56
|
+
reject
|
|
57
|
+
}));
|
|
58
|
+
return [this.post({
|
|
59
|
+
...message,
|
|
60
|
+
callId
|
|
61
|
+
}, transfer), promise];
|
|
62
|
+
}
|
|
63
|
+
register(id, element, receiver, release) {
|
|
64
|
+
this.#receivers.set(id, new WeakRef(receiver));
|
|
65
|
+
this.#registry?.register(element, {
|
|
66
|
+
id,
|
|
67
|
+
release
|
|
68
|
+
}, receiver);
|
|
69
|
+
}
|
|
70
|
+
unregister(id, receiver, element) {
|
|
71
|
+
this.#receivers.delete(id);
|
|
72
|
+
this.#registry?.unregister(receiver);
|
|
73
|
+
this.#pinned.delete(element);
|
|
74
|
+
}
|
|
75
|
+
pin(element, pinned) {
|
|
76
|
+
if (pinned) this.#pinned.add(element);
|
|
77
|
+
else this.#pinned.delete(element);
|
|
78
|
+
}
|
|
79
|
+
canPlayType(type) {
|
|
80
|
+
const cached = this.#canPlayType.get(type);
|
|
81
|
+
if (cached !== void 0) return cached;
|
|
82
|
+
const callId = this.#nextCallId++;
|
|
83
|
+
this.#calls.set(callId, {
|
|
84
|
+
resolve: (value) => this.#canPlayType.set(type, value),
|
|
85
|
+
reject: () => {}
|
|
86
|
+
});
|
|
87
|
+
this.post({
|
|
88
|
+
kind: "canPlayType",
|
|
89
|
+
type,
|
|
90
|
+
callId
|
|
91
|
+
});
|
|
92
|
+
return guessCanPlayType(type);
|
|
93
|
+
}
|
|
94
|
+
#onMessage = (event) => {
|
|
95
|
+
if (!isProtocolMessage(event.data)) return;
|
|
96
|
+
const message = event.data;
|
|
97
|
+
switch (message.kind) {
|
|
98
|
+
case "welcome":
|
|
99
|
+
this.#welcomed = true;
|
|
100
|
+
clearTimeout(this.#missingHostTimer);
|
|
101
|
+
this.timeOffset = message.timeOrigin - performance.timeOrigin;
|
|
102
|
+
for (const [type, result] of Object.entries(message.canPlayType)) this.#canPlayType.set(type, result);
|
|
103
|
+
return;
|
|
104
|
+
case "result": {
|
|
105
|
+
const call = this.#calls.get(message.callId);
|
|
106
|
+
if (!call) return;
|
|
107
|
+
this.#calls.delete(message.callId);
|
|
108
|
+
if (message.error) call.reject(new DOMException(message.error.message, message.error.name));
|
|
109
|
+
else call.resolve(message.value);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
default: {
|
|
113
|
+
const receiver = this.#receivers.get(message.id)?.deref();
|
|
114
|
+
if (receiver) receiver.receive(message);
|
|
115
|
+
else if ("frame" in message) message.frame?.close();
|
|
116
|
+
if (message.kind === "frame") this.post({
|
|
117
|
+
kind: "frameAck",
|
|
118
|
+
id: message.id
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const CONTAINER_CODECS = {
|
|
125
|
+
"video/webm": [
|
|
126
|
+
"vp8",
|
|
127
|
+
"vp9",
|
|
128
|
+
"av01.0.05M.08"
|
|
129
|
+
],
|
|
130
|
+
"video/mp4": [
|
|
131
|
+
"avc1.42E01E",
|
|
132
|
+
"av01.0.05M.08",
|
|
133
|
+
"hvc1.1.6.L93.B0",
|
|
134
|
+
"vp09.00.10.08"
|
|
135
|
+
],
|
|
136
|
+
"audio/webm": ["opus", "vorbis"],
|
|
137
|
+
"audio/mp4": [
|
|
138
|
+
"mp4a.40.2",
|
|
139
|
+
"opus",
|
|
140
|
+
"flac"
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
function guessCanPlayType(type) {
|
|
144
|
+
const MediaSourceCtor = globalThis.MediaSource;
|
|
145
|
+
if (!MediaSourceCtor) return "";
|
|
146
|
+
if (/codecs\s*=/i.test(type)) return MediaSourceCtor.isTypeSupported(type) ? "probably" : "";
|
|
147
|
+
if (MediaSourceCtor.isTypeSupported(type)) return "maybe";
|
|
148
|
+
const container = type.split(";")[0].trim().toLowerCase();
|
|
149
|
+
return (CONTAINER_CODECS[container] ?? []).some((codec) => MediaSourceCtor.isTypeSupported(`${container}; codecs="${codec}"`)) ? "maybe" : "";
|
|
150
|
+
}
|
|
151
|
+
const bridges = /* @__PURE__ */ new WeakMap();
|
|
152
|
+
let defaultEndpoint;
|
|
153
|
+
function bridgeFor(endpoint) {
|
|
154
|
+
const target = endpoint ?? defaultEndpoint ?? globalThis;
|
|
155
|
+
if (!isMessageEndpoint(target)) throw new TypeError("html-video-element-webworker-polyfill: `transport` must be a MessagePort or another object with postMessage/addEventListener/removeEventListener.");
|
|
156
|
+
let bridge = bridges.get(target);
|
|
157
|
+
if (!bridge) {
|
|
158
|
+
bridge = new Bridge(target);
|
|
159
|
+
bridges.set(target, bridge);
|
|
160
|
+
}
|
|
161
|
+
return bridge;
|
|
162
|
+
}
|
|
163
|
+
function configureVideoTransport(endpoint) {
|
|
164
|
+
assertWorker("configureVideoTransport()");
|
|
165
|
+
if (!isMessageEndpoint(endpoint)) throw new TypeError("html-video-element-webworker-polyfill: configureVideoTransport() expects a MessagePort or another object with postMessage/addEventListener/removeEventListener.");
|
|
166
|
+
defaultEndpoint = endpoint;
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/worker/media-types.ts
|
|
170
|
+
var WorkerTimeRanges = class {
|
|
171
|
+
#ranges;
|
|
172
|
+
constructor(ranges = []) {
|
|
173
|
+
this.#ranges = ranges;
|
|
174
|
+
}
|
|
175
|
+
get length() {
|
|
176
|
+
return this.#ranges.length;
|
|
177
|
+
}
|
|
178
|
+
start(index) {
|
|
179
|
+
return this.#at(index)[0];
|
|
180
|
+
}
|
|
181
|
+
end(index) {
|
|
182
|
+
return this.#at(index)[1];
|
|
183
|
+
}
|
|
184
|
+
#at(index) {
|
|
185
|
+
const range = this.#ranges[index];
|
|
186
|
+
if (!range) throw new DOMException(`The index provided (${index}) is greater than or equal to the maximum bound (${this.#ranges.length}).`, "IndexSizeError");
|
|
187
|
+
return range;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var WorkerMediaError = class {
|
|
191
|
+
static MEDIA_ERR_ABORTED = 1;
|
|
192
|
+
static MEDIA_ERR_NETWORK = 2;
|
|
193
|
+
static MEDIA_ERR_DECODE = 3;
|
|
194
|
+
static MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
|
195
|
+
MEDIA_ERR_ABORTED = 1;
|
|
196
|
+
MEDIA_ERR_NETWORK = 2;
|
|
197
|
+
MEDIA_ERR_DECODE = 3;
|
|
198
|
+
MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
|
199
|
+
code;
|
|
200
|
+
message;
|
|
201
|
+
constructor({ code, message }) {
|
|
202
|
+
this.code = code;
|
|
203
|
+
this.message = message;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/worker/video-element.ts
|
|
208
|
+
const EMPTY_STATE = {
|
|
209
|
+
currentSrc: "",
|
|
210
|
+
currentTime: 0,
|
|
211
|
+
duration: NaN,
|
|
212
|
+
ended: false,
|
|
213
|
+
error: null,
|
|
214
|
+
muted: false,
|
|
215
|
+
networkState: 0,
|
|
216
|
+
paused: true,
|
|
217
|
+
playbackRate: 1,
|
|
218
|
+
readyState: 0,
|
|
219
|
+
seeking: false,
|
|
220
|
+
videoHeight: 0,
|
|
221
|
+
videoWidth: 0,
|
|
222
|
+
volume: 1,
|
|
223
|
+
buffered: [],
|
|
224
|
+
played: [],
|
|
225
|
+
seekable: [],
|
|
226
|
+
quality: null
|
|
227
|
+
};
|
|
228
|
+
const LOAD_RESET = {
|
|
229
|
+
currentTime: 0,
|
|
230
|
+
duration: NaN,
|
|
231
|
+
ended: false,
|
|
232
|
+
error: null,
|
|
233
|
+
paused: true,
|
|
234
|
+
readyState: 0,
|
|
235
|
+
seeking: false,
|
|
236
|
+
videoHeight: 0,
|
|
237
|
+
videoWidth: 0,
|
|
238
|
+
buffered: [],
|
|
239
|
+
played: [],
|
|
240
|
+
seekable: []
|
|
241
|
+
};
|
|
242
|
+
const ATTRIBUTES = {
|
|
243
|
+
autoplay: {
|
|
244
|
+
prop: "autoplay",
|
|
245
|
+
type: "bool"
|
|
246
|
+
},
|
|
247
|
+
controls: {
|
|
248
|
+
prop: "controls",
|
|
249
|
+
type: "bool"
|
|
250
|
+
},
|
|
251
|
+
crossorigin: {
|
|
252
|
+
prop: "crossOrigin",
|
|
253
|
+
type: "nullable"
|
|
254
|
+
},
|
|
255
|
+
disablepictureinpicture: {
|
|
256
|
+
prop: "disablePictureInPicture",
|
|
257
|
+
type: "bool"
|
|
258
|
+
},
|
|
259
|
+
disableremoteplayback: {
|
|
260
|
+
prop: "disableRemotePlayback",
|
|
261
|
+
type: "bool"
|
|
262
|
+
},
|
|
263
|
+
height: {
|
|
264
|
+
prop: "height",
|
|
265
|
+
type: "number"
|
|
266
|
+
},
|
|
267
|
+
loop: {
|
|
268
|
+
prop: "loop",
|
|
269
|
+
type: "bool"
|
|
270
|
+
},
|
|
271
|
+
muted: {
|
|
272
|
+
prop: "defaultMuted",
|
|
273
|
+
type: "bool"
|
|
274
|
+
},
|
|
275
|
+
playsinline: {
|
|
276
|
+
prop: "playsInline",
|
|
277
|
+
type: "bool"
|
|
278
|
+
},
|
|
279
|
+
poster: {
|
|
280
|
+
prop: "poster",
|
|
281
|
+
type: "string"
|
|
282
|
+
},
|
|
283
|
+
preload: {
|
|
284
|
+
prop: "preload",
|
|
285
|
+
type: "string"
|
|
286
|
+
},
|
|
287
|
+
src: {
|
|
288
|
+
prop: "src",
|
|
289
|
+
type: "string"
|
|
290
|
+
},
|
|
291
|
+
width: {
|
|
292
|
+
prop: "width",
|
|
293
|
+
type: "number"
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var Internals = class {
|
|
297
|
+
element;
|
|
298
|
+
bridge;
|
|
299
|
+
id;
|
|
300
|
+
state = { ...EMPTY_STATE };
|
|
301
|
+
props = {
|
|
302
|
+
autoplay: false,
|
|
303
|
+
controls: false,
|
|
304
|
+
crossOrigin: null,
|
|
305
|
+
defaultMuted: false,
|
|
306
|
+
defaultPlaybackRate: 1,
|
|
307
|
+
disablePictureInPicture: false,
|
|
308
|
+
disableRemotePlayback: false,
|
|
309
|
+
height: 0,
|
|
310
|
+
loop: false,
|
|
311
|
+
playsInline: false,
|
|
312
|
+
poster: "",
|
|
313
|
+
preload: "metadata",
|
|
314
|
+
preservesPitch: true,
|
|
315
|
+
width: 0
|
|
316
|
+
};
|
|
317
|
+
src = "";
|
|
318
|
+
srcObject = null;
|
|
319
|
+
hasSrcAttribute = false;
|
|
320
|
+
frame = { current: null };
|
|
321
|
+
localWrites = /* @__PURE__ */ new Map();
|
|
322
|
+
frameCallbacks = /* @__PURE__ */ new Map();
|
|
323
|
+
nextFrameCallbackId = 1;
|
|
324
|
+
handlers = /* @__PURE__ */ new Map();
|
|
325
|
+
loadSeq = 0;
|
|
326
|
+
disposed = false;
|
|
327
|
+
constructor(element, bridge) {
|
|
328
|
+
this.element = element;
|
|
329
|
+
this.bridge = bridge;
|
|
330
|
+
this.id = bridge.nextId();
|
|
331
|
+
const frame = this.frame;
|
|
332
|
+
bridge.register(this.id, element, this, () => {
|
|
333
|
+
frame.current?.close();
|
|
334
|
+
frame.current = null;
|
|
335
|
+
});
|
|
336
|
+
bridge.post({
|
|
337
|
+
kind: "create",
|
|
338
|
+
id: this.id,
|
|
339
|
+
props: {}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
write(prop, value, local = {}, transfer) {
|
|
343
|
+
if (this.disposed) return 0;
|
|
344
|
+
const seq = this.bridge.post({
|
|
345
|
+
kind: "set",
|
|
346
|
+
id: this.id,
|
|
347
|
+
prop,
|
|
348
|
+
value
|
|
349
|
+
}, transfer);
|
|
350
|
+
this.applyLocal(seq, local);
|
|
351
|
+
return seq;
|
|
352
|
+
}
|
|
353
|
+
invoke(method, args, local = {}) {
|
|
354
|
+
if (this.disposed) return 0;
|
|
355
|
+
const seq = this.bridge.post({
|
|
356
|
+
kind: "call",
|
|
357
|
+
id: this.id,
|
|
358
|
+
method,
|
|
359
|
+
args
|
|
360
|
+
});
|
|
361
|
+
this.applyLocal(seq, local);
|
|
362
|
+
return seq;
|
|
363
|
+
}
|
|
364
|
+
markLoad(seq) {
|
|
365
|
+
if (seq) this.loadSeq = seq;
|
|
366
|
+
}
|
|
367
|
+
applyLocal(seq, local) {
|
|
368
|
+
for (const [key, value] of Object.entries(local)) {
|
|
369
|
+
this.state[key] = value;
|
|
370
|
+
this.localWrites.set(key, seq);
|
|
371
|
+
}
|
|
372
|
+
if ("readyState" in local && (local.readyState ?? 0) < 2) this.setFrame(null);
|
|
373
|
+
this.updatePin();
|
|
374
|
+
}
|
|
375
|
+
updatePin() {
|
|
376
|
+
const { paused, seeking, networkState } = this.state;
|
|
377
|
+
this.bridge.pin(this.element, !paused || seeking || networkState === 2);
|
|
378
|
+
}
|
|
379
|
+
applyState(ack, state) {
|
|
380
|
+
const target = this.state;
|
|
381
|
+
for (const [key, value] of Object.entries(state)) {
|
|
382
|
+
const pending = this.localWrites.get(key);
|
|
383
|
+
if (pending !== void 0) {
|
|
384
|
+
if (pending > ack) continue;
|
|
385
|
+
this.localWrites.delete(key);
|
|
386
|
+
}
|
|
387
|
+
target[key] = value;
|
|
388
|
+
}
|
|
389
|
+
this.updatePin();
|
|
390
|
+
}
|
|
391
|
+
setFrame(frame) {
|
|
392
|
+
if (this.frame.current === frame) return;
|
|
393
|
+
this.frame.current?.close();
|
|
394
|
+
this.frame.current = frame;
|
|
395
|
+
}
|
|
396
|
+
receive(message) {
|
|
397
|
+
if (this.disposed || message.kind !== "state" && message.ack < this.loadSeq) {
|
|
398
|
+
if ("frame" in message) message.frame?.close();
|
|
399
|
+
if (this.disposed) return;
|
|
400
|
+
}
|
|
401
|
+
this.applyState(message.ack, message.state);
|
|
402
|
+
if (message.kind !== "state" && message.ack < this.loadSeq) return;
|
|
403
|
+
switch (message.kind) {
|
|
404
|
+
case "state": return;
|
|
405
|
+
case "frame":
|
|
406
|
+
this.setFrame(message.frame);
|
|
407
|
+
this.runFrameCallbacks(message.metadata);
|
|
408
|
+
return;
|
|
409
|
+
case "event":
|
|
410
|
+
if (message.frame) this.setFrame(message.frame);
|
|
411
|
+
else if (message.type === "emptied") this.setFrame(null);
|
|
412
|
+
this.element.dispatchEvent(new Event(message.type));
|
|
413
|
+
if (message.frame && message.metadata) this.runFrameCallbacks(message.metadata);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
runFrameCallbacks(metadata) {
|
|
417
|
+
if (this.frameCallbacks.size === 0) return;
|
|
418
|
+
const callbacks = [...this.frameCallbacks.values()];
|
|
419
|
+
this.frameCallbacks.clear();
|
|
420
|
+
const offset = this.bridge.timeOffset;
|
|
421
|
+
const local = {
|
|
422
|
+
...metadata,
|
|
423
|
+
presentationTime: metadata.presentationTime + offset,
|
|
424
|
+
expectedDisplayTime: metadata.expectedDisplayTime + offset
|
|
425
|
+
};
|
|
426
|
+
const now = performance.now();
|
|
427
|
+
for (const callback of callbacks) try {
|
|
428
|
+
callback.call(this.element, now, local);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
reportError(error);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
resolveUrl(value) {
|
|
434
|
+
if (value === "") return value;
|
|
435
|
+
const base = globalThis.location?.href;
|
|
436
|
+
try {
|
|
437
|
+
return new URL(value, base).href;
|
|
438
|
+
} catch {
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
function reportError(error) {
|
|
444
|
+
const report = globalThis.reportError;
|
|
445
|
+
if (report) report(error);
|
|
446
|
+
else setTimeout(() => {
|
|
447
|
+
throw error;
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const internals = /* @__PURE__ */ new WeakMap();
|
|
451
|
+
function $(element) {
|
|
452
|
+
const value = internals.get(element);
|
|
453
|
+
if (!value) throw new TypeError("Illegal invocation");
|
|
454
|
+
return value;
|
|
455
|
+
}
|
|
456
|
+
var WorkerVideoElement = class extends EventTarget {
|
|
457
|
+
static NETWORK_EMPTY = 0;
|
|
458
|
+
static NETWORK_IDLE = 1;
|
|
459
|
+
static NETWORK_LOADING = 2;
|
|
460
|
+
static NETWORK_NO_SOURCE = 3;
|
|
461
|
+
static HAVE_NOTHING = 0;
|
|
462
|
+
static HAVE_METADATA = 1;
|
|
463
|
+
static HAVE_CURRENT_DATA = 2;
|
|
464
|
+
static HAVE_FUTURE_DATA = 3;
|
|
465
|
+
static HAVE_ENOUGH_DATA = 4;
|
|
466
|
+
NETWORK_EMPTY = 0;
|
|
467
|
+
NETWORK_IDLE = 1;
|
|
468
|
+
NETWORK_LOADING = 2;
|
|
469
|
+
NETWORK_NO_SOURCE = 3;
|
|
470
|
+
HAVE_NOTHING = 0;
|
|
471
|
+
HAVE_METADATA = 1;
|
|
472
|
+
HAVE_CURRENT_DATA = 2;
|
|
473
|
+
HAVE_FUTURE_DATA = 3;
|
|
474
|
+
HAVE_ENOUGH_DATA = 4;
|
|
475
|
+
tagName = "VIDEO";
|
|
476
|
+
nodeName = "VIDEO";
|
|
477
|
+
localName = "video";
|
|
478
|
+
constructor(options = {}) {
|
|
479
|
+
assertWorker("createVideoElement()");
|
|
480
|
+
super();
|
|
481
|
+
const bridge = bridgeFor(options.transport);
|
|
482
|
+
bridge.watchForHost();
|
|
483
|
+
internals.set(this, new Internals(this, bridge));
|
|
484
|
+
}
|
|
485
|
+
get currentFrame() {
|
|
486
|
+
const frame = $(this).frame.current;
|
|
487
|
+
return frame && frame.format !== null ? frame : null;
|
|
488
|
+
}
|
|
489
|
+
get disposed() {
|
|
490
|
+
return $(this).disposed;
|
|
491
|
+
}
|
|
492
|
+
dispose() {
|
|
493
|
+
const core = $(this);
|
|
494
|
+
if (core.disposed) return;
|
|
495
|
+
core.bridge.post({
|
|
496
|
+
kind: "dispose",
|
|
497
|
+
id: core.id
|
|
498
|
+
});
|
|
499
|
+
core.disposed = true;
|
|
500
|
+
core.setFrame(null);
|
|
501
|
+
core.frameCallbacks.clear();
|
|
502
|
+
core.bridge.unregister(core.id, core, this);
|
|
503
|
+
core.state = { ...EMPTY_STATE };
|
|
504
|
+
core.localWrites.clear();
|
|
505
|
+
}
|
|
506
|
+
[Symbol.dispose]() {
|
|
507
|
+
this.dispose();
|
|
508
|
+
}
|
|
509
|
+
get src() {
|
|
510
|
+
return $(this).src;
|
|
511
|
+
}
|
|
512
|
+
set src(value) {
|
|
513
|
+
const core = $(this);
|
|
514
|
+
core.src = core.resolveUrl(String(value));
|
|
515
|
+
core.hasSrcAttribute = true;
|
|
516
|
+
core.markLoad(core.write("src", core.src, {
|
|
517
|
+
...LOAD_RESET,
|
|
518
|
+
currentSrc: "",
|
|
519
|
+
networkState: 0
|
|
520
|
+
}));
|
|
521
|
+
}
|
|
522
|
+
get srcObject() {
|
|
523
|
+
return $(this).srcObject;
|
|
524
|
+
}
|
|
525
|
+
set srcObject(value) {
|
|
526
|
+
const core = $(this);
|
|
527
|
+
core.srcObject = value ?? null;
|
|
528
|
+
const transfer = value ? [value] : [];
|
|
529
|
+
core.markLoad(core.write("srcObject", value ?? null, {
|
|
530
|
+
...LOAD_RESET,
|
|
531
|
+
currentSrc: "",
|
|
532
|
+
networkState: 0
|
|
533
|
+
}, transfer));
|
|
534
|
+
}
|
|
535
|
+
get currentSrc() {
|
|
536
|
+
return $(this).state.currentSrc;
|
|
537
|
+
}
|
|
538
|
+
get duration() {
|
|
539
|
+
return $(this).state.duration;
|
|
540
|
+
}
|
|
541
|
+
get paused() {
|
|
542
|
+
return $(this).state.paused;
|
|
543
|
+
}
|
|
544
|
+
get ended() {
|
|
545
|
+
return $(this).state.ended;
|
|
546
|
+
}
|
|
547
|
+
get seeking() {
|
|
548
|
+
return $(this).state.seeking;
|
|
549
|
+
}
|
|
550
|
+
get readyState() {
|
|
551
|
+
return $(this).state.readyState;
|
|
552
|
+
}
|
|
553
|
+
get networkState() {
|
|
554
|
+
return $(this).state.networkState;
|
|
555
|
+
}
|
|
556
|
+
get videoWidth() {
|
|
557
|
+
return $(this).state.videoWidth;
|
|
558
|
+
}
|
|
559
|
+
get videoHeight() {
|
|
560
|
+
return $(this).state.videoHeight;
|
|
561
|
+
}
|
|
562
|
+
get error() {
|
|
563
|
+
const error = $(this).state.error;
|
|
564
|
+
return error ? new WorkerMediaError(error) : null;
|
|
565
|
+
}
|
|
566
|
+
get buffered() {
|
|
567
|
+
return new WorkerTimeRanges($(this).state.buffered);
|
|
568
|
+
}
|
|
569
|
+
get played() {
|
|
570
|
+
return new WorkerTimeRanges($(this).state.played);
|
|
571
|
+
}
|
|
572
|
+
get seekable() {
|
|
573
|
+
return new WorkerTimeRanges($(this).state.seekable);
|
|
574
|
+
}
|
|
575
|
+
get currentTime() {
|
|
576
|
+
return $(this).state.currentTime;
|
|
577
|
+
}
|
|
578
|
+
set currentTime(value) {
|
|
579
|
+
const time = Number(value);
|
|
580
|
+
if (!Number.isFinite(time)) throw new TypeError("Failed to set the 'currentTime' property: The provided double value is non-finite.");
|
|
581
|
+
const core = $(this);
|
|
582
|
+
const local = {
|
|
583
|
+
currentTime: time,
|
|
584
|
+
ended: false
|
|
585
|
+
};
|
|
586
|
+
if (core.state.readyState > 0) local.seeking = true;
|
|
587
|
+
core.write("currentTime", time, local);
|
|
588
|
+
}
|
|
589
|
+
fastSeek(time) {
|
|
590
|
+
const core = $(this);
|
|
591
|
+
const local = {
|
|
592
|
+
currentTime: time,
|
|
593
|
+
ended: false
|
|
594
|
+
};
|
|
595
|
+
if (core.state.readyState > 0) local.seeking = true;
|
|
596
|
+
core.invoke("fastSeek", [time], local);
|
|
597
|
+
}
|
|
598
|
+
get volume() {
|
|
599
|
+
return $(this).state.volume;
|
|
600
|
+
}
|
|
601
|
+
set volume(value) {
|
|
602
|
+
const volume = Number(value);
|
|
603
|
+
if (!(volume >= 0 && volume <= 1)) throw new DOMException(`Failed to set the 'volume' property: The volume provided (${value}) is outside the range [0, 1].`, "IndexSizeError");
|
|
604
|
+
$(this).write("volume", volume, { volume });
|
|
605
|
+
}
|
|
606
|
+
get muted() {
|
|
607
|
+
return $(this).state.muted;
|
|
608
|
+
}
|
|
609
|
+
set muted(value) {
|
|
610
|
+
$(this).write("muted", Boolean(value), { muted: Boolean(value) });
|
|
611
|
+
}
|
|
612
|
+
get playbackRate() {
|
|
613
|
+
return $(this).state.playbackRate;
|
|
614
|
+
}
|
|
615
|
+
set playbackRate(value) {
|
|
616
|
+
$(this).write("playbackRate", Number(value), { playbackRate: Number(value) });
|
|
617
|
+
}
|
|
618
|
+
get autoplay() {
|
|
619
|
+
return $(this).props.autoplay;
|
|
620
|
+
}
|
|
621
|
+
set autoplay(value) {
|
|
622
|
+
this.#mirror("autoplay", Boolean(value));
|
|
623
|
+
}
|
|
624
|
+
get controls() {
|
|
625
|
+
return $(this).props.controls;
|
|
626
|
+
}
|
|
627
|
+
set controls(value) {
|
|
628
|
+
this.#mirror("controls", Boolean(value));
|
|
629
|
+
}
|
|
630
|
+
get crossOrigin() {
|
|
631
|
+
return $(this).props.crossOrigin;
|
|
632
|
+
}
|
|
633
|
+
set crossOrigin(value) {
|
|
634
|
+
this.#mirror("crossOrigin", value === null ? null : String(value));
|
|
635
|
+
}
|
|
636
|
+
get defaultMuted() {
|
|
637
|
+
return $(this).props.defaultMuted;
|
|
638
|
+
}
|
|
639
|
+
set defaultMuted(value) {
|
|
640
|
+
this.#mirror("defaultMuted", Boolean(value));
|
|
641
|
+
}
|
|
642
|
+
get defaultPlaybackRate() {
|
|
643
|
+
return $(this).props.defaultPlaybackRate;
|
|
644
|
+
}
|
|
645
|
+
set defaultPlaybackRate(value) {
|
|
646
|
+
this.#mirror("defaultPlaybackRate", Number(value));
|
|
647
|
+
}
|
|
648
|
+
get disablePictureInPicture() {
|
|
649
|
+
return $(this).props.disablePictureInPicture;
|
|
650
|
+
}
|
|
651
|
+
set disablePictureInPicture(value) {
|
|
652
|
+
this.#mirror("disablePictureInPicture", Boolean(value));
|
|
653
|
+
}
|
|
654
|
+
get disableRemotePlayback() {
|
|
655
|
+
return $(this).props.disableRemotePlayback;
|
|
656
|
+
}
|
|
657
|
+
set disableRemotePlayback(value) {
|
|
658
|
+
this.#mirror("disableRemotePlayback", Boolean(value));
|
|
659
|
+
}
|
|
660
|
+
get height() {
|
|
661
|
+
return $(this).props.height;
|
|
662
|
+
}
|
|
663
|
+
set height(value) {
|
|
664
|
+
this.#mirror("height", Math.max(0, Math.trunc(Number(value))) || 0);
|
|
665
|
+
}
|
|
666
|
+
get width() {
|
|
667
|
+
return $(this).props.width;
|
|
668
|
+
}
|
|
669
|
+
set width(value) {
|
|
670
|
+
this.#mirror("width", Math.max(0, Math.trunc(Number(value))) || 0);
|
|
671
|
+
}
|
|
672
|
+
get loop() {
|
|
673
|
+
return $(this).props.loop;
|
|
674
|
+
}
|
|
675
|
+
set loop(value) {
|
|
676
|
+
this.#mirror("loop", Boolean(value));
|
|
677
|
+
}
|
|
678
|
+
get playsInline() {
|
|
679
|
+
return $(this).props.playsInline;
|
|
680
|
+
}
|
|
681
|
+
set playsInline(value) {
|
|
682
|
+
this.#mirror("playsInline", Boolean(value));
|
|
683
|
+
}
|
|
684
|
+
get poster() {
|
|
685
|
+
return $(this).props.poster;
|
|
686
|
+
}
|
|
687
|
+
set poster(value) {
|
|
688
|
+
this.#mirror("poster", $(this).resolveUrl(String(value)));
|
|
689
|
+
}
|
|
690
|
+
get preload() {
|
|
691
|
+
return $(this).props.preload;
|
|
692
|
+
}
|
|
693
|
+
set preload(value) {
|
|
694
|
+
const normalized = String(value).toLowerCase();
|
|
695
|
+
this.#mirror("preload", [
|
|
696
|
+
"none",
|
|
697
|
+
"metadata",
|
|
698
|
+
"auto"
|
|
699
|
+
].includes(normalized) ? normalized : "auto");
|
|
700
|
+
}
|
|
701
|
+
get preservesPitch() {
|
|
702
|
+
return $(this).props.preservesPitch;
|
|
703
|
+
}
|
|
704
|
+
set preservesPitch(value) {
|
|
705
|
+
this.#mirror("preservesPitch", Boolean(value));
|
|
706
|
+
}
|
|
707
|
+
#mirror(prop, value) {
|
|
708
|
+
const core = $(this);
|
|
709
|
+
core.props[prop] = value;
|
|
710
|
+
core.write(prop, value);
|
|
711
|
+
}
|
|
712
|
+
getAttribute(name) {
|
|
713
|
+
const core = $(this);
|
|
714
|
+
const attribute = ATTRIBUTES[name.toLowerCase()];
|
|
715
|
+
if (!attribute) return null;
|
|
716
|
+
if (attribute.prop === "src") return core.hasSrcAttribute ? core.src : null;
|
|
717
|
+
const value = this[attribute.prop];
|
|
718
|
+
switch (attribute.type) {
|
|
719
|
+
case "bool": return value ? "" : null;
|
|
720
|
+
case "nullable": return value === null ? null : String(value);
|
|
721
|
+
default: return String(value);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
hasAttribute(name) {
|
|
725
|
+
return this.getAttribute(name) !== null;
|
|
726
|
+
}
|
|
727
|
+
setAttribute(name, value) {
|
|
728
|
+
const attribute = ATTRIBUTES[name.toLowerCase()];
|
|
729
|
+
if (!attribute) return;
|
|
730
|
+
const target = this;
|
|
731
|
+
switch (attribute.type) {
|
|
732
|
+
case "bool":
|
|
733
|
+
target[attribute.prop] = true;
|
|
734
|
+
break;
|
|
735
|
+
case "number":
|
|
736
|
+
target[attribute.prop] = Number.parseInt(value, 10) || 0;
|
|
737
|
+
break;
|
|
738
|
+
default: target[attribute.prop] = String(value);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
removeAttribute(name) {
|
|
742
|
+
const attribute = ATTRIBUTES[name.toLowerCase()];
|
|
743
|
+
if (!attribute) return;
|
|
744
|
+
const core = $(this);
|
|
745
|
+
if (attribute.prop === "src") {
|
|
746
|
+
core.src = "";
|
|
747
|
+
core.hasSrcAttribute = false;
|
|
748
|
+
core.invoke("removeAttribute", ["src"]);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const target = this;
|
|
752
|
+
switch (attribute.type) {
|
|
753
|
+
case "bool":
|
|
754
|
+
target[attribute.prop] = false;
|
|
755
|
+
break;
|
|
756
|
+
case "nullable":
|
|
757
|
+
target[attribute.prop] = null;
|
|
758
|
+
break;
|
|
759
|
+
case "number":
|
|
760
|
+
target[attribute.prop] = 0;
|
|
761
|
+
break;
|
|
762
|
+
default: target[attribute.prop] = attribute.prop === "preload" ? "metadata" : "";
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
play() {
|
|
766
|
+
const core = $(this);
|
|
767
|
+
if (core.disposed) return Promise.reject(new DOMException("The element has been disposed.", "InvalidStateError"));
|
|
768
|
+
const [seq, promise] = core.bridge.call({
|
|
769
|
+
kind: "call",
|
|
770
|
+
id: core.id,
|
|
771
|
+
method: "play",
|
|
772
|
+
args: []
|
|
773
|
+
});
|
|
774
|
+
core.applyLocal(seq, { paused: false });
|
|
775
|
+
return promise;
|
|
776
|
+
}
|
|
777
|
+
pause() {
|
|
778
|
+
$(this).invoke("pause", [], { paused: true });
|
|
779
|
+
}
|
|
780
|
+
load() {
|
|
781
|
+
const core = $(this);
|
|
782
|
+
core.markLoad(core.invoke("load", [], {
|
|
783
|
+
...LOAD_RESET,
|
|
784
|
+
networkState: 0
|
|
785
|
+
}));
|
|
786
|
+
}
|
|
787
|
+
canPlayType(type) {
|
|
788
|
+
return $(this).bridge.canPlayType(String(type));
|
|
789
|
+
}
|
|
790
|
+
getVideoPlaybackQuality() {
|
|
791
|
+
return $(this).state.quality ?? {
|
|
792
|
+
creationTime: performance.now(),
|
|
793
|
+
droppedVideoFrames: 0,
|
|
794
|
+
totalVideoFrames: 0
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
requestVideoFrameCallback(callback) {
|
|
798
|
+
const core = $(this);
|
|
799
|
+
const handle = core.nextFrameCallbackId++;
|
|
800
|
+
core.frameCallbacks.set(handle, callback);
|
|
801
|
+
return handle;
|
|
802
|
+
}
|
|
803
|
+
cancelVideoFrameCallback(handle) {
|
|
804
|
+
$(this).frameCallbacks.delete(handle);
|
|
805
|
+
}
|
|
806
|
+
requestPictureInPicture() {
|
|
807
|
+
return Promise.reject(new DOMException("Picture-in-Picture is not available in workers.", "NotSupportedError"));
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
Object.defineProperty(WorkerVideoElement.prototype, FRAME_SOURCE, {
|
|
811
|
+
configurable: true,
|
|
812
|
+
value() {
|
|
813
|
+
return this.currentFrame;
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
for (const type of MEDIA_EVENTS) Object.defineProperty(WorkerVideoElement.prototype, `on${type}`, {
|
|
817
|
+
configurable: true,
|
|
818
|
+
enumerable: true,
|
|
819
|
+
get() {
|
|
820
|
+
return $(this).handlers.get(type)?.handler ?? null;
|
|
821
|
+
},
|
|
822
|
+
set(handler) {
|
|
823
|
+
const core = $(this);
|
|
824
|
+
const existing = core.handlers.get(type);
|
|
825
|
+
if (typeof handler !== "function") {
|
|
826
|
+
if (existing) this.removeEventListener(type, existing.listener);
|
|
827
|
+
core.handlers.delete(type);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
if (existing) {
|
|
831
|
+
existing.handler = handler;
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const entry = {
|
|
835
|
+
handler,
|
|
836
|
+
listener: (event) => {
|
|
837
|
+
if (entry.handler?.call(this, event) === false) event.preventDefault();
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
core.handlers.set(type, entry);
|
|
841
|
+
this.addEventListener(type, entry.listener);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
function createVideoElement(options) {
|
|
845
|
+
return new WorkerVideoElement(options);
|
|
846
|
+
}
|
|
847
|
+
//#endregion
|
|
848
|
+
//#region src/worker/document.ts
|
|
849
|
+
const PATCHED = Symbol.for("html-video-element-webworker-polyfill.patched");
|
|
850
|
+
function polyfillDocument(options) {
|
|
851
|
+
assertWorker("polyfillDocument()");
|
|
852
|
+
const scope = globalThis;
|
|
853
|
+
scope.HTMLVideoElement ??= WorkerVideoElement;
|
|
854
|
+
scope.HTMLMediaElement ??= WorkerVideoElement;
|
|
855
|
+
scope.MediaError ??= WorkerMediaError;
|
|
856
|
+
scope.TimeRanges ??= WorkerTimeRanges;
|
|
857
|
+
const existing = scope.document;
|
|
858
|
+
if (existing && typeof existing.createElement === "function") {
|
|
859
|
+
if (existing[PATCHED]) return;
|
|
860
|
+
const original = existing.createElement.bind(existing);
|
|
861
|
+
existing.createElement = (tagName, elementOptions) => isVideo(tagName) ? new WorkerVideoElement(options) : original(tagName, elementOptions);
|
|
862
|
+
existing[PATCHED] = true;
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
scope.document = {
|
|
866
|
+
createElement(tagName) {
|
|
867
|
+
if (isVideo(tagName)) return new WorkerVideoElement(options);
|
|
868
|
+
throw new DOMException(`document.createElement("${tagName}") is not supported in a worker; only "video" is polyfilled.`, "NotSupportedError");
|
|
869
|
+
},
|
|
870
|
+
[PATCHED]: true
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function isVideo(tagName) {
|
|
874
|
+
return String(tagName).toLowerCase() === "video";
|
|
875
|
+
}
|
|
876
|
+
//#endregion
|
|
877
|
+
export { WorkerTimeRanges as a, WorkerMediaError as i, WorkerVideoElement as n, configureVideoTransport as o, createVideoElement as r, polyfillDocument as t };
|
|
878
|
+
|
|
879
|
+
//# sourceMappingURL=document-GIe-DKmi.js.map
|