@bendyline/squisq-video-react 1.1.1 → 1.1.2

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 (41) hide show
  1. package/dist/index.d.ts +171 -19
  2. package/dist/index.js +921 -21
  3. package/dist/index.js.map +1 -1
  4. package/dist/workers/encode.worker.d.ts +2 -13
  5. package/dist/workers/encode.worker.js +255 -303
  6. package/dist/workers/encode.worker.js.map +1 -1
  7. package/package.json +4 -4
  8. package/dist/VideoExportButton.d.ts +0 -28
  9. package/dist/VideoExportButton.d.ts.map +0 -1
  10. package/dist/VideoExportButton.js +0 -18
  11. package/dist/VideoExportButton.js.map +0 -1
  12. package/dist/VideoExportModal.d.ts +0 -26
  13. package/dist/VideoExportModal.d.ts.map +0 -1
  14. package/dist/VideoExportModal.js +0 -164
  15. package/dist/VideoExportModal.js.map +0 -1
  16. package/dist/hooks/useFrameCapture.d.ts +0 -27
  17. package/dist/hooks/useFrameCapture.d.ts.map +0 -1
  18. package/dist/hooks/useFrameCapture.js +0 -211
  19. package/dist/hooks/useFrameCapture.js.map +0 -1
  20. package/dist/hooks/useVideoExport.d.ts +0 -75
  21. package/dist/hooks/useVideoExport.d.ts.map +0 -1
  22. package/dist/hooks/useVideoExport.js +0 -266
  23. package/dist/hooks/useVideoExport.js.map +0 -1
  24. package/dist/index.d.ts.map +0 -1
  25. package/dist/mainThreadEncoder.d.ts +0 -44
  26. package/dist/mainThreadEncoder.d.ts.map +0 -1
  27. package/dist/mainThreadEncoder.js +0 -123
  28. package/dist/mainThreadEncoder.js.map +0 -1
  29. package/dist/mp4Mux.d.ts +0 -22
  30. package/dist/mp4Mux.d.ts.map +0 -1
  31. package/dist/mp4Mux.js +0 -32
  32. package/dist/mp4Mux.js.map +0 -1
  33. package/dist/workerEncoder.d.ts +0 -17
  34. package/dist/workerEncoder.d.ts.map +0 -1
  35. package/dist/workerEncoder.js +0 -103
  36. package/dist/workerEncoder.js.map +0 -1
  37. package/dist/workers/encode.worker.d.ts.map +0 -1
  38. package/dist/workers/workerTypes.d.ts +0 -63
  39. package/dist/workers/workerTypes.d.ts.map +0 -1
  40. package/dist/workers/workerTypes.js +0 -8
  41. package/dist/workers/workerTypes.js.map +0 -1
@@ -1,341 +1,293 @@
1
- /**
2
- * Video Encoding Web Worker
3
- *
4
- * Encodes video frames to MP4 using one of two backends:
5
- * - **WebCodecs** (preferred): Streaming H.264 encoding via VideoEncoder + mp4-muxer.
6
- * Each frame is encoded as it arrives — minimal memory footprint.
7
- * - **ffmpeg.wasm** (fallback): Batch encoding in ~10-second segments.
8
- * Used when WebCodecs is unavailable (older browsers).
9
- *
10
- * Frames arrive as ImageBitmap (zero-copy transfer from main thread).
11
- */
12
- import { createMp4Muxer } from '../mp4Mux.js';
13
- // ── State ──────────────────────────────────────────────────────────
14
- let backend = null;
15
- let cancelled = false;
16
- // WebCodecs state
17
- let videoEncoder = null;
18
- let muxer = null;
19
- // ffmpeg.wasm state
20
- let ffmpegInstance = null;
21
- let ffmpegFrames = [];
22
- let ffmpegConfig = null;
23
- // Frame tracking
24
- let totalFramesReceived = 0;
25
- let _totalFramesEncoded = 0;
26
- // ── Helpers ────────────────────────────────────────────────────────
1
+ import {
2
+ createMp4Muxer
3
+ } from "../chunk-LZYWP4HB.js";
4
+
5
+ // src/workers/encode.worker.ts
6
+ var backend = null;
7
+ var cancelled = false;
8
+ var videoEncoder = null;
9
+ var muxer = null;
10
+ var ffmpegInstance = null;
11
+ var ffmpegFrames = [];
12
+ var ffmpegConfig = null;
13
+ var totalFramesReceived = 0;
14
+ var _totalFramesEncoded = 0;
27
15
  function post(msg, transfer) {
28
- self.postMessage(msg, { transfer: transfer ?? [] });
16
+ self.postMessage(msg, { transfer: transfer ?? [] });
29
17
  }
30
18
  function postProgress(percent, phase) {
31
- post({ type: 'progress', percent, phase });
19
+ post({ type: "progress", percent, phase });
32
20
  }
33
21
  function postError(message) {
34
- post({ type: 'error', message });
22
+ post({ type: "error", message });
35
23
  }
36
- // ── Feature Detection ──────────────────────────────────────────────
37
24
  function hasWebCodecs() {
38
- return typeof VideoEncoder !== 'undefined' && typeof VideoFrame !== 'undefined';
25
+ return typeof VideoEncoder !== "undefined" && typeof VideoFrame !== "undefined";
39
26
  }
40
27
  function hasSharedArrayBuffer() {
41
- return typeof SharedArrayBuffer !== 'undefined';
28
+ return typeof SharedArrayBuffer !== "undefined";
42
29
  }
43
- /**
44
- * Check whether the browser's WebCodecs implementation actually supports
45
- * the H.264 Baseline profile we use. Linux Chromium ships without the
46
- * proprietary H.264 encoder, so `VideoEncoder` exists but `configure()`
47
- * for `avc1.*` fails asynchronously.
48
- */
49
30
  async function supportsWebCodecsH264(config) {
50
- if (!hasWebCodecs())
51
- return false;
52
- try {
53
- const support = await VideoEncoder.isConfigSupported({
54
- codec: 'avc1.42001f',
55
- width: config.width,
56
- height: config.height,
57
- bitrate: bitrateForQuality(config.quality, config.width, config.height),
58
- framerate: config.fps,
59
- });
60
- return support.supported === true;
61
- }
62
- catch {
63
- return false;
64
- }
31
+ if (!hasWebCodecs()) return false;
32
+ try {
33
+ const support = await VideoEncoder.isConfigSupported({
34
+ codec: "avc1.42001f",
35
+ width: config.width,
36
+ height: config.height,
37
+ bitrate: bitrateForQuality(config.quality, config.width, config.height),
38
+ framerate: config.fps
39
+ });
40
+ return support.supported === true;
41
+ } catch {
42
+ return false;
43
+ }
65
44
  }
66
- // ── WebCodecs Backend ──────────────────────────────────────────────
67
45
  function initWebCodecs(config) {
68
- muxer = createMp4Muxer({
69
- width: config.width,
70
- height: config.height,
71
- fps: config.fps,
72
- });
73
- videoEncoder = new VideoEncoder({
74
- output(chunk, meta) {
75
- if (cancelled)
76
- return;
77
- muxer.addVideoChunk(chunk, meta ?? undefined);
78
- _totalFramesEncoded++;
79
- },
80
- error(err) {
81
- postError(`WebCodecs encoder error: ${err.message}`);
82
- },
83
- });
84
- // Use H.264 Baseline for maximum compatibility
85
- videoEncoder.configure({
86
- codec: 'avc1.42001f',
87
- width: config.width,
88
- height: config.height,
89
- bitrate: bitrateForQuality(config.quality, config.width, config.height),
90
- framerate: config.fps,
91
- });
46
+ muxer = createMp4Muxer({
47
+ width: config.width,
48
+ height: config.height,
49
+ fps: config.fps
50
+ });
51
+ videoEncoder = new VideoEncoder({
52
+ output(chunk, meta) {
53
+ if (cancelled) return;
54
+ muxer.addVideoChunk(chunk, meta ?? void 0);
55
+ _totalFramesEncoded++;
56
+ },
57
+ error(err) {
58
+ postError(`WebCodecs encoder error: ${err.message}`);
59
+ }
60
+ });
61
+ videoEncoder.configure({
62
+ codec: "avc1.42001f",
63
+ width: config.width,
64
+ height: config.height,
65
+ bitrate: bitrateForQuality(config.quality, config.width, config.height),
66
+ framerate: config.fps
67
+ });
92
68
  }
93
69
  function bitrateForQuality(quality, width, height) {
94
- const pixels = width * height;
95
- const baseBitrate = pixels * 4; // ~4 bits per pixel baseline
96
- switch (quality) {
97
- case 'draft':
98
- return Math.round(baseBitrate * 0.5);
99
- case 'high':
100
- return Math.round(baseBitrate * 2);
101
- default: // normal
102
- return baseBitrate;
103
- }
70
+ const pixels = width * height;
71
+ const baseBitrate = pixels * 4;
72
+ switch (quality) {
73
+ case "draft":
74
+ return Math.round(baseBitrate * 0.5);
75
+ case "high":
76
+ return Math.round(baseBitrate * 2);
77
+ default:
78
+ return baseBitrate;
79
+ }
104
80
  }
105
81
  async function encodeFrameWebCodecs(msg) {
106
- if (!videoEncoder || cancelled) {
107
- msg.bitmap.close();
108
- return;
109
- }
110
- const frame = new VideoFrame(msg.bitmap, { timestamp: msg.timestamp });
111
- const keyFrame = msg.frameIndex % 30 === 0; // Key frame every 30 frames
112
- videoEncoder.encode(frame, { keyFrame });
113
- frame.close();
82
+ if (!videoEncoder || cancelled) {
114
83
  msg.bitmap.close();
115
- totalFramesReceived++;
116
- postProgress(Math.round((totalFramesReceived / (totalFramesReceived + 1)) * 50), `Encoding frame ${totalFramesReceived}`);
84
+ return;
85
+ }
86
+ const frame = new VideoFrame(msg.bitmap, { timestamp: msg.timestamp });
87
+ const keyFrame = msg.frameIndex % 30 === 0;
88
+ videoEncoder.encode(frame, { keyFrame });
89
+ frame.close();
90
+ msg.bitmap.close();
91
+ totalFramesReceived++;
92
+ postProgress(
93
+ Math.round(totalFramesReceived / (totalFramesReceived + 1) * 50),
94
+ `Encoding frame ${totalFramesReceived}`
95
+ );
117
96
  }
118
97
  async function finalizeWebCodecs() {
119
- if (!videoEncoder || !muxer || cancelled)
120
- return;
121
- postProgress(50, 'Flushing encoder…');
122
- await videoEncoder.flush();
123
- videoEncoder.close();
124
- videoEncoder = null;
125
- postProgress(90, 'Finalizing MP4…');
126
- const buffer = muxer.finalize();
127
- muxer = null;
128
- post({ type: 'complete', data: buffer, size: buffer.byteLength }, [buffer]);
98
+ if (!videoEncoder || !muxer || cancelled) return;
99
+ postProgress(50, "Flushing encoder\u2026");
100
+ await videoEncoder.flush();
101
+ videoEncoder.close();
102
+ videoEncoder = null;
103
+ postProgress(90, "Finalizing MP4\u2026");
104
+ const buffer = muxer.finalize();
105
+ muxer = null;
106
+ post({ type: "complete", data: buffer, size: buffer.byteLength }, [buffer]);
129
107
  }
130
- // ── ffmpeg.wasm Backend ────────────────────────────────────────────
131
108
  async function initFfmpegWasm(config) {
132
- ffmpegConfig = config;
133
- ffmpegFrames = [];
134
- totalFramesReceived = 0;
135
- // Lazy-load ffmpeg.wasm
136
- try {
137
- const { FFmpeg } = await import('@ffmpeg/ffmpeg');
138
- const ffmpeg = new FFmpeg();
139
- await ffmpeg.load();
140
- ffmpegInstance = ffmpeg;
141
- }
142
- catch (err) {
143
- const message = err instanceof Error ? err.message : String(err);
144
- postError(`Failed to load ffmpeg.wasm: ${message}. ` +
145
- 'Ensure @ffmpeg/ffmpeg and @ffmpeg/util are installed and ' +
146
- 'Cross-Origin-Isolation headers (COOP/COEP) are set.');
147
- }
109
+ ffmpegConfig = config;
110
+ ffmpegFrames = [];
111
+ totalFramesReceived = 0;
112
+ try {
113
+ const { FFmpeg } = await import("@ffmpeg/ffmpeg");
114
+ const ffmpeg = new FFmpeg();
115
+ await ffmpeg.load();
116
+ ffmpegInstance = ffmpeg;
117
+ } catch (err) {
118
+ const message = err instanceof Error ? err.message : String(err);
119
+ postError(
120
+ `Failed to load ffmpeg.wasm: ${message}. Ensure @ffmpeg/ffmpeg and @ffmpeg/util are installed and Cross-Origin-Isolation headers (COOP/COEP) are set.`
121
+ );
122
+ }
148
123
  }
149
124
  async function encodeFrameFfmpeg(msg) {
150
- if (cancelled) {
151
- msg.bitmap.close();
152
- return;
153
- }
154
- // Convert ImageBitmap to PNG bytes via OffscreenCanvas
155
- const canvas = new OffscreenCanvas(msg.bitmap.width, msg.bitmap.height);
156
- const ctx = canvas.getContext('2d');
157
- if (!ctx) {
158
- msg.bitmap.close();
159
- postError('Failed to create OffscreenCanvas 2D context');
160
- return;
161
- }
162
- ctx.drawImage(msg.bitmap, 0, 0);
125
+ if (cancelled) {
163
126
  msg.bitmap.close();
164
- const blob = await canvas.convertToBlob({ type: 'image/png' });
165
- const arrayBuffer = await blob.arrayBuffer();
166
- ffmpegFrames.push({ data: new Uint8Array(arrayBuffer), index: msg.frameIndex });
167
- totalFramesReceived++;
168
- postProgress(Math.round((totalFramesReceived / (totalFramesReceived + 1)) * 40), `Collecting frame ${totalFramesReceived}`);
169
- // Batch encode every ~10 seconds worth of frames
170
- const batchSize = (ffmpegConfig?.fps ?? 30) * 10;
171
- if (ffmpegFrames.length >= batchSize) {
172
- await encodeFfmpegBatch();
173
- }
127
+ return;
128
+ }
129
+ const canvas = new OffscreenCanvas(msg.bitmap.width, msg.bitmap.height);
130
+ const ctx = canvas.getContext("2d");
131
+ if (!ctx) {
132
+ msg.bitmap.close();
133
+ postError("Failed to create OffscreenCanvas 2D context");
134
+ return;
135
+ }
136
+ ctx.drawImage(msg.bitmap, 0, 0);
137
+ msg.bitmap.close();
138
+ const blob = await canvas.convertToBlob({ type: "image/png" });
139
+ const arrayBuffer = await blob.arrayBuffer();
140
+ ffmpegFrames.push({ data: new Uint8Array(arrayBuffer), index: msg.frameIndex });
141
+ totalFramesReceived++;
142
+ postProgress(
143
+ Math.round(totalFramesReceived / (totalFramesReceived + 1) * 40),
144
+ `Collecting frame ${totalFramesReceived}`
145
+ );
146
+ const batchSize = (ffmpegConfig?.fps ?? 30) * 10;
147
+ if (ffmpegFrames.length >= batchSize) {
148
+ await encodeFfmpegBatch();
149
+ }
174
150
  }
175
- /** Segments already encoded as MP4 data. */
176
- const ffmpegSegments = [];
151
+ var ffmpegSegments = [];
177
152
  async function encodeFfmpegBatch() {
178
- if (!ffmpegInstance || ffmpegFrames.length === 0 || cancelled)
179
- return;
180
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
- const ffmpeg = ffmpegInstance;
182
- const config = ffmpegConfig;
183
- const batchIndex = ffmpegSegments.length;
184
- postProgress(40 + batchIndex * 5, `Encoding batch ${batchIndex + 1}…`);
185
- // Write frames to virtual filesystem
186
- for (const frame of ffmpegFrames) {
187
- const paddedIndex = String(frame.index).padStart(6, '0');
188
- await ffmpeg.writeFile(`frame_${paddedIndex}.png`, frame.data);
153
+ if (!ffmpegInstance || ffmpegFrames.length === 0 || cancelled) return;
154
+ const ffmpeg = ffmpegInstance;
155
+ const config = ffmpegConfig;
156
+ const batchIndex = ffmpegSegments.length;
157
+ postProgress(40 + batchIndex * 5, `Encoding batch ${batchIndex + 1}\u2026`);
158
+ for (const frame of ffmpegFrames) {
159
+ const paddedIndex = String(frame.index).padStart(6, "0");
160
+ await ffmpeg.writeFile(`frame_${paddedIndex}.png`, frame.data);
161
+ }
162
+ const firstIndex = ffmpegFrames[0].index;
163
+ const segmentName = `segment_${batchIndex}.mp4`;
164
+ await ffmpeg.exec([
165
+ "-framerate",
166
+ String(config.fps),
167
+ "-start_number",
168
+ String(firstIndex),
169
+ "-i",
170
+ `frame_%06d.png`,
171
+ "-frames:v",
172
+ String(ffmpegFrames.length),
173
+ "-c:v",
174
+ "libx264",
175
+ "-pix_fmt",
176
+ "yuv420p",
177
+ "-preset",
178
+ config.quality === "draft" ? "ultrafast" : config.quality === "high" ? "slow" : "medium",
179
+ "-crf",
180
+ config.quality === "draft" ? "28" : config.quality === "high" ? "18" : "23",
181
+ segmentName
182
+ ]);
183
+ const segmentData = await ffmpeg.readFile(segmentName);
184
+ ffmpegSegments.push(segmentData);
185
+ for (const frame of ffmpegFrames) {
186
+ const paddedIndex = String(frame.index).padStart(6, "0");
187
+ await ffmpeg.deleteFile(`frame_${paddedIndex}.png`);
188
+ }
189
+ await ffmpeg.deleteFile(segmentName);
190
+ ffmpegFrames = [];
191
+ }
192
+ async function finalizeFfmpeg() {
193
+ if (!ffmpegInstance || cancelled) return;
194
+ await encodeFfmpegBatch();
195
+ const ffmpeg = ffmpegInstance;
196
+ postProgress(85, "Concatenating segments\u2026");
197
+ let finalData;
198
+ if (ffmpegSegments.length === 1) {
199
+ finalData = ffmpegSegments[0];
200
+ } else if (ffmpegSegments.length > 1) {
201
+ const concatList = [];
202
+ for (let i = 0; i < ffmpegSegments.length; i++) {
203
+ const name = `seg_${i}.mp4`;
204
+ await ffmpeg.writeFile(name, ffmpegSegments[i]);
205
+ concatList.push(`file '${name}'`);
189
206
  }
190
- const firstIndex = ffmpegFrames[0].index;
191
- const segmentName = `segment_${batchIndex}.mp4`;
192
- // Encode batch to MP4 segment
207
+ await ffmpeg.writeFile("concat.txt", new TextEncoder().encode(concatList.join("\n")));
193
208
  await ffmpeg.exec([
194
- '-framerate',
195
- String(config.fps),
196
- '-start_number',
197
- String(firstIndex),
198
- '-i',
199
- `frame_%06d.png`,
200
- '-frames:v',
201
- String(ffmpegFrames.length),
202
- '-c:v',
203
- 'libx264',
204
- '-pix_fmt',
205
- 'yuv420p',
206
- '-preset',
207
- config.quality === 'draft' ? 'ultrafast' : config.quality === 'high' ? 'slow' : 'medium',
208
- '-crf',
209
- config.quality === 'draft' ? '28' : config.quality === 'high' ? '18' : '23',
210
- segmentName,
209
+ "-f",
210
+ "concat",
211
+ "-safe",
212
+ "0",
213
+ "-i",
214
+ "concat.txt",
215
+ "-c",
216
+ "copy",
217
+ "output.mp4"
211
218
  ]);
212
- // Read segment and clean up frames
213
- const segmentData = await ffmpeg.readFile(segmentName);
214
- ffmpegSegments.push(segmentData);
215
- // Clean up frame files
216
- for (const frame of ffmpegFrames) {
217
- const paddedIndex = String(frame.index).padStart(6, '0');
218
- await ffmpeg.deleteFile(`frame_${paddedIndex}.png`);
219
+ finalData = await ffmpeg.readFile("output.mp4");
220
+ for (let i = 0; i < ffmpegSegments.length; i++) {
221
+ await ffmpeg.deleteFile(`seg_${i}.mp4`);
219
222
  }
220
- await ffmpeg.deleteFile(segmentName);
221
- // Free batch memory
222
- ffmpegFrames = [];
223
+ await ffmpeg.deleteFile("concat.txt");
224
+ await ffmpeg.deleteFile("output.mp4");
225
+ } else {
226
+ postError("No frames were encoded");
227
+ return;
228
+ }
229
+ postProgress(95, "Preparing download\u2026");
230
+ const sliced = new Uint8Array(finalData).buffer;
231
+ post({ type: "complete", data: sliced, size: sliced.byteLength }, [sliced]);
232
+ ffmpegInstance = null;
233
+ ffmpegSegments.length = 0;
223
234
  }
224
- async function finalizeFfmpeg() {
225
- if (!ffmpegInstance || cancelled)
226
- return;
227
- // Encode any remaining frames
228
- await encodeFfmpegBatch();
229
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
- const ffmpeg = ffmpegInstance;
231
- postProgress(85, 'Concatenating segments…');
232
- let finalData;
233
- if (ffmpegSegments.length === 1) {
234
- // Single segment — no concat needed
235
- finalData = ffmpegSegments[0];
236
- }
237
- else if (ffmpegSegments.length > 1) {
238
- // Write segments and concat list
239
- const concatList = [];
240
- for (let i = 0; i < ffmpegSegments.length; i++) {
241
- const name = `seg_${i}.mp4`;
242
- await ffmpeg.writeFile(name, ffmpegSegments[i]);
243
- concatList.push(`file '${name}'`);
235
+ self.onmessage = async (event) => {
236
+ const msg = event.data;
237
+ try {
238
+ switch (msg.type) {
239
+ case "init": {
240
+ cancelled = false;
241
+ totalFramesReceived = 0;
242
+ _totalFramesEncoded = 0;
243
+ if (await supportsWebCodecsH264(msg)) {
244
+ backend = "webcodecs";
245
+ initWebCodecs(msg);
246
+ } else if (hasSharedArrayBuffer()) {
247
+ backend = "ffmpeg-wasm";
248
+ await initFfmpegWasm(msg);
249
+ } else {
250
+ postError(
251
+ "No video encoding support available. WebCodecs H.264 is unavailable (typical on Linux Chromium without proprietary codecs) and ffmpeg.wasm requires SharedArrayBuffer (Cross-Origin-Isolation headers)."
252
+ );
253
+ return;
244
254
  }
245
- await ffmpeg.writeFile('concat.txt', new TextEncoder().encode(concatList.join('\n')));
246
- await ffmpeg.exec([
247
- '-f',
248
- 'concat',
249
- '-safe',
250
- '0',
251
- '-i',
252
- 'concat.txt',
253
- '-c',
254
- 'copy',
255
- 'output.mp4',
256
- ]);
257
- finalData = await ffmpeg.readFile('output.mp4');
258
- // Clean up
259
- for (let i = 0; i < ffmpegSegments.length; i++) {
260
- await ffmpeg.deleteFile(`seg_${i}.mp4`);
255
+ post({ type: "capabilities", backend });
256
+ postProgress(0, "Encoder ready");
257
+ break;
258
+ }
259
+ case "frame": {
260
+ if (backend === "webcodecs") {
261
+ await encodeFrameWebCodecs(msg);
262
+ } else if (backend === "ffmpeg-wasm") {
263
+ await encodeFrameFfmpeg(msg);
261
264
  }
262
- await ffmpeg.deleteFile('concat.txt');
263
- await ffmpeg.deleteFile('output.mp4');
264
- }
265
- else {
266
- postError('No frames were encoded');
267
- return;
268
- }
269
- postProgress(95, 'Preparing download…');
270
- // Convert Uint8Array to a transferable ArrayBuffer
271
- const sliced = new Uint8Array(finalData).buffer;
272
- post({ type: 'complete', data: sliced, size: sliced.byteLength }, [sliced]);
273
- // Clean up
274
- ffmpegInstance = null;
275
- ffmpegSegments.length = 0;
276
- }
277
- // ── Message Handler ────────────────────────────────────────────────
278
- self.onmessage = async (event) => {
279
- const msg = event.data;
280
- try {
281
- switch (msg.type) {
282
- case 'init': {
283
- cancelled = false;
284
- totalFramesReceived = 0;
285
- _totalFramesEncoded = 0;
286
- if (await supportsWebCodecsH264(msg)) {
287
- backend = 'webcodecs';
288
- initWebCodecs(msg);
289
- }
290
- else if (hasSharedArrayBuffer()) {
291
- backend = 'ffmpeg-wasm';
292
- await initFfmpegWasm(msg);
293
- }
294
- else {
295
- postError('No video encoding support available. ' +
296
- 'WebCodecs H.264 is unavailable (typical on Linux Chromium without proprietary codecs) ' +
297
- 'and ffmpeg.wasm requires SharedArrayBuffer (Cross-Origin-Isolation headers).');
298
- return;
299
- }
300
- post({ type: 'capabilities', backend });
301
- postProgress(0, 'Encoder ready');
302
- break;
303
- }
304
- case 'frame': {
305
- if (backend === 'webcodecs') {
306
- await encodeFrameWebCodecs(msg);
307
- }
308
- else if (backend === 'ffmpeg-wasm') {
309
- await encodeFrameFfmpeg(msg);
310
- }
311
- break;
312
- }
313
- case 'finalize': {
314
- if (backend === 'webcodecs') {
315
- await finalizeWebCodecs();
316
- }
317
- else if (backend === 'ffmpeg-wasm') {
318
- await finalizeFfmpeg();
319
- }
320
- break;
321
- }
322
- case 'cancel': {
323
- cancelled = true;
324
- if (videoEncoder && videoEncoder.state !== 'closed') {
325
- videoEncoder.close();
326
- videoEncoder = null;
327
- }
328
- muxer = null;
329
- ffmpegInstance = null;
330
- ffmpegFrames = [];
331
- ffmpegSegments.length = 0;
332
- break;
333
- }
265
+ break;
266
+ }
267
+ case "finalize": {
268
+ if (backend === "webcodecs") {
269
+ await finalizeWebCodecs();
270
+ } else if (backend === "ffmpeg-wasm") {
271
+ await finalizeFfmpeg();
334
272
  }
273
+ break;
274
+ }
275
+ case "cancel": {
276
+ cancelled = true;
277
+ if (videoEncoder && videoEncoder.state !== "closed") {
278
+ videoEncoder.close();
279
+ videoEncoder = null;
280
+ }
281
+ muxer = null;
282
+ ffmpegInstance = null;
283
+ ffmpegFrames = [];
284
+ ffmpegSegments.length = 0;
285
+ break;
286
+ }
335
287
  }
336
- catch (err) {
337
- const message = err instanceof Error ? err.message : String(err);
338
- postError(message);
339
- }
288
+ } catch (err) {
289
+ const message = err instanceof Error ? err.message : String(err);
290
+ postError(message);
291
+ }
340
292
  };
341
293
  //# sourceMappingURL=encode.worker.js.map