canvas-record
Advanced tools
Comparing version 4.2.0 to 5.0.0-beta.0
# Changelog | ||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. | ||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. | ||
# [5.0.0-beta.0](https://github.com/dmnsgn/canvas-record/compare/v4.2.0...v5.0.0-beta.0) (2023-11-04) | ||
### Bug Fixes | ||
* correct RecorderOptions extension type ([084a155](https://github.com/dmnsgn/canvas-record/commit/084a155f515fcff5a27b0cde73fdef40a173b96d)), closes [#12](https://github.com/dmnsgn/canvas-record/issues/12) | ||
* framerate spelling is not following camelcase in VideoEncoder config ([f9c2662](https://github.com/dmnsgn/canvas-record/commit/f9c2662aced88880e40bff8c8ea0b690938b3235)) | ||
### Build System | ||
* update dependencies ([66defa1](https://github.com/dmnsgn/canvas-record/commit/66defa1d1ba1acb8428f10ab4d43f556bf538b23)) | ||
### Features | ||
* educated guess for bitrate from resolution and framerate ([acb38c5](https://github.com/dmnsgn/canvas-record/commit/acb38c56994a76263c3a66e714763ea96ef3a050)), closes [#13](https://github.com/dmnsgn/canvas-record/issues/13) | ||
### BREAKING CHANGES | ||
* ffmpeg has different binaries and likely to break | ||
# [4.2.0](https://github.com/dmnsgn/canvas-record/compare/v4.1.0...v4.2.0) (2023-05-20) | ||
@@ -6,0 +31,0 @@ |
/** | ||
* Re-export Recorder, RecorderStatus, all Encoders and utils. | ||
* @module index | ||
* @module canvas-record | ||
*/ | ||
@@ -5,0 +5,0 @@ |
{ | ||
"name": "canvas-record", | ||
"version": "4.2.0", | ||
"version": "5.0.0-beta.0", | ||
"description": "Record a video in the browser or directly on the File System from a canvas (2D/WebGL/WebGPU) as MP4, WebM, MKV, GIF, PNG/JPG Sequence using WebCodecs and wasm when available.", | ||
@@ -47,3 +47,4 @@ "keywords": [ | ||
"dependencies": { | ||
"@ffmpeg/ffmpeg": "^0.11.6", | ||
"@ffmpeg/ffmpeg": "^0.12.7", | ||
"@ffmpeg/util": "^0.12.1", | ||
"canvas-screenshot": "^4.1.0", | ||
@@ -53,18 +54,21 @@ "gifenc": "^1.0.3", | ||
"media-codecs": "^2.0.0", | ||
"mp4-muxer": "^2.1.1", | ||
"webm-muxer": "^3.1.0" | ||
"mp4-muxer": "^3.0.0", | ||
"webm-muxer": "^3.1.3" | ||
}, | ||
"devDependencies": { | ||
"@web/rollup-plugin-import-meta-assets": "^2.1.0", | ||
"canvas-context": "^3.2.0", | ||
"es-module-shims": "^1.7.2", | ||
"es-module-shims": "^1.8.1", | ||
"mp4-wasm": "^1.0.6", | ||
"tweakpane": "^3.1.9" | ||
"rollup": "^4.1.5", | ||
"tweakpane": "^4.0.1" | ||
}, | ||
"optionalDependencies": { | ||
"@ffmpeg/core": "^0.12.3", | ||
"@ffmpeg/core-mt": "^0.12.3" | ||
}, | ||
"engines": { | ||
"node": ">=18.0.0", | ||
"npm": ">=8.0.0" | ||
}, | ||
"snowdev": { | ||
"crossOriginIsolation": true | ||
} | ||
} |
490
README.md
@@ -118,3 +118,3 @@ # canvas-record | ||
<dl> | ||
<dt><a href="#module_index">index</a></dt> | ||
<dt><a href="#module_canvas-record">canvas-record</a></dt> | ||
<dd><p>Re-export Recorder, RecorderStatus, all Encoders and utils.</p> | ||
@@ -128,3 +128,26 @@ </dd> | ||
<dt><a href="#Recorder">Recorder</a></dt> | ||
<dd><p>Base Recorder class.</p> | ||
<dd></dd> | ||
<dt><a href="#Encoder">Encoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#FFmpegEncoder">FFmpegEncoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#FrameEncoder">FrameEncoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#GIFEncoder">GIFEncoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#H264MP4Encoder">H264MP4Encoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#MP4WasmEncoder">MP4WasmEncoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#MediaCaptureEncoder">MediaCaptureEncoder</a></dt> | ||
<dd></dd> | ||
<dt><a href="#WebCodecsEncoder">WebCodecsEncoder</a></dt> | ||
<dd></dd> | ||
</dl> | ||
## Constants | ||
<dl> | ||
<dt><a href="#isWebCodecsSupported">isWebCodecsSupported</a> : <code>boolean</code></dt> | ||
<dd><p>Check for WebCodecs support on the current platform.</p> | ||
</dd> | ||
@@ -136,4 +159,5 @@ </dl> | ||
<dl> | ||
<dt><a href="#onStatusChangeCb">onStatusChangeCb(RecorderStatus)</a></dt> | ||
<dd><p>A callback to notify on the status change. To compare with RecorderStatus enum values.</p> | ||
<dt><a href="#estimateBitRate">estimateBitRate(width, height, frameRate, motionRank, bitrateMode)</a> ⇒ <code>number</code></dt> | ||
<dd><p>Estimate the bit rate of a video rounded to nearest megabit. | ||
Based on "H.264 for the rest of us" by Kush Amerasinghe.</p> | ||
</dd> | ||
@@ -145,2 +169,5 @@ </dl> | ||
<dl> | ||
<dt><a href="#onStatusChangeCb">onStatusChangeCb</a> : <code>function</code></dt> | ||
<dd><p>A callback to notify on the status change. To compare with RecorderStatus enum values.</p> | ||
</dd> | ||
<dt><a href="#RecorderOptions">RecorderOptions</a> : <code>object</code></dt> | ||
@@ -150,9 +177,41 @@ <dd><p>Options for recording. All optional.</p> | ||
<dt><a href="#RecorderStartOptions">RecorderStartOptions</a> : <code>object</code></dt> | ||
<dd><p>Options for recording. All optional.</p> | ||
<dd><p>Options for recording initialisation. All optional.</p> | ||
</dd> | ||
<dt><a href="#EncoderExtensions">EncoderExtensions</a> : <code>"mp4"</code> | <code>"webm"</code> | <code>"png"</code> | <code>"jpg"</code> | <code>"gif"</code> | <code>"mkv"</code></dt> | ||
<dd></dd> | ||
<dt><a href="#EncoderTarget">EncoderTarget</a> : <code>"in-browser"</code> | <code>"file-system"</code></dt> | ||
<dd></dd> | ||
<dt><a href="#FFmpegEncoderOptions">FFmpegEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#FFmpegEncoderEncoderOptions">FFmpegEncoderEncoderOptions</a> : <code>module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig</code></dt> | ||
<dd></dd> | ||
<dt><a href="#GIFEncoderOptions">GIFEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#GIFEncoderQuantizeOptions">GIFEncoderQuantizeOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#GIFEncoderEncoderOptions">GIFEncoderEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#H264MP4EncoderOptions">H264MP4EncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#H264MP4EncoderEncoderOptions">H264MP4EncoderEncoderOptions</a> : <code>module:h264-mp4-encoder~H264MP4Encoder</code></dt> | ||
<dd></dd> | ||
<dt><a href="#MP4WasmEncoderOptions">MP4WasmEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#MP4WasmEncoderEncoderOptions">MP4WasmEncoderEncoderOptions</a> : <code>VideoEncoderConfig</code></dt> | ||
<dd></dd> | ||
<dt><a href="#MediaCaptureEncoderOptions">MediaCaptureEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#MediaCaptureEncoderEncoderOptions">MediaCaptureEncoderEncoderOptions</a> : <code>MediaRecorderOptions</code></dt> | ||
<dd></dd> | ||
<dt><a href="#WebCodecsEncoderOptions">WebCodecsEncoderOptions</a> : <code>object</code></dt> | ||
<dd></dd> | ||
<dt><a href="#WebCodecsEncoderEncoderOptions">WebCodecsEncoderEncoderOptions</a> : <code>VideoEncoderConfig</code></dt> | ||
<dd></dd> | ||
<dt><a href="#WebCodecsMuxerOptions">WebCodecsMuxerOptions</a> : <code>MuxerOptions</code></dt> | ||
<dd></dd> | ||
</dl> | ||
<a name="module_index"></a> | ||
<a name="module_canvas-record"></a> | ||
## index | ||
## canvas-record | ||
@@ -165,14 +224,9 @@ Re-export Recorder, RecorderStatus, all Encoders and utils. | ||
Base Recorder class. | ||
**Kind**: global class | ||
**Properties** | ||
| Name | Type | Default | Description | | ||
| --------- | -------------------- | ----------------- | ----------------------------------------------- | | ||
| [enabled] | <code>boolean</code> | <code>true</code> | Enable/disable pointer interaction and drawing. | | ||
- [Recorder](#Recorder) | ||
- [new Recorder(context, options)](#new_Recorder_new) | ||
- [.start(startOptions)](#Recorder+start) | ||
- [new Recorder(context, [options])](#new_Recorder_new) | ||
- [.defaultOptions](#Recorder+defaultOptions) : [<code>RecorderOptions</code>](#RecorderOptions) | ||
- [.mimeTypes](#Recorder+mimeTypes) : <code>object</code> | ||
- [.start([startOptions])](#Recorder+start) | ||
- [.step()](#Recorder+step) | ||
@@ -184,12 +238,28 @@ - [.stop()](#Recorder+stop) ⇒ <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Array.<Blob></code> \| <code>undefined</code> | ||
### new Recorder(context, options) | ||
### new Recorder(context, [options]) | ||
| Param | Type | | ||
| ------- | ------------------------------------------------ | | ||
| context | <code>RenderingContext</code> | | ||
| options | [<code>RecorderOptions</code>](#RecorderOptions) | | ||
Create a Recorder instance | ||
| Param | Type | Default | | ||
| --------- | ------------------------------------------------ | --------------- | | ||
| context | <code>RenderingContext</code> | | | ||
| [options] | [<code>RecorderOptions</code>](#RecorderOptions) | <code>{}</code> | | ||
<a name="Recorder+defaultOptions"></a> | ||
### recorder.defaultOptions : [<code>RecorderOptions</code>](#RecorderOptions) | ||
Sensible defaults for recording so that the recorder "just works". | ||
**Kind**: instance property of [<code>Recorder</code>](#Recorder) | ||
<a name="Recorder+mimeTypes"></a> | ||
### recorder.mimeTypes : <code>object</code> | ||
A mapping of extension to their mime types | ||
**Kind**: instance property of [<code>Recorder</code>](#Recorder) | ||
<a name="Recorder+start"></a> | ||
### recorder.start(startOptions) | ||
### recorder.start([startOptions]) | ||
@@ -200,5 +270,5 @@ Start the recording by initializing and optionally calling the initial step. | ||
| Param | Type | | ||
| ------------ | ---------------------------------------------------------- | | ||
| startOptions | [<code>RecorderStartOptions</code>](#RecorderStartOptions) | | ||
| Param | Type | Default | | ||
| -------------- | ---------------------------------------------------------- | --------------- | | ||
| [startOptions] | [<code>RecorderStartOptions</code>](#RecorderStartOptions) | <code>{}</code> | | ||
@@ -226,5 +296,174 @@ <a name="Recorder+step"></a> | ||
Clean up | ||
Clean up the recorder and encoder | ||
**Kind**: instance method of [<code>Recorder</code>](#Recorder) | ||
<a name="Encoder"></a> | ||
## Encoder | ||
**Kind**: global class | ||
**Properties** | ||
| Name | Type | | ||
| ---------------- | ---------------------------------------------------- | | ||
| target | [<code>EncoderTarget</code>](#EncoderTarget) | | ||
| extension | [<code>EncoderExtensions</code>](#EncoderExtensions) | | ||
| [encoderOptions] | <code>object</code> | | ||
| [muxerOptions] | <code>object</code> | | ||
- [Encoder](#Encoder) | ||
- [new Encoder(options)](#new_Encoder_new) | ||
- [.supportedExtensions](#Encoder+supportedExtensions) : <code>Array.<Extensions></code> | ||
- [.supportedTargets](#Encoder+supportedTargets) : [<code>Array.<EncoderTarget></code>](#EncoderTarget) | ||
- [.init(options)](#Encoder+init) | ||
- [.encode(frame, [frameNumber])](#Encoder+encode) | ||
- [.stop()](#Encoder+stop) ⇒ <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Array.<Blob></code> \| <code>undefined</code> | ||
- [.dispose()](#Encoder+dispose) | ||
<a name="new_Encoder_new"></a> | ||
### new Encoder(options) | ||
Base Encoder class. All Encoders extend it and its method are called by the Recorder. | ||
| Param | Type | | ||
| ------- | ------------------- | | ||
| options | <code>object</code> | | ||
<a name="Encoder+supportedExtensions"></a> | ||
### encoder.supportedExtensions : <code>Array.<Extensions></code> | ||
The extension the encoder supports | ||
**Kind**: instance property of [<code>Encoder</code>](#Encoder) | ||
<a name="Encoder+supportedTargets"></a> | ||
### encoder.supportedTargets : [<code>Array.<EncoderTarget></code>](#EncoderTarget) | ||
The target to download the file to. | ||
**Kind**: instance property of [<code>Encoder</code>](#Encoder) | ||
<a name="Encoder+init"></a> | ||
### encoder.init(options) | ||
Setup the encoder: load binary, instantiate muxers, setup file system target... | ||
**Kind**: instance method of [<code>Encoder</code>](#Encoder) | ||
| Param | Type | | ||
| ------- | ------------------- | | ||
| options | <code>object</code> | | ||
<a name="Encoder+encode"></a> | ||
### encoder.encode(frame, [frameNumber]) | ||
Encode a single frame. The frameNumber is usually used for GOP (Group Of Pictures). | ||
**Kind**: instance method of [<code>Encoder</code>](#Encoder) | ||
| Param | Type | | ||
| ------------- | ------------------- | | ||
| frame | <code>number</code> | | ||
| [frameNumber] | <code>number</code> | | ||
<a name="Encoder+stop"></a> | ||
### encoder.stop() ⇒ <code>ArrayBuffer</code> \| <code>Uint8Array</code> \| <code>Array.<Blob></code> \| <code>undefined</code> | ||
Stop the encoding process and cleanup the temporary data. | ||
**Kind**: instance method of [<code>Encoder</code>](#Encoder) | ||
<a name="Encoder+dispose"></a> | ||
### encoder.dispose() | ||
Clean up the encoder | ||
**Kind**: instance method of [<code>Encoder</code>](#Encoder) | ||
<a name="FFmpegEncoder"></a> | ||
## FFmpegEncoder | ||
**Kind**: global class | ||
<a name="new_FFmpegEncoder_new"></a> | ||
### new FFmpegEncoder([options]) | ||
| Param | Type | | ||
| --------- | ---------------------------------------------------------- | | ||
| [options] | [<code>FFmpegEncoderOptions</code>](#FFmpegEncoderOptions) | | ||
<a name="FrameEncoder"></a> | ||
## FrameEncoder | ||
**Kind**: global class | ||
<a name="GIFEncoder"></a> | ||
## GIFEncoder | ||
**Kind**: global class | ||
<a name="new_GIFEncoder_new"></a> | ||
### new GIFEncoder([options]) | ||
| Param | Type | | ||
| --------- | ---------------------------------------------------- | | ||
| [options] | [<code>GIFEncoderOptions</code>](#GIFEncoderOptions) | | ||
<a name="H264MP4Encoder"></a> | ||
## H264MP4Encoder | ||
**Kind**: global class | ||
<a name="new_H264MP4Encoder_new"></a> | ||
### new H264MP4Encoder([options]) | ||
| Param | Type | | ||
| --------- | ------------------------------------------------------------ | | ||
| [options] | [<code>H264MP4EncoderOptions</code>](#H264MP4EncoderOptions) | | ||
<a name="MP4WasmEncoder"></a> | ||
## MP4WasmEncoder | ||
**Kind**: global class | ||
<a name="new_MP4WasmEncoder_new"></a> | ||
### new MP4WasmEncoder([options]) | ||
| Param | Type | | ||
| --------- | ------------------------------------------------------------ | | ||
| [options] | [<code>MP4WasmEncoderOptions</code>](#MP4WasmEncoderOptions) | | ||
<a name="MediaCaptureEncoder"></a> | ||
## MediaCaptureEncoder | ||
**Kind**: global class | ||
<a name="new_MediaCaptureEncoder_new"></a> | ||
### new MediaCaptureEncoder([options]) | ||
| Param | Type | | ||
| --------- | ---------------------------------------------------------------------- | | ||
| [options] | [<code>MediaCaptureEncoderOptions</code>](#MediaCaptureEncoderOptions) | | ||
<a name="WebCodecsEncoder"></a> | ||
## WebCodecsEncoder | ||
**Kind**: global class | ||
<a name="new_WebCodecsEncoder_new"></a> | ||
### new WebCodecsEncoder([options]) | ||
| Param | Type | | ||
| --------- | ---------------------------------------------------------------- | | ||
| [options] | [<code>WebCodecsEncoderOptions</code>](#WebCodecsEncoderOptions) | | ||
<a name="RecorderStatus"></a> | ||
@@ -247,9 +486,42 @@ | ||
<a name="isWebCodecsSupported"></a> | ||
## isWebCodecsSupported : <code>boolean</code> | ||
Check for WebCodecs support on the current platform. | ||
**Kind**: global constant | ||
<a name="estimateBitRate"></a> | ||
## estimateBitRate(width, height, frameRate, motionRank, bitrateMode) ⇒ <code>number</code> | ||
Estimate the bit rate of a video rounded to nearest megabit. | ||
Based on "H.264 for the rest of us" by Kush Amerasinghe. | ||
**Kind**: global function | ||
**Returns**: <code>number</code> - A bitrate value in bits per second | ||
| Param | Type | Default | Description | | ||
| ----------- | ---------------------------------------------------------------------- | --------------------- | --------------------- | | ||
| width | <code>number</code> | | | | ||
| height | <code>number</code> | | | | ||
| frameRate | <code>number</code> | <code>30</code> | | | ||
| motionRank | <code>number</code> | <code>4</code> | A factor of 1, 2 or 4 | | ||
| bitrateMode | <code>"variable"</code> \| <code>"constant"</code> | <code>variable</code> | | | ||
**Example** | ||
```js | ||
// Full HD (1080p) | ||
const bitRate = estimateBitRate(1920, 1080, 30, "variable"); | ||
const bitRateMbps = bitRate * 1_000_000; // => 13 Mbps | ||
``` | ||
<a name="onStatusChangeCb"></a> | ||
## onStatusChangeCb(RecorderStatus) | ||
## onStatusChangeCb : <code>function</code> | ||
A callback to notify on the status change. To compare with RecorderStatus enum values. | ||
**Kind**: global function | ||
**Kind**: global typedef | ||
@@ -275,3 +547,3 @@ | Param | Type | Description | | ||
| [download] | <code>boolean</code> | <code>true</code> | Automatically download the recording when duration is reached or when `await canvasRecorder.stop()` is manually called. | | ||
| [extension] | <code>boolean</code> | <code>"mp4"</code> | Default file extension: infers which Encoder is selected. | | ||
| [extension] | <code>string</code> | <code>"\"mp4\""</code> | Default file extension: infers which Encoder is selected. | | ||
| [target] | <code>string</code> | <code>"\"in-browser\""</code> | Default writing target: in-browser or file-system when available. | | ||
@@ -287,3 +559,3 @@ | [encoder] | <code>object</code> | | A specific encoder. Default encoder based on options.extension: GIF > WebCodecs > H264MP4. | | ||
Options for recording. All optional. | ||
Options for recording initialisation. All optional. | ||
@@ -298,2 +570,160 @@ **Kind**: global typedef | ||
<a name="EncoderExtensions"></a> | ||
## EncoderExtensions : <code>"mp4"</code> \| <code>"webm"</code> \| <code>"png"</code> \| <code>"jpg"</code> \| <code>"gif"</code> \| <code>"mkv"</code> | ||
**Kind**: global typedef | ||
<a name="EncoderTarget"></a> | ||
## EncoderTarget : <code>"in-browser"</code> \| <code>"file-system"</code> | ||
**Kind**: global typedef | ||
<a name="FFmpegEncoderOptions"></a> | ||
## FFmpegEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ---------------- | ------------------------------------------------------------------------ | --------------- | | ||
| [encoderOptions] | [<code>FFmpegEncoderEncoderOptions</code>](#FFmpegEncoderEncoderOptions) | <code>{}</code> | | ||
<a name="FFmpegEncoderEncoderOptions"></a> | ||
## FFmpegEncoderEncoderOptions : <code>module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig</code> | ||
**Kind**: global typedef | ||
**See**: [FFmpeg#load](https://ffmpegwasm.netlify.app/docs/api/ffmpeg/classes/FFmpeg#load) | ||
<a name="GIFEncoderOptions"></a> | ||
## GIFEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ----------------- | -------------------------------------------------------------------- | ---------------- | | ||
| [maxColors] | <code>number</code> | <code>256</code> | | ||
| [quantizeOptions] | [<code>GIFEncoderQuantizeOptions</code>](#GIFEncoderQuantizeOptions) | | | ||
| [encoderOptions] | [<code>GIFEncoderEncoderOptions</code>](#GIFEncoderEncoderOptions) | <code>{}</code> | | ||
<a name="GIFEncoderQuantizeOptions"></a> | ||
## GIFEncoderQuantizeOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**See**: [QuantizeOptions](https://github.com/mattdesl/gifenc#palette--quantizergba-maxcolors-options--) | ||
**Properties** | ||
| Name | Type | Default | | ||
| --------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------- | | ||
| [format] | <code>"rgb565"</code> \| <code>"rgb444"</code> \| <code>"rgba4444"</code> | <code>"rgb565"</code> | | ||
| [oneBitAlpha] | <code>boolean</code> \| <code>number</code> | <code>false</code> | | ||
| [clearAlpha] | <code>boolean</code> | <code>true</code> | | ||
| [clearAlphaThreshold] | <code>number</code> | <code>0</code> | | ||
| [clearAlphaColor] | <code>number</code> | <code>0x00</code> | | ||
<a name="GIFEncoderEncoderOptions"></a> | ||
## GIFEncoderEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**See**: [WriteFrameOpts](https://github.com/mattdesl/gifenc#gifwriteframeindex-width-height-opts--) | ||
**Properties** | ||
| Name | Type | Default | | ||
| ------------------ | ----------------------------------------------- | ------------------ | | ||
| [palette] | <code>Array.<Array.<number>></code> | | | ||
| [first] | <code>boolean</code> | <code>false</code> | | ||
| [transparent] | <code>boolean</code> | <code>0</code> | | ||
| [transparentIndex] | <code>number</code> | <code>0</code> | | ||
| [delay] | <code>number</code> | <code>0</code> | | ||
| [repeat] | <code>number</code> | <code>0</code> | | ||
| [dispose] | <code>number</code> | <code>-1</code> | | ||
<a name="H264MP4EncoderOptions"></a> | ||
## H264MP4EncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ---------------- | -------------------------------------------------------------------------- | --------------- | | ||
| [debug] | <code>boolean</code> | | | ||
| [encoderOptions] | [<code>H264MP4EncoderEncoderOptions</code>](#H264MP4EncoderEncoderOptions) | <code>{}</code> | | ||
<a name="H264MP4EncoderEncoderOptions"></a> | ||
## H264MP4EncoderEncoderOptions : <code>module:h264-mp4-encoder~H264MP4Encoder</code> | ||
**Kind**: global typedef | ||
**See**: [h264-mp4-encoder#api](https://github.com/TrevorSundberg/h264-mp4-encoder#api) | ||
<a name="MP4WasmEncoderOptions"></a> | ||
## MP4WasmEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ----------------- | -------------------------------------------------------------------------- | --------------- | | ||
| [groupOfPictures] | <code>number</code> | <code>20</code> | | ||
| [flushFrequency] | <code>number</code> | <code>10</code> | | ||
| [encoderOptions] | [<code>MP4WasmEncoderEncoderOptions</code>](#MP4WasmEncoderEncoderOptions) | <code>{}</code> | | ||
<a name="MP4WasmEncoderEncoderOptions"></a> | ||
## MP4WasmEncoderEncoderOptions : <code>VideoEncoderConfig</code> | ||
**Kind**: global typedef | ||
**See**: [VideoEncoder.configure](https://developer.mozilla.org/en-US/docs/Web/API/VideoEncoder/configure#config) | ||
<a name="MediaCaptureEncoderOptions"></a> | ||
## MediaCaptureEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ---------------- | ------------------------------------------------------------------------------------ | --------------- | | ||
| [flushFrequency] | <code>number</code> | <code>10</code> | | ||
| [encoderOptions] | [<code>MediaCaptureEncoderEncoderOptions</code>](#MediaCaptureEncoderEncoderOptions) | <code>{}</code> | | ||
<a name="MediaCaptureEncoderEncoderOptions"></a> | ||
## MediaCaptureEncoderEncoderOptions : <code>MediaRecorderOptions</code> | ||
**Kind**: global typedef | ||
**See**: [MediaRecorder#options](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options) | ||
<a name="WebCodecsEncoderOptions"></a> | ||
## WebCodecsEncoderOptions : <code>object</code> | ||
**Kind**: global typedef | ||
**Properties** | ||
| Name | Type | Default | | ||
| ----------------- | ------------------------------------------------------------------------------ | --------------- | | ||
| [groupOfPictures] | <code>number</code> | <code>20</code> | | ||
| [flushFrequency] | <code>number</code> | <code>10</code> | | ||
| [encoderOptions] | [<code>WebCodecsEncoderEncoderOptions</code>](#WebCodecsEncoderEncoderOptions) | <code>{}</code> | | ||
<a name="WebCodecsEncoderEncoderOptions"></a> | ||
## WebCodecsEncoderEncoderOptions : <code>VideoEncoderConfig</code> | ||
**Kind**: global typedef | ||
**See**: [VideoEncoder.configure](https://developer.mozilla.org/en-US/docs/Web/API/VideoEncoder/configure#config) | ||
<a name="WebCodecsMuxerOptions"></a> | ||
## WebCodecsMuxerOptions : <code>MuxerOptions</code> | ||
**Kind**: global typedef | ||
**See** | ||
- [Mp4.MuxerOptions](https://github.com/Vanilagy/mp4-muxer/#usage) | ||
- [WebM.MuxerOptions](https://github.com/Vanilagy/webm-muxer/#usage) | ||
<!-- api-end --> | ||
@@ -300,0 +730,0 @@ |
@@ -0,4 +1,20 @@ | ||
/** | ||
* @typedef {"mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"} EncoderExtensions | ||
*/ | ||
/** | ||
* @typedef {"in-browser" | "file-system"} EncoderTarget | ||
*/ | ||
class Encoder { | ||
/** | ||
* The extension the encoder supports | ||
* @type {Extensions[]} | ||
*/ | ||
static supportedExtensions = ["mp4", "webm"]; | ||
static supportedTargets = ["in-browser"]; // or "file-system" | ||
/** | ||
* The target to download the file to. | ||
* @type {EncoderTarget[]} | ||
*/ | ||
static supportedTargets = ["in-browser"]; | ||
@@ -11,2 +27,12 @@ static defaultOptions = { | ||
/** | ||
* Base Encoder class. All Encoders extend it and its method are called by the Recorder. | ||
* @class Encoder | ||
* @param {object} options | ||
* | ||
* @property {EncoderTarget} target | ||
* @property {EncoderExtensions} extension | ||
* @property {object} [encoderOptions] | ||
* @property {object} [muxerOptions] | ||
*/ | ||
constructor(options) { | ||
@@ -16,2 +42,6 @@ Object.assign(this, options); | ||
/** | ||
* Setup the encoder: load binary, instantiate muxers, setup file system target... | ||
* @param {object} options | ||
*/ | ||
async init(options) { | ||
@@ -54,7 +84,17 @@ Object.assign(this, options); | ||
/** | ||
* Encode a single frame. The frameNumber is usually used for GOP (Group Of Pictures). | ||
* @param {number} frame | ||
* @param {number} frameNumber | ||
* @param {number} [frameNumber] | ||
*/ | ||
async encode() {} | ||
/** | ||
* Stop the encoding process and cleanup the temporary data. | ||
* @returns {(ArrayBuffer|Uint8Array|Blob[]|undefined)} | ||
*/ | ||
async stop() {} | ||
/** | ||
* Clean up the encoder | ||
*/ | ||
dispose() {} | ||
@@ -61,0 +101,0 @@ } |
@@ -1,2 +0,3 @@ | ||
import FFmpeg from "@ffmpeg/ffmpeg"; | ||
import { FFmpeg } from "@ffmpeg/ffmpeg"; | ||
import { fetchFile } from "@ffmpeg/util"; | ||
@@ -7,15 +8,31 @@ import Encoder from "./Encoder.js"; | ||
/** | ||
* @typedef {object} FFmpegEncoderOptions | ||
* @property {FFmpegEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {import("@ffmpeg/ffmpeg/dist/esm/types.js").FFMessageLoadConfig} FFmpegEncoderEncoderOptions | ||
* @see [FFmpeg#load]{@link https://ffmpegwasm.netlify.app/docs/api/ffmpeg/classes/FFmpeg#load} | ||
*/ | ||
class FFmpegEncoder extends Encoder { | ||
static supportedExtensions = ["mp4", "webm"]; | ||
/** | ||
* @param {FFmpegEncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
super(options); | ||
} | ||
async init(options) { | ||
super.init(options); | ||
this.encoder = FFmpeg.createFFmpeg({ | ||
corePath: "https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js", | ||
log: this.debug, | ||
...this.encoderOptions, | ||
this.encoder = new FFmpeg(); | ||
this.encoder.on("log", ({ message }) => { | ||
console.log(message); | ||
}); | ||
await this.encoder.load(); | ||
await this.encoder.load({ ...this.encoderOptions }); | ||
this.frameCount = 0; | ||
@@ -25,6 +42,5 @@ } | ||
async encode(frame, frameNumber) { | ||
this.encoder.FS( | ||
"writeFile", | ||
await this.encoder.writeFile( | ||
getFrameName(frameNumber), | ||
await FFmpeg.fetchFile(frame) | ||
await fetchFile(frame), | ||
); | ||
@@ -37,13 +53,14 @@ this.frameCount++; | ||
const codec = this.extension === "mp4" ? "libx264" : "libvpx"; | ||
await this.encoder.run( | ||
...`-framerate ${this.frameRate} -pattern_type glob -i *.png -s ${this.width}x${this.height} -pix_fmt yuv420p -c:v ${codec} ${outputFilename}`.split( | ||
" " | ||
) | ||
await this.encoder.exec( | ||
`-framerate ${this.frameRate} -pattern_type glob -i *.png -s ${this.width}x${this.height} -pix_fmt yuv420p -c:v ${codec} ${outputFilename}`.split( | ||
" ", | ||
), | ||
); | ||
const data = await this.encoder.FS("readFile", outputFilename); | ||
const data = await this.encoder.readFile(outputFilename); | ||
for (let i = 0; i < this.frameCount; i++) { | ||
try { | ||
this.encoder.FS("unlink", getFrameName(i)); | ||
this.encoder.deleteFile(getFrameName(i)); | ||
} catch (error) { | ||
@@ -55,3 +72,3 @@ console.error(error); | ||
try { | ||
this.encoder.FS("unlink", outputFilename); | ||
this.encoder.deleteFile(outputFilename); | ||
} catch (error) { | ||
@@ -64,4 +81,4 @@ console.error(error); | ||
dispose() { | ||
this.encoder.exit(); | ||
async dispose() { | ||
await this.encoder.terminate(); | ||
this.encoder = null; | ||
@@ -68,0 +85,0 @@ } |
@@ -5,2 +5,3 @@ import Encoder from "./Encoder.js"; | ||
/** @class */ | ||
class FrameEncoder extends Encoder { | ||
@@ -11,4 +12,4 @@ static supportedExtensions = ["png", "jpg"]; | ||
static defaultOptions = { | ||
extension: FrameEncoder.supportedExtensions[0], | ||
frameMethod: "blob", | ||
extension: FrameEncoder.supportedExtensions[0], | ||
}; | ||
@@ -27,3 +28,3 @@ | ||
this.directory, | ||
this.filename | ||
this.filename, | ||
); | ||
@@ -53,3 +54,3 @@ } | ||
`${`${frameNumber}`.padStart(5, "0")}.${this.extension}`, | ||
frame | ||
frame, | ||
); | ||
@@ -56,0 +57,0 @@ } |
import { GIFEncoder as GIFEnc, quantize, applyPalette } from "gifenc"; | ||
import Encoder from "./Encoder.js"; | ||
/** | ||
* @typedef {object} GIFEncoderOptions | ||
* @property {number} [maxColors=256] | ||
* @property {GIFEncoderQuantizeOptions} [quantizeOptions] | ||
* @property {GIFEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {object} GIFEncoderQuantizeOptions | ||
* @property {"rgb565" | "rgb444" | "rgba4444"} [format="rgb565"] | ||
* @property {boolean | number} [oneBitAlpha=false] | ||
* @property {boolean} [clearAlpha=true] | ||
* @property {number} [clearAlphaThreshold=0] | ||
* @property {number} [clearAlphaColor=0x00] | ||
* @see [QuantizeOptions]{@link https://github.com/mattdesl/gifenc#palette--quantizergba-maxcolors-options--} | ||
*/ | ||
/** | ||
* @typedef {object} GIFEncoderEncoderOptions | ||
* @property {number[][]} [palette] | ||
* @property {boolean} [first=false] | ||
* @property {boolean} [transparent=0] | ||
* @property {number} [transparentIndex=0] | ||
* @property {number} [delay=0] | ||
* @property {number} [repeat=0] | ||
* @property {number} [dispose=-1] | ||
* @see [WriteFrameOpts]{@link https://github.com/mattdesl/gifenc#gifwriteframeindex-width-height-opts--} | ||
*/ | ||
class GIFEncoder extends Encoder { | ||
@@ -20,2 +48,5 @@ static supportedExtensions = ["gif"]; | ||
/** | ||
* @param {GIFEncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
@@ -22,0 +53,0 @@ super({ ...GIFEncoder.defaultOptions, ...options }); |
@@ -1,7 +0,16 @@ | ||
import { createH264MP4Encoder } from "h264-mp4-encoder"; | ||
import HME from "h264-mp4-encoder"; | ||
import Encoder from "./Encoder.js"; | ||
import { nextMultiple } from "../utils.js"; | ||
import { estimateBitRate, nextMultiple } from "../utils.js"; | ||
/** | ||
* @typedef {object} H264MP4EncoderOptions | ||
* @property {boolean} [debug] | ||
* @property {H264MP4EncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {import("h264-mp4-encoder").H264MP4Encoder} H264MP4EncoderEncoderOptions | ||
* @see [h264-mp4-encoder#api]{@link https://github.com/TrevorSundberg/h264-mp4-encoder#api} | ||
*/ | ||
class H264MP4Encoder extends Encoder { | ||
@@ -15,2 +24,5 @@ static supportedExtensions = ["mp4"]; | ||
/** | ||
* @param {H264MP4EncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
@@ -23,3 +35,3 @@ super({ ...H264MP4Encoder.defaultOptions, ...options }); | ||
this.encoder = await createH264MP4Encoder(); | ||
this.encoder = await HME.createH264MP4Encoder(); | ||
@@ -31,3 +43,3 @@ Object.assign(this.encoder, { | ||
frameRate: this.frameRate, // 30 | ||
// kbps: 0, // The bitrate in kbps relative to the frame_rate. Overwrites quantization_parameter if not 0. | ||
kbps: estimateBitRate(this.width, this.height, this.frameRate) / 1000, // The bitrate in kbps relative to the frame_rate. Overwrites quantization_parameter if not 0. | ||
// speed: 0, // Speed where 0 means best quality and 10 means fastest speed [0..10]. | ||
@@ -34,0 +46,0 @@ // quantizationParameter: 33, // Higher means better compression, and lower means better quality [10..51]. |
@@ -5,2 +5,13 @@ import Encoder from "./Encoder.js"; | ||
/** | ||
* @typedef {object} MediaCaptureEncoderOptions | ||
* @property {number} [flushFrequency=10] | ||
* @property {MediaCaptureEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {MediaRecorderOptions} MediaCaptureEncoderEncoderOptions | ||
* @see [MediaRecorder#options]{@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options} | ||
*/ | ||
class MediaCaptureEncoder extends Encoder { | ||
@@ -11,6 +22,9 @@ static supportedExtensions = ["mkv", "webm"]; | ||
extension: MediaCaptureEncoder.supportedExtensions[0], | ||
frameMethod: "requestFrame", | ||
flushFrequency: 10, | ||
frameMethod: "requestFrame", | ||
}; | ||
/** | ||
* @param {MediaCaptureEncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
@@ -17,0 +31,0 @@ super({ ...MediaCaptureEncoder.defaultOptions, ...options }); |
import MP4Wasm from "./mp4.embed.js"; // mp4-wasm | ||
import Encoder from "./Encoder.js"; | ||
import { estimateBitRate } from "../utils.js"; | ||
/** | ||
* @typedef {object} MP4WasmEncoderOptions | ||
* @property {number} [groupOfPictures=20] | ||
* @property {number} [flushFrequency=10] | ||
* @property {MP4WasmEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {VideoEncoderConfig} MP4WasmEncoderEncoderOptions | ||
* @see [VideoEncoder.configure]{@link https://developer.mozilla.org/en-US/docs/Web/API/VideoEncoder/configure#config} | ||
*/ | ||
let mp4wasm; | ||
@@ -21,2 +33,5 @@ | ||
/** | ||
* @param {MP4WasmEncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
@@ -39,2 +54,8 @@ super({ ...MP4WasmEncoder.defaultOptions, ...options }); | ||
framerate: this.frameRate, | ||
bitrate: estimateBitRate( | ||
this.width, | ||
this.height, | ||
this.frameRate, | ||
this.encoderOptions.bitrateMode, | ||
), | ||
...this.encoderOptions, | ||
@@ -41,0 +62,0 @@ }, |
@@ -6,3 +6,20 @@ import * as MP4Muxer from "mp4-muxer"; | ||
import Encoder from "./Encoder.js"; | ||
import { estimateBitRate } from "../utils.js"; | ||
/** | ||
* @typedef {object} WebCodecsEncoderOptions | ||
* @property {number} [groupOfPictures=20] | ||
* @property {number} [flushFrequency=10] | ||
* @property {WebCodecsEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {VideoEncoderConfig} WebCodecsEncoderEncoderOptions | ||
* @see [VideoEncoder.configure]{@link https://developer.mozilla.org/en-US/docs/Web/API/VideoEncoder/configure#config} | ||
*/ | ||
/** | ||
* @typedef {MuxerOptions} WebCodecsMuxerOptions | ||
* @see [Mp4.MuxerOptions]{@link https://github.com/Vanilagy/mp4-muxer/#usage} | ||
* @see [WebM.MuxerOptions]{@link https://github.com/Vanilagy/webm-muxer/#usage} | ||
*/ | ||
class WebCodecsEncoder extends Encoder { | ||
@@ -22,2 +39,5 @@ static supportedExtensions = ["mp4", "webm", "mkv"]; | ||
/** | ||
* @param {WebCodecsEncoderOptions} [options] | ||
*/ | ||
constructor(options) { | ||
@@ -74,2 +94,3 @@ super({ ...WebCodecsEncoder.defaultOptions, ...options }); | ||
firstTimestampBehavior: "offset", // "strict" | "offset" | "permissive" | ||
fastStart: this.writableFileStream ? false : "in-memory", | ||
...this.muxerOptions, | ||
@@ -86,4 +107,10 @@ }); | ||
height: this.height, | ||
frameRate: this.frameRate, | ||
bitrate: 1e6, | ||
framerate: this.frameRate, | ||
bitrate: estimateBitRate( | ||
this.width, | ||
this.height, | ||
this.frameRate, | ||
this.encoderOptions.bitrateMode, | ||
), | ||
// bitrate: 1e6, | ||
// alpha: "discard", // "keep" | ||
@@ -101,4 +128,4 @@ // bitrateMode: "variable", // "constant" | ||
`canvas-record: Unsupported VideoEncoder config\n ${JSON.stringify( | ||
config | ||
)}` | ||
config, | ||
)}`, | ||
); | ||
@@ -105,0 +132,0 @@ } |
@@ -39,4 +39,3 @@ import canvasScreenshot from "canvas-screenshot"; | ||
* A callback to notify on the status change. To compare with RecorderStatus enum values. | ||
* @name onStatusChangeCb | ||
* @function | ||
* @callback onStatusChangeCb | ||
* @param {number} RecorderStatus the status | ||
@@ -51,3 +50,3 @@ */ | ||
* @property {boolean} [download=true] Automatically download the recording when duration is reached or when `await canvasRecorder.stop()` is manually called. | ||
* @property {boolean} [extension="mp4"] Default file extension: infers which Encoder is selected. | ||
* @property {string} [extension="mp4"] Default file extension: infers which Encoder is selected. | ||
* @property {string} [target="in-browser"] Default writing target: in-browser or file-system when available. | ||
@@ -61,3 +60,3 @@ * @property {object} [encoder] A specific encoder. Default encoder based on options.extension: GIF > WebCodecs > H264MP4. | ||
/** | ||
* @typedef {object} RecorderStartOptions Options for recording. All optional. | ||
* @typedef {object} RecorderStartOptions Options for recording initialisation. All optional. | ||
* @property {string} [filename] Overwrite the file name completely. | ||
@@ -67,7 +66,7 @@ * @property {boolean} [initOnly] Only initialised the recorder and don't call the first await recorder.step(). | ||
/** | ||
* Base Recorder class. | ||
* @property {boolean} [enabled=true] Enable/disable pointer interaction and drawing. | ||
*/ | ||
class Recorder { | ||
/** | ||
* Sensible defaults for recording so that the recorder "just works". | ||
* @type {RecorderOptions} | ||
*/ | ||
static defaultOptions = { | ||
@@ -83,2 +82,6 @@ name: "", | ||
/** | ||
* A mapping of extension to their mime types | ||
* @type {object} | ||
*/ | ||
static mimeTypes = { | ||
@@ -141,3 +144,3 @@ mkv: "video/x-matroska;codecs=avc1", | ||
const isExtensionSupported = CurrentEncoder.supportedExtensions.includes( | ||
this.extension | ||
this.extension, | ||
); | ||
@@ -150,3 +153,3 @@ const extension = isExtensionSupported | ||
console.warn( | ||
`canvas-record: unsupported extension for encoder "${CurrentEncoder.name}". Defaulting to "${extension}".` | ||
`canvas-record: unsupported extension for encoder "${CurrentEncoder.name}". Defaulting to "${extension}".`, | ||
); | ||
@@ -160,3 +163,3 @@ } | ||
let isTargetSupported = CurrentEncoder.supportedTargets.includes( | ||
this.target | ||
this.target, | ||
); | ||
@@ -174,3 +177,3 @@ | ||
console.warn( | ||
`canvas-record: unsupported target for encoder "${CurrentEncoder.name}". Defaulting to "${target}".` | ||
`canvas-record: unsupported target for encoder "${CurrentEncoder.name}". Defaulting to "${target}".`, | ||
); | ||
@@ -182,4 +185,6 @@ } | ||
/** | ||
* Create a Recorder instance | ||
* @class Recorder | ||
* @param {RenderingContext} context | ||
* @param {RecorderOptions} options | ||
* @param {RecorderOptions} [options={}] | ||
*/ | ||
@@ -244,3 +249,3 @@ constructor(context, options = {}) { | ||
* Start the recording by initializing and optionally calling the initial step. | ||
* @param {RecorderStartOptions} startOptions | ||
* @param {RecorderStartOptions} [startOptions={}] | ||
*/ | ||
@@ -293,3 +298,3 @@ async start(startOptions = {}) { | ||
this.context.UNSIGNED_BYTE, | ||
pixels | ||
pixels, | ||
); | ||
@@ -311,3 +316,3 @@ | ||
nextMultiple(this.width, 2), | ||
nextMultiple(this.height, 2) | ||
nextMultiple(this.height, 2), | ||
).data; | ||
@@ -336,3 +341,3 @@ } | ||
await this.getFrame(this.encoder.frameMethod), | ||
this.frame | ||
this.frame, | ||
); | ||
@@ -363,3 +368,3 @@ this.time += this.deltaTime; | ||
Array.isArray(buffer) ? buffer : [buffer], | ||
this.encoder.mimeType | ||
this.encoder.mimeType, | ||
); | ||
@@ -373,3 +378,3 @@ } | ||
/** | ||
* Clean up | ||
* Clean up the recorder and encoder | ||
*/ | ||
@@ -376,0 +381,0 @@ async dispose() { |
@@ -0,1 +1,5 @@ | ||
/** | ||
* Check for WebCodecs support on the current platform. | ||
* @type {boolean} | ||
*/ | ||
const isWebCodecsSupported = | ||
@@ -29,3 +33,3 @@ typeof window !== "undefined" && typeof window.VideoEncoder === "function"; | ||
return `${String(minutes).padStart(2, "0")}:${String( | ||
remainingSeconds | ||
remainingSeconds, | ||
).padStart(2, "0")}`; | ||
@@ -48,2 +52,37 @@ }; | ||
/** | ||
* Estimate the bit rate of a video rounded to nearest megabit. | ||
* Based on "H.264 for the rest of us" by Kush Amerasinghe. | ||
* | ||
* @example | ||
* ```js | ||
* // Full HD (1080p) | ||
* const bitRate = estimateBitRate(1920, 1080, 30, "variable"); | ||
* const bitRateMbps = bitRate * 1_000_000; // => 13 Mbps | ||
* ``` | ||
* | ||
* @param {number} width | ||
* @param {number} height | ||
* @param {number} frameRate | ||
* @param {number} motionRank A factor of 1, 2 or 4 | ||
* @param {"variable" | "constant"} bitrateMode | ||
* @returns {number} A bitrate value in bits per second | ||
*/ | ||
const estimateBitRate = ( | ||
width, | ||
height, | ||
frameRate = 30, | ||
motionRank = 4, | ||
bitrateMode = "variable", | ||
) => | ||
Math.round( | ||
(width * | ||
height * | ||
frameRate * | ||
motionRank * | ||
0.07 * | ||
(bitrateMode === "variable" ? 0.75 : 1)) / | ||
1_000_000, | ||
) * 1_000_000; | ||
export { | ||
@@ -56,2 +95,3 @@ isWebCodecsSupported, | ||
Deferred, | ||
estimateBitRate, | ||
}; |
export default Encoder; | ||
export type EncoderExtensions = "mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"; | ||
export type EncoderTarget = "in-browser" | "file-system"; | ||
/** | ||
* @typedef {"mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"} EncoderExtensions | ||
*/ | ||
/** | ||
* @typedef {"in-browser" | "file-system"} EncoderTarget | ||
*/ | ||
declare class Encoder { | ||
static supportedExtensions: string[]; | ||
static supportedTargets: string[]; | ||
/** | ||
* The extension the encoder supports | ||
* @type {Extensions[]} | ||
*/ | ||
static supportedExtensions: Extensions[]; | ||
/** | ||
* The target to download the file to. | ||
* @type {EncoderTarget[]} | ||
*/ | ||
static supportedTargets: EncoderTarget[]; | ||
static defaultOptions: { | ||
frameMethod: string; | ||
extension: string; | ||
target: string; | ||
extension: Extensions; | ||
target: EncoderTarget; | ||
}; | ||
constructor(options: any); | ||
init(options: any): Promise<void>; | ||
/** | ||
* Base Encoder class. All Encoders extend it and its method are called by the Recorder. | ||
* @class Encoder | ||
* @param {object} options | ||
* | ||
* @property {EncoderTarget} target | ||
* @property {EncoderExtensions} extension | ||
* @property {object} [encoderOptions] | ||
* @property {object} [muxerOptions] | ||
*/ | ||
constructor(options: object); | ||
/** | ||
* Setup the encoder: load binary, instantiate muxers, setup file system target... | ||
* @param {object} options | ||
*/ | ||
init(options: object): Promise<void>; | ||
getDirectory(): Promise<FileSystemDirectoryHandle>; | ||
@@ -17,8 +47,16 @@ getDirectoryHandle(directory: any, name: any): Promise<any>; | ||
/** | ||
* Encode a single frame. The frameNumber is usually used for GOP (Group Of Pictures). | ||
* @param {number} frame | ||
* @param {number} frameNumber | ||
* @param {number} [frameNumber] | ||
*/ | ||
encode(): Promise<void>; | ||
stop(): Promise<void>; | ||
/** | ||
* Stop the encoding process and cleanup the temporary data. | ||
* @returns {(ArrayBuffer|Uint8Array|Blob[]|undefined)} | ||
*/ | ||
stop(): (ArrayBuffer | Uint8Array | Blob[] | undefined); | ||
/** | ||
* Clean up the encoder | ||
*/ | ||
dispose(): void; | ||
} |
export default FFmpegEncoder; | ||
export type FFmpegEncoderOptions = { | ||
encoderOptions?: any; | ||
}; | ||
export type FFmpegEncoderEncoderOptions = any; | ||
/** | ||
* @typedef {object} FFmpegEncoderOptions | ||
* @property {FFmpegEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {import("@ffmpeg/ffmpeg/dist/esm/types.js").FFMessageLoadConfig} FFmpegEncoderEncoderOptions | ||
* @see [FFmpeg#load]{@link https://ffmpegwasm.netlify.app/docs/api/ffmpeg/classes/FFmpeg#load} | ||
*/ | ||
declare class FFmpegEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
/** | ||
* @param {FFmpegEncoderOptions} [options] | ||
*/ | ||
constructor(options?: FFmpegEncoderOptions); | ||
init(options: any): Promise<void>; | ||
encoder: any; | ||
@@ -7,3 +25,4 @@ frameCount: number; | ||
stop(): Promise<any>; | ||
dispose(): Promise<void>; | ||
} | ||
import Encoder from "./Encoder.js"; |
export default FrameEncoder; | ||
/** @class */ | ||
declare class FrameEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static supportedTargets: string[]; | ||
static defaultOptions: { | ||
extension: string; | ||
frameMethod: string; | ||
extension: string; | ||
}; | ||
constructor(options: any); | ||
init(options: any): Promise<void>; | ||
directoryHandle: any; | ||
@@ -8,0 +13,0 @@ writeFile(frameFileName: any, blob: any): Promise<void>; |
export default GIFEncoder; | ||
export type GIFEncoderOptions = { | ||
maxColors?: number; | ||
quantizeOptions?: GIFEncoderQuantizeOptions; | ||
encoderOptions?: GIFEncoderEncoderOptions; | ||
}; | ||
export type GIFEncoderQuantizeOptions = { | ||
format?: "rgb565" | "rgb444" | "rgba4444"; | ||
oneBitAlpha?: boolean | number; | ||
clearAlpha?: boolean; | ||
clearAlphaThreshold?: number; | ||
clearAlphaColor?: number; | ||
}; | ||
export type GIFEncoderEncoderOptions = { | ||
palette?: number[][]; | ||
first?: boolean; | ||
transparent?: boolean; | ||
transparentIndex?: number; | ||
delay?: number; | ||
repeat?: number; | ||
dispose?: number; | ||
}; | ||
/** | ||
* @typedef {object} GIFEncoderOptions | ||
* @property {number} [maxColors=256] | ||
* @property {GIFEncoderQuantizeOptions} [quantizeOptions] | ||
* @property {GIFEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {object} GIFEncoderQuantizeOptions | ||
* @property {"rgb565" | "rgb444" | "rgba4444"} [format="rgb565"] | ||
* @property {boolean | number} [oneBitAlpha=false] | ||
* @property {boolean} [clearAlpha=true] | ||
* @property {number} [clearAlphaThreshold=0] | ||
* @property {number} [clearAlphaColor=0x00] | ||
* @see [QuantizeOptions]{@link https://github.com/mattdesl/gifenc#palette--quantizergba-maxcolors-options--} | ||
*/ | ||
/** | ||
* @typedef {object} GIFEncoderEncoderOptions | ||
* @property {number[][]} [palette] | ||
* @property {boolean} [first=false] | ||
* @property {boolean} [transparent=0] | ||
* @property {number} [transparentIndex=0] | ||
* @property {number} [delay=0] | ||
* @property {number} [repeat=0] | ||
* @property {number} [dispose=-1] | ||
* @see [WriteFrameOpts]{@link https://github.com/mattdesl/gifenc#gifwriteframeindex-width-height-opts--} | ||
*/ | ||
declare class GIFEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static defaultOptions: { | ||
@@ -15,2 +63,7 @@ extension: string; | ||
}; | ||
/** | ||
* @param {GIFEncoderOptions} [options] | ||
*/ | ||
constructor(options?: GIFEncoderOptions); | ||
init(options: any): Promise<void>; | ||
encoder: any; | ||
@@ -17,0 +70,0 @@ start(): Promise<void>; |
export default H264MP4Encoder; | ||
export type H264MP4EncoderOptions = { | ||
debug?: boolean; | ||
encoderOptions?: any; | ||
}; | ||
export type H264MP4EncoderEncoderOptions = any; | ||
/** | ||
* @typedef {object} H264MP4EncoderOptions | ||
* @property {boolean} [debug] | ||
* @property {H264MP4EncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {import("h264-mp4-encoder").H264MP4Encoder} H264MP4EncoderEncoderOptions | ||
* @see [h264-mp4-encoder#api]{@link https://github.com/TrevorSundberg/h264-mp4-encoder#api} | ||
*/ | ||
declare class H264MP4Encoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static defaultOptions: { | ||
@@ -7,7 +22,12 @@ extension: string; | ||
}; | ||
encoder: import("h264-mp4-encoder").H264MP4Encoder; | ||
/** | ||
* @param {H264MP4EncoderOptions} [options] | ||
*/ | ||
constructor(options?: H264MP4EncoderOptions); | ||
init(options: any): Promise<void>; | ||
encoder: any; | ||
start(): Promise<void>; | ||
encode(frame: any): void; | ||
stop(): Uint8Array; | ||
stop(): any; | ||
} | ||
import Encoder from "./Encoder.js"; |
export default MediaCaptureEncoder; | ||
export type MediaCaptureEncoderOptions = { | ||
flushFrequency?: number; | ||
encoderOptions?: MediaCaptureEncoderEncoderOptions; | ||
}; | ||
export type MediaCaptureEncoderEncoderOptions = MediaRecorderOptions; | ||
/** | ||
* @typedef {object} MediaCaptureEncoderOptions | ||
* @property {number} [flushFrequency=10] | ||
* @property {MediaCaptureEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {MediaRecorderOptions} MediaCaptureEncoderEncoderOptions | ||
* @see [MediaRecorder#options]{@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options} | ||
*/ | ||
declare class MediaCaptureEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static defaultOptions: { | ||
extension: string; | ||
frameMethod: string; | ||
flushFrequency: number; | ||
frameMethod: string; | ||
}; | ||
/** | ||
* @param {MediaCaptureEncoderOptions} [options] | ||
*/ | ||
constructor(options?: MediaCaptureEncoderOptions); | ||
init(options: any): Promise<void>; | ||
chunks: any[]; | ||
@@ -9,0 +29,0 @@ stream: any; |
export default MP4WasmEncoder; | ||
export type MP4WasmEncoderOptions = { | ||
groupOfPictures?: number; | ||
flushFrequency?: number; | ||
encoderOptions?: MP4WasmEncoderEncoderOptions; | ||
}; | ||
export type MP4WasmEncoderEncoderOptions = VideoEncoderConfig; | ||
declare class MP4WasmEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static supportedTargets: string[]; | ||
static defaultOptions: { | ||
@@ -8,3 +16,8 @@ extension: string; | ||
}; | ||
/** | ||
* @param {MP4WasmEncoderOptions} [options] | ||
*/ | ||
constructor(options?: MP4WasmEncoderOptions); | ||
get frameMethod(): string; | ||
init(options: any): Promise<void>; | ||
encoder: any; | ||
@@ -11,0 +24,0 @@ encode(frame: any): Promise<void>; |
export default WebCodecsEncoder; | ||
export type WebCodecsEncoderOptions = { | ||
groupOfPictures?: number; | ||
flushFrequency?: number; | ||
encoderOptions?: WebCodecsEncoderEncoderOptions; | ||
}; | ||
export type WebCodecsEncoderEncoderOptions = VideoEncoderConfig; | ||
export type WebCodecsMuxerOptions = MuxerOptions; | ||
/** | ||
* @typedef {object} WebCodecsEncoderOptions | ||
* @property {number} [groupOfPictures=20] | ||
* @property {number} [flushFrequency=10] | ||
* @property {WebCodecsEncoderEncoderOptions} [encoderOptions={}] | ||
*/ | ||
/** | ||
* @typedef {VideoEncoderConfig} WebCodecsEncoderEncoderOptions | ||
* @see [VideoEncoder.configure]{@link https://developer.mozilla.org/en-US/docs/Web/API/VideoEncoder/configure#config} | ||
*/ | ||
/** | ||
* @typedef {MuxerOptions} WebCodecsMuxerOptions | ||
* @see [Mp4.MuxerOptions]{@link https://github.com/Vanilagy/mp4-muxer/#usage} | ||
* @see [WebM.MuxerOptions]{@link https://github.com/Vanilagy/webm-muxer/#usage} | ||
*/ | ||
declare class WebCodecsEncoder extends Encoder { | ||
static supportedExtensions: string[]; | ||
static supportedTargets: string[]; | ||
static defaultOptions: { | ||
@@ -8,5 +32,10 @@ extension: string; | ||
}; | ||
/** | ||
* @param {WebCodecsEncoderOptions} [options] | ||
*/ | ||
constructor(options?: WebCodecsEncoderOptions); | ||
get frameMethod(): string; | ||
init(options: any): Promise<void>; | ||
writableFileStream: any; | ||
muxer: MP4Muxer.Muxer<MP4Muxer.ArrayBufferTarget | MP4Muxer.StreamTarget | MP4Muxer.FileSystemWritableFileStreamTarget> | WebMMuxer.Muxer<MP4Muxer.ArrayBufferTarget | MP4Muxer.StreamTarget | MP4Muxer.FileSystemWritableFileStreamTarget>; | ||
muxer: any; | ||
encoder: VideoEncoder; | ||
@@ -18,3 +47,1 @@ encode(frame: any, number: any): Promise<void>; | ||
import Encoder from "./Encoder.js"; | ||
import * as MP4Muxer from "mp4-muxer"; | ||
import * as WebMMuxer from "webm-muxer"; |
/** | ||
* A callback to notify on the status change. To compare with RecorderStatus enum values. | ||
*/ | ||
export type onStatusChangeCb = (RecorderStatus: number) => any; | ||
/** | ||
* Options for recording. All optional. | ||
@@ -24,3 +28,3 @@ */ | ||
*/ | ||
extension?: boolean; | ||
extension?: string; | ||
/** | ||
@@ -45,3 +49,3 @@ * Default writing target: in-browser or file-system when available. | ||
/** | ||
* Options for recording. All optional. | ||
* Options for recording initialisation. All optional. | ||
*/ | ||
@@ -60,4 +64,3 @@ export type RecorderStartOptions = { | ||
* A callback to notify on the status change. To compare with RecorderStatus enum values. | ||
* @name onStatusChangeCb | ||
* @function | ||
* @callback onStatusChangeCb | ||
* @param {number} RecorderStatus the status | ||
@@ -71,3 +74,3 @@ */ | ||
* @property {boolean} [download=true] Automatically download the recording when duration is reached or when `await canvasRecorder.stop()` is manually called. | ||
* @property {boolean} [extension="mp4"] Default file extension: infers which Encoder is selected. | ||
* @property {string} [extension="mp4"] Default file extension: infers which Encoder is selected. | ||
* @property {string} [target="in-browser"] Default writing target: in-browser or file-system when available. | ||
@@ -80,29 +83,22 @@ * @property {object} [encoder] A specific encoder. Default encoder based on options.extension: GIF > WebCodecs > H264MP4. | ||
/** | ||
* @typedef {object} RecorderStartOptions Options for recording. All optional. | ||
* @typedef {object} RecorderStartOptions Options for recording initialisation. All optional. | ||
* @property {string} [filename] Overwrite the file name completely. | ||
* @property {boolean} [initOnly] Only initialised the recorder and don't call the first await recorder.step(). | ||
*/ | ||
/** | ||
* Base Recorder class. | ||
* @property {boolean} [enabled=true] Enable/disable pointer interaction and drawing. | ||
*/ | ||
export class Recorder { | ||
static defaultOptions: { | ||
name: string; | ||
duration: number; | ||
frameRate: number; | ||
download: boolean; | ||
extension: string; | ||
target: string; | ||
onStatusChange: () => void; | ||
}; | ||
static mimeTypes: { | ||
mkv: string; | ||
webm: string; | ||
mp4: string; | ||
gif: string; | ||
}; | ||
/** | ||
* Sensible defaults for recording so that the recorder "just works". | ||
* @type {RecorderOptions} | ||
*/ | ||
static defaultOptions: RecorderOptions; | ||
/** | ||
* A mapping of extension to their mime types | ||
* @type {object} | ||
*/ | ||
static mimeTypes: object; | ||
/** | ||
* Create a Recorder instance | ||
* @class Recorder | ||
* @param {RenderingContext} context | ||
* @param {RecorderOptions} options | ||
* @param {RecorderOptions} [options={}] | ||
*/ | ||
@@ -139,3 +135,3 @@ constructor(context: RenderingContext, options?: RecorderOptions); | ||
* Start the recording by initializing and optionally calling the initial step. | ||
* @param {RecorderStartOptions} startOptions | ||
* @param {RecorderStartOptions} [startOptions={}] | ||
*/ | ||
@@ -161,3 +157,3 @@ start(startOptions?: RecorderStartOptions): Promise<void>; | ||
/** | ||
* Clean up | ||
* Clean up the recorder and encoder | ||
*/ | ||
@@ -164,0 +160,0 @@ dispose(): Promise<void>; |
@@ -0,1 +1,5 @@ | ||
/** | ||
* Check for WebCodecs support on the current platform. | ||
* @type {boolean} | ||
*/ | ||
export const isWebCodecsSupported: boolean; | ||
@@ -11,1 +15,20 @@ export function downloadBlob(filename: any, blobPart: any, mimeType: any): void; | ||
} | ||
/** | ||
* Estimate the bit rate of a video rounded to nearest megabit. | ||
* Based on "H.264 for the rest of us" by Kush Amerasinghe. | ||
* | ||
* @example | ||
* ```js | ||
* // Full HD (1080p) | ||
* const bitRate = estimateBitRate(1920, 1080, 30, "variable"); | ||
* const bitRateMbps = bitRate * 1_000_000; // => 13 Mbps | ||
* ``` | ||
* | ||
* @param {number} width | ||
* @param {number} height | ||
* @param {number} frameRate | ||
* @param {number} motionRank A factor of 1, 2 or 4 | ||
* @param {"variable" | "constant"} bitrateMode | ||
* @returns {number} A bitrate value in bits per second | ||
*/ | ||
export function estimateBitRate(width: number, height: number, frameRate?: number, motionRank?: number, bitrateMode?: "variable" | "constant"): number; |
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
189140
33
3420
730
10
6
1
+ Added@ffmpeg/util@^0.12.1
+ Added@ffmpeg/core@0.12.6(transitive)
+ Added@ffmpeg/core-mt@0.12.6(transitive)
+ Added@ffmpeg/ffmpeg@0.12.10(transitive)
+ Added@ffmpeg/types@0.12.2(transitive)
+ Added@ffmpeg/util@0.12.1(transitive)
+ Addedmp4-muxer@3.0.5(transitive)
- Removed@ffmpeg/ffmpeg@0.11.6(transitive)
- Removedis-url@1.2.4(transitive)
- Removedmp4-muxer@2.3.0(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedregenerator-runtime@0.13.11(transitive)
- Removedresolve-url@0.2.1(transitive)
- Removedtr46@0.0.3(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
Updated@ffmpeg/ffmpeg@^0.12.7
Updatedmp4-muxer@^3.0.0
Updatedwebm-muxer@^3.1.3