dmnsgn / canvas-record

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.
https://dmnsgn.github.io/canvas-record/
MIT License
326 stars 18 forks source link
canvas capture download filesystem gif mp4 record video webcodecs

canvas-record

npm version stability-stable npm minzipped size dependencies types Conventional Commits styled with prettier linted with eslint license

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.

paypal coinbase twitter

Installation

npm install canvas-record

Usage

import { Recorder, RecorderStatus, Encoders } from "canvas-record";
import createCanvasContext from "canvas-context";
import { AVC } from "media-codecs";

// Setup
const pixelRatio = devicePixelRatio;
const width = 512;
const height = 512;
const { context, canvas } = createCanvasContext("2d", {
  width: width * pixelRatio,
  height: height * pixelRatio,
  contextAttributes: { willReadFrequently: true },
});
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` });

const mainElement = document.querySelector("main");
mainElement.appendChild(canvas);

// Animation
let canvasRecorder;

function render() {
  const width = canvas.width;
  const height = canvas.height;

  const t = canvasRecorder.frame / canvasRecorder.frameTotal || Number.EPSILON;

  context.clearRect(0, 0, width, height);
  context.fillStyle = "red";
  context.fillRect(0, 0, t * width, height);
}

const tick = async () => {
  render();

  if (canvasRecorder.status !== RecorderStatus.Recording) return;
  await canvasRecorder.step();

  if (canvasRecorder.status !== RecorderStatus.Stopped) {
    requestAnimationFrame(() => tick());
  }
};

canvasRecorder = new Recorder(context, {
  name: "canvas-record-example",
  encoderOptions: {
    codec: AVC.getCodec({ profile: "Main", level: "5.2" }),
  },
});

// Start and encode frame 0
await canvasRecorder.start();

// Animate to encode the rest
tick(canvasRecorder);

API

Encoder comparison:

Encoder Extension Required Web API WASM Speed
WebCodecs mp4 / webm / mkv WebCodecs Fast
MP4Wasm mp4 WebCodecs ✅ (embed) Fast
H264MP4 mp4 ✅ (embed) Medium
FFmpeg mp4 / webm SharedArrayBuffer ✅ (need binary path) Slow
GIF gif WebWorkers (wip) Fast
Frame png / jpg File System Access Fast
MediaCapture mkv / webm MediaStream Realtime

Note:

Roadmap:

Modules

canvas-record

Re-export Recorder, RecorderStatus, all Encoders and utils.

Classes

Recorder
Encoder
FFmpegEncoder
FrameEncoder
GIFEncoder
H264MP4Encoder
MediaCaptureEncoder
MP4WasmEncoder
WebCodecsEncoder

Constants

isWebCodecsSupported : boolean

Check for WebCodecs support on the current platform.

Functions

estimateBitRate(width, height, frameRate, motionRank, bitrateMode)number

Estimate the bit rate of a video rounded to nearest megabit. Based on "H.264 for the rest of us" by Kush Amerasinghe.

Typedefs

onStatusChangeCb : function

A callback to notify on the status change. To compare with RecorderStatus enum values.

RecorderOptions : object

Options for recording. All optional.

RecorderStartOptions : object

Options for recording initialisation. All optional.

EncoderExtensions : "mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"
EncoderTarget : "in-browser" | "file-system"
FFmpegEncoderOptions : object
FFmpegEncoderEncoderOptions : module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig
GIFEncoderOptions : object
GIFEncoderQuantizeOptions : object
GIFEncoderEncoderOptions : object
H264MP4EncoderOptions : object
H264MP4EncoderEncoderOptions : module:h264-mp4-encoder~H264MP4Encoder
MediaCaptureEncoderOptions : object
MediaCaptureEncoderEncoderOptions : MediaRecorderOptions
MP4WasmEncoderOptions : object
MP4WasmEncoderEncoderOptions : VideoEncoderConfig
WebCodecsEncoderOptions : object
WebCodecsEncoderEncoderOptions : VideoEncoderConfig
WebCodecsMuxerOptions : MuxerOptions

canvas-record

Re-export Recorder, RecorderStatus, all Encoders and utils.

Recorder

Kind: global class

new Recorder(context, [options])

Create a Recorder instance

Param Type Default
context RenderingContext
[options] RecorderOptions {}

recorder.defaultOptions : RecorderOptions

Sensible defaults for recording so that the recorder "just works".

Kind: instance property of Recorder

recorder.mimeTypes : object

A mapping of extension to their mime types

Kind: instance property of Recorder

recorder.start([startOptions])

Start the recording by initializing and optionally calling the initial step.

Kind: instance method of Recorder

Param Type Default
[startOptions] RecorderStartOptions {}

recorder.step()

Encode a frame and increment the time and the playhead. Calls await canvasRecorder.stop() when duration is reached.

Kind: instance method of Recorder

recorder.stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined

Stop the recording and return the recorded buffer. If options.download is set, automatically start downloading the resulting file. Is called when duration is reached or manually.

Kind: instance method of Recorder

recorder.dispose()

Clean up the recorder and encoder

Kind: instance method of Recorder

Encoder

Kind: global class Properties

Name Type
target EncoderTarget
extension EncoderExtensions
[encoderOptions] object
[muxerOptions] object

new Encoder(options)

Base Encoder class. All Encoders extend it and its method are called by the Recorder.

Param Type
options object

encoder.supportedExtensions : Array.<Extensions>

The extension the encoder supports

Kind: instance property of Encoder

encoder.supportedTargets : Array.<EncoderTarget>

The target to download the file to.

Kind: instance property of Encoder

encoder.init(options)

Setup the encoder: load binary, instantiate muxers, setup file system target...

Kind: instance method of Encoder

Param Type
options object

encoder.encode(frame, [frameNumber])

Encode a single frame. The frameNumber is usually used for GOP (Group Of Pictures).

Kind: instance method of Encoder

Param Type
frame number
[frameNumber] number

encoder.stop() ⇒ ArrayBuffer | Uint8Array | Array.<Blob> | undefined

Stop the encoding process and cleanup the temporary data.

Kind: instance method of Encoder

encoder.dispose()

Clean up the encoder

Kind: instance method of Encoder

FFmpegEncoder

Kind: global class

new FFmpegEncoder([options])

Param Type
[options] FFmpegEncoderOptions

FrameEncoder

Kind: global class

GIFEncoder

Kind: global class

new GIFEncoder([options])

Param Type
[options] GIFEncoderOptions

H264MP4Encoder

Kind: global class

new H264MP4Encoder([options])

Param Type
[options] H264MP4EncoderOptions

MediaCaptureEncoder

Kind: global class

new MediaCaptureEncoder([options])

Param Type
[options] MediaCaptureEncoderOptions

MP4WasmEncoder

Kind: global class

new MP4WasmEncoder([options])

Param Type
[options] MP4WasmEncoderOptions

WebCodecsEncoder

Kind: global class

new WebCodecsEncoder([options])

Param Type
[options] WebCodecsEncoderOptions

RecorderStatus : enum

Enum for recorder status

Kind: global enum Read only: true Example

// Check recorder status before continuing
if (canvasRecorder.status !== RecorderStatus.Stopped) {
  rAFId = requestAnimationFrame(() => tick());
}

isWebCodecsSupported : boolean

Check for WebCodecs support on the current platform.

Kind: global constant

estimateBitRate(width, height, frameRate, motionRank, bitrateMode) ⇒ number

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: number - A bitrate value in bits per second

Param Type Default Description
width number
height number
frameRate number 30
motionRank number 4 A factor of 1, 2 or 4
bitrateMode "variable" | "constant" variable

Example

// Full HD (1080p)
const bitRate = estimateBitRate(1920, 1080, 30, "variable");
const bitRateMbps = bitRate * 1_000_000; // => 13 Mbps

onStatusChangeCb : function

A callback to notify on the status change. To compare with RecorderStatus enum values.

Kind: global typedef

Param Type Description
RecorderStatus number the status

RecorderOptions : object

Options for recording. All optional.

Kind: global typedef Properties

Name Type Default Description
[name] string "\"\"" A name for the recorder, used as prefix for the default file name.
[duration] number 10 The recording duration in seconds. If set to Infinity, await canvasRecorder.stop() needs to be called manually.
[frameRate] number 30 The frame rate in frame per seconds. Use await canvasRecorder.step(); to go to the next frame.
[download] boolean true Automatically download the recording when duration is reached or when await canvasRecorder.stop() is manually called.
[extension] string "\"mp4\"" Default file extension: infers which Encoder is selected.
[target] string "\"in-browser\"" Default writing target: in-browser or file-system when available.
[encoder] object A specific encoder. Default encoder based on options.extension: GIF > WebCodecs > H264MP4.
[encoderOptions] object See src/encoders or individual packages for a list of options.
[muxerOptions] object See "mp4-muxer" and "webm-muxer" for a list of options.
[onStatusChange] onStatusChangeCb

RecorderStartOptions : object

Options for recording initialisation. All optional.

Kind: global typedef Properties

Name Type Description
[filename] string Overwrite the file name completely.
[initOnly] boolean Only initialised the recorder and don't call the first await recorder.step().

EncoderExtensions : "mp4" | "webm" | "png" | "jpg" | "gif" | "mkv"

Kind: global typedef

EncoderTarget : "in-browser" | "file-system"

Kind: global typedef

FFmpegEncoderOptions : object

Kind: global typedef Properties

Name Type Default
[encoderOptions] FFmpegEncoderEncoderOptions {}

FFmpegEncoderEncoderOptions : code>module:@ffmpeg/ffmpeg/dist/esm/types.js~FFMessageLoadConfig</code

Kind: global typedef See: FFmpeg#load

GIFEncoderOptions : object

Kind: global typedef Properties

Name Type Default
[maxColors] number 256
[quantizeOptions] GIFEncoderQuantizeOptions
[encoderOptions] GIFEncoderEncoderOptions {}

GIFEncoderQuantizeOptions : object

Kind: global typedef See: QuantizeOptions Properties

Name Type Default
[format] "rgb565" | "rgb444" | "rgba4444" "rgb565"
[oneBitAlpha] boolean | number false
[clearAlpha] boolean true
[clearAlphaThreshold] number 0
[clearAlphaColor] number 0x00

GIFEncoderEncoderOptions : object

Kind: global typedef See: WriteFrameOpts Properties

Name Type Default
[palette] Array.<Array.<number>>
[first] boolean false
[transparent] boolean 0
[transparentIndex] number 0
[delay] number 0
[repeat] number 0
[dispose] number -1

H264MP4EncoderOptions : object

Kind: global typedef Properties

Name Type Default
[debug] boolean
[encoderOptions] H264MP4EncoderEncoderOptions {}

H264MP4EncoderEncoderOptions : module:h264-mp4-encoder~H264MP4Encoder

Kind: global typedef See: h264-mp4-encoder#api

MediaCaptureEncoderOptions : object

Kind: global typedef Properties

Name Type Default
[flushFrequency] number 10
[encoderOptions] MediaCaptureEncoderEncoderOptions {}

MediaCaptureEncoderEncoderOptions : MediaRecorderOptions

Kind: global typedef See: MediaRecorder#options

MP4WasmEncoderOptions : object

Kind: global typedef Properties

Name Type Default
[groupOfPictures] number 20
[flushFrequency] number 10
[encoderOptions] MP4WasmEncoderEncoderOptions {}

MP4WasmEncoderEncoderOptions : VideoEncoderConfig

Kind: global typedef See: VideoEncoder.configure

WebCodecsEncoderOptions : object

Kind: global typedef Properties

Name Type Default
[groupOfPictures] number 20
[flushFrequency] number 10
[encoderOptions] WebCodecsEncoderEncoderOptions {}

WebCodecsEncoderEncoderOptions : VideoEncoderConfig

Kind: global typedef See: VideoEncoder.configure

WebCodecsMuxerOptions : MuxerOptions

Kind: global typedef See

License

All MIT:

MIT. See license file.