@bendyline/squisq-video

中文文档
2.4.20发布于 2026/11/08MITTypeScriptES Module构建来源已验证近 30 天下载 6537

Cross-runtime video and animated-GIF helpers with browser-based ffmpeg.wasm encoding for Squisq documents

原始使用文档
文档目录11 节
  1. Install
  2. What's Inside
  3. Quick Start
  4. Generate Render HTML
  5. Encode Frames to MP4
  6. ffmpeg.wasm runtime assets
  7. Schedule a Doc's Audio
  8. Quality Presets
  9. Orientation Dimensions
  10. Related Packages
  11. License

@bendyline/squisq-video

Video and animated-GIF rendering foundation for Squisq documents. Its pure timeline, quality, palette, and render-HTML helpers work in Node.js and browsers; framesToMp4Wasm encodes frames in browser runtimes without native dependencies.

Part of the Squisq monorepo.

npm MIT License

Install

npm install @bendyline/squisq-video

What's Inside

ExportDescription
generateRenderHtml(doc, options)Self-contained HTML page with embedded media for headless frame capture
framesToMp4Wasm(frames, audio, options)Encode PNG frames (+ optional audio) to MP4 via ffmpeg.wasm
computeAudioTimeline(doc, coverPreRoll?)Flatten a doc's narration + timed media into absolute-timed audio clips (browser export and CLI mix share this)
bitrateForQuality(quality, width, height)Target H.264 bitrate (w*h*bitsPerPixel) — the single source of truth for WebCodecs bitrate
ffmpegVideoQualityArgs, audioBitrateArg, ffmpegAudioMuxArgsShared FFmpeg flags; audio muxing pads short narration so it cannot truncate the video
ffmpegGifFilterGraph, ffmpegGifOutputArgs, ffmpegGifPalette* helpersShared global-palette GIF filters, including bounded two-pass palette generation/application
QUALITY_PRESETS, ORIENTATION_DIMENSIONS, resolveDimensionsQuality/dimension presets and helpers
validateVideoExportOptions(options)Fail-fast runtime validation for FPS, dimensions, quality, and orientation
VideoExportOptions, VideoQuality, VideoOrientation, GifDither, QualityPreset, EncoderResultShared video and GIF types
fetchFileRe-export of @ffmpeg/util's fetchFile for preparing audio bytes

This package is the shared foundation under @bendyline/squisq-video-react (in-browser export UI) and @bendyline/squisq-cli (squisq video, which pairs the render HTML with Playwright capture and native ffmpeg encoding).

Quick Start

Generate Render HTML

Create a self-contained HTML page that mounts the standalone Squisq player in render mode, with all images and audio embedded as base64 data URIs. A headless browser (Playwright, Puppeteer) obtains the player-specific handle with SquisqPlayer.getHandle(root) and uses its render API to step through frames and screenshot each one:

Render methods are deliberately instance-scoped; generated pages do not expose legacy top-level window.seekTo or window.getDuration functions.

import { generateRenderHtml } from '@bendyline/squisq-video';
import { PLAYER_BUNDLE } from '@bendyline/squisq-react/standalone-source';

const html = generateRenderHtml(doc, {
  playerScript: PLAYER_BUNDLE, // the IIFE player bundle source string
  images, // Map<string, ArrayBuffer> — embedded as data URIs
  audio, // Map<string, ArrayBuffer> — embedded as data URIs
  width: 1920, // default 1920
  height: 1080, // default 1080
  captionStyle: 'standard', // 'standard' | 'social' — omit for no captions
});

Encode Frames to MP4

import { framesToMp4Wasm } from '@bendyline/squisq-video';

const { data, duration } = await framesToMp4Wasm(
  frames, // Uint8Array[] — PNG frame bytes, in order
  audioBytes, // Uint8Array | null — optional WAV/MP3/AAC track (muxed as AAC)
  {
    fps: 30, // default 30
    quality: 'normal', // 'draft' | 'normal' | 'high' (default 'normal')
    orientation: 'landscape', // 'landscape' | 'portrait' (default 'landscape')
    // width / height override the orientation defaults (both must be EVEN)
    // REQUIRED — see "ffmpeg.wasm runtime assets" below:
    ffmpegWasm: {
      coreURL: '/vendor/ffmpeg-core.js',
      wasmURL: '/vendor/ffmpeg-core.wasm',
    },
    onProgress: (percent, phase) => console.log(`${phase}: ${percent}%`),
  },
);
// data: Uint8Array of MP4 bytes; duration: seconds (frames.length / fps)

Encoding is H.264 (libx264, yuv420p) with optional AAC audio; frames are scaled/padded to the target dimensions preserving aspect ratio. ffmpeg.wasm needs SharedArrayBuffer, which normally means serving COOP/COEP headers. framesToMp4Wasm is browser-only; Node callers can use framesToMp4Native or framesToMp4NativeBytes from @bendyline/squisq-cli/api.

Dimensions must be even. validateVideoExportOptions (called by resolveDimensions, and therefore by every render path) rejects an odd width/height up front rather than rounding: H.264's yuv420p chroma subsampling cannot represent odd dimensions, and silently shipping a size the caller never asked for breaks aspect-ratio-sensitive pipelines. The rule is applied uniformly to MP4 and GIF so it does not depend on output format or runtime.

ffmpeg.wasm runtime assets

ffmpegWasm.coreURL is required for every ffmpeg.wasm code path (framesToMp4Wasm, plus GIF transcode and audio muxing in @bendyline/squisq-video-react). Omitting it throws an actionable error.

This is deliberate. @ffmpeg/ffmpeg's own load() falls back to a hard-coded https://unpkg.com/@ffmpeg/core@<version>/… URL, which would make this library fetch and execute unpinned third-party code at runtime — an opaque failure on offline/CSP-restricted hosts and a supply-chain surface for every consumer. Squisq never triggers that fallback.

Host wiring: install @ffmpeg/core, publish its assets at a same-origin path, and point ffmpegWasm at them.

import type { FfmpegWasmLoadConfig } from '@bendyline/squisq-video';

// Copy node_modules/@ffmpeg/core/dist/esm/ffmpeg-core.{js,wasm}
// into your app's static assets, then:
export const FFMPEG_WASM: FfmpegWasmLoadConfig = {
  coreURL: '/ffmpeg-core/ffmpeg-core.js',
  wasmURL: '/ffmpeg-core/ffmpeg-core.wasm',
};

packages/site/vite.config.ts (the ffmpegCorePlugin) plus packages/site/src/ffmpegWasmConfig.ts are a complete worked example for Vite, including the GPL notice that must travel with the core files. To deliberately accept a remote core, pass that URL as coreURL explicitly — the requirement is that the choice is yours, not a silent default.

Schedule a Doc's Audio

computeAudioTimeline(doc, coverPreRoll?) turns a doc's narration segments and timed media clips into a flat list of absolute-timed AudioTimelineClips. It's pure and Node-testable, and is the single source of truth both the browser MP4 export and the CLI mix path use to place audio (so the two never drift). Narration segments are laid sequentially; timed media clips are placed at their absolute positions; every start is shifted by coverPreRoll (default 0) to stay in sync with a silent cover pre-roll.

import { computeAudioTimeline } from '@bendyline/squisq-video';

const clips = computeAudioTimeline(doc, 2); // [{ src, startSec, sourceInSec, durationSec }, …]

Quality Presets

Each QualityPreset also carries bitsPerPixel (for WebCodecs bitrate targeting via bitrateForQuality) and audioBitrate (target AAC bits/sec):

PresetFFmpeg presetCRFbits/pixelAAC bitrateUse Case
draftultrafast28296 kbpsQuick previews
normalmedium234128 kbpsGeneral-purpose export
highslow188192 kbpsFinal output

Orientation Dimensions

OrientationWidthHeight
landscape19201080
portrait10801920

resolveDimensions(options) applies these defaults, honoring explicit width/height overrides.

See the full API Reference for all types.

PackageDescription
@bendyline/squisqHeadless core — schemas, templates, markdown
@bendyline/squisq-reactReact components + standalone player bundle
@bendyline/squisq-video-reactReact UI for in-browser video export
@bendyline/squisq-cliCLI for document conversion and video rendering

License

MIT

版本与发布记录

@bendyline/squisq-video 共发布 75 个版本,首次发布于 2026/03/26;过去 12 个月发布了 75 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.4.20latest2026/11/08今天
2.4.192026/11/07今天
2.4.182026/11/052 天前
2.4.172026/11/053 天前
2.4.162026/11/017 天前
2.4.152026/10/308 天前

关于 @bendyline/squisq-video 的常见问题

@bendyline/squisq-video 是什么?

Cross-runtime video and animated-GIF helpers with browser-based ffmpeg.wasm encoding for Squisq documents

如何安装 @bendyline/squisq-video?

在项目目录运行 npm install @bendyline/squisq-video,或使用 pnpm add / yarn add / bun add。

@bendyline/squisq-video 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bendyline/squisq-video 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bendyline/squisq-video 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^22.22.2 || ^24.15.0 || >=26.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bendyline/squisq-video 可以商用吗?

@bendyline/squisq-video 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bendyline/squisq-video 的下载量有多少?

根据 npm 官方下载统计,@bendyline/squisq-video 在 2026/10/08 – 2026/11/06 这 30 天内被下载 6,537 次,日均约 218 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bendyline/squisq-video 还在维护吗?

最新版本 2.4.20 发布于 2026/11/08(今天);至今共发布 75 个版本,其中过去 12 个月发布了 75 个;首次发布于 2026/03/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。