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.
Files changed (2) hide show
  1. package/README.md +76 -88
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -3,141 +3,135 @@
3
3
  [![CI](https://github.com/stepancar/html-video-element-webworker-polyfill/actions/workflows/ci.yml/badge.svg)](https://github.com/stepancar/html-video-element-webworker-polyfill/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/html-video-element-webworker-polyfill)](https://www.npmjs.com/package/html-video-element-webworker-polyfill)
5
5
 
6
- Create and control an [`HTMLVideoElement`][HTMLVideoElement] from a [Web Worker][Web Workers] and read its decoded
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
- **[Open in StackBlitz](https://stackblitz.com/github/stepancar/html-video-element-webworker-polyfill/tree/main/examples/stackblitz?file=worker.js&initialPath=/index.html&startScript=start)**
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
- ## Usage
14
+ ## 1. Get video frames in a worker and draw them on a canvas
34
15
 
35
- Main thread:
16
+ `main.js`
36
17
 
37
- ```ts
18
+ ```js
38
19
  import { createVideoHost } from "html-video-element-webworker-polyfill/host";
39
20
 
40
- const worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
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
- Worker:
28
+ `worker.js`
45
29
 
46
- ```ts
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
- const ctx = new OffscreenCanvas(640, 360).getContext("2d")!;
54
- const video = document.createElement("video");
55
- video.src = "/movie.webm";
56
- video.addEventListener("loadedmetadata", () => {
57
- video.currentTime = 10;
58
- });
59
- video.addEventListener("seeked", () => {
60
- ctx.drawImage(video, 0, 0, 640, 360);
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
- Or without global patches:
51
+ [Open in StackBlitz][example-process]
65
52
 
66
- ```ts
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
- const video = createVideoElement();
70
- video.src = "/movie.webm";
71
- await video.play();
55
+ `index.html`
72
56
 
73
- video.requestVideoFrameCallback(function onFrame() {
74
- render(video.currentFrame);
75
- video.requestVideoFrameCallback(onFrame);
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
- video.dispose();
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
- Set an `id` right after creating an element to find its real `<video>` on the main thread, for example to put it on
82
- the page. Existing `document.createElement("video")` code works unchanged:
78
+ [Open in StackBlitz][example-manage]
83
79
 
84
- ```ts
85
- createVideoHost(worker, {
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.querySelector("#slot")!.append(video);
89
+ if (video.id === "preview") document.body.append(video);
88
90
  },
89
91
  });
90
92
  ```
91
93
 
92
- ```ts
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
- Or take control of a `<video>` that is already on the page:
98
-
99
- ```ts
100
- import { connectToHostVideo } from "html-video-element-webworker-polyfill/worker";
108
+ [Open in StackBlitz][example-create]
101
109
 
102
- const video = await connectToHostVideo("#player");
103
- video.currentTime = 10;
104
- ```
110
+ ## Why not WebCodecs?
105
111
 
106
- The element stays where it is. Changes made by the page, such as native controls or other scripts, reach the worker,
107
- and `dispose()` only releases control. Pass `canConnect` to `createVideoHost` to restrict which elements a worker may
108
- take over.
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)`, `WorkerVideoElement`, `polyfillDocument()` — [`document.createElement`][createElement] |
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
- Each entry point is tree-shakeable, so you only ship what you import.
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] ([`crossOrigin`][crossOrigin] `= "anonymous"`), as with drawing a video on a
139
- canvas.
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.0",
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",