html-video-element-webworker-polyfill 0.2.0 → 0.2.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/README.md +76 -88
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,141 +3,135 @@
|
|
|
3
3
|
[](https://github.com/stepancar/html-video-element-webworker-polyfill/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/html-video-element-webworker-polyfill)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
frames there.
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
const video = document.createElement("video");
|
|
11
|
-
video.src = "https://example.com/movie.webm";
|
|
12
|
-
video.addEventListener("seeked", () => {
|
|
13
|
-
ctx.drawImage(video, 0, 0);
|
|
14
|
-
});
|
|
15
|
-
video.currentTime = 12.5;
|
|
16
|
-
```
|
|
17
|
-
|
|
6
|
+
[`HTMLVideoElement`][HTMLVideoElement] for [Web Workers][Web Workers].
|
|
18
7
|
**[Live demo](https://stepancar.github.io/html-video-element-webworker-polyfill/)** ·
|
|
19
|
-
**[
|
|
20
|
-
|
|
21
|
-
## Why not WebCodecs?
|
|
22
|
-
|
|
23
|
-
[WebCodecs][WebCodecs] gives you a decoder, not a player: you demux containers yourself, configure codecs, feed chunks
|
|
24
|
-
and find keyframes to seek. [`HTMLVideoElement`][HTMLVideoElement] already handles every format the browser can play
|
|
25
|
-
and gives frame-accurate seeking with [`currentTime`][currentTime] and [`seeked`][seeked] out of the box. It just does not exist in workers. This package makes it available there.
|
|
26
|
-
|
|
27
|
-
## Install
|
|
8
|
+
**[npm package](https://www.npmjs.com/package/html-video-element-webworker-polyfill)**
|
|
28
9
|
|
|
29
10
|
```sh
|
|
30
11
|
npm install html-video-element-webworker-polyfill
|
|
31
12
|
```
|
|
32
13
|
|
|
33
|
-
##
|
|
14
|
+
## 1. Get video frames in a worker and draw them on a canvas
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
`main.js`
|
|
36
17
|
|
|
37
|
-
```
|
|
18
|
+
```js
|
|
38
19
|
import { createVideoHost } from "html-video-element-webworker-polyfill/host";
|
|
39
20
|
|
|
40
|
-
const worker = new Worker(
|
|
21
|
+
const worker = new Worker("./worker.js", { type: "module" });
|
|
41
22
|
createVideoHost(worker);
|
|
23
|
+
|
|
24
|
+
const canvas = document.querySelector("canvas").transferControlToOffscreen();
|
|
25
|
+
worker.postMessage({ canvas }, [canvas]);
|
|
42
26
|
```
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
`worker.js`
|
|
45
29
|
|
|
46
|
-
```
|
|
30
|
+
```js
|
|
47
31
|
import { polyfillDocument } from "html-video-element-webworker-polyfill/worker";
|
|
48
32
|
import { polyfill2dCanvasApi } from "html-video-element-webworker-polyfill/canvas2d";
|
|
49
33
|
|
|
50
34
|
polyfillDocument();
|
|
51
35
|
polyfill2dCanvasApi();
|
|
52
36
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
video.
|
|
57
|
-
video.
|
|
58
|
-
|
|
59
|
-
video.
|
|
60
|
-
|
|
37
|
+
self.addEventListener("message", ({ data }) => {
|
|
38
|
+
if (!data.canvas) return;
|
|
39
|
+
const ctx = data.canvas.getContext("2d");
|
|
40
|
+
const video = document.createElement("video");
|
|
41
|
+
video.muted = true;
|
|
42
|
+
video.src = "movie.mp4";
|
|
43
|
+
video.play();
|
|
44
|
+
video.requestVideoFrameCallback(function draw() {
|
|
45
|
+
ctx.drawImage(video, 0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
46
|
+
video.requestVideoFrameCallback(draw);
|
|
47
|
+
});
|
|
61
48
|
});
|
|
62
49
|
```
|
|
63
50
|
|
|
64
|
-
|
|
51
|
+
[Open in StackBlitz][example-process]
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
import { createVideoElement } from "html-video-element-webworker-polyfill/worker";
|
|
53
|
+
## 2. Connect to an existing video and manage it from a worker
|
|
68
54
|
|
|
69
|
-
|
|
70
|
-
video.src = "/movie.webm";
|
|
71
|
-
await video.play();
|
|
55
|
+
`index.html`
|
|
72
56
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
```html
|
|
58
|
+
<video id="player" src="movie.mp4"></video>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`main.js`
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import { createVideoHost } from "html-video-element-webworker-polyfill/host";
|
|
65
|
+
|
|
66
|
+
createVideoHost(new Worker("./worker.js", { type: "module" }));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`worker.js`
|
|
77
70
|
|
|
78
|
-
|
|
71
|
+
```js
|
|
72
|
+
import { connectToHostVideo } from "html-video-element-webworker-polyfill/worker";
|
|
73
|
+
|
|
74
|
+
const video = await connectToHostVideo("#player");
|
|
75
|
+
video.currentTime = 42;
|
|
79
76
|
```
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
the page. Existing `document.createElement("video")` code works unchanged:
|
|
78
|
+
[Open in StackBlitz][example-manage]
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
## 3. React to a video created by the worker and add it to the page
|
|
81
|
+
|
|
82
|
+
`main.js`
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { createVideoHost } from "html-video-element-webworker-polyfill/host";
|
|
86
|
+
|
|
87
|
+
createVideoHost(new Worker("./worker.js", { type: "module" }), {
|
|
86
88
|
onVideoCreated(video) {
|
|
87
|
-
if (video.id === "preview") document.
|
|
89
|
+
if (video.id === "preview") document.body.append(video);
|
|
88
90
|
},
|
|
89
91
|
});
|
|
90
92
|
```
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
`worker.js`
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { polyfillDocument } from "html-video-element-webworker-polyfill/worker";
|
|
98
|
+
|
|
99
|
+
polyfillDocument();
|
|
100
|
+
|
|
93
101
|
const video = document.createElement("video");
|
|
94
102
|
video.id = "preview";
|
|
103
|
+
video.muted = true;
|
|
104
|
+
video.src = "movie.mp4";
|
|
105
|
+
video.play();
|
|
95
106
|
```
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
import { connectToHostVideo } from "html-video-element-webworker-polyfill/worker";
|
|
108
|
+
[Open in StackBlitz][example-create]
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
video.currentTime = 10;
|
|
104
|
-
```
|
|
110
|
+
## Why not WebCodecs?
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
and
|
|
108
|
-
|
|
112
|
+
[WebCodecs][WebCodecs] gives you a decoder, not a player: you demux containers yourself, configure codecs, feed chunks
|
|
113
|
+
and find keyframes to seek. `HTMLVideoElement` already handles every format the browser can play and seeks
|
|
114
|
+
frame-accurately. It just does not exist in workers. This package makes it available there.
|
|
109
115
|
|
|
110
116
|
## API
|
|
111
117
|
|
|
112
118
|
| Import | Exports |
|
|
113
119
|
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
114
120
|
| `html-video-element-webworker-polyfill/host` | `createVideoHost(worker, { container?, onVideoCreated?, canConnect? })` — main thread |
|
|
115
|
-
| `html-video-element-webworker-polyfill/worker` | `createVideoElement()`, `connectToHostVideo(selector)`, `
|
|
121
|
+
| `html-video-element-webworker-polyfill/worker` | `createVideoElement()`, `connectToHostVideo(selector)`, `polyfillDocument()` ([`document.createElement`][createElement]), `WorkerVideoElement` |
|
|
116
122
|
| `html-video-element-webworker-polyfill/canvas2d` | `polyfill2dCanvasApi()` — [`drawImage`][drawImage], [`createPattern`][createPattern] |
|
|
117
123
|
| `html-video-element-webworker-polyfill/webgl` | `polyfillWebglAPI()` — [`texImage2D`][texImage2D] and friends, [WebGL][WebGL] 1 and 2 |
|
|
118
124
|
| `html-video-element-webworker-polyfill/webgpu` | `polyfillWebgpu()` — [`copyExternalImageToTexture`][copyExternalImageToTexture], [`importExternalTexture`][importExternalTexture] |
|
|
119
125
|
| `html-video-element-webworker-polyfill/image-bitmap` | `polyfillCreateImageBitmap()` — [`createImageBitmap`][createImageBitmap], `polyfillVideoFrame()` — [`new VideoFrame`][VideoFrame constructor] |
|
|
120
126
|
| `html-video-element-webworker-polyfill/auto` | installs every worker polyfill |
|
|
121
127
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
The worker element implements the [`HTMLVideoElement`][HTMLVideoElement] API: properties, [`play()`][play],
|
|
125
|
-
[`pause()`][pause], [`load()`][load], seeking, [media events][media events] and
|
|
126
|
-
[`requestVideoFrameCallback`][requestVideoFrameCallback]. It adds two non-standard members:
|
|
127
|
-
|
|
128
|
-
- `currentFrame` — the [`VideoFrame`][VideoFrame] currently on screen, or `null`. It is closed when the next frame
|
|
129
|
-
arrives; [`clone()`][clone] it to keep it.
|
|
130
|
-
- `dispose()` — releases the element. Unreferenced elements are also released automatically on garbage collection
|
|
131
|
-
via [`FinalizationRegistry`][FinalizationRegistry].
|
|
132
|
-
|
|
133
|
-
Calling a main-thread function in a worker, or a worker function on the main thread, throws an
|
|
134
|
-
`InvalidEnvironmentError`.
|
|
128
|
+
The worker element implements the [`HTMLVideoElement`][HTMLVideoElement] API, plus `currentFrame` (the
|
|
129
|
+
[`VideoFrame`][VideoFrame] on screen) and `dispose()`.
|
|
135
130
|
|
|
136
131
|
## Notes
|
|
137
132
|
|
|
138
|
-
- Cross-origin media needs [CORS][CORS]
|
|
139
|
-
|
|
140
|
-
- Frames are decoded on the main thread, so it must not be blocked for long.
|
|
133
|
+
- Cross-origin media needs [CORS][CORS].
|
|
134
|
+
- The package shares the worker's `postMessage` channel with your code, so check your own messages, as in example 1.
|
|
141
135
|
- Requires [`VideoFrame`][VideoFrame] and [`requestVideoFrameCallback`][requestVideoFrameCallback]: Chrome/Edge 94+,
|
|
142
136
|
Firefox 132+, Safari 17+.
|
|
143
137
|
|
|
@@ -145,15 +139,12 @@ Calling a main-thread function in a worker, or a worker function on the main thr
|
|
|
145
139
|
|
|
146
140
|
Apache-2.0
|
|
147
141
|
|
|
142
|
+
[example-process]: https://stackblitz.com/github/stepancar/html-video-element-webworker-polyfill/tree/main/examples/process-frames?file=worker.js&initialPath=/index.html&startScript=start
|
|
143
|
+
[example-manage]: https://stackblitz.com/github/stepancar/html-video-element-webworker-polyfill/tree/main/examples/manage-existing-video?file=worker.js&initialPath=/index.html&startScript=start
|
|
144
|
+
[example-create]: https://stackblitz.com/github/stepancar/html-video-element-webworker-polyfill/tree/main/examples/create-and-mount?file=worker.js&initialPath=/index.html&startScript=start
|
|
148
145
|
[HTMLVideoElement]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement
|
|
149
146
|
[Web Workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
|
|
150
147
|
[WebCodecs]: https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API
|
|
151
|
-
[currentTime]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/currentTime
|
|
152
|
-
[seeked]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event
|
|
153
|
-
[play]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play
|
|
154
|
-
[pause]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause
|
|
155
|
-
[load]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/load
|
|
156
|
-
[media events]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events
|
|
157
148
|
[requestVideoFrameCallback]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestVideoFrameCallback
|
|
158
149
|
[createElement]: https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
|
|
159
150
|
[drawImage]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
|
|
@@ -165,7 +156,4 @@ Apache-2.0
|
|
|
165
156
|
[createImageBitmap]: https://developer.mozilla.org/en-US/docs/Web/API/Window/createImageBitmap
|
|
166
157
|
[VideoFrame]: https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame
|
|
167
158
|
[VideoFrame constructor]: https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame/VideoFrame
|
|
168
|
-
[clone]: https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame/clone
|
|
169
|
-
[FinalizationRegistry]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
|
|
170
159
|
[CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS
|
|
171
|
-
[crossOrigin]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/crossOrigin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "html-video-element-webworker-polyfill",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "HTMLVideoElement inside Web Workers: a main-thread <video> mirrored into the worker with synchronous state, events and VideoFrames, plus Canvas 2D / WebGL / WebGPU polyfills.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|