@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,75 +0,0 @@
1
- /**
2
- * useVideoExport — Main orchestration hook for browser video export.
3
- *
4
- * Coordinates frame capture (hidden iframe + html2canvas) with
5
- * main-thread WebCodecs encoding via mp4-muxer. Manages the full
6
- * lifecycle: prepare → capture + encode → download.
7
- *
8
- * Encoding runs on the main thread because frame capture via html2canvas
9
- * (~100-200ms per frame) is the bottleneck, not encoding (~1ms per frame
10
- * with hardware-accelerated WebCodecs). Worker offloading would add
11
- * complexity with minimal benefit.
12
- *
13
- * Usage:
14
- * const { state, progress, phase, startExport, cancel, downloadUrl } = useVideoExport();
15
- * <button onClick={() => startExport(doc, options)}>Export</button>
16
- */
17
- import type { Doc } from '@bendyline/squisq/schemas';
18
- import type { MediaProvider } from '@bendyline/squisq/schemas';
19
- import type { VideoQuality, VideoOrientation } from '@bendyline/squisq-video';
20
- import type { CaptionMode } from '@bendyline/squisq-react';
21
- export type VideoExportState = 'idle' | 'preparing' | 'capturing' | 'encoding' | 'complete' | 'error';
22
- export interface VideoExportConfig {
23
- /** Encoding quality preset (default: 'normal') */
24
- quality?: VideoQuality;
25
- /** Frames per second (default: 30) */
26
- fps?: number;
27
- /** Viewport orientation (default: 'landscape') */
28
- orientation?: VideoOrientation;
29
- /**
30
- * Map of relative image paths to binary data.
31
- * Used to embed images into the render HTML.
32
- */
33
- images?: Map<string, ArrayBuffer>;
34
- /**
35
- * Map of audio segment names to binary data.
36
- * Used to embed audio into the render HTML.
37
- */
38
- audio?: Map<string, ArrayBuffer>;
39
- /** MediaProvider to resolve media URLs (alternative to passing images directly) */
40
- mediaProvider?: MediaProvider;
41
- /** Caption mode for the exported video (default: 'off') */
42
- captionMode?: CaptionMode;
43
- /** Player IIFE bundle (unused in browser export, kept for CLI/Playwright path) */
44
- playerScript?: string;
45
- }
46
- export interface VideoExportResult {
47
- /** Current export state */
48
- state: VideoExportState;
49
- /** 0–100 progress percentage */
50
- progress: number;
51
- /** Human-readable description of the current phase */
52
- phase: string;
53
- /** Video duration detected from the doc (seconds) */
54
- duration: number;
55
- /** Encoder backend ('webcodecs' when WebCodecs H.264 active, 'ffmpeg-wasm' when worker fallback active, null when idle) */
56
- backend: 'webcodecs' | 'ffmpeg-wasm' | null;
57
- /** Blob download URL (populated when state === 'complete') */
58
- downloadUrl: string | null;
59
- /** File size in bytes (populated when state === 'complete') */
60
- fileSize: number;
61
- /** Error message (populated when state === 'error') */
62
- error: string | null;
63
- /** Seconds elapsed since export started */
64
- elapsed: number;
65
- /** Estimated seconds remaining (0 when idle or complete) */
66
- estimatedRemaining: number;
67
- /** Start a new export */
68
- startExport: (doc: Doc, config: VideoExportConfig) => Promise<void>;
69
- /** Cancel an in-progress export */
70
- cancel: () => void;
71
- /** Reset state back to idle (e.g., after complete or error) */
72
- reset: () => void;
73
- }
74
- export declare function useVideoExport(): VideoExportResult;
75
- //# sourceMappingURL=useVideoExport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useVideoExport.d.ts","sourceRoot":"","sources":["../../src/hooks/useVideoExport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY3D,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAClC;;;OAGG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACjC,mFAAmF;IACnF,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,2HAA2H;IAC3H,OAAO,EAAE,WAAW,GAAG,aAAa,GAAG,IAAI,CAAC;IAC5C,8DAA8D;IAC9D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,wBAAgB,cAAc,IAAI,iBAAiB,CAsRlD"}
@@ -1,266 +0,0 @@
1
- /**
2
- * useVideoExport — Main orchestration hook for browser video export.
3
- *
4
- * Coordinates frame capture (hidden iframe + html2canvas) with
5
- * main-thread WebCodecs encoding via mp4-muxer. Manages the full
6
- * lifecycle: prepare → capture + encode → download.
7
- *
8
- * Encoding runs on the main thread because frame capture via html2canvas
9
- * (~100-200ms per frame) is the bottleneck, not encoding (~1ms per frame
10
- * with hardware-accelerated WebCodecs). Worker offloading would add
11
- * complexity with minimal benefit.
12
- *
13
- * Usage:
14
- * const { state, progress, phase, startExport, cancel, downloadUrl } = useVideoExport();
15
- * <button onClick={() => startExport(doc, options)}>Export</button>
16
- */
17
- import { useState, useRef, useCallback, useEffect } from 'react';
18
- import { resolveDimensions } from '@bendyline/squisq-video';
19
- import { createEncoder, supportsWebCodecs, supportsWebCodecsH264, } from '../mainThreadEncoder.js';
20
- import { createWorkerEncoder } from '../workerEncoder.js';
21
- import { useFrameCapture } from './useFrameCapture.js';
22
- // ── Hook ───────────────────────────────────────────────────────────
23
- export function useVideoExport() {
24
- const [state, setState] = useState('idle');
25
- const [progress, setProgress] = useState(0);
26
- const [phase, setPhase] = useState('');
27
- const [duration, setDuration] = useState(0);
28
- const [backend, setBackend] = useState(null);
29
- const [downloadUrl, setDownloadUrl] = useState(null);
30
- const [fileSize, setFileSize] = useState(0);
31
- const [error, setError] = useState(null);
32
- const [elapsed, setElapsed] = useState(0);
33
- const [estimatedRemaining, setEstimatedRemaining] = useState(0);
34
- const encoderRef = useRef(null);
35
- const cancelledRef = useRef(false);
36
- const downloadUrlRef = useRef(null);
37
- const startTimeRef = useRef(0);
38
- const elapsedTimerRef = useRef(null);
39
- const frameCapture = useFrameCapture();
40
- // Clean up on unmount
41
- useEffect(() => {
42
- return () => {
43
- if (elapsedTimerRef.current)
44
- clearInterval(elapsedTimerRef.current);
45
- if (downloadUrlRef.current) {
46
- URL.revokeObjectURL(downloadUrlRef.current);
47
- }
48
- if (encoderRef.current) {
49
- encoderRef.current.close();
50
- }
51
- frameCapture.destroy();
52
- };
53
- }, [frameCapture]);
54
- const reset = useCallback(() => {
55
- if (downloadUrlRef.current) {
56
- URL.revokeObjectURL(downloadUrlRef.current);
57
- downloadUrlRef.current = null;
58
- }
59
- if (encoderRef.current) {
60
- encoderRef.current.close();
61
- encoderRef.current = null;
62
- }
63
- frameCapture.destroy();
64
- setState('idle');
65
- setProgress(0);
66
- setPhase('');
67
- setDuration(0);
68
- setBackend(null);
69
- setDownloadUrl(null);
70
- setFileSize(0);
71
- setError(null);
72
- setElapsed(0);
73
- setEstimatedRemaining(0);
74
- if (elapsedTimerRef.current)
75
- clearInterval(elapsedTimerRef.current);
76
- cancelledRef.current = false;
77
- }, [frameCapture]);
78
- const cancel = useCallback(() => {
79
- cancelledRef.current = true;
80
- if (elapsedTimerRef.current)
81
- clearInterval(elapsedTimerRef.current);
82
- if (encoderRef.current) {
83
- encoderRef.current.close();
84
- encoderRef.current = null;
85
- }
86
- frameCapture.destroy();
87
- setState('idle');
88
- setProgress(0);
89
- setPhase('Cancelled');
90
- }, [frameCapture]);
91
- const startExport = useCallback(async (doc, config) => {
92
- // Clear previous state
93
- cancelledRef.current = false;
94
- if (downloadUrlRef.current) {
95
- URL.revokeObjectURL(downloadUrlRef.current);
96
- downloadUrlRef.current = null;
97
- }
98
- setDownloadUrl(null);
99
- setFileSize(0);
100
- setError(null);
101
- const quality = config.quality ?? 'normal';
102
- const fps = config.fps ?? 30;
103
- const orientation = config.orientation ?? 'landscape';
104
- const { width, height } = resolveDimensions({ orientation });
105
- try {
106
- // ── Check browser support ─────────────────────────────────
107
- const webCodecsAvailable = supportsWebCodecs();
108
- const sharedArrayBufferAvailable = typeof SharedArrayBuffer !== 'undefined';
109
- if (!webCodecsAvailable && !sharedArrayBufferAvailable) {
110
- throw new Error('No video encoder available. WebCodecs requires Chrome 94+ / Edge 94+, ' +
111
- 'and the ffmpeg.wasm fallback requires SharedArrayBuffer ' +
112
- '(Cross-Origin-Isolation headers).');
113
- }
114
- // ── Step 1: Prepare ───────────────────────────────────────
115
- setState('preparing');
116
- setPhase('Loading document…');
117
- setProgress(0);
118
- setElapsed(0);
119
- setEstimatedRemaining(0);
120
- // Start elapsed timer
121
- startTimeRef.current = performance.now();
122
- if (elapsedTimerRef.current)
123
- clearInterval(elapsedTimerRef.current);
124
- elapsedTimerRef.current = setInterval(() => {
125
- setElapsed(Math.floor((performance.now() - startTimeRef.current) / 1000));
126
- }, 1000);
127
- // Collect images from MediaProvider if provided and images not passed directly
128
- let images = config.images;
129
- if (!images && config.mediaProvider) {
130
- images = new Map();
131
- const entries = await config.mediaProvider.listMedia();
132
- for (const entry of entries) {
133
- const url = await config.mediaProvider.resolveUrl(entry.name);
134
- const res = await fetch(url);
135
- if (res.ok) {
136
- const data = await res.arrayBuffer();
137
- images.set(entry.name, data);
138
- }
139
- }
140
- }
141
- const docDuration = await frameCapture.init(doc, { images, audio: config.audio, width, height }, config.captionMode);
142
- if (cancelledRef.current)
143
- return;
144
- setDuration(docDuration);
145
- if (docDuration <= 0) {
146
- throw new Error('Document has zero duration — nothing to export');
147
- }
148
- // ── Step 2: Create encoder ────────────────────────────────
149
- setPhase('Starting encoder…');
150
- setProgress(5);
151
- // Prefer main-thread WebCodecs (fast), but probe whether H.264
152
- // is actually supported. Linux Chromium has VideoEncoder but
153
- // no proprietary H.264 codec — fall back to the worker, which
154
- // loads ffmpeg.wasm in that case.
155
- const canUseWebCodecs = webCodecsAvailable && (await supportsWebCodecsH264({ width, height, fps, quality }));
156
- let encoder;
157
- if (canUseWebCodecs) {
158
- encoder = createEncoder({ width, height, fps, quality });
159
- setBackend('webcodecs');
160
- }
161
- else if (sharedArrayBufferAvailable) {
162
- const workerEncoder = createWorkerEncoder({ width, height, fps, quality });
163
- encoder = workerEncoder;
164
- const selectedBackend = await workerEncoder.ready;
165
- setBackend(selectedBackend);
166
- setPhase(selectedBackend === 'ffmpeg-wasm'
167
- ? 'Starting encoder (ffmpeg.wasm)…'
168
- : 'Starting encoder…');
169
- }
170
- else {
171
- throw new Error('WebCodecs H.264 is unavailable in this browser and the ffmpeg.wasm ' +
172
- 'fallback requires SharedArrayBuffer (Cross-Origin-Isolation headers).');
173
- }
174
- encoderRef.current = encoder;
175
- if (cancelledRef.current)
176
- return;
177
- // ── Step 3: Capture frames and encode ─────────────────────
178
- setState('capturing');
179
- const totalFrames = Math.ceil(docDuration * fps);
180
- const captureStartTime = performance.now();
181
- // Throttle UI updates to every ~10 frames to avoid excessive re-renders.
182
- // Each setState between awaits triggers a separate render cycle.
183
- const UI_UPDATE_INTERVAL = 10;
184
- for (let i = 0; i < totalFrames; i++) {
185
- if (cancelledRef.current)
186
- return;
187
- const time = i / fps;
188
- // Update UI periodically (not every frame)
189
- if (i % UI_UPDATE_INTERVAL === 0 || i === totalFrames - 1) {
190
- const captureProgress = Math.round((i / totalFrames) * 90);
191
- setProgress(5 + captureProgress);
192
- setPhase(`Capturing frame ${i + 1}/${totalFrames} (${time.toFixed(1)}s)`);
193
- if (i > 0) {
194
- const elapsedCapture = (performance.now() - captureStartTime) / 1000;
195
- const avgPerFrame = elapsedCapture / i;
196
- const remaining = Math.round(avgPerFrame * (totalFrames - i));
197
- setEstimatedRemaining(remaining);
198
- }
199
- }
200
- const bitmap = await frameCapture.captureFrame(time);
201
- if (cancelledRef.current) {
202
- bitmap.close();
203
- return;
204
- }
205
- // Encode immediately — WebCodecs is fast and async internally
206
- encoder.encodeFrame(bitmap, i);
207
- }
208
- if (cancelledRef.current)
209
- return;
210
- // ── Step 4: Finalize MP4 ──────────────────────────────────
211
- setState('encoding');
212
- setPhase('Finalizing video…');
213
- setProgress(95);
214
- const mp4Buffer = await encoder.finalize();
215
- encoderRef.current = null;
216
- if (cancelledRef.current)
217
- return;
218
- // ── Step 5: Create download URL ───────────────────────────
219
- const blob = new Blob([mp4Buffer], { type: 'video/mp4' });
220
- const url = URL.createObjectURL(blob);
221
- downloadUrlRef.current = url;
222
- setDownloadUrl(url);
223
- setFileSize(mp4Buffer.byteLength);
224
- setState('complete');
225
- setProgress(100);
226
- setPhase('Export complete');
227
- setEstimatedRemaining(0);
228
- if (elapsedTimerRef.current)
229
- clearInterval(elapsedTimerRef.current);
230
- // Clean up
231
- frameCapture.destroy();
232
- }
233
- catch (err) {
234
- if (elapsedTimerRef.current)
235
- clearInterval(elapsedTimerRef.current);
236
- if (cancelledRef.current)
237
- return;
238
- const message = err instanceof Error ? err.message : String(err);
239
- setState('error');
240
- setError(message);
241
- setPhase('Export failed');
242
- // Clean up on error
243
- if (encoderRef.current) {
244
- encoderRef.current.close();
245
- encoderRef.current = null;
246
- }
247
- frameCapture.destroy();
248
- }
249
- }, [frameCapture]);
250
- return {
251
- state,
252
- progress,
253
- phase,
254
- duration,
255
- backend,
256
- downloadUrl,
257
- fileSize,
258
- error,
259
- elapsed,
260
- estimatedRemaining,
261
- startExport,
262
- cancel,
263
- reset,
264
- };
265
- }
266
- //# sourceMappingURL=useVideoExport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useVideoExport.js","sourceRoot":"","sources":["../../src/hooks/useVideoExport.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1,44 +0,0 @@
1
- /**
2
- * Main-thread WebCodecs encoder.
3
- *
4
- * Encodes video frames to MP4 using the WebCodecs API and mp4-muxer,
5
- * running directly on the main thread. This is simpler and avoids
6
- * worker module-resolution issues with bundlers. Since frame capture
7
- * via html2canvas (~100-200ms per frame) is the bottleneck — not
8
- * encoding (~1ms per frame with hardware-accelerated WebCodecs) —
9
- * worker offloading provides minimal benefit.
10
- *
11
- * Requirements: Chrome 94+ / Edge 94+ (WebCodecs support).
12
- */
13
- export interface EncoderConfig {
14
- width: number;
15
- height: number;
16
- fps: number;
17
- quality: 'draft' | 'normal' | 'high';
18
- }
19
- export interface MainThreadEncoder {
20
- /** Encode a single frame. The bitmap is closed after encoding. */
21
- encodeFrame(bitmap: ImageBitmap, frameIndex: number): void;
22
- /** Flush pending frames and finalize the MP4. Returns the MP4 ArrayBuffer. */
23
- finalize(): Promise<ArrayBuffer>;
24
- /** Close the encoder without producing output (e.g., on cancel). */
25
- close(): void;
26
- }
27
- /**
28
- * Check whether the browser supports WebCodecs video encoding.
29
- */
30
- export declare function supportsWebCodecs(): boolean;
31
- /**
32
- * Probe whether the WebCodecs encoder actually supports the H.264 profile
33
- * we use. The `VideoEncoder` global can exist while the specific codec is
34
- * unavailable — this is the case on Linux Chromium, which ships without
35
- * the proprietary H.264 encoder.
36
- */
37
- export declare function supportsWebCodecsH264(config: EncoderConfig): Promise<boolean>;
38
- /**
39
- * Create a main-thread WebCodecs encoder.
40
- *
41
- * Throws if WebCodecs is not available.
42
- */
43
- export declare function createEncoder(config: EncoderConfig): MainThreadEncoder;
44
- //# sourceMappingURL=mainThreadEncoder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mainThreadEncoder.d.ts","sourceRoot":"","sources":["../src/mainThreadEncoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D,8EAA8E;IAC9E,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,oEAAoE;IACpE,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAcnF;AAeD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,iBAAiB,CAuEtE"}
@@ -1,123 +0,0 @@
1
- /**
2
- * Main-thread WebCodecs encoder.
3
- *
4
- * Encodes video frames to MP4 using the WebCodecs API and mp4-muxer,
5
- * running directly on the main thread. This is simpler and avoids
6
- * worker module-resolution issues with bundlers. Since frame capture
7
- * via html2canvas (~100-200ms per frame) is the bottleneck — not
8
- * encoding (~1ms per frame with hardware-accelerated WebCodecs) —
9
- * worker offloading provides minimal benefit.
10
- *
11
- * Requirements: Chrome 94+ / Edge 94+ (WebCodecs support).
12
- */
13
- import { createMp4Muxer } from './mp4Mux.js';
14
- /**
15
- * Check whether the browser supports WebCodecs video encoding.
16
- */
17
- export function supportsWebCodecs() {
18
- return typeof VideoEncoder !== 'undefined' && typeof VideoFrame !== 'undefined';
19
- }
20
- /**
21
- * Probe whether the WebCodecs encoder actually supports the H.264 profile
22
- * we use. The `VideoEncoder` global can exist while the specific codec is
23
- * unavailable — this is the case on Linux Chromium, which ships without
24
- * the proprietary H.264 encoder.
25
- */
26
- export async function supportsWebCodecsH264(config) {
27
- if (!supportsWebCodecs())
28
- return false;
29
- try {
30
- const support = await VideoEncoder.isConfigSupported({
31
- codec: 'avc1.640028',
32
- width: config.width,
33
- height: config.height,
34
- bitrate: bitrateForQuality(config.quality, config.width, config.height),
35
- framerate: config.fps,
36
- });
37
- return support.supported === true;
38
- }
39
- catch {
40
- return false;
41
- }
42
- }
43
- function bitrateForQuality(quality, width, height) {
44
- const pixels = width * height;
45
- const baseBitrate = pixels * 4; // ~4 bits per pixel baseline
46
- switch (quality) {
47
- case 'draft':
48
- return Math.round(baseBitrate * 0.5);
49
- case 'high':
50
- return Math.round(baseBitrate * 2);
51
- default: // normal
52
- return baseBitrate;
53
- }
54
- }
55
- /**
56
- * Create a main-thread WebCodecs encoder.
57
- *
58
- * Throws if WebCodecs is not available.
59
- */
60
- export function createEncoder(config) {
61
- if (!supportsWebCodecs()) {
62
- throw new Error('WebCodecs is not available in this browser. ' +
63
- 'Video export requires Chrome 94+, Edge 94+, or another Chromium-based browser.');
64
- }
65
- if (!config.fps || config.fps <= 0 || !config.width || !config.height) {
66
- throw new Error(`Invalid encoder config: fps=${config.fps}, width=${config.width}, height=${config.height}`);
67
- }
68
- const muxer = createMp4Muxer({
69
- width: config.width,
70
- height: config.height,
71
- fps: config.fps,
72
- });
73
- let closed = false;
74
- const frameDuration = 1000000 / config.fps; // microseconds per frame
75
- const encoder = new VideoEncoder({
76
- output(chunk, meta) {
77
- if (closed)
78
- return;
79
- muxer.addVideoChunk(chunk, meta ?? undefined);
80
- },
81
- error(err) {
82
- console.error('WebCodecs encoder error:', err.message);
83
- },
84
- });
85
- encoder.configure({
86
- codec: 'avc1.640028', // H.264 High profile, level 4.0 (supports up to 1080p)
87
- width: config.width,
88
- height: config.height,
89
- bitrate: bitrateForQuality(config.quality, config.width, config.height),
90
- framerate: config.fps,
91
- });
92
- return {
93
- encodeFrame(bitmap, frameIndex) {
94
- if (closed) {
95
- bitmap.close();
96
- return;
97
- }
98
- const timestamp = Math.round(frameIndex * frameDuration);
99
- const frame = new VideoFrame(bitmap, { timestamp });
100
- const keyFrame = frameIndex % 30 === 0;
101
- encoder.encode(frame, { keyFrame });
102
- frame.close();
103
- bitmap.close();
104
- },
105
- async finalize() {
106
- if (closed)
107
- throw new Error('Encoder already closed');
108
- await encoder.flush();
109
- encoder.close();
110
- closed = true;
111
- return muxer.finalize();
112
- },
113
- close() {
114
- if (closed)
115
- return;
116
- closed = true;
117
- if (encoder.state !== 'closed') {
118
- encoder.close();
119
- }
120
- },
121
- };
122
- }
123
- //# sourceMappingURL=mainThreadEncoder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mainThreadEncoder.js","sourceRoot":"","sources":["../src/mainThreadEncoder.ts"],"names":[],"mappings":"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"}
package/dist/mp4Mux.d.ts DELETED
@@ -1,22 +0,0 @@
1
- /**
2
- * mp4Mux — Thin wrapper around mp4-muxer for WebCodecs encoding.
3
- *
4
- * Creates a Muxer instance configured for H.264 video, accumulates
5
- * encoded chunks, and produces a final MP4 ArrayBuffer.
6
- */
7
- export interface Mp4MuxerOptions {
8
- width: number;
9
- height: number;
10
- fps: number;
11
- }
12
- export interface Mp4MuxerHandle {
13
- /** Add an encoded video chunk to the muxer. */
14
- addVideoChunk(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void;
15
- /** Finalize and return the MP4 as an ArrayBuffer. */
16
- finalize(): ArrayBuffer;
17
- }
18
- /**
19
- * Create an MP4 muxer configured for H.264 video.
20
- */
21
- export declare function createMp4Muxer(options: Mp4MuxerOptions): Mp4MuxerHandle;
22
- //# sourceMappingURL=mp4Mux.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mp4Mux.d.ts","sourceRoot":"","sources":["../src/mp4Mux.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,IAAI,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAChF,qDAAqD;IACrD,QAAQ,IAAI,WAAW,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAuBvE"}
package/dist/mp4Mux.js DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * mp4Mux — Thin wrapper around mp4-muxer for WebCodecs encoding.
3
- *
4
- * Creates a Muxer instance configured for H.264 video, accumulates
5
- * encoded chunks, and produces a final MP4 ArrayBuffer.
6
- */
7
- import { Muxer, ArrayBufferTarget } from 'mp4-muxer';
8
- /**
9
- * Create an MP4 muxer configured for H.264 video.
10
- */
11
- export function createMp4Muxer(options) {
12
- const target = new ArrayBufferTarget();
13
- const muxer = new Muxer({
14
- target,
15
- video: {
16
- codec: 'avc',
17
- width: options.width,
18
- height: options.height,
19
- },
20
- fastStart: 'in-memory',
21
- });
22
- return {
23
- addVideoChunk(chunk, meta) {
24
- muxer.addVideoChunk(chunk, meta);
25
- },
26
- finalize() {
27
- muxer.finalize();
28
- return target.buffer;
29
- },
30
- };
31
- }
32
- //# sourceMappingURL=mp4Mux.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mp4Mux.js","sourceRoot":"","sources":["../src/mp4Mux.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAerD;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAEvC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,MAAM;QACN,KAAK,EAAE;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB;QACD,SAAS,EAAE,WAAW;KACvB,CAAC,CAAC;IAEH,OAAO;QACL,aAAa,CAAC,KAAwB,EAAE,IAAgC;YACtE,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,QAAQ;YACN,KAAK,CAAC,QAAQ,EAAE,CAAC;YACjB,OAAO,MAAM,CAAC,MAAM,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,17 +0,0 @@
1
- /**
2
- * Worker-backed video encoder.
3
- *
4
- * Spawns `workers/encode.worker.ts` and exposes the same shape as
5
- * `MainThreadEncoder` so `useVideoExport` can treat both backends
6
- * uniformly. Used as a fallback when WebCodecs H.264 isn't supported
7
- * (typical on Linux Chromium): the worker auto-selects its ffmpeg.wasm
8
- * path in that case.
9
- */
10
- import type { MainThreadEncoder, EncoderConfig } from './mainThreadEncoder.js';
11
- /** Resolves once the worker has reported which backend it picked. */
12
- export interface WorkerEncoder extends MainThreadEncoder {
13
- /** Backend the worker selected ('webcodecs' or 'ffmpeg-wasm'). */
14
- readonly ready: Promise<'webcodecs' | 'ffmpeg-wasm'>;
15
- }
16
- export declare function createWorkerEncoder(config: EncoderConfig): WorkerEncoder;
17
- //# sourceMappingURL=workerEncoder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workerEncoder.d.ts","sourceRoot":"","sources":["../src/workerEncoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG/E,qEAAqE;AACrE,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,GAAG,aAAa,CAAC,CAAC;CACtD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAuGxE"}