@bendyline/squisq-video-react 1.1.1 → 1.2.0
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/dist/index.d.ts +171 -19
- package/dist/index.js +921 -21
- package/dist/index.js.map +1 -1
- package/dist/workers/encode.worker.d.ts +2 -13
- package/dist/workers/encode.worker.js +258 -304
- package/dist/workers/encode.worker.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/encodeParams.test.ts +33 -0
- package/src/workers/encode.worker.ts +1 -13
- package/src/workers/encodeParams.ts +24 -0
- package/dist/VideoExportButton.d.ts +0 -28
- package/dist/VideoExportButton.d.ts.map +0 -1
- package/dist/VideoExportButton.js +0 -18
- package/dist/VideoExportButton.js.map +0 -1
- package/dist/VideoExportModal.d.ts +0 -26
- package/dist/VideoExportModal.d.ts.map +0 -1
- package/dist/VideoExportModal.js +0 -164
- package/dist/VideoExportModal.js.map +0 -1
- package/dist/hooks/useFrameCapture.d.ts +0 -27
- package/dist/hooks/useFrameCapture.d.ts.map +0 -1
- package/dist/hooks/useFrameCapture.js +0 -211
- package/dist/hooks/useFrameCapture.js.map +0 -1
- package/dist/hooks/useVideoExport.d.ts +0 -75
- package/dist/hooks/useVideoExport.d.ts.map +0 -1
- package/dist/hooks/useVideoExport.js +0 -266
- package/dist/hooks/useVideoExport.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/mainThreadEncoder.d.ts +0 -44
- package/dist/mainThreadEncoder.d.ts.map +0 -1
- package/dist/mainThreadEncoder.js +0 -123
- package/dist/mainThreadEncoder.js.map +0 -1
- package/dist/mp4Mux.d.ts +0 -22
- package/dist/mp4Mux.d.ts.map +0 -1
- package/dist/mp4Mux.js +0 -32
- package/dist/mp4Mux.js.map +0 -1
- package/dist/workerEncoder.d.ts +0 -17
- package/dist/workerEncoder.d.ts.map +0 -1
- package/dist/workerEncoder.js +0 -103
- package/dist/workerEncoder.js.map +0 -1
- package/dist/workers/encode.worker.d.ts.map +0 -1
- package/dist/workers/workerTypes.d.ts +0 -63
- package/dist/workers/workerTypes.d.ts.map +0 -1
- package/dist/workers/workerTypes.js +0 -8
- package/dist/workers/workerTypes.js.map +0 -1
|
@@ -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
|
package/dist/mp4Mux.d.ts.map
DELETED
|
@@ -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
|
package/dist/mp4Mux.js.map
DELETED
|
@@ -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"}
|
package/dist/workerEncoder.d.ts
DELETED
|
@@ -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"}
|
package/dist/workerEncoder.js
DELETED
|
@@ -1,103 +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
|
-
export function createWorkerEncoder(config) {
|
|
11
|
-
if (!config.fps || config.fps <= 0 || !config.width || !config.height) {
|
|
12
|
-
throw new Error(`Invalid encoder config: fps=${config.fps}, width=${config.width}, height=${config.height}`);
|
|
13
|
-
}
|
|
14
|
-
const worker = new Worker(new URL('./workers/encode.worker.js', import.meta.url), {
|
|
15
|
-
type: 'module',
|
|
16
|
-
});
|
|
17
|
-
let closed = false;
|
|
18
|
-
let fatalError = null;
|
|
19
|
-
let finalizeResolve = null;
|
|
20
|
-
let finalizeReject = null;
|
|
21
|
-
let readyResolve = null;
|
|
22
|
-
let readyReject = null;
|
|
23
|
-
const ready = new Promise((resolve, reject) => {
|
|
24
|
-
readyResolve = resolve;
|
|
25
|
-
readyReject = reject;
|
|
26
|
-
});
|
|
27
|
-
const frameDuration = 1000000 / config.fps; // microseconds per frame
|
|
28
|
-
function post(msg, transfer) {
|
|
29
|
-
worker.postMessage(msg, transfer ?? []);
|
|
30
|
-
}
|
|
31
|
-
worker.onmessage = (event) => {
|
|
32
|
-
const msg = event.data;
|
|
33
|
-
switch (msg.type) {
|
|
34
|
-
case 'capabilities':
|
|
35
|
-
readyResolve?.(msg.backend);
|
|
36
|
-
readyResolve = readyReject = null;
|
|
37
|
-
break;
|
|
38
|
-
case 'complete':
|
|
39
|
-
finalizeResolve?.(msg.data);
|
|
40
|
-
finalizeResolve = finalizeReject = null;
|
|
41
|
-
worker.terminate();
|
|
42
|
-
break;
|
|
43
|
-
case 'error': {
|
|
44
|
-
const err = new Error(msg.message);
|
|
45
|
-
fatalError = err;
|
|
46
|
-
readyReject?.(err);
|
|
47
|
-
finalizeReject?.(err);
|
|
48
|
-
readyResolve = readyReject = null;
|
|
49
|
-
finalizeResolve = finalizeReject = null;
|
|
50
|
-
worker.terminate();
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
// 'progress' is informational; ignored at this layer
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
worker.onerror = (event) => {
|
|
57
|
-
const err = new Error(event.message || 'Worker error');
|
|
58
|
-
fatalError = err;
|
|
59
|
-
readyReject?.(err);
|
|
60
|
-
finalizeReject?.(err);
|
|
61
|
-
readyResolve = readyReject = null;
|
|
62
|
-
finalizeResolve = finalizeReject = null;
|
|
63
|
-
worker.terminate();
|
|
64
|
-
};
|
|
65
|
-
post({
|
|
66
|
-
type: 'init',
|
|
67
|
-
width: config.width,
|
|
68
|
-
height: config.height,
|
|
69
|
-
fps: config.fps,
|
|
70
|
-
quality: config.quality,
|
|
71
|
-
});
|
|
72
|
-
return {
|
|
73
|
-
ready,
|
|
74
|
-
encodeFrame(bitmap, frameIndex) {
|
|
75
|
-
if (closed || fatalError) {
|
|
76
|
-
bitmap.close();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const timestamp = Math.round(frameIndex * frameDuration);
|
|
80
|
-
post({ type: 'frame', bitmap, frameIndex, timestamp }, [bitmap]);
|
|
81
|
-
},
|
|
82
|
-
async finalize() {
|
|
83
|
-
if (closed)
|
|
84
|
-
throw new Error('Encoder already closed');
|
|
85
|
-
if (fatalError)
|
|
86
|
-
throw fatalError;
|
|
87
|
-
closed = true;
|
|
88
|
-
return new Promise((resolve, reject) => {
|
|
89
|
-
finalizeResolve = resolve;
|
|
90
|
-
finalizeReject = reject;
|
|
91
|
-
post({ type: 'finalize' });
|
|
92
|
-
});
|
|
93
|
-
},
|
|
94
|
-
close() {
|
|
95
|
-
if (closed)
|
|
96
|
-
return;
|
|
97
|
-
closed = true;
|
|
98
|
-
post({ type: 'cancel' });
|
|
99
|
-
worker.terminate();
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=workerEncoder.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workerEncoder.js","sourceRoot":"","sources":["../src/workerEncoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACvD,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,GAAG,WAAW,MAAM,CAAC,KAAK,YAAY,MAAM,CAAC,MAAM,EAAE,CAC5F,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAChF,IAAI,EAAE,QAAQ;KACf,CAAC,CAAC;IAEH,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,UAAU,GAAiB,IAAI,CAAC;IACpC,IAAI,eAAe,GAA2C,IAAI,CAAC;IACnE,IAAI,cAAc,GAAkC,IAAI,CAAC;IACzD,IAAI,YAAY,GAA4D,IAAI,CAAC;IACjF,IAAI,WAAW,GAAkC,IAAI,CAAC;IAEtD,MAAM,KAAK,GAAG,IAAI,OAAO,CAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACzE,YAAY,GAAG,OAAO,CAAC;QACvB,WAAW,GAAG,MAAM,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,OAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,yBAAyB;IAEvE,SAAS,IAAI,CAAC,GAAwB,EAAE,QAAyB;QAC/D,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,CAAC,SAAS,GAAG,CAAC,KAAwC,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,cAAc;gBACjB,YAAY,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5B,YAAY,GAAG,WAAW,GAAG,IAAI,CAAC;gBAClC,MAAM;YACR,KAAK,UAAU;gBACb,eAAe,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC5B,eAAe,GAAG,cAAc,GAAG,IAAI,CAAC;gBACxC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnB,MAAM;YACR,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACnC,UAAU,GAAG,GAAG,CAAC;gBACjB,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;gBACnB,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;gBACtB,YAAY,GAAG,WAAW,GAAG,IAAI,CAAC;gBAClC,eAAe,GAAG,cAAc,GAAG,IAAI,CAAC;gBACxC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnB,MAAM;YACR,CAAC;YACD,qDAAqD;QACvD,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;QACzB,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,CAAC;QACvD,UAAU,GAAG,GAAG,CAAC;QACjB,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;QACnB,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;QACtB,YAAY,GAAG,WAAW,GAAG,IAAI,CAAC;QAClC,eAAe,GAAG,cAAc,GAAG,IAAI,CAAC;QACxC,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QAEL,WAAW,CAAC,MAAmB,EAAE,UAAkB;YACjD,IAAI,MAAM,IAAI,UAAU,EAAE,CAAC;gBACzB,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,aAAa,CAAC,CAAC;YACzD,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,CAAC;QAED,KAAK,CAAC,QAAQ;YACZ,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACtD,IAAI,UAAU;gBAAE,MAAM,UAAU,CAAC;YACjC,MAAM,GAAG,IAAI,CAAC;YACd,OAAO,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAClD,eAAe,GAAG,OAAO,CAAC;gBAC1B,cAAc,GAAG,MAAM,CAAC;gBACxB,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACzB,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encode.worker.d.ts","sourceRoot":"","sources":["../../src/workers/encode.worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -1,63 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workerTypes.js","sourceRoot":"","sources":["../../src/workers/workerTypes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|