@bendyline/squisq-video-react 1.0.0 → 1.0.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 (38) hide show
  1. package/README.md +137 -0
  2. package/dist/VideoExportButton.d.ts +28 -0
  3. package/dist/VideoExportButton.d.ts.map +1 -0
  4. package/dist/VideoExportButton.js +18 -0
  5. package/dist/VideoExportButton.js.map +1 -0
  6. package/dist/VideoExportModal.d.ts +26 -0
  7. package/dist/VideoExportModal.d.ts.map +1 -0
  8. package/dist/VideoExportModal.js +164 -0
  9. package/dist/VideoExportModal.js.map +1 -0
  10. package/dist/hooks/useFrameCapture.d.ts +27 -0
  11. package/dist/hooks/useFrameCapture.d.ts.map +1 -0
  12. package/dist/hooks/useFrameCapture.js +211 -0
  13. package/dist/hooks/useFrameCapture.js.map +1 -0
  14. package/dist/hooks/useVideoExport.d.ts +75 -0
  15. package/dist/hooks/useVideoExport.d.ts.map +1 -0
  16. package/dist/hooks/useVideoExport.js +241 -0
  17. package/dist/hooks/useVideoExport.js.map +1 -0
  18. package/dist/index.d.ts +19 -171
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +21 -815
  21. package/dist/index.js.map +1 -1
  22. package/dist/mainThreadEncoder.d.ts +37 -0
  23. package/dist/mainThreadEncoder.d.ts.map +1 -0
  24. package/dist/mainThreadEncoder.js +100 -0
  25. package/dist/mainThreadEncoder.js.map +1 -0
  26. package/dist/mp4Mux.d.ts +22 -0
  27. package/dist/mp4Mux.d.ts.map +1 -0
  28. package/dist/mp4Mux.js +32 -0
  29. package/dist/mp4Mux.js.map +1 -0
  30. package/dist/workers/encode.worker.d.ts +13 -0
  31. package/dist/workers/encode.worker.d.ts.map +1 -0
  32. package/dist/workers/encode.worker.js +318 -0
  33. package/dist/workers/encode.worker.js.map +1 -0
  34. package/dist/workers/workerTypes.d.ts +63 -0
  35. package/dist/workers/workerTypes.d.ts.map +1 -0
  36. package/dist/workers/workerTypes.js +8 -0
  37. package/dist/workers/workerTypes.js.map +1 -0
  38. package/package.json +4 -4
@@ -0,0 +1,318 @@
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 ────────────────────────────────────────────────────────
27
+ function post(msg, transfer) {
28
+ self.postMessage(msg, { transfer: transfer ?? [] });
29
+ }
30
+ function postProgress(percent, phase) {
31
+ post({ type: 'progress', percent, phase });
32
+ }
33
+ function postError(message) {
34
+ post({ type: 'error', message });
35
+ }
36
+ // ── Feature Detection ──────────────────────────────────────────────
37
+ function hasWebCodecs() {
38
+ return typeof VideoEncoder !== 'undefined' && typeof VideoFrame !== 'undefined';
39
+ }
40
+ function hasSharedArrayBuffer() {
41
+ return typeof SharedArrayBuffer !== 'undefined';
42
+ }
43
+ // ── WebCodecs Backend ──────────────────────────────────────────────
44
+ function initWebCodecs(config) {
45
+ muxer = createMp4Muxer({
46
+ width: config.width,
47
+ height: config.height,
48
+ fps: config.fps,
49
+ });
50
+ videoEncoder = new VideoEncoder({
51
+ output(chunk, meta) {
52
+ if (cancelled)
53
+ return;
54
+ muxer.addVideoChunk(chunk, meta ?? undefined);
55
+ _totalFramesEncoded++;
56
+ },
57
+ error(err) {
58
+ postError(`WebCodecs encoder error: ${err.message}`);
59
+ },
60
+ });
61
+ // Use H.264 Baseline for maximum compatibility
62
+ videoEncoder.configure({
63
+ codec: 'avc1.42001f',
64
+ width: config.width,
65
+ height: config.height,
66
+ bitrate: bitrateForQuality(config.quality, config.width, config.height),
67
+ framerate: config.fps,
68
+ });
69
+ }
70
+ function bitrateForQuality(quality, width, height) {
71
+ const pixels = width * height;
72
+ const baseBitrate = pixels * 4; // ~4 bits per pixel baseline
73
+ switch (quality) {
74
+ case 'draft':
75
+ return Math.round(baseBitrate * 0.5);
76
+ case 'high':
77
+ return Math.round(baseBitrate * 2);
78
+ default: // normal
79
+ return baseBitrate;
80
+ }
81
+ }
82
+ async function encodeFrameWebCodecs(msg) {
83
+ if (!videoEncoder || cancelled) {
84
+ msg.bitmap.close();
85
+ return;
86
+ }
87
+ const frame = new VideoFrame(msg.bitmap, { timestamp: msg.timestamp });
88
+ const keyFrame = msg.frameIndex % 30 === 0; // Key frame every 30 frames
89
+ videoEncoder.encode(frame, { keyFrame });
90
+ frame.close();
91
+ msg.bitmap.close();
92
+ totalFramesReceived++;
93
+ postProgress(Math.round((totalFramesReceived / (totalFramesReceived + 1)) * 50), `Encoding frame ${totalFramesReceived}`);
94
+ }
95
+ async function finalizeWebCodecs() {
96
+ if (!videoEncoder || !muxer || cancelled)
97
+ return;
98
+ postProgress(50, 'Flushing encoder…');
99
+ await videoEncoder.flush();
100
+ videoEncoder.close();
101
+ videoEncoder = null;
102
+ postProgress(90, 'Finalizing MP4…');
103
+ const buffer = muxer.finalize();
104
+ muxer = null;
105
+ post({ type: 'complete', data: buffer, size: buffer.byteLength }, [buffer]);
106
+ }
107
+ // ── ffmpeg.wasm Backend ────────────────────────────────────────────
108
+ async function initFfmpegWasm(config) {
109
+ ffmpegConfig = config;
110
+ ffmpegFrames = [];
111
+ totalFramesReceived = 0;
112
+ // Lazy-load ffmpeg.wasm
113
+ try {
114
+ const { FFmpeg } = await import('@ffmpeg/ffmpeg');
115
+ const ffmpeg = new FFmpeg();
116
+ await ffmpeg.load();
117
+ ffmpegInstance = ffmpeg;
118
+ }
119
+ catch (err) {
120
+ const message = err instanceof Error ? err.message : String(err);
121
+ postError(`Failed to load ffmpeg.wasm: ${message}. ` +
122
+ 'Ensure @ffmpeg/ffmpeg and @ffmpeg/util are installed and ' +
123
+ 'Cross-Origin-Isolation headers (COOP/COEP) are set.');
124
+ }
125
+ }
126
+ async function encodeFrameFfmpeg(msg) {
127
+ if (cancelled) {
128
+ msg.bitmap.close();
129
+ return;
130
+ }
131
+ // Convert ImageBitmap to PNG bytes via OffscreenCanvas
132
+ const canvas = new OffscreenCanvas(msg.bitmap.width, msg.bitmap.height);
133
+ const ctx = canvas.getContext('2d');
134
+ if (!ctx) {
135
+ msg.bitmap.close();
136
+ postError('Failed to create OffscreenCanvas 2D context');
137
+ return;
138
+ }
139
+ ctx.drawImage(msg.bitmap, 0, 0);
140
+ msg.bitmap.close();
141
+ const blob = await canvas.convertToBlob({ type: 'image/png' });
142
+ const arrayBuffer = await blob.arrayBuffer();
143
+ ffmpegFrames.push({ data: new Uint8Array(arrayBuffer), index: msg.frameIndex });
144
+ totalFramesReceived++;
145
+ postProgress(Math.round((totalFramesReceived / (totalFramesReceived + 1)) * 40), `Collecting frame ${totalFramesReceived}`);
146
+ // Batch encode every ~10 seconds worth of frames
147
+ const batchSize = (ffmpegConfig?.fps ?? 30) * 10;
148
+ if (ffmpegFrames.length >= batchSize) {
149
+ await encodeFfmpegBatch();
150
+ }
151
+ }
152
+ /** Segments already encoded as MP4 data. */
153
+ const ffmpegSegments = [];
154
+ async function encodeFfmpegBatch() {
155
+ if (!ffmpegInstance || ffmpegFrames.length === 0 || cancelled)
156
+ return;
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const ffmpeg = ffmpegInstance;
159
+ const config = ffmpegConfig;
160
+ const batchIndex = ffmpegSegments.length;
161
+ postProgress(40 + batchIndex * 5, `Encoding batch ${batchIndex + 1}…`);
162
+ // Write frames to virtual filesystem
163
+ for (const frame of ffmpegFrames) {
164
+ const paddedIndex = String(frame.index).padStart(6, '0');
165
+ await ffmpeg.writeFile(`frame_${paddedIndex}.png`, frame.data);
166
+ }
167
+ const firstIndex = ffmpegFrames[0].index;
168
+ const segmentName = `segment_${batchIndex}.mp4`;
169
+ // Encode batch to MP4 segment
170
+ await ffmpeg.exec([
171
+ '-framerate',
172
+ String(config.fps),
173
+ '-start_number',
174
+ String(firstIndex),
175
+ '-i',
176
+ `frame_%06d.png`,
177
+ '-frames:v',
178
+ String(ffmpegFrames.length),
179
+ '-c:v',
180
+ 'libx264',
181
+ '-pix_fmt',
182
+ 'yuv420p',
183
+ '-preset',
184
+ config.quality === 'draft' ? 'ultrafast' : config.quality === 'high' ? 'slow' : 'medium',
185
+ '-crf',
186
+ config.quality === 'draft' ? '28' : config.quality === 'high' ? '18' : '23',
187
+ segmentName,
188
+ ]);
189
+ // Read segment and clean up frames
190
+ const segmentData = await ffmpeg.readFile(segmentName);
191
+ ffmpegSegments.push(segmentData);
192
+ // Clean up frame files
193
+ for (const frame of ffmpegFrames) {
194
+ const paddedIndex = String(frame.index).padStart(6, '0');
195
+ await ffmpeg.deleteFile(`frame_${paddedIndex}.png`);
196
+ }
197
+ await ffmpeg.deleteFile(segmentName);
198
+ // Free batch memory
199
+ ffmpegFrames = [];
200
+ }
201
+ async function finalizeFfmpeg() {
202
+ if (!ffmpegInstance || cancelled)
203
+ return;
204
+ // Encode any remaining frames
205
+ await encodeFfmpegBatch();
206
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
+ const ffmpeg = ffmpegInstance;
208
+ postProgress(85, 'Concatenating segments…');
209
+ let finalData;
210
+ if (ffmpegSegments.length === 1) {
211
+ // Single segment — no concat needed
212
+ finalData = ffmpegSegments[0];
213
+ }
214
+ else if (ffmpegSegments.length > 1) {
215
+ // Write segments and concat list
216
+ const concatList = [];
217
+ for (let i = 0; i < ffmpegSegments.length; i++) {
218
+ const name = `seg_${i}.mp4`;
219
+ await ffmpeg.writeFile(name, ffmpegSegments[i]);
220
+ concatList.push(`file '${name}'`);
221
+ }
222
+ await ffmpeg.writeFile('concat.txt', new TextEncoder().encode(concatList.join('\n')));
223
+ await ffmpeg.exec([
224
+ '-f',
225
+ 'concat',
226
+ '-safe',
227
+ '0',
228
+ '-i',
229
+ 'concat.txt',
230
+ '-c',
231
+ 'copy',
232
+ 'output.mp4',
233
+ ]);
234
+ finalData = await ffmpeg.readFile('output.mp4');
235
+ // Clean up
236
+ for (let i = 0; i < ffmpegSegments.length; i++) {
237
+ await ffmpeg.deleteFile(`seg_${i}.mp4`);
238
+ }
239
+ await ffmpeg.deleteFile('concat.txt');
240
+ await ffmpeg.deleteFile('output.mp4');
241
+ }
242
+ else {
243
+ postError('No frames were encoded');
244
+ return;
245
+ }
246
+ postProgress(95, 'Preparing download…');
247
+ // Convert Uint8Array to a transferable ArrayBuffer
248
+ const sliced = new Uint8Array(finalData).buffer;
249
+ post({ type: 'complete', data: sliced, size: sliced.byteLength }, [sliced]);
250
+ // Clean up
251
+ ffmpegInstance = null;
252
+ ffmpegSegments.length = 0;
253
+ }
254
+ // ── Message Handler ────────────────────────────────────────────────
255
+ self.onmessage = async (event) => {
256
+ const msg = event.data;
257
+ try {
258
+ switch (msg.type) {
259
+ case 'init': {
260
+ cancelled = false;
261
+ totalFramesReceived = 0;
262
+ _totalFramesEncoded = 0;
263
+ if (hasWebCodecs()) {
264
+ backend = 'webcodecs';
265
+ initWebCodecs(msg);
266
+ }
267
+ else if (hasSharedArrayBuffer()) {
268
+ backend = 'ffmpeg-wasm';
269
+ await initFfmpegWasm(msg);
270
+ }
271
+ else {
272
+ postError('No video encoding support available. ' +
273
+ 'WebCodecs requires Chrome 94+ or Edge 94+. ' +
274
+ 'ffmpeg.wasm requires SharedArrayBuffer (Cross-Origin-Isolation headers).');
275
+ return;
276
+ }
277
+ post({ type: 'capabilities', backend });
278
+ postProgress(0, 'Encoder ready');
279
+ break;
280
+ }
281
+ case 'frame': {
282
+ if (backend === 'webcodecs') {
283
+ await encodeFrameWebCodecs(msg);
284
+ }
285
+ else if (backend === 'ffmpeg-wasm') {
286
+ await encodeFrameFfmpeg(msg);
287
+ }
288
+ break;
289
+ }
290
+ case 'finalize': {
291
+ if (backend === 'webcodecs') {
292
+ await finalizeWebCodecs();
293
+ }
294
+ else if (backend === 'ffmpeg-wasm') {
295
+ await finalizeFfmpeg();
296
+ }
297
+ break;
298
+ }
299
+ case 'cancel': {
300
+ cancelled = true;
301
+ if (videoEncoder && videoEncoder.state !== 'closed') {
302
+ videoEncoder.close();
303
+ videoEncoder = null;
304
+ }
305
+ muxer = null;
306
+ ffmpegInstance = null;
307
+ ffmpegFrames = [];
308
+ ffmpegSegments.length = 0;
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ catch (err) {
314
+ const message = err instanceof Error ? err.message : String(err);
315
+ postError(message);
316
+ }
317
+ };
318
+ //# sourceMappingURL=encode.worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode.worker.js","sourceRoot":"","sources":["../../src/workers/encode.worker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Worker Message Protocol
3
+ *
4
+ * Defines the message types exchanged between the main thread and
5
+ * the video encoding Web Worker.
6
+ */
7
+ import type { VideoQuality } from '@bendyline/squisq-video';
8
+ /** Initialize the encoder with video parameters. */
9
+ export interface InitMessage {
10
+ type: 'init';
11
+ width: number;
12
+ height: number;
13
+ fps: number;
14
+ quality: VideoQuality;
15
+ }
16
+ /** Send a single video frame to the encoder. */
17
+ export interface FrameMessage {
18
+ type: 'frame';
19
+ /** Frame bitmap — transferred (zero-copy) from main thread */
20
+ bitmap: ImageBitmap;
21
+ /** Frame index (0-based) */
22
+ frameIndex: number;
23
+ /** Timestamp in microseconds */
24
+ timestamp: number;
25
+ }
26
+ /** Signal that all frames have been sent; finalize the video. */
27
+ export interface FinalizeMessage {
28
+ type: 'finalize';
29
+ }
30
+ /** Cancel the export and clean up resources. */
31
+ export interface CancelMessage {
32
+ type: 'cancel';
33
+ }
34
+ export type MainToWorkerMessage = InitMessage | FrameMessage | FinalizeMessage | CancelMessage;
35
+ /** Encoder backend detection result, sent after init. */
36
+ export interface CapabilitiesMessage {
37
+ type: 'capabilities';
38
+ /** Which encoder backend the worker selected */
39
+ backend: 'webcodecs' | 'ffmpeg-wasm';
40
+ }
41
+ /** Progress update during encoding. */
42
+ export interface ProgressMessage {
43
+ type: 'progress';
44
+ /** 0–100 completion percentage */
45
+ percent: number;
46
+ /** Human-readable phase description */
47
+ phase: string;
48
+ }
49
+ /** Encoding complete — MP4 data returned. */
50
+ export interface CompleteMessage {
51
+ type: 'complete';
52
+ /** MP4 file data — transferred (zero-copy) back to main thread */
53
+ data: ArrayBuffer;
54
+ /** File size in bytes */
55
+ size: number;
56
+ }
57
+ /** An error occurred during encoding. */
58
+ export interface ErrorMessage {
59
+ type: 'error';
60
+ message: string;
61
+ }
62
+ export type WorkerToMainMessage = CapabilitiesMessage | ProgressMessage | CompleteMessage | ErrorMessage;
63
+ //# sourceMappingURL=workerTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workerTypes.d.ts","sourceRoot":"","sources":["../../src/workers/workerTypes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAI5D,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,MAAM,EAAE,WAAW,CAAC;IACpB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,aAAa,CAAC;AAI/F,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,gDAAgD;IAChD,OAAO,EAAE,WAAW,GAAG,aAAa,CAAC;CACtC;AAED,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,kEAAkE;IAClE,IAAI,EAAE,WAAW,CAAC;IAClB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC3B,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Worker Message Protocol
3
+ *
4
+ * Defines the message types exchanged between the main thread and
5
+ * the video encoding Web Worker.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=workerTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workerTypes.js","sourceRoot":"","sources":["../../src/workers/workerTypes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-video-react",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "React components for browser-based video export of Squisq documents via WebCodecs",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -44,9 +44,9 @@
44
44
  "react-dom": "^18.0.0 || ^19.0.0"
45
45
  },
46
46
  "dependencies": {
47
- "@bendyline/squisq": "1.1.0",
48
- "@bendyline/squisq-video": "1.0.0",
49
- "@bendyline/squisq-react": "1.0.2",
47
+ "@bendyline/squisq": "1.2.0",
48
+ "@bendyline/squisq-video": "1.0.2",
49
+ "@bendyline/squisq-react": "1.1.0",
50
50
  "html2canvas": "^1.4.1",
51
51
  "mp4-muxer": "^5.1.3"
52
52
  },