UNPKG

web-video-creator

Version:

A framework for creating videos based on Node.js + Puppeteer + FFmpeg.

186 lines (173 loc) 10.2 kB
import assert from "assert"; import ffmpeg from "fluent-ffmpeg"; import _ from "lodash"; import globalConfig from "../lib/global-config.js"; import { VIDEO_ENCODER } from "../lib/const.js"; import ResourcePool from "../core/ResourcePool.js"; import SingleVideo from "./SingleVideo.js"; import ChunkVideo from "./ChunkVideo.js"; import MultiVideo from "./MultiVideo.js"; import logger from "../lib/logger.js"; import cleaner from "../lib/cleaner.js"; /** * @typedef {import('puppeteer-core').WaitForOptions} WaitForOptions * @typedef {import('puppeteer-core').Viewport} Viewport */ export default class WebVideoCreator { /** @type {ResourcePool} - 资源池 */ pool = null; /** @type {boolean} - 是否已配置 */ #configured = false; /** * 配置引擎 * * @param {globalConfig} config - 配置对象 */ config(config = {}) { for (let key in globalConfig) { if (!_.isUndefined(config[key])) globalConfig[key] = config[key]; } const { ffmpegExecutablePath, ffprobeExecutablePath, browserUseGPU, mp4Encoder } = globalConfig; // 未启用浏览器GPU发出性能警告 if (!browserUseGPU) logger.warn("browserUseGPU is turn off, recommended to turn it on to improve rendering performance"); // 未使用硬编码器发出性能警告 if (Object.values(VIDEO_ENCODER.CPU).includes(mp4Encoder)) logger.warn(`Recommended to use video hard coder to accelerate video synthesis, currently used is [${globalConfig.mp4Encoder}]`); // 设置FFmpeg可执行文件路径 ffmpegExecutablePath && ffmpeg.setFfmpegPath(ffmpegExecutablePath); // 设置FFprobe可执行文件路径 ffprobeExecutablePath && ffmpeg.setFfprobePath(ffprobeExecutablePath); // 实例化浏览器资源池 this.pool = new ResourcePool(); // 设置已配置 this.#configured = true; } /** * 创建单幕视频 * * @param {Object} options - 单幕视频选项 * @param {string} [options.url] - 页面URL * @param {string} [options.content] - 页面内容 * @param {string} options.outputPath - 输出路径 * @param {number} options.width - 视频宽度 * @param {number} options.height - 视频高度 * @param {number} options.duration - 视频时长 * @param {number} [options.startTime=0] - 开始捕获时间点 * @param {number} [options.fps=30] - 视频帧率 * @param {string} [options.format] - 导出视频格式(mp4/webm) * @param {string} [options.attachCoverPath] - 附加到视频首帧的封面路径 * @param {string} [options.coverCapture=false] - 是否捕获封面并输出 * @param {number} [options.coverCaptureTime] - 封面捕获时间点(毫秒) * @param {string} [options.coverCaptureFormat="jpg"] - 封面捕获格式(jpg/png/bmp) * @param {string} [options.videoEncoder="libx264"] - 视频编码器 * @param {number} [options.videoQuality=100] - 视频质量(0-100) * @param {string} [options.videoBitrate] - 视频码率(设置码率将忽略videoQuality) * @param {string} [options.pixelFormat="yuv420p"] - 像素格式(yuv420p/yuv444p/rgb24) * @param {string} [options.audioEncoder="aac"] - 音频编码器 * @param {string} [options.audioBitrate] - 音频码率 * @param {number} [options.volume] - 视频音量(0-100) * @param {number} [options.parallelWriteFrames=10] - 并行写入帧数 * @param {boolean} [options.showProgress=false] - 是否在命令行展示进度 * @param {boolean} [options.backgroundOpacity=1] - 背景不透明度(0-1),仅webm格式支持 * @param {boolean} [options.autostartRender=true] - 是否自动启动渲染,如果为false请务必在页面中执行 captureCtx.start() * @param {boolean} [options.consoleLog=false] - 是否开启控制台日志输出 * @param {boolean} [options.videoPreprocessLog=false] - 是否开启视频预处理日志输出 * @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项 * @param {Viewport} [options.pageViewport] - 页面视窗参数 * @param {Function} [options.pagePrepareFn] - 页面预处理函数 * @param {{[key: number]: Function}} [options.timeActions] - 动作序列 */ createSingleVideo(options) { assert(this.#configured, "WebVideoCreator has not been configured yet, please execute config() first"); const singleVideo = new SingleVideo(options); // 注册获取页面函数 singleVideo.onPageAcquire(async () => await this.pool.acquirePage()); return singleVideo; } /** * 创建多幕视频 * * @param {Object} options - 序列帧合成器选项 * @param {string} options.outputPath - 导出视频路径 * @param {number} options.width - 视频宽度 * @param {number} options.height - 视频高度 * @param {number} options.duration - 视频时长 * @param {ChunkVideo[]} options.chunks - 分块视频列表 * @param {number} [options.fps=30] - 视频合成帧率 * @param {string} [options.format] - 导出视频格式(mp4/webm) * @param {string} [options.attachCoverPath] - 附加到视频首帧的封面路径 * @param {string} [options.coverCapture=false] - 是否捕获封面并输出 * @param {number} [options.coverCaptureTime] - 封面捕获时间点(毫秒) * @param {string} [options.coverCaptureFormat="jpg"] - 封面捕获格式(jpg/png/bmp) * @param {string} [options.videoEncoder="libx264"] - 视频编码器 * @param {number} [options.videoQuality=100] - 视频质量(0-100) * @param {string} [options.videoBitrate] - 视频码率(设置码率将忽略videoQuality) * @param {string} [options.pixelFormat="yuv420p"] - 像素格式(yuv420p/yuv444p/rgb24) * @param {string} [options.audioEncoder="aac"] - 音频编码器 * @param {string} [options.audioBitrate] - 音频码率 * @param {number} [options.volume] - 视频音量(0-100) * @param {number} [options.parallelWriteFrames=10] - 并行写入帧数 * @param {boolean} [options.showProgress=false] - 是否在命令行展示进度 * @param {Function} [options.pagePrepareFn] - 页面预处理函数 */ createMultiVideo(options) { assert(this.#configured, "WebVideoCreator has not been configured yet, please execute config() first"); const multiVideo = new MultiVideo(options); // 注册获取页面函数 multiVideo.onPageAcquire(async () => await this.pool.acquirePage()) return multiVideo; } /** * 创建分块视频 * * @param {Object} options - 分块视频选项 * @param {string} [options.url] - 页面URL * @param {string} [options.content] - 页面内容 * @param {string} options.outputPath - 输出路径 * @param {number} options.width - 视频宽度 * @param {number} options.height - 视频高度 * @param {number} options.duration - 视频时长 * @param {number} [options.startTime=0] - 开始捕获时间点 * @param {number} [options.fps=30] - 视频帧率 * @param {Transition} [options.transition] - 进入下一视频分块的转场 * @param {string} [options.format] - 导出视频格式(mp4/webm) * @param {string} [options.attachCoverPath] - 附加到视频首帧的封面路径 * @param {string} [options.coverCapture=false] - 是否捕获封面并输出 * @param {number} [options.coverCaptureTime] - 封面捕获时间点(毫秒) * @param {string} [options.coverCaptureFormat="jpg"] - 封面捕获格式(jpg/png/bmp) * @param {string} [options.videoEncoder="libx264"] - 视频编码器 * @param {number} [options.videoQuality=100] - 视频质量(0-100) * @param {string} [options.videoBitrate] - 视频码率(设置码率将忽略videoQuality) * @param {string} [options.pixelFormat="yuv420p"] - 像素格式(yuv420p/yuv444p/rgb24) * @param {string} [options.audioEncoder="aac"] - 音频编码器 * @param {string} [options.audioBitrate] - 音频码率 * @param {number} [options.volume] - 视频音量(0-100) * @param {number} [options.parallelWriteFrames=10] - 并行写入帧数 * @param {boolean} [options.showProgress=false] - 是否在命令行展示进度 * @param {boolean} [options.backgroundOpacity=1] - 背景不透明度(0-1),仅webm格式支持 * @param {boolean} [options.autostartRender=true] - 是否自动启动渲染,如果为false请务必在页面中执行 captureCtx.start() * @param {boolean} [options.consoleLog=false] - 是否开启控制台日志输出 * @param {boolean} [options.videoPreprocessLog=false] - 是否开启视频预处理日志输出 * @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项 * @param {Viewport} [options.pageViewport] - 页面视窗参数 * @param {Function} [options.pagePrepareFn] - 页面预处理函数 * @param {{[key: number]: Function}} [options.timeActions] - 动作序列 */ createChunkVideo(options) { assert(this.#configured, "WebVideoCreator has not been configured yet, please execute config() first"); const chunkVideo = new ChunkVideo(options); // 注册获取页面函数 chunkVideo.onPageAcquire(async () => await this.pool.acquirePage()); return chunkVideo; } /** 清理浏览器缓存 */ cleanBrowserCache = cleaner.cleanBrowserCache.bind(cleaner); /** 清理预处理缓存 */ cleanPreprocessCache = cleaner.cleanPreprocessCache.bind(cleaner); /** 清理合成缓存 */ cleanSynthesizeCache = cleaner.cleanSynthesizeCache.bind(cleaner); /** 清理本地字体缓存 */ cleanLocalFontCache = cleaner.cleanLocalFontCache.bind(cleaner); }