Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
web-video-creator
Advanced tools
A framework for creating videos based on Node.js + Puppeteer + FFmpeg.
WebVideoCreator(简称WVC)是一个将Web动画渲染为视频的框架,基于 Node.js + Puppeteer + Chrome + FFmpeg 实现,它执行确定性的渲染,准确的以目标帧率捕获任何可在HTML5播放动画(CSS3动画/SVG动画/Lottie动画/GIF动画/APNG动画/WEBP动画)以及任何基于时间轴使用RAF驱动的动画(anime.js是一个不错的选择 :D),当然您也可以调皮的使用setInterval或者setTimeout来控制动画,支持嵌入mp4和透明webm视频,还支持转场合成、音频合成与字体加载等功能。让我们快速开始。
WVC为您酷炫的动画页面创造了一个虚拟时间环境(也许可以想象成是一个《楚门的世界》),它的主要职责是将一个 不确定性渲染的环境 转化到 确定性渲染的环境。
这一切的前提由Chrome提供的确定性渲染模式和无头实验API支持:HeadlessExperimental.beginFrame
答疑交流QQ群:752693580
我们还缺少动画设计师,不过还是从开放的平台中使用WVC捕获渲染了一些优秀的动画Demo。
在这里查看所有DEMO:渲染示例页面
理论上所有的Web动画/图形库都能够在WVC环境正常运行,以下仅列出我已验证可用的库:
Anime.js / GSAP / D3.js / Three.js / Echart / Lottie-Web / PixiJS / Animate.css / Mo.js / Tween.js
需要注意的是,如果您手动使用RAF驱动动画,请确保从回调中接收timestamp参数设置动画的进度到该时间点,否则可能出现帧率不同步。
# 从NPM安装WebVideoCreator
npm i web-video-creator
如遇到ffmpeg-static下载失败,请先设置环境变量:FFMPEG_BINARIES_URL=https://cdn.npmmirror.com/binaries/ffmpeg-static
WVC需要从Web页面中捕获动画,您可以在本地创建一个临时的Web服务器来提供静态页面服务,方便接下来的测试,使用live-server是最简单的方式之一,如果您已经有静态页面可跳过这个步骤。
# 从NPM全局安装live-server
npm i -g live-server
# 启用Web服务
live-server
创建一个测试页面到Web服务根路径,以下html内容展示一个自动旋转的三角形svg动画。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>测试页面</title>
</head>
<body>
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon points="60,30 90,90 30,90">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 60 70" to="360 60 70"
dur="10s" repeatCount="indefinite" />
</polygon>
</svg>
</body>
</html>
import WebVideoCreator, { VIDEO_ENCODER, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// 配置WVC
wvc.config({
// 根据您的硬件设备选择适合的编码器,这里采用的是Nvidia显卡的h264_nvenc编码器
// 编码器选择可参考 docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// 创建单幕视频
const video = wvc.createSingleVideo({
// 需要渲染的页面地址
url: "http://localhost:8080/test.html",
// 或者可以直接设置页面内容
// content: "<h1>Hello WebVideoCreator</h1>",
// 视频宽度
width: 1280,
// 视频高度
height: 720,
// 视频帧率
fps: 30,
// 视频时长
duration: 10000,
// 视频输出路径
outputPath: "./test.mp4",
// 是否在cli显示进度条,默认是不显示
showProgress: true
});
// 监听合成完成事件
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// 启动合成
video.start();
import WebVideoCreator, { VIDEO_ENCODER, TRANSITION, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// 配置WVC
wvc.config({
// 根据您的硬件设备选择适合的编码器,这里采用的是Nvidia显卡的h264_nvenc编码器
// 编码器选择可参考 docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// 创建多幕视频
const video = wvc.createMultiVideo({
// 视频宽度
width: 1280,
// 视频高度
height: 720,
// 视频帧率
fps: 30,
// 视频段参数
chunks: [
{
url: "http://localhost:8080/scene-1.html",
// 或者可以直接设置页面内容
// content: "<h1>Hello WebVideoCreator</h1>",
duration: 10000,
// 在第一和第二幕之间插入转场
transition: TRANSITION.CIRCLE_CROP
},
{
url: "http://localhost:8080/scene-2.html",
duration: 10000
}
],
// 视频输出路径
outputPath: "./test.mp4",
// 是否在cli显示进度条,默认是不显示
showProgress: true
});
// 监听合成完成事件
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// 启动合成
video.start();
import WebVideoCreator, { VIDEO_ENCODER, TRANSITION, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// 配置WVC
wvc.config({
// 根据您的硬件设备选择适合的编码器,这里采用的是Nvidia显卡的h264_nvenc编码器
// 编码器选择可参考 docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// 创建分块视频1
const chunk1 = wvc.createChunkVideo({
url: "http://localhost:8080/scene-1.html",
// 或者可以直接设置页面内容
// content: "<h1>Hello WebVideoCreator</h1>",
width: 1280,
height: 720,
fps: 30,
duration: 10000,
showProgress: true
});
// 创建分块视频2
const chunk2 = wvc.createChunkVideo({
url: "http://localhost:8080/scene-2.html",
width: 1280,
height: 720,
fps: 30,
duration: 10000,
showProgress: true
});
// 等待分块们渲染完成
await Promise.all([chunk1.startAndWait(), chunk2.startAndWait()]);
// 设置chunk1和chunk2之间的转场效果为淡入淡出
chunk1.setTransition({ id: TRANSITION.FADE, duration: 500 });
// 不设置时长可以直接提供效果ID
// chunk1.setTransition(TRANSITION.FADE);
// 创建多幕视频
const video = wvc.createMultiVideo({
width: 1280,
height: 720,
fps: 30,
// 视频段
chunks: [
chunk1,
chunk2
],
// 视频输出路径
outputPath: "./test.mp4",
// 是否在cli显示进度条
showProgress: true
});
// 监听合成完成事件
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// 启动合成
video.start();
您可以全局配置WVC调整一些通用参数。
import WebVideoCreator, { VIDEO_ENCODER, AUDIO_ENCODER } from "web-video-creator";
const wvc = new WebVideoCreator();
wvc.config({
// 开启后将输出一些WVC的调试日志
debug: true,
// 开启后将输出浏览器的运行日志
browserDebug: true,
// 开启后将输出每一条执行的FFmpeg命令
ffmpegDebug: true,
// ffmpeg可执行文件路径,设置后将禁用内部的ffmpeg-static,建议您默认使用内部的FFmpeg以确保功能完整性
ffmpegExecutablePath: "...",
// ffprobe可执行文件路径,设置后将禁用内部的ffprobe-static,建议您默认使用内部的ffprobe以确保功能完整性
ffprobeExecutablePath: "...",
// 浏览器GPU加速开关,建议开启提高渲染性能,如果您没有GPU设备或遭遇了诡异的渲染问题则可以关闭它
browserUseGPU: true,
// 浏览器是否使用Angle作为渲染后端,建议开启增强渲染跨平台兼容性和性能
browserUseAngle: true,
// 是否禁用浏览器使用共享内存,当/dev/shm分区较小时建议开启此选项
browserDisableDevShm: false,
// 浏览器可执行文件路径,设置后将禁用内部的浏览器,建议您默认使用内部的浏览器以确保功能完整性
browserExecutablePath: "...",
// 是否允许不安全的上下文,默认禁用,开启后能够导航到不安全的URL,但由于不安全上下文限制,将无法在页面中使用动态图像和内嵌视频
allowUnsafeContext: false,
// 兼容渲染模式,不建议启用,启用后将禁用HeadlessExperimental.beginFrame API调用改为普通的Page.screenshot
// 这会导致渲染性能下降,且部分动画可能帧率无法同步,当你遭遇 TargetCloseError: Protocol error (HeadlessExperimental.beginFrame): Target closed 错误的时候可以尝试开启它
compatibleRenderingMode: false,
// 资源池最小浏览器实例数量
numBrowserMin: 1,
// 资源池最大浏览器实例数量
numBrowserMax: 5,
// 每个浏览器实例最小页面实例数量
numPageMin: 1,
// 每个浏览器实例最大页面实例数量
numPageMax: 5,
// 访问页面时的用户UA
userAgent: null,
// 捕获帧图质量(0-100),仅jpeg有效
frameQuality: 80,
// 帧图格式(jpeg/png),建议使用jpeg,png捕获较为耗时
frameFormat: "jpeg",
// BeginFrame捕获图像超时时间
beginFrameTimeout: 5000,
// MP4格式的视频编码器,默认使用libx264软编码器,建议根据您的硬件选用合适的硬编码器加速合成,编码器选择可参考 docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.CPU.H264,
// WEBM格式的视频编码器,默认使用libvpx软编码器,建议根据您的硬件选用合适的硬编码器加速合成
webmEncoder: VIDEO_ENCODER.CPU.VP8,
// 音频编码器,建议采用默认的aac编码器
audioEncoder: AUDIO_ENCODER.AAC
});
只需在需要渲染的html中添加 <audio>
元素,您还可以设置循环,WVC会自动为视频合入循环音轨。
<audio src="bgm.mp3" loop></audio>
还可以设置一些其它属性控制音频的行为,这些属性并不总是需要成对出现,您可以根据自己的需求定制。
<!-- 控制音频音量为原来的一半 -->
<audio src="bgm.mp3" volume="0.5"></audio>
<!-- 控制音频在3秒后开始播放并在10秒处停止播放 -->
<audio src="bgm.mp3" startTime="3000" endTime="10000"></audio>
<!-- 截取音频第5秒到第15秒的片段并循环播放它 -->
<audio src="bgm.mp3" seekStart="5000" seekEnd="15000" loop></audio>
<!-- 控制音频300毫秒淡入且500毫秒淡出 -->
<audio src="bgm.mp3" fadeInDuration="300" fadeOutDuration="500"></audio>
在代码中添加和移除 <audio>
元素来实现音频出入场也是被允许的,WVC将检测到它们。
const audio = document.createElement("audio");
audio.src = "bgm.mp3";
// 音频在视频第3秒入场
setTimeout(() => document.body.appendChild(audio), 3000);
// 音频在视频第8秒出场
setTimeout(() => audio.remove(), 8000);
或者在页面中调用 captureCtx.addAudio 添加音频到视频中。
// 添加单个音频
captureCtx.addAudio({
url: "bgm.mp3",
startTime: 500,
loop: true,
// 80%的音量
volume: 80
});
// 添加多个音频
captureCtx.addAudios([...]);
也可以在WVC中直接使用 addAudio 将本地或远程的音频添加到视频中。
const video = wvc.createSingleVideo({ ... });
// 添加单个音频
video.addAudio({
// url: "http://.../bgm.mp3"
path: "bgm.mp3",
startTime: 500,
loop: true,
// 80%的音量
volume: 80
});
// 添加多个音频
video.addAudios([...]);
这样的操作同样适用于 MultiVideo 和 ChunkVideo 。
目前支持 mp4
和 webm
格式的视频,只需在需要渲染的html中添加 <video>
元素,您可以设置循环和静音,如果您的src不包含 .mp4
后缀名可能无法被识别,请添加 capture
属性标识为需要捕获的元素。
<video src="background.mp4" loop muted></video>
如果希望插入透明通道的视频请见:透明通道视频,对视频帧率同步或透明视频绘制感兴趣可以参考:技术实现。
和音频一样,它也支持设置一些属性控制视频的行为,这些属性并不总是需要成对出现,您可以根据自己的需求定制。
<!-- 控制音频音量为原来的70% -->
<video src="test.mp4" volume="0.7"></video>
<!-- 控制视频在3秒后开始播放并在10秒处停止播放 -->
<video src="test.mp4" startTime="3000" endTime="10000"></video>
<!-- 截取视频第5秒到第15秒的片段并循环播放它 -->
<video src="test.mp4" seekStart="5000" seekEnd="15000" loop></video>
<!-- 控制视频的音频在300毫秒淡入且500毫秒淡出 -->
<video src="test.mp4" fadeInDuration="300" fadeOutDuration="500"></video>
在代码中添加和移除 <video>
元素来实现视频出入场也是被允许的,WVC将检测到它们。
const video = document.createElement("video");
video.src = "test.mp4";
// 视频在第3秒入场
setTimeout(() => document.body.appendChild(video), 3000);
// 视频在第8秒出场
setTimeout(() => video.remove(), 8000);
透明视频非常适合用于将vtuber数字人合成到视频画面中,结合精美的动画可以获得非常好的观看体验,合成效果请参考 渲染示例页面 最后一个Demo。
透明通道视频格式需为 webm
,在内部它会被重新编码为两个mp4容器的视频,分别是原色底视频和蒙版视频后在浏览器canvas中使用进行 globalCompositeOperation
进行图像混合并绘制。
对于使用者是无感的,像下面代码演示中那样,只需需要渲染的html中添加 <video>
元素,并设置src为webm格式视频地址即可。
<video src="vtuber.webm"></video>
webm编解码通常比较耗时,如果您可以直接获得原始mp4视频和蒙版mp4视频是更好的方案,只需增加设置maskSrc即可。
<video src="vtuber.mp4" maskSrc="vtuber_mask.mp4"></video>
动态图像指的是 gif
/ apng
/ webp
格式的序列帧动画,他们可以在浏览器中自然播放,帧率通常是不可控的,但WVC代理了它们的绘制,img元素被替换为canvas并通过ImageDecoder解码绘制每一帧,让序列帧动画按照虚拟时间同步绘制。
以下这些动图都能够正常绘制,您也可以照常给他们设置样式。
<img src="test.gif"/>
<img src="test.apng"/>
<img src="test.webp"/>
WVC已经内置 lottie-web 动画库,如果您的页面有自己实现的lottie动效则可以忽略本内容,因为它们也能够正常工作。
只需要插入一个 <lottie>
元素并设置src即可。
<lottie src="example.json"></lottie>
WVC能够检测样式表中的 @font-face
声明并等待字体加载完成再开始渲染。
<style>
@font-face {
font-family: "FontTest";
src: url("font.ttf") format("truetype");
}
</style>
<p style='font-family: "FontTest"'>Hello World</p>
或者,可以通过代码注册本地或远程的字体。
const video = wvc.createSingleVideo({ ... });
// 注册单个字体
video.registerFont({
// url: "http://.../font.ttf"
path: "font.ttf",
family: "FontTest",
format: "truetype"
});
// 注册多个字体
video.registerFonts([...]);
您需要确保字体能够正常加载,否则可能无法启动渲染。
WVC支持使用FFmpeg所支持的 Xfade 滤镜来合成转场效果,可参考转场列表、
每个分块视频参数都能够设置转场效果和持续时长。
import WebVideoCreator, { TRANSITION } from "web-video-creator";
...
const video = wvc.createMultiVideo({
...
// 视频段参数
chunks: [
{
url: "http://localhost:8080/scene-1.html",
duration: 10000,
// 在第一和第二幕之间插入淡入淡出转场
transition: {
id: TRANSITION.FADE,
duration: 500
},
// 如果不需要设置时长也可以直接设置转场ID
// transition: TRANSITION.FADE
},
{
url: "http://localhost:8080/scene-2.html",
duration: 10000
}
],
...
});
...
需要注意的是,应用转场会导致视频总时长缩短,转场效果实际上是两段视频的部分重叠,两段5秒的视频插入转场,会合成时长为9秒的视频。
Lottie动画也很适合作为转场效果,您可以在一段视频的尾部播放一半时长的全屏Lottie动画,然后在下一段视频开头播放另一半时长的全屏Lottie动画实现更动感的转场效果。
WVC默认页面导航完成后立即启动渲染,如果希望在渲染之前进行一些工作,可以在选项中禁用自动启动渲染,禁用后请记得在您的页面中调用 captureCtx.start(),否则将永远阻塞。
const video = wvc.createSingleVideo({
url: "http://localhost:8080/test.html",
width: 1280,
height: 720,
duration: 10000,
// 禁用自动启动渲染
autostartRender: false
});
页面代码中,在您觉得合适的时机调用启动。
<script>
// 数据加载完成后启动渲染
loadData()
.then(() => captureCtx.start())
.catch(err => console.error(err));
</script>
const video = wvc.createSingleVideo({
url: "http://localhost:8080/test.html",
width: 1280,
height: 720,
duration: 10000,
pagePrepareFn: async page => {
// 获取puppeteer Page对象
const _page = page.target;
// 点击按钮
await _page.tap("#play-button");
}
});
如果想看到页面的日志,可在视频选项中开启consoleLog。开启videoPreprocessLog将输出内嵌视频预处理日志。
const video = wvc.createSingleVideo({
...,
// 输出页面控制台打印的日志
consoleLog: true,
// 输出内嵌视频预处理日志
videoPreprocessLog: true
});
合成视频后可以截取某一帧图像并保存,可以作为视频封面图。
const video = wvc.createSingleVideo({
...,
// 是否截取图像
coverCapture: true,
// 图像截取时间点(毫秒),默认是视频时长的20%位置)
coverCaptureTime: 1000,
// 图像保存格式(jpg/png/bmp),默认jpg
coverCaptureFormat: "jpg"
});
WVC支持往视频的首帧插入图像,当视频未被播放时将展示首帧图像。
const video = wvc.createSingleVideo({
...,
// 设置附加的封面图地址,支持jpg/png/bmp
attachCoverPath: "./cover.jpg"
});
您可以控制输出视频的总音量。
const video = wvc.createSingleVideo({
...,
// 设置视频音量为原来的80%
volume: 80
});
WVC支持通过 videoQuality
或 videoBitrate
控制视频图像质量。
videoQuality是通过图像总像素量简单计算码率,以下WVC内计算视频码率方法。
const pixels = width * height;
const videoBitrate = (2560 / 921600 * pixels) * (videoQuality / 100);
可以在视频选项中提供videoQuality(0-100)
const video = wvc.createSingleVideo({
...,
// 设置视频质量为80%
videoQuality: 80
});
如果您认为码率不合适,可以单独设置videoBitrate。
const video = wvc.createSingleVideo({
...,
// 设置视频码率为8Mbps
videoBitrate: "8192k"
});
另外还可以调整帧图质量,当使用jpeg作为帧图格式时可以调整frameQuality,详见 全局配置。
音频质量则可以通过设置音频码率audioBitrate调整。
const video = wvc.createSingleVideo({
...,
// 设置音频码率为320Kbps
audioBitrate: "320k"
});
WVC目前支持输出 yuv420p
/ yuv444p
/ rgb24
像素格式的视频,默认采用兼容性更好的 yuv420p ,如果您发现输出的视频与页面的颜色有较大的差异,可以切换为 rgb24 改善这个问题。
const video = wvc.createSingleVideo({
...,
// 设置像素格式为rgb24
pixelFormat: "rgb24"
});
浏览器渲染输出帧图流输入FFmpeg时需要通过视频编码器将图像数据按指定帧率编码为视频数据并存储于指定格式容器中,视频编码是一项较为消耗资源的操作,选用硬编码器可以加速这个过程并降低CPU的负载。
WVC支持的视频编码器请参考:视频编码器说明
您可以通过视频实例的 progress
事件监听渲染合成进度。
const video = wvc.createSingleVideo({ ... });
video.on("progress", (progress, synthesizedFrameCount, totalFrameCount) => {
// 输出 进度 / 已合成帧数 / 总帧数
console.log(progress, synthesizedFrameCount, totalFrameCount);
});
这同样适用于 MultiVideo
/ ChunkVideo
以及低级别API的合成器。
您可以在页面中主动抛出错误来中断渲染。
<script>
____throwError(-1, "Abort");
</script>
如果您的页面存在大量密集计算或者占用过多的运行内存,页面将可能崩溃,从而导致渲染中断。
如果使用高级别API,页面崩溃时通过视频实例的 error
事件通知。
const video = wvc.createSingleVideo({ ... });
// 错误时输出 Page crashed:...
video.on("error", err => console.error(err));
使用低级别API时,页面崩溃时通过Page实例的 crashed
事件通知
// 错误时输出崩溃错误
page.on("crashed", err => console.error(err));
如果使用高级别API,页面崩溃时通过视频实例的 error
事件通知。
const video = wvc.createSingleVideo({ ... });
video.on("error", err => console.error(err));
使用低级别API时,页面崩溃时通过Page实例的 error
事件通知
page.on("error", err => console.error(err));
如果您有多台设备可以为这些设备部署WVC,它提供了 MultiVideo
和 ChunkVideo
,您可以将动画页面分为很多个分段,如0-10秒、10-20秒...,将它们的参数分发到不同设备的WVC上,在这些设备上创建ChunkVideo实例并执行并行渲染为多个视频 ts
分段,将他们回传到核心节点上,并最终输入MultiVideo进行合并以及转场、音轨合成输出。这个分发以及回传流程WVC还未实现,但它并不难,您可以根据自己的场景进行封装并欢迎为WVC贡献PR!
大部分时候,建议使用高级别API,因为它足够的简单,但可能不够灵活。
性能通常受动画和媒体的复杂程度影响,您可以将长时间动画分为多个分段动画播放,比如为每个页面地址带一个seek参数,加载页面后seek到指定时间点开始播放,然后作为多幕视频进行渲染合成,可以显著的降低长视频的渲染耗时。
目前手上没有更好的测试设备,我将以我的个人主机的性能参数作为参考:
系统:Windows10(在Linux系统中性能表现更好)
CPU: AMD Ryzen 7 3700X(主频3.6-4.4GHz 8核16线程)
GPU: Nvidia GeForce GTX 1660 SUPER(6GB显存 支持NVENC)
RAM: 16GB(DDR4 2400MHz)
视频类型:SVG动画+GIF+Lottie动画播放
视频分辨率:1280x720
视频帧率:30
视频时长:300s(5分钟)
渲染耗时:61s(1分钟)
实时率:4.844
并行渲染数:16
正在编写中...
FAQs
A framework for creating videos based on Node.js + Puppeteer + FFmpeg.
We found that web-video-creator demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.