Doramagic 项目包 · 项目说明书
openvideo 项目
生成时间:2026-05-14 00:03:22 UTC
项目介绍
OpenVideo 是一个开源的 Web 端视频编辑与渲染引擎,旨在为开发者提供基于现代 Web 技术的视频处理能力。该项目充分利用 WebCodecs API 实现硬件加速的视频编解码,结合 PixiJS 渲染引擎实现高性能的 2D/3D 图形渲染。OpenVideo 的设计理念是让视频编辑功能能够在浏览器环境中高效运行,同时支持服务端 Node.js 环境下的视频渲染输出。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
OpenVideo 是一个开源的 Web 端视频编辑与渲染引擎,旨在为开发者提供基于现代 Web 技术的视频处理能力。该项目充分利用 WebCodecs API 实现硬件加速的视频编解码,结合 PixiJS 渲染引擎实现高性能的 2D/3D 图形渲染。OpenVideo 的设计理念是让视频编辑功能能够在浏览器环境中高效运行,同时支持服务端 Node.js 环境下的视频渲染输出。
核心功能
OpenVideo 提供了完整的视频编辑工作流支持,包括时间线管理、轨道编辑、剪辑合成、特效转场等功能模块。开发者可以通过声明式的 JSON 配置或程序化的 API 调用来构建复杂的视频编辑场景。
主要能力
| 功能类别 | 支持内容 |
|---|---|
| 媒体剪辑 | 视频、音频、图片、字幕、文字 |
| 视频特效 | 动画预设、滤镜、透明度、镜像、缩放 |
| 转场效果 | 超过 30 种转场动画(淡入淡出、滑动、缩放等) |
| 渲染输出 | WebCodecs 硬件编码、MP4 封装 |
| 实时预览 | Canvas 2D/WebGL 渲染、帧级同步 |
技术架构
技术栈
OpenVideo 基于以下核心技术构建:
| 技术 | 用途 |
|---|---|
| WebCodecs API | 硬件加速的视频编码/解码处理 |
| PixiJS | 高性能 2D/3D 渲染引擎 |
| wrapbox | MP4 容器操作的底层工具库 |
| TypeScript | 类型安全的开发环境 |
| Playwright | 服务端渲染的浏览器自动化 |
架构层次
┌─────────────────────────────────────────────┐
│ 应用层 (Application) │
│ Studio │ Compositor │ PreviewCanvas │
├─────────────────────────────────────────────┤
│ 剪辑层 (Clips) │
│ VideoClip │ AudioClip │ TextClip │ ImageClip │
│ CaptionClip │ SubtitleClip │
├─────────────────────────────────────────────┤
│ 精灵层 (Sprites) │
│ BaseSprite │ Transform │ Animations │
├─────────────────────────────────────────────┤
│ 渲染层 (Rendering) │
│ PixiJS App │ WebGL │ Compositor │
├─────────────────────────────────────────────┤
│ 工具层 (Utils) │
│ Chromakey │ MP4 Utils │ Serialization │
├─────────────────────────────────────────────┤
│ 编码层 (Codecs) │
│ VideoEncoder │ VideoDecoder │ MP4 Muxer │
└─────────────────────────────────────────────┘
项目结构
OpenVideo 采用 Monorepo 架构,主要包含以下包:
openvideo/
├── packages/
│ ├── openvideo/ # 核心库
│ │ └── src/
│ │ ├── clips/ # 各类剪辑实现
│ │ │ ├── video-clip.ts
│ │ │ ├── text-clip.ts
│ │ │ ├── caption-clip.ts
│ │ │ └── ...
│ │ ├── animation/ # 动画预设
│ │ │ └── presets.ts
│ │ ├── transition/ # 转场效果
│ │ │ └── transition.ts
│ │ ├── sprite/ # 精灵基类
│ │ │ └── base-sprite.ts
│ │ ├── mp4-utils/ # MP4 封装工具
│ │ │ └── index.ts
│ │ ├── utils/ # 工具函数
│ │ │ └── chromakey.ts
│ │ └── render.html # 渲染模板
│ │
│ └── node/ # Node.js 服务端渲染包
│ └── src/
│ ├── renderer.ts # 主渲染器
│ ├── types.ts # 类型定义
│ ├── template.html # HTML 模板
│ └── sample.ts # 使用示例
│
└── examples/ # 示例项目
核心组件
Studio(工作室)
Studio 是项目的顶层状态管理器,负责协调整个视频编辑项目的运行。它管理时间线配置、轨道层级、剪辑序列以及预览播放状态。
import { Studio } from 'openvideo';
const studio = new Studio({
canvas: document.getElementById('preview-canvas') as HTMLCanvasElement,
spacing: 20
});
const video = await Video.fromUrl('https://example.com/video.mp4');
await studio.addClip(video);
studio.play();
资料来源:README.md
Compositor(合成器)
Compositor 是核心渲染引擎,负责处理视频帧的合成、编码输出和最终导出。它封装了 PixiJS 应用和 WebCodecs 视频编码器,提供统一的渲染接口。
| 配置选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| width | number | 1280 | 输出视频宽度 |
| height | number | 720 | 输出视频高度 |
| fps | number | 30 | 帧率 |
| bgColor | string | '#000000' | 背景颜色 |
| videoCodec | string | - | 视频编码器 |
| bitrate | number | - | 比特率 |
| audio | boolean | true | 是否包含音频 |
资料来源:packages/node/src/template.html:29-48
Clips(剪辑)
Clips 是各类媒体资源的抽象表示,包括以下类型:
| 剪辑类型 | 文件 | 功能 |
|---|---|---|
| VideoClip | video-clip.ts | 视频片段处理,支持 URL、Stream、OPFS 文件加载 |
| TextClip | text-clip.ts | 文字渲染,支持样式、描边、阴影 |
| CaptionClip | caption-clip.ts | 字幕处理,支持时间轴对齐和关键词高亮 |
| AudioClip | - | 音频处理 |
| ImageClip | - | 静态图像处理 |
| SubtitleClip | - | 字幕轨道管理 |
#### VideoClip 示例
const clip = await Video.fromUrl('https://example.com/video.mp4', {
x: 100,
y: 200,
width: 640,
height: 480
});
资料来源:packages/openvideo/src/clips/video-clip.ts:1-50
#### TextClip 配置
TextClip 支持丰富的文本样式配置:
- fontFamily: 字体系列
- fontSize: 字号
- fontWeight: 字重
- fontStyle: 斜体
- fill: 填充颜色(支持渐变)
- stroke: 描边效果
- dropShadow: 阴影效果
- align: 文本对齐
- wordWrap: 自动换行
资料来源:packages/openvideo/src/clips/text-clip.ts:1-50
动画系统
动画预设
OpenVideo 内置了丰富的动画预设,可在 presets.ts 中查看:
| 预设名称 | 效果类型 |
|---|---|
| fadeIn | 淡入效果 |
| fadeOut | 淡出效果 |
| slideIn | 滑入动画 |
| slideOut | 滑出动画 |
| pulse | 脉冲动画 |
| blurIn | 模糊淡入 |
| blurOut | 模糊淡出 |
// 动画配置结构
{
"0%": {
x: 0,
y: 0,
opacity: 0,
scale: 1,
mirror: 0
},
"100%": {
x: 100,
y: 0,
opacity: 1,
scale: 1,
mirror: 0
}
}
资料来源:packages/openvideo/src/animation/presets.ts:1-80
变换属性
BaseSprite 提供了完整的变换控制:
| 属性 | 说明 |
|---|---|
| x, y | 位置坐标 |
| width, height | 尺寸 |
| scale, scaleX, scaleY | 缩放 |
| angle | 旋转角度 |
| opacity | 透明度 |
| blur | 模糊程度 |
| brightness | 亮度 |
| mirror | 镜像模式 |
| motionBlur | 运动模糊 |
资料来源:packages/openvideo/src/sprite/base-sprite.ts:1-50
转场效果
支持的转场类型
OpenVideo 实现了超过 30 种 GLSL 着色器转场效果:
| 转场名称 | 说明 |
|---|---|
| GridFlip | 网格翻转 |
| Circle | 圆形展开 |
| Directional | 方向擦除 |
| UndulatingBurnOut | 波纹燃烧 |
| SquaresWire | 方块线条 |
| RotateScaleFade | 旋转缩放淡入 |
| RandomSquares | 随机方块 |
| PolarFunction | 极坐标变换 |
| Hexagonalize | 六边形化 |
| Heart | 心形展开 |
| Displacement | 置换变形 |
| DirectionalWipe | 方向擦除 |
| DirectionalWarp | 方向扭曲 |
| Crosshatch | 网格线 |
| CircleOpen | 圆环展开 |
| CannabisLeaf | 叶片形状 |
| StereoViewer | 立体视差 |
| GlitchDisplace | 故障艺术 |
| CrossZoom | 交叉缩放 |
| BowTieHorizontal | 蝴蝶结水平 |
| PolkaDotsCurtain | 波点帘幕 |
| Pixelize | 像素化 |
转场效果的实现通过 GLSL 片段着色器完成,支持自定义 uniform 参数配置。
资料来源:packages/openvideo/src/transition/transition.ts:1-100
渲染流程
服务端渲染
Node.js 包提供了无头浏览器环境下的视频渲染能力:
graph TD
A[创建 Renderer 实例] --> B[启动本地服务器]
B --> C[Playwright 打开浏览器]
C --> D[加载 HTML 模板]
D --> E[注入 JSON 配置]
E --> F[Compositor 初始化]
F --> G[逐帧渲染]
G --> H[WebCodecs 编码]
H --> I[MP4 封装]
I --> J[保存输出文件]渲染器配置
const renderer = new Renderer({
json: videoConfig, // 视频配置 JSON
outputPath: './out.mp4', // 输出路径
browserOptions: {
headless: true, // 无头模式
timeout: 600000 // 10 分钟超时
}
});
renderer.on('progress', ({ phase, progress }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}%`);
});
await renderer.render();
渲染进度事件
| 阶段 | 说明 |
|---|---|
| initializing | 初始化中 |
| loading | 加载资源 |
| rendering | 渲染中 |
| saving | 保存文件 |
| complete | 完成 |
资料来源:packages/node/README.md:60-90
工具模块
MP4 工具
mp4-utils 提供了 MP4 容器级别的操作能力,包括样本注入、时间戳规范化、轨道管理等功能。
// 时间戳规范化处理
samples.forEach((s) => {
if (firstVDTS === null) {
firstVDTS = s.dts;
firstVCTS = s.cts;
}
const normalizedDTS = s.dts - firstVDTS;
const normalizedCTS = s.cts - (firstVCTS ?? 0);
outfile.addSample(trackId, new Uint8Array(s.data), {
duration: s.duration,
dts: normalizedDTS + offsetDTS,
cts: normalizedCTS + offsetCTS,
is_sync: s.is_sync
});
});
资料来源:packages/openvideo/src/mp4-utils/index.ts:1-50
绿幕抠像
chromakey 模块提供了颜色键抠像功能:
import { createChromakey } from 'openvideo/utils/chromakey';
const chromakeyFilter = createChromakey({
keyColor: [0, 255, 0], // 绿色
similarity: 0.4,
smoothness: 0.1
});
// 用于 VideoClip 的处理管道
videoClip.setFilter(chromakeyFilter);
资料来源:packages/openvideo/src/utils/chromakey.ts:1-50
使用方式
前端使用
import { Studio, Compositor, Video, TextClip } from 'openvideo';
// 1. 创建工作室
const studio = new Studio({
canvas: document.getElementById('canvas'),
width: 1920,
height: 1080
});
// 2. 添加视频
const video = await Video.fromUrl('input.mp4');
studio.addClip(video);
// 3. 添加文字
const text = new TextClip('Hello World', {
fontSize: 48,
fontFamily: 'Arial'
});
studio.addClip(text);
// 4. 预览播放
studio.play();
服务端渲染
# 安装依赖
pnpm install
# 构建项目
pnpm build
# 运行示例
cd packages/node
pnpm render
环境要求
| 环境 | 版本要求 |
|---|---|
| Node.js | >= 18 |
| 浏览器 | 支持 WebCodecs(Chrome 94+, Edge 94+) |
| Playwright | Chromium 已安装 |
许可证
OpenVideo 采用 MIT 许可证开源。详情请参阅 LICENSE 文件。
联系方式
如有问题或合作意向,可通过以下方式联系:
- 邮箱:[email protected]
- 官网:https://openvideo.dev
资料来源:[README.md]()
安装与配置
OpenVideo 是一个基于 WebCodecs API 的高性能视频处理和渲染库,支持在浏览器和 Node.js 环境中进行视频合成、编辑和导出。本页面详细介绍 OpenVideo 项目的安装流程、依赖要求以及各平台的配置方法。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
系统要求
浏览器环境要求
OpenVideo 核心库依赖 WebCodecs API 进行硬件加速的视频编解码处理。根据 packages/openvideo/package.json 中的信息,浏览器环境需要满足以下要求:
| 组件 | 最低要求 | 说明 |
|---|---|---|
| WebCodecs API | 必须支持 | 用于视频编码和解码的核心 API |
| 浏览器版本 | Chrome 94+ / Edge 94+ / Opera 80+ | 主流浏览器需开启相关特性标志 |
| ES Modules | 必须支持 | 包使用 ES Module 格式发布 |
| OPFS API | 推荐支持 | 用于本地大文件存储,提升性能 |
WebCodecs API 在部分浏览器中可能需要手动启用特性标志。Chromium 内核浏览器可通过以下方式启用:
// 启用 WebCodecs API
await navigator.mediaCapabilities.decodingInfo({
type: 'file',
video: { type: 'video/mp4', width: 1920, height: 1080, bitrate: 5000000 }
});
资料来源:packages/openvideo/package.json:1-35
Node.js 环境要求
当使用 @combo/node 包进行服务端视频渲染时,需要满足以下条件:
| 要求 | 版本 | 说明 |
|---|---|---|
| Node.js | >= 18 | 需要支持 ES Modules 和部分 Web API |
| Playwright | 最新版 | 用于无头浏览器环境运行渲染 |
| 操作系统 | Windows/macOS/Linux | 支持 Playwright 运行的所有平台 |
资料来源:packages/node/README.md:1-100
安装方式
使用 pnpm(推荐)
OpenVideo 采用 Monorepo 结构管理多个包,推荐使用 pnpm 进行安装:
# 安装根依赖
pnpm install
# 安装 openvideo 核心包
pnpm --filter openvideo build
# 安装 Node.js 渲染包
pnpm --filter @combo/node build
资料来源:packages/openvideo/package.json:1-40
使用 npm
# 安装核心库
npm install openvideo
# 安装 Node.js 渲染包
npm install @combo/node
使用 yarn
# 安装核心库
yarn add openvideo
# 安装 Node.js 渲染包
yarn add @combo/node
CDN 引入
对于浏览器环境,可以直接通过 CDN 引入 OpenVideo:
<script type="importmap">
{
"imports": {
"openvideo": "https://unpkg.com/openvideo/dist/index.es.js"
}
}
</script>
<script type="module">
import { Compositor, Studio } from 'openvideo';
// 使用 OpenVideo API
</script>
资料来源:packages/node/src/template.html:1-20
包结构说明
OpenVideo 项目包含多个子包,每个包承担特定功能:
核心包 (openvideo)
| 属性 | 值 | 说明 |
|---|---|---|
| 包名 | openvideo | 核心视频处理库 |
| 版本 | 0.2.18 | 当前稳定版本 |
| 入口 (main) | dist/index.umd.js | UMD 格式,用于传统 script 标签 |
| 入口 (module) | dist/index.es.js | ES Module 格式 |
| 类型定义 | dist/index.d.ts | TypeScript 类型定义 |
{
"name": "openvideo",
"version": "0.2.18",
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts"
}
资料来源:packages/openvideo/package.json:1-20
Node.js 渲染包 (@combo/node)
用于在 Node.js 环境中进行服务端视频渲染,通过 Playwright 控制无头浏览器执行渲染任务:
{
"name": "@combo/node",
"dependencies": {
"playwright": "latest"
}
}
资料来源:packages/node/package.json
核心组件导入
浏览器环境导入
// 方式一:从完整包导入
import { Studio, Compositor, Video, Audio, Text, Image } from 'openvideo';
// 方式二:按需导入(Tree-shaking 友好)
import { Studio } from 'openvideo';
import { Compositor } from 'openvideo';
import { Video } from 'openvideo/clips/video-clip';
Node.js 环境导入
import { Renderer } from '@combo/node';
const renderer = new Renderer({
json: videoConfig,
outputPath: './output.mp4'
});
renderer.on('progress', ({ phase, progress }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}%`);
});
await renderer.render();
资料来源:packages/node/README.md:80-120
构建配置
浏览器端构建
OpenVideo 使用 Vite 进行构建,生成 UMD 和 ES Module 两种格式:
# 在 packages/openvideo 目录下
pnpm build
构建产物包含:
| 文件 | 格式 | 用途 |
|---|---|---|
| dist/index.umd.js | UMD | 兼容 AMD/CommonJS/全局变量 |
| dist/index.es.js | ES Module | 现代浏览器和打包工具 |
| dist/index.d.ts | TypeScript | 类型定义 |
资料来源:packages/openvideo/package.json:20-35
Node.js 包构建
# 构建 Node.js 渲染包
pnpm --filter @combo/node build
构建后需复制 HTML 模板到 dist 目录:
# 复制模板文件
cp src/template.html dist/template.html
资料来源:packages/node/README.md:40-80
渲染器配置
Compositor 配置选项
在浏览器环境中,Compositor 是核心渲染引擎:
const compositorOpts = {
width: 1920, // 视频宽度
height: 1080, // 视频高度
fps: 30, // 帧率
bgColor: '#000000', // 背景颜色
videoCodec: 'avc1.64001f', // 视频编码器(可选)
bitrate: 5000000, // 比特率(可选)
audio: true, // 是否包含音频
metaDataTags: { // 元数据标签(可选)
title: 'My Video',
author: 'Creator'
}
};
const compositor = new Compositor(compositorOpts);
await compositor.initPixiApp();
资料来源:packages/node/src/template.html:30-60
Renderer 配置选项(Node.js)
| 选项 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| json | object | 是 | - | 视频配置 JSON 对象 |
| outputPath | string | 是 | - | 输出文件路径 |
| browserOptions | object | 否 | 见下文 | 浏览器配置 |
browserOptions 子选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| headless | boolean | true | 是否使用无头模式 |
| timeout | number | 300000 | 超时时间(毫秒) |
const renderer = new Renderer({
json: videoConfig,
outputPath: './output.mp4',
browserOptions: {
headless: true,
timeout: 600000 // 10 分钟
}
});
资料来源:packages/node/README.md:100-140
快速开始示例
浏览器端视频编辑
import { Studio, Video, Audio } from 'openvideo';
// 1. 创建 Studio 实例
const studio = new Studio({
canvas: document.getElementById('preview-canvas') as HTMLCanvasElement,
spacing: 20
});
// 2. 加载并添加视频片段
const video = await Video.fromUrl('https://example.com/video.mp4');
await studio.addClip(video);
// 3. 添加音频
const audio = await Audio.fromUrl('https://example.com/audio.mp3');
await studio.addClip(audio);
// 4. 开始预览
studio.play();
资料来源:README.md:1-50
服务端批量渲染
import { Renderer } from '@combo/node';
// 批量渲染配置
const videos = [
{ config: videoConfig1, output: 'output1.mp4' },
{ config: videoConfig2, output: 'output2.mp4' }
];
for (const { config, output } of videos) {
const renderer = new Renderer({
json: config,
outputPath: output
});
renderer.on('progress', ({ phase, progress }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}%`);
});
await renderer.render();
console.log(`✅ 已生成: ${output}`);
}
开发环境配置
TypeScript 配置
确保 tsconfig.json 正确配置模块解析:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"]
}
}
开发工作流
graph TD
A[修改源代码] --> B[运行构建命令]
B --> C{目标平台}
C -->|浏览器| D[pnpm --filter openvideo build]
C -->|Node.js| E[pnpm --filter @combo/node build]
D --> F[测试功能]
E --> F
F --> G{验证通过?}
G -->|是| H[提交代码]
G -->|否| A开发调试步骤:
- 修改源代码 - 在
packages/openvideo/src/或packages/node/src/目录下修改代码 - 重新构建 - 运行相应的构建命令
- 运行测试 - 执行
pnpm test验证功能 - 调试运行 - 在 Node.js 中测试
node dist/sample.js
资料来源:packages/node/README.md:60-90
常见问题排查
Playwright 安装失败
# 手动安装 Chromium
npx playwright install chromium
模块解析错误
确保 openvideo 包已构建:
pnpm --filter openvideo build
渲染超时
增加浏览器超时时间:
browserOptions: {
timeout: 600000 // 10 分钟
}
WebCodecs 不可用
检查浏览器是否支持 WebCodecs:
if ('VideoDecoder' in window) {
console.log('WebCodecs API 支持正常');
} else {
console.error('浏览器不支持 WebCodecs API');
}
技术栈依赖
OpenVideo 基于以下核心技术构建:
| 技术 | 用途 | 版本要求 |
|---|---|---|
| WebCodecs | 硬件加速视频编解码 | 现代浏览器 |
| PixiJS | 2D 渲染引擎 | 最新稳定版 |
| wrapbox | MP4 容器操作 | 内置工具库 |
| Playwright | 无头浏览器控制 | Node.js 环境 |
核心组件架构:
graph TB
subgraph "用户层"
A[Studio API]
B[Compositor API]
C[Clip API]
end
subgraph "渲染引擎层"
D[PixiJS Renderer]
E[WebCodecs Encoder]
F[WebCodecs Decoder]
end
subgraph "文件处理层"
G[MP4 Muxer]
H[wrapbox]
I[OPFS Manager]
end
A --> D
B --> E
C --> F
D --> G
E --> G
F --> I
G --> H资料来源:README.md:50-80
下一步
完成安装配置后,建议继续阅读以下文档:
资料来源:[packages/openvideo/package.json:1-35]()
系统架构
OpenVideo 是一个基于 Web 技术的视频渲染和处理库,其核心设计围绕客户端视频合成展开。该项目利用现代浏览器原生 API(主要是 WebCodecs)实现硬件加速的视频编解码,并通过 PixiJS 提供高性能的 2D/3D 渲染能力。 资料来源:[README.md]()
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
OpenVideo 是一个基于 Web 技术的视频渲染和处理库,其核心设计围绕客户端视频合成展开。该项目利用现代浏览器原生 API(主要是 WebCodecs)实现硬件加速的视频编解码,并通过 PixiJS 提供高性能的 2D/3D 渲染能力。 资料来源:README.md
系统的设计目标是提供一个纯前端实现的视频编辑框架,使开发者能够在浏览器环境中完成从素材加载、合成编辑到最终 MP4 输出的完整流程。架构上采用分层设计,核心层负责渲染引擎与媒体处理,节点层提供服务端自动化渲染能力。 资料来源:packages/openvideo/package.json:1-14
核心架构分层
OpenVideo 系统采用三层架构设计,从上至下分别为:
| 层级 | 组件 | 职责 |
|---|---|---|
| 应用层 | Studio | 项目状态管理、时间线编排、轨道与剪辑管理 |
| 渲染层 | Compositor | PixiJS 应用初始化、帧渲染、播放控制、导出编码 |
| 媒体层 | Clips、MP4 Utils | 视频/音频/文本等素材解码、MP4 封装与解封装 |
graph TD
A[用户应用] --> B[Studio<br/>项目状态管理层]
B --> C[Compositor<br/>渲染引擎]
C --> D[PixiJS 渲染器]
C --> E[WebCodecs 编码器]
D --> F[Canvas 2D/WebGL]
E --> G[MP4 文件输出]
H[Clips 素材层] --> B
I[Node Renderer<br/>服务端自动化] --> E资料来源:README.md:30-45
核心组件详解
Studio(项目状态管理)
Studio 是整个系统的状态管理中心,负责管理项目配置、轨道布局、剪辑排列以及时间线相关的逻辑。它维护着项目中所有的轨道(Track)和剪辑(Clip)对象,并协调它们与 Compositor 之间的数据交互。
主要职责包括:
- 项目配置管理:存储和管理视频的全局设置(如分辨率、帧率、背景色等)
- 轨道管理:维护多个轨道的层级关系,处理轨道的添加、删除、排序
- 剪辑调度:管理每个轨道上的剪辑对象,处理剪辑之间的对齐、重叠逻辑
- 播放控制:协调 Compositor 的播放、暂停、寻址操作
Studio 的设计使得开发者可以通过声明式的方式定义视频结构,然后交由下层的渲染引擎执行具体的画面合成。 资料来源:packages/openvideo/src/studio.ts
Compositor(渲染引擎)
Compositor 是整个系统的核心渲染引擎,负责将 Studio 管理的轨道和剪辑数据转换为可视化的帧序列,并最终输出为 MP4 文件。它是衔接上层业务逻辑与底层媒体 API 的关键桥梁。
graph LR
A[轨道数据] --> B[Compositor]
B --> C[PixiJS 应用]
C --> D[Canvas 渲染]
B --> E[VideoFrame]
E --> F[WebCodecs<br/>VideoEncoder]
F --> G[MP4 Muxer]
G --> H[output.mp4]Compositor 的初始化过程如下:
- 创建 WebCodecs VideoEncoder 和 AudioEncoder 实例
- 初始化 PixiJS Application 作为 2D 渲染上下文
- 加载并解析所有轨道上的剪辑资源
- 设置事件监听器用于进度报告和错误处理
渲染流程中,Compositor 按照指定帧率(默认 30fps)驱动 PixiJS 渲染每一帧画面,同时将 VideoFrame 数据送入 VideoEncoder 进行编码。编码后的样本(Sample)被传递给 MP4 封装器生成最终的输出文件。 资料来源:packages/openvideo/src/compositor.ts
Compositor 的核心配置选项:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| width | number | 1280 | 输出视频宽度(像素) |
| height | number | 720 | 输出视频高度(像素) |
| fps | number | 30 | 输出帧率 |
| bgColor | string | '#000000' | 背景颜色 |
| videoCodec | string | - | 视频编码器标识 |
| bitrate | number | - | 视频码率(bps) |
| audio | boolean | true | 是否包含音频轨道 |
| metaDataTags | object | - | MP4 元数据标签 |
资料来源:packages/node/src/template.html:20-40
Clips(素材剪辑)
Clips 是处理各类媒体素材的专门对象体系,每种媒体类型对应一个独立的 Clip 类。Clip 对象负责素材的加载、解码、以及在渲染时向 Compositor 提供对应的画面数据。
#### 剪辑类型体系
| 剪辑类型 | 类名 | 功能描述 |
|---|---|---|
| 视频剪辑 | Video | 加载并解码 MP4/WebM 视频流 |
| 音频剪辑 | Audio | 处理音频轨道和音量控制 |
| 文本剪辑 | Text | 渲染文字内容,支持样式和动画 |
| 图像剪辑 | Image | 加载并显示静态图片 |
| 字幕剪辑 | Caption | 时间轴驱动的字幕渲染 |
#### Video Clip 实现
Video Clip 是最复杂的剪辑类型,它整合了资源加载、流解码和帧提取三个环节。核心逻辑位于 video-clip.ts:
graph TD
A[Video.fromUrl<br/>静态工厂方法] --> B[ResourceManager<br/>获取可读流]
B --> C[new Video<br/>实例化剪辑]
C --> D[write<br/>写入 OPFS]
D --> E[ready promise<br/>等待解码完成]
E --> F[本地文件引用]构造函数接受三种数据源类型:
ReadableStream<Uint8Array>- 直接传入的流数据OPFSToolFile- 已存在于 OPFS(Origin Private File System)中的文件MPClipCloneArgs- 用于克隆已有剪辑状态的参数对象
音频处理通过 opts.audio 参数控制,支持布尔值和对象两种配置方式。当为对象时,可通过 volume 属性独立控制音量。 资料来源:packages/openvideo/src/clips/video-clip.ts:50-80
#### Text Clip 实现
Text Clip 负责文字内容的渲染,其样式系统基于 PixiJS 的 TextStyle 构建,支持丰富的排版选项:
- 字体样式:fontFamily、fontWeight、fontStyle
- 颜色填充:支持纯色和渐变填充(gradient)
- 描边效果:stroke 对象定义描边颜色、宽度、连接方式和尖角样式
- 阴影效果:dropShadow 配置阴影颜色、透明度、模糊度和偏移
渐变填充需要使用 FillGradient 类创建,通过 x0、y0、x1、y1 定义渐变向量方向,色彩数组通过 ratio 和 color 指定各节点的位置和颜色。 资料来源:packages/openvideo/src/clips/text-clip.ts:60-100
过渡效果系统(Transitions)
Transition 系统负责处理轨道之间的画面切换效果。该系统支持多种内置过渡类型,每种过渡都有对应的 GLSL 着色器实现。
#### 支持的过渡效果
| 过渡名称 | 说明 | 匹配变体 |
|---|---|---|
| GridFlip | 网格翻转 | gridflip、GridFlip、grid_flip |
| Circle | 圆形展开 | circle、circle_open |
| Directional | 方向性擦除 | directional、directional_wipe |
| UndulatingBurnOut | 波动燃烧 | undulatingburnout、UndulatingBurnOut |
| SquaresWire | 方形线框 | squareswire、SquaresWire |
| RotateScaleFade | 旋转缩放淡入淡出 | rotateScaleFade、rotatescalefade |
| RandomSquares | 随机方块 | randomSquares、RandomSquares |
| PolkaDotsCurtain | 圆点幕布 | polkadotscurtain、PolkaDotsCurtain |
| Pixelize | 像素化 | pixelize、Pixelize |
过渡名称的解析采用宽松匹配策略,支持驼峰命名、下划线分隔、全大写等多种变体形式。解析流程首先在用户传入的过渡对象中查找,然后搜索内置过渡注册表,最后尝试各种命名变体。 资料来源:packages/openvideo/src/transition/transition.ts:1-80
动画预设系统(Animation Presets)
动画预设提供预定义的补间动画(tween)模式,可应用于剪辑的位置、缩放、透明度等属性变化。
#### 支持的预设动画
| 预设名称 | 描述 | 关键帧示例 |
|---|---|---|
| slideIn | 从指定方向滑入 | 0%: 外部位置 → 100%: 目标位置 |
| slideOut | 向指定方向滑出 | 0%: 当前位置 → 100%: 外部位置 |
| pulse | 脉冲缩放效果 | 0%→25%→50%→75%→100% 循环缩放 |
| blurIn | 模糊淡入 | 0%: 高模糊低透明度 → 100%: 无模糊完全透明 |
动画参数通过 normalized 对象自定义,slideIn/slideOut 预设支持:
direction: 方向(left、right、top、bottom)distance: 移动距离(像素)duration: 持续时间(毫秒,由调用方控制) 资料来源:packages/openvideo/src/animation/presets.ts:20-60
媒体封装与解封装
MP4 Utils 模块
MP4 Utils 模块(位于 src/mp4-utils/index.ts)负责 MP4 文件格式的解析和生成,是实现视频导入和导出的底层基础设施。
#### 时间戳规范化
视频和音频样本的时间戳( DTS / CTS )在封装前需要经过规范化处理,确保时间线从零开始:
- 记录第一个视频样本和音频样本的时间戳作为基准
- 所有后续样本的 DTS 和 CTS 减去对应基准值
- 加上调用方指定的偏移量(offsetDTS / offsetCTS)
这种处理方式保证了多轨道合成的正确性,使得音频和视频能够精确同步。 资料来源:packages/openvideo/src/mp4-utils/index.ts:80-120
#### 样本数据结构
样本数据通过 addSample 方法添加到 MP4 文件中,每个样本包含:
| 属性 | 类型 | 说明 |
|---|---|---|
| trackId | number | 所属轨道 ID |
| data | Uint8Array | 编码后的样本数据 |
| duration | number | 样本持续时间 |
| dts | number | 解码时间戳 |
| cts | number | composition 时间戳 |
| is_sync | boolean | 是否为同步帧(关键帧) |
Thumbnail 生成器
视频剪辑包含独立的缩略图生成功能,使用独立的 VideoDecoder 实例进行解码。解码器支持软件降级模式(downgrade = true),当硬件加速不可用时自动回退到软件解码以提高兼容性。
错误处理机制会捕获解码器状态、队列长度和输入输出计数,便于诊断缩略图生成失败的原因。 资料来源:packages/openvideo/src/clips/video-clip.ts:120-160
Node.js 渲染层
Renderer 类架构
Node.js 包(@combo/node)提供了服务端自动化渲染能力,通过 Playwright 控制无头浏览器执行 HTML 模板中的渲染逻辑。
graph TD
A[new Renderer<br/>配置初始化] --> B[render<br/>开始渲染]
B --> C[启动本地 HTTP 服务器]
C --> D[Playwright 启动 Chromium]
D --> E[加载 template.html]
E --> F[注入 RENDER_CONFIG]
F --> G[执行 Compositor<br/>WebCodecs 编码]
G --> H[触发 download 事件]
H --> I[保存文件到 outputPath]
I --> J[返回输出路径]Renderer 继承自 EventEmitter,暴露三个事件:
| 事件名称 | 回调参数 | 触发时机 |
|---|---|---|
| progress | { phase, progress, message } | 各阶段进度更新 |
| error | Error | 渲染过程中发生错误 |
| complete | string | 渲染完成,返回输出文件路径 |
进度报告的阶段(phase)包括:initializing、loading、rendering、saving、complete。 资料来源:packages/node/src/renderer.ts
HTML 渲染模板
渲染模板(template.html)运行在浏览器环境中,负责:
- 从
window.RENDER_CONFIG获取渲染配置 - 创建 Compositor 实例并初始化 PixiJS 应用
- 监听 Compositor 事件并向父进程报告进度
- 触发视频文件下载
模板使用 Import Maps 解析 openvideo 包,确保模块能够正确加载:
<script type="importmap">
{
"imports": {
"openvideo": "/node_modules/openvideo/dist/index.es.js"
}
}
</script>
配置参数通过 JSON 注入,支持设置分辨率、帧率、背景色、视频编码器、码率和元数据标签。 资料来源:packages/node/src/template.html:1-50
技术栈概览
| 技术 | 用途 | 层级 |
|---|---|---|
| WebCodecs | 视频/音频编解码 | 媒体层 |
| PixiJS | 2D 渲染引擎 | 渲染层 |
| Compositor | 帧合成与导出 | 渲染层 |
| wrapbox | MP4 Box 解析与写入 | 媒体层 |
| Playwright | 浏览器自动化控制 | 节点层 |
| TypeScript | 类型安全开发 | 全栈 |
WebCodecs 提供硬件加速的视频编解码能力,是实现高性能渲染的关键技术。PixiJS 则负责处理文本、图像、过渡效果等视觉元素的渲染,两者的结合使得 OpenVideo 能够在浏览器环境中实现接近原生的视频处理性能。 资料来源:README.md:45-55
包导出结构
OpenVideo 主包(openvideo)通过 src/index.ts 统一导出所有公共 API:
| 导出项 | 来源 | 说明 |
|---|---|---|
| Studio | studio.ts | 项目状态管理类 |
| Compositor | compositor.ts | 渲染引擎类 |
| Video | video-clip.ts | 视频剪辑类 |
| Text | text-clip.ts | 文本剪辑类 |
| Caption | caption-clip.ts | 字幕剪辑类 |
| Image | image-clip.ts | 图像剪辑类 |
| Audio | audio-clip.ts | 音频剪辑类 |
| JsonSerialization | serialization.ts | JSON 序列化工具 |
包的入口配置支持 ES Modules 和 CommonJS 两种模块系统:
{
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.es.js",
"require": "./dist/index.umd.js"
}
}
}
资料来源:packages/openvideo/package.json:15-30
数据流总览
graph LR
A[视频素材<br/>MP4/WebM] --> B[Video Clip<br/>解码器]
B --> C[VideoFrame]
D[音频素材<br/>MP4/WAV] --> E[Audio Clip<br/>解码器]
E --> F[AudioFrame]
G[文本内容] --> H[Text Clip<br/>PixiJS 渲染]
H --> I[Sprite/Texture]
C --> J[Compositor<br/>帧合成]
F --> J
I --> J
J --> K[VideoEncoder<br/>WebCodecs]
K --> L[EncodedVideoChunk]
L --> M[MP4 Muxer<br/>wrapbox]
M --> N[output.mp4]整个数据流从素材输入开始,视频和音频素材分别由对应的 Clip 对象负责解码。Text Clip 生成的纹理与解码后的视频帧一同送入 Compositor 进行帧合成。合成后的画面通过 VideoEncoder 编码为压缩的 Chunk,最后由 MP4 Muxer 按照 ISO Base Media File Format 规范封装为最终的 MP4 文件。
资料来源:[README.md:30-45]()
Studio - 项目状态管理
Studio 是 OpenVideo 库的核心模块,负责管理整个视频编辑项目的状态、轨道、剪辑片段和时间线配置。它作为顶层协调器,整合了时间轴模型、资源管理、选择管理、历史记录管理和播放传输等多个子系统,为用户提供完整的视频编辑体验。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Studio 是 OpenVideo 库的核心模块,负责管理整个视频编辑项目的状态、轨道、剪辑片段和时间线配置。它作为顶层协调器,整合了时间轴模型、资源管理、选择管理、历史记录管理和播放传输等多个子系统,为用户提供完整的视频编辑体验。
Studio 模块的核心职责包括:管理项目中的所有剪辑片段、协调各子系统之间的通信、处理播放控制、以及维护项目状态的序列化与反序列化。通过 EventEmitter 模式,Studio 实现了模块间的松耦合通信,确保状态变更能够及时传播到所有监听者。
架构概览
Studio 的架构采用分层设计,每个子系统专注于特定的功能领域。这种设计使得代码职责清晰,便于维护和扩展。
graph TD
A[Studio 核心] --> B[TimelineModel 时间轴模型]
A --> C[ResourceManager 资源管理]
A --> D[SelectionManager 选择管理]
A --> E[HistoryManager 历史管理]
A --> F[Transport 播放传输]
A --> G[PixiJS 渲染引擎]
B --> H[Tracks 轨道]
B --> I[Clips 剪辑片段]
C --> J[文件系统]
C --> K[媒体资源]
F --> L[播放状态]
F --> M[帧控制]
G --> N[Canvas 画布]
G --> O[Sprites 精灵]核心组件
TimelineModel(时间轴模型)
TimelineModel 是 Studio 中最核心的数据结构,负责管理项目的时间线和轨道系统。每个 Studio 实例都持有一个 TimelineModel 实例,通过它可以访问和操作项目中的所有轨道和剪辑片段。
TimelineModel 的主要职责包括:创建和管理轨道、添加和删除剪辑片段、更新剪辑属性、以及处理时间相关的计算。轨道(Track)是组织剪辑片段的容器,支持视频轨道、音频轨道、文字轨道等多种类型。
| 方法 | 描述 | 返回值 |
|---|---|---|
addTrack(type) | 添加指定类型的轨道 | Promise<Track> |
removeTrack(id) | 移除指定轨道 | Promise<void> |
addClip(trackId, clip) | 在轨道中添加剪辑 | Promise<Clip> |
removeClip(clipId) | 移除剪辑片段 | Promise<void> |
updateSelected(updates) | 更新选中的剪辑 | Promise<void> |
clear() | 清空所有轨道和剪辑 | Promise<void> |
资料来源:packages/openvideo/src/studio/timeline-model.ts
ResourceManager(资源管理)
ResourceManager 负责管理系统中的媒体资源,包括视频、音频、图片等文件的加载和缓存。它提供了统一的资源获取接口,支持从 URL、OPFS(Origin Private File System)以及其他来源加载媒体资源。
ResourceManager 使用流式加载机制,能够高效处理大型媒体文件。它还维护了一个资源缓存池,避免重复加载相同的资源。当项目序列化时,ResourceManager 负责将资源引用转换为可序列化的格式,并在反序列化时重建资源连接。
// 从 URL 加载视频资源
const stream = await ResourceManager.getReadableStream(url);
// 加载并配置视频剪辑
const clip = new Video(stream, {}, url);
资料来源:packages/openvideo/src/studio/resource-manager.ts
HistoryManager(历史管理)
HistoryManager 实现了撤销/重做功能,允许用户回退到之前的操作状态。它维护了一个操作历史栈,记录每次状态变更的快照。
每次用户执行修改操作时(如移动剪辑、调整属性等),HistoryManager 会自动保存当前状态的快照。当用户请求撤销时,系统会从历史栈中弹出上一个状态并恢复。类似地,重做功能会重新应用被撤销的操作。
| 属性 | 类型 | 描述 |
|---|---|---|
canUndo | boolean | 是否可以执行撤销 |
canRedo | boolean | 是否可以执行重做 |
historyStack | StateSnapshot[] | 历史状态栈 |
资料来源:packages/openvideo/src/studio/history-manager.ts
SelectionManager(选择管理)
SelectionManager 负责处理剪辑片段的选中状态和变换操作。当用户在画布上点击剪辑时,SelectionManager 会确定被选中的剪辑,并激活相应的变换控制器(Transformer)。
变换控制器支持多种操作模式:移动(无手柄)、调整大小(四角和四边手柄)、旋转(角落外侧手柄)。SelectionManager 还实现了多选功能,通过 Shift 键可以同时选中多个剪辑,并对它们应用统一的变换操作。
// 双击处理
if (topmostClip.type === 'Text' || topmostClip.type === 'Caption') {
this.studio.emit('clip:dblclick', { clip: topmostClip });
}
// 多选支持
this.selectClip(topmostClip, e.shiftKey);
资料来源:packages/openvideo/src/studio/selection-manager.ts:60-75
Transport(播放传输)
Transport 模块控制项目的播放状态,包括播放、暂停、停止和跳转等操作。它维护了当前播放头位置,并负责协调渲染引擎按照指定的时间点进行画面渲染。
Transport 提供了精细的帧级控制能力,支持逐帧前进和后退。这对于精确编辑和对齐剪辑片段非常有帮助。时间单位统一使用微秒(microseconds),确保时间计算的精度。
| 方法 | 描述 |
|---|---|
play() | 开始播放 |
pause() | 暂停播放 |
stop() | 停止播放并重置到开始位置 |
seek(time) | 跳转到指定时间(微秒) |
frameNext() | 前进一帧 |
framePrev() | 后退一帧 |
资料来源:packages/openvideo/src/studio/transport.ts
状态管理机制
事件驱动架构
Studio 继承自 EventEmitter,实现了完整的事件驱动架构。所有状态变更都通过事件进行传播,监听者可以订阅感兴趣的事件并做出响应。
graph LR
A[状态变更] --> B[emit 事件]
B --> C[EventEmitter]
C --> D[进度监听器]
C --> E[完成监听器]
C --> F[错误监听器]
C --> G[重置监听器]Studio 核心事件列表:
| 事件名 | 回调参数 | 描述 |
|---|---|---|
progress | { phase, progress, message } | 渲染进度更新 |
complete | outputPath | 渲染完成 |
error | Error | 错误发生 |
reset | - | 项目重置/清空 |
clip:dblclick | { clip } | 剪辑片段双击 |
状态重置流程
当调用 clear() 方法时,Studio 会执行完整的状态重置流程。这包括清空时间轴、销毁 PixiJS 纹理资源、清理过渡渲染器等。
async clear(): Promise<void> {
// 清空时间轴
await this.timeline.clear();
// 销毁过渡纹理
if (this.transFromTexture) {
this.transFromTexture.destroy(true);
this.transFromTexture = null;
}
if (this.transToTexture) {
this.transToTexture.destroy(true);
this.transToTexture = null;
}
// 销毁过渡渲染器和精灵
this.transitionRenderers.forEach((r: any) => r.destroy());
this.transitionRenderers.clear();
this.transitionSprites.forEach((s) => s.destroy());
this.transitionSprites.clear();
this.emit("reset");
}
资料来源:packages/openvideo/src/studio.ts:280-302
PixiJS 渲染集成
Studio 与 PixiJS 紧密集成,利用其强大的 2D 渲染能力来显示和操作项目中的视觉元素。每个剪辑片段都被渲染为 PixiJS 的 Sprite(精灵)对象,支持缩放、旋转、透明度等变换操作。
渲染管道的主要组件包括:
- SpriteRenderers:负责将每个剪辑渲染为对应的 Sprite
- TransitionRenderers:处理过渡效果的专用渲染器
- ActiveTransformer:用户交互时的变换控制器
- Artboard:画布容器,承载所有视觉元素
渲染状态通过 pixiApp.render() 方法触发更新,确保画面与数据状态保持同步。
使用示例
基础使用
import { Studio } from 'openvideo';
// 创建 Studio 实例
const studio = new Studio({
previewCanvas: document.getElementById('preview-canvas') as HTMLCanvasElement,
spacing: 20
});
// 添加剪辑
const video = await Video.fromUrl('https://example.com/video.mp4');
await studio.addClip(video);
// 开始预览
studio.play();
进度跟踪
studio.on('progress', ({ phase, progress, message }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}% - ${message}`);
});
studio.on('complete', (outputPath) => {
console.log(`✅ 视频保存至: ${outputPath}`);
});
studio.on('error', (error) => {
console.error('渲染失败:', error);
});
相关文档
资料来源:[packages/openvideo/src/studio/timeline-model.ts]()
Compositor - 渲染引擎
Compositor(合成器)是 OpenVideo 项目中的核心渲染引擎,负责视频的最终合成、编码和导出工作。它基于 WebCodecs API 实现硬件加速的视频编码,结合 PixiJS 进行 2D/3D 图形渲染,为开发者提供高性能的客户端视频渲染能力。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Compositor(合成器)是 OpenVideo 项目中的核心渲染引擎,负责视频的最终合成、编码和导出工作。它基于 WebCodecs API 实现硬件加速的视频编码,结合 PixiJS 进行 2D/3D 图形渲染,为开发者提供高性能的客户端视频渲染能力。
Compositor 的核心职责包括:
- 初始化 PixiJS 应用作为渲染画布
- 接收来自 Studio 的视频帧数据进行合成
- 使用 WebCodecs VideoEncoder 将帧编码为 MP4 格式
- 管理音频轨道和音视频同步
- 支持多种视频编码器和比特率配置
资料来源:packages/openvideo/render.html:42-60
架构设计
组件层级关系
Compositor 在整个渲染管线中处于下游位置,接收来自 Studio 和各种 Clip(视频、文本、图像等)的渲染数据。
graph TD
A[Studio 项目容器] --> B[Timeline 时间轴]
B --> C[Track 轨道]
C --> D[Clip 媒体片段]
D --> E[VideoClip 视频片段]
D --> F[TextClip 文本片段]
D --> G[ImageClip 图像片段]
D --> H[CaptionClip 字幕片段]
D --> I[AudioClip 音频片段]
E --> J[Compositor 渲染引擎]
F --> J
G --> J
H --> J
I --> J
J --> K[PixiJS 渲染层]
J --> L[WebCodecs VideoEncoder]
J --> M[MP4 Muxer]
K --> N[Canvas 画布渲染]
L --> O[Encoded Video Frames]
M --> P[output.mp4]渲染流程
sequenceDiagram
participant User as 用户代码
participant Studio as Studio
participant Compositor as Compositor
participant PixiJS as PixiJS
participant Encoder as VideoEncoder
participant Muxer as MP4 Muxer
User->>Compositor: new Compositor(options)
Compositor->>PixiJS: initPixiApp()
PixiJS-->>Compositor: App initialized
User->>Studio: addClip(clip)
Studio->>Compositor: 注册渲染任务
User->>Compositor: 开始播放/render
Compositor->>PixiJS: 逐帧渲染
PixiJS-->>Compositor: VideoFrame
Compositor->>Encoder: encode(frame)
Encoder-->>Compositor: EncodedChunk
Compositor->>Muxer: addSample()
Muxer-->>Compositor: MP4 Container
Compositor-->>User: 输出文件路径初始化配置
构造函数选项
创建 Compositor 实例时需要传入配置对象,控制输出视频的基本参数。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width | number | 1280 | 输出视频宽度(像素) |
height | number | 720 | 输出视频高度(像素) |
fps | number | 30 | 输出帧率(fps) |
bgColor | string | #000000 | 背景颜色(十六进制) |
videoCodec | string | - | 视频编码器标识符 |
bitrate | number | - | 视频比特率(bps) |
audio | boolean | true | 是否包含音频轨道 |
metaDataTags | object | - | MP4 元数据标签 |
资料来源:packages/openvideo/render.html:35-50
初始化流程
// 典型初始化代码
const compositorOpts = {
width: settings.width || 1280,
height: settings.height || 720,
fps: settings.fps || 30,
bgColor: settings.bgColor || '#000000',
};
if (settings.videoCodec) {
compositorOpts.videoCodec = settings.videoCodec;
}
if (settings.bitrate) {
compositorOpts.bitrate = settings.bitrate;
}
if (settings.audio === false) {
compositorOpts.audio = false;
}
if (settings.metaDataTags) {
compositorOpts.metaDataTags = settings.metaDataTags;
}
const compositor = new Compositor(compositorOpts);
await compositor.initPixiApp();
资料来源:packages/node/src/template.html:38-58
事件系统
Compositor 继承自 EventEmitter,提供了完善的事件机制用于监控渲染进度。
OutputProgress 事件
用于追踪视频编码进度:
compositor.on('OutputProgress', (progress: {
progress: number; // 0-1 之间的进度值
phase: string; // 当前阶段
message?: string; // 可选的状态消息
}) => {
console.log(`[${phase}] ${Math.round(progress * 100)}%`);
});
渲染阶段
| 阶段标识 | 说明 |
|---|---|
initializing | 初始化阶段 |
loading | 加载资源阶段 |
rendering | 正在渲染阶段 |
saving | 保存文件阶段 |
complete | 完成阶段 |
视频编码底层实现
MP4 样本处理
Compositor 内部使用 MP4-box 库处理 MP4 容器格式,核心功能包括:
- 时间戳归一化:将所有样本的时间戳归一化到以 0 为起点
- 轨道管理:分别为视频和音频创建独立的 track
- 样本交织:正确处理 DTS(解码时间戳)和 CTS(组合时间戳)
// 时间戳归一化逻辑
if (firstVDTS === null) {
firstVDTS = s.dts;
firstVCTS = s.cts;
}
// 归一化到从 0 开始,然后加上偏移
normalizedDTS = s.dts - firstVDTS!;
normalizedCTS = s.cts - (firstVCTS ?? 0);
资料来源:packages/openvideo/src/mp4-utils/index.ts:45-65
视频解码与降级
对于缩略图生成等场景,Compositor 依赖的解码器支持硬件加速降级:
function createVideoDec(downgrade = false) {
const encoderConf = {
...decConf,
...(downgrade ? { hardwareAcceleration: "prefer-software" } : {}),
} as VideoDecoderConfig;
// 首次尝试硬件加速,失败后降级到软件解码
}
资料来源:packages/openvideo/src/clips/video-clip.ts:89-105
渲染模式与动画支持
动画预设
Compositor 支持丰富的动画预设,通过 PixiJS 实现平滑过渡效果:
| 动画类型 | 效果说明 |
|---|---|
fadeIn | 淡入效果 |
fadeOut | 淡出效果 |
slideIn | 滑入效果(支持方向和距离配置) |
slideOut | 滑出效果 |
pulse | 脉冲效果 |
blurIn | 模糊淡入 |
blurOut | 模糊淡出 |
// 滑出动画配置示例
case "slideOut": {
const direction = normalized?.direction || "left";
const distance = normalized?.distance || 300;
return {
"0%": { x: xPositionInit ?? 0, opacity: opacityInit ?? 1 },
"100%": {
x: direction === "left" ? -distance : distance,
opacity: opacityEnd ?? 0,
},
};
}
资料来源:packages/openvideo/src/animation/presets.ts:45-70
转场效果
Compositor 支持多种转场效果,通过 GLSL Shader 实现:
| 转场名称 | 类型标识 | 说明 |
|---|---|---|
GridFlip | gridflip | 网格翻转 |
CircleOpen | circleopen | 圆形展开 |
Directional | directional | 方向性渐变 |
UndulatingBurnOut | undulatingburnout | 波浪燃烧 |
SquaresWire | squareswire | 方块线条 |
RotateScaleFade | rotatescalefade | 旋转缩放淡入淡出 |
PolkaDotsCurtain | polkadotscurtain | 圆点幕布 |
Pixelize | pixelize | 像素化 |
Heart | heart | 心形效果 |
Displacement | displacement | 置换效果 |
DirectionalWipe | directionalwipe | 方向性擦除 |
Crosshatch | crosshatch | 十字阴影 |
if (transitionCircleOpen) {
transitionGlsl = CIRCLE_FRAGMENT;
transitionUniforms = {
...uniforms.basics,
...CIRCLEOPEN_UNIFORMS,
};
}
资料来源:packages/openvideo/src/transition/transition.ts:35-50
文本渲染
Compositor 通过 TextClip 支持丰富的文本样式配置:
| 样式属性 | 类型 | 说明 | |
|---|---|---|---|
fontSize | number | 字体大小 | |
fontFamily | string | 字体系列 | |
fontWeight | `string\ | number` | 字重 |
fontStyle | string | 字体样式(normal/italic) | |
fill | `string\ | number` | 填充颜色 |
align | string | 文本对齐 | |
wordWrap | boolean | 是否自动换行 | |
wordWrapWidth | number | 换行宽度 | |
lineHeight | number | 行高 | |
letterSpacing | number | 字间距 | |
stroke | object | 描边配置 | |
dropShadow | object | 阴影配置 |
if (this.originalOpts.stroke) {
if (typeof this.originalOpts.stroke === 'object') {
style.stroke = {
color: this.originalOpts.stroke.color,
width: this.originalOpts.stroke.width,
join: this.originalOpts.stroke.join,
cap: this.originalOpts.stroke.cap,
miterLimit: this.originalOpts.stroke.miterLimit,
};
}
}
资料来源:packages/openvideo/src/clips/text-clip.ts:85-105
字幕支持
CaptionClip 提供专业的字幕渲染功能,支持动态颜色变化和词动画:
// 字幕颜色状态配置
if (processedOpts.caption?.colors?.appeared !== undefined)
this.opts.appeared = processedOpts.caption.colors.appeared;
if (processedOpts.caption?.colors?.active !== undefined)
this.opts.active = processedOpts.caption.colors.active;
if (processedOpts.caption?.colors?.keyword !== undefined)
this.opts.keyword = processedOpts.caption.colors.keyword;
支持的功能包括:
- 渐变色填充文本
- 关键词高亮颜色保持
- 单词级动画效果
- 背景色和描边样式
资料来源:packages/openvideo/src/clips/caption-clip.ts:50-75
使用示例
基础渲染流程
import { Compositor } from 'openvideo';
async function renderVideo() {
const compositor = new Compositor({
width: 1920,
height: 1080,
fps: 30,
bgColor: '#000000',
bitrate: 5_000_000,
});
await compositor.initPixiApp();
compositor.on('OutputProgress', (progress) => {
console.log(`[${progress.phase}] ${Math.round(progress.progress * 100)}%`);
});
// 添加片段并触发渲染...
}
与 Renderer 集成
在 Node.js 环境下,通常使用 Renderer 类包装 Compositor:
import { Renderer } from '@combo/node';
const renderer = new Renderer({
json: videoConfig,
outputPath: './output.mp4',
});
renderer.on('progress', ({ phase, progress, message }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}% - ${message}`);
});
await renderer.render();
console.log('视频渲染完成!');
资料来源:packages/node/README.md:90-120
总结
Compositor 是 OpenVideo 项目的核心渲染引擎,通过整合 WebCodecs 和 PixiJS 两大技术栈,为开发者提供了:
- 高性能编码:利用浏览器原生 WebCodecs API 实现硬件加速视频编码
- 灵活配置:支持多种分辨率、帧率、编码器和比特率组合
- 丰富视觉效果:内置大量动画预设和转场效果
- 完善的事件系统:实时反馈渲染进度,便于构建用户界面
- 跨平台兼容:基于 Web 标准,可在桌面和移动浏览器中运行
Compositor 的设计遵循模块化原则,与上层的 Studio、Clip 系统松耦合,同时通过标准化的事件机制保证各组件间的有效通信。
资料来源:[packages/openvideo/render.html:42-60]()
剪辑系统
剪辑系统(Clip System)是 OpenVideo 项目的核心模块之一,负责管理和处理各种类型的媒体资源。该系统采用面向对象的设计思想,通过统一的接口定义和继承体系,支持视频、音频、文本、图像、字幕等多种媒体类型的剪辑操作。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
剪辑系统(Clip System)是 OpenVideo 项目的核心模块之一,负责管理和处理各种类型的媒体资源。该系统采用面向对象的设计思想,通过统一的接口定义和继承体系,支持视频、音频、文本、图像、字幕等多种媒体类型的剪辑操作。
剪辑系统的设计目标是为开发者提供一个灵活、可扩展的媒体处理框架,使得创建复杂的视频编辑功能变得简单直观。系统通过抽象基类和接口定义,确保了不同类型剪辑之间的一致性,同时允许各类型保留其特有的属性和行为。
架构设计
类层次结构
剪辑系统采用经典的继承层次结构,从顶层到底层依次为:接口层(IClip)、抽象基类层(BaseClip)、具体实现类层。每一层都有明确的职责划分,下层继承并扩展上层的功能。
graph TD
A[IClip 接口] --> B[BaseClip 抽象基类]
B --> C[VideoClip 视频剪辑]
B --> D[AudioClip 音频剪辑]
B --> E[TextClip 文本剪辑]
B --> F[ImageClip 图像剪辑]
B --> G[CaptionClip 字幕剪辑]
B --> H[EffectClip 特效剪辑]
B --> I[TransitionClip 过渡剪辑]
C --> C1[fromUrl 静态方法]
C --> C2[本地文件处理]
H --> H1[内置特效]
H --> H2[自定义特效]
I --> I1[过渡效果名称匹配]
I --> I2[GLSL着色器实现]核心接口 IClip
IClip 接口是整个剪辑系统的契约定义,所有剪辑类型都必须实现该接口规定的方法和属性。该接口定义了剪辑的基本行为规范,包括时间定位、状态管理、事件处理等核心功能。通过实现 IClip 接口,不同类型的剪辑可以在统一的方式下被 Studio 系统管理和调度。
接口的设计遵循了最小化原则,仅包含最核心的方法签名,具体实现细节由各个子类自行决定。这种设计使得系统具有高度的灵活性,同时保持了接口的一致性和可预测性。
抽象基类 BaseClip
BaseClip 提供了所有剪辑类型的共同实现,是系统的核心基类。该类封装了剪辑共有的属性和方法,包括时间轴控制、位置尺寸设置、样式配置等通用功能。子类通过继承 BaseClip 自动获得这些基础能力,只需关注其特有的业务逻辑。
BaseClip 的设计体现了代码复用原则,避免了子类之间的重复代码。同时,该基类预留了大量可扩展的钩子方法,允许子类在特定生命周期节点插入自定义逻辑。
剪辑类型详解
视频剪辑 VideoClip
视频剪辑是系统中最重要的剪辑类型之一,负责处理视频媒体资源。该类型支持从网络 URL 创建实例,并自动下载和管理本地文件。VideoClip 提供了灵活的音视频控制选项,开发者可以独立控制音频的开关和音量大小。
// 视频剪辑创建示例
const video = await Video.fromUrl('https://example.com/video.mp4', {
x: 100,
y: 200,
width: 1280,
height: 720
});
studio.addClip(video);
视频剪辑支持从 OPFS(Origin Private File System)文件、ReadableStream 或已存在的视频样本数据三种方式创建实例。这种多源支持的设计使得系统可以灵活应对不同的视频数据来源,无论是实时流、本地文件还是内存中的视频数据都能得到妥善处理。
| 属性/方法 | 类型 | 说明 |
|---|---|---|
| fromUrl | 静态方法 | 从网络 URL 创建视频剪辑 |
| src | string | 视频源路径 |
| audio | boolean | 是否包含音频轨道 |
| volume | number | 音量大小(0-1) |
| localFile | OPFSToolFile | 本地缓存文件引用 |
文本剪辑 TextClip
文本剪辑用于在视频中添加文字内容。该类型支持丰富的样式配置,包括字体、字号、颜色、对齐方式、字间距、行高等属性。文本剪辑还支持文字换行功能,通过 wordWrapWidth 属性可以控制文字的最大宽度。
文本剪辑的一个重要特性是支持文字描边(stroke)和投影(dropShadow)效果。描边可以通过颜色和宽度进行自定义配置,而投影效果则支持颜色、透明度、模糊度、距离和角度等多个参数的自由组合。
字幕剪辑 CaptionClip
字幕剪辑是专门为视频字幕场景设计的类型,它在 TextClip 的基础上增加了逐词高亮显示的动画功能。该类型支持词汇级别的颜色控制,可以为已显示词汇、当前活跃词汇、关键词等分别设置不同的颜色方案。
| 颜色配置项 | 说明 |
|---|---|
| appeared | 已显示完成的词汇颜色 |
| active | 当前正在播放的词汇颜色 |
| activeFill | 当前词汇的填充颜色 |
| background | 背景高亮颜色 |
| keyword | 关键词颜色 |
字幕剪辑还支持逐词动画效果(wordAnimation),可以为每个词汇的显示配置不同的动画样式。这种精细的控制能力使得字幕展示可以达到接近专业视频编辑软件的动态效果。
特效剪辑 EffectClip
特效剪辑用于在视频轨道上添加视觉特效。该类型支持多种内置特效,包括常见的淡入淡出、滑动、脉冲等预设动画。特效剪辑的设计允许开发者通过简单的配置参数快速应用复杂的视觉效果。
// 预设特效配置示例
const presetAnimation = {
preset: "blurIn",
params: {
blurInit: 20,
blurEnd: 0,
opacityInit: 0,
opacityEnd: 1
}
};
特效剪辑还支持多个方向的配置选项,包括左、右、上、下四个方向,以及不同的距离参数。这种灵活性使得特效的展示方式可以根据具体需求进行精确调整。
过渡剪辑 TransitionClip
过渡剪辑用于实现两个剪辑之间的切换效果。系统支持多种过渡类型,包括网格翻转、圆形展开、方向擦拭、不规则燃烧等多种视觉效果。过渡剪辑通过名称匹配机制识别所需的过渡效果,支持多种命名变体的识别。
graph LR
A[剪辑 A] --> B[TransitionClip]
C[剪辑 B] --> B
B --> D[过渡效果输出]
B --> E[名称匹配]
B --> F[GLSL 着色器]
B --> G[Uniform 参数]过渡效果的实现依赖于 WebGL 着色器(Fragment Shader),每个过渡效果都有对应的 GLSL 代码和 Uniform 参数定义。这种基于着色器的实现方式确保了过渡效果的高性能和硬件加速支持。
状态管理
生命周期状态
剪辑对象在运行时有多个生命周期状态,系统通过精细的状态转换机制确保对象在正确的时间执行相应的操作。状态转换遵循严格的规则,不允许跳跃式状态变更。
stateDiagram-v2
[*] --> 创建中: 构造函数
创建中 --> 已就绪: 初始化完成
已就绪 --> 播放中: play()
播放中 --> 已暂停: pause()
已暂停 --> 播放中: play()
播放中 --> 已结束: 时间到达终点
已结束 --> [*]: 销毁
已就绪 --> [*]: 销毁事件系统
剪辑系统使用事件发射器模式进行状态通知。主要事件包括进度事件(OutputProgress)、错误事件(error)和完成事件(complete)。开发者可以通过监听这些事件来跟踪剪辑的渲染进度或处理异常情况。
// 事件监听示例
clip.on('progress', ({ phase, progress, message }) => {
console.log(`[${phase}] ${Math.round(progress * 100)}% - ${message}`);
});
clip.on('error', (error) => {
console.error('处理失败:', error);
});
clip.on('complete', () => {
console.log('剪辑处理完成');
});
渲染流程
渲染阶段划分
视频渲染过程分为多个阶段,每个阶段都有明确的职责和输出:
| 阶段名称 | 说明 | 典型耗时 |
|---|---|---|
| initializing | 初始化渲染器 | 1-5秒 |
| loading | 加载媒体资源 | 取决于网络 |
| rendering | 执行渲染计算 | 取决于视频时长 |
| saving | 保存输出文件 | 取决于文件大小 |
| complete | 渲染完成 | - |
渲染配置选项
Compositor 是实际执行渲染的核心类,它接收多种配置参数来控制输出视频的属性:
const compositorOpts = {
width: 1280, // 输出宽度
height: 720, // 输出高度
fps: 30, // 帧率
bgColor: '#000000', // 背景颜色
videoCodec: 'avc1.640028', // 视频编码器
bitrate: 5000000, // 比特率
audio: true, // 是否包含音频
metaDataTags: { // 元数据标签
title: 'My Video',
author: 'Creator'
}
};
样式配置系统
文字样式
文字样式的配置采用分层设计,从基类继承通用样式属性,同时允许子类添加特定样式选项。主要的文字样式属性包括:
- fontFamily:字体家族
- fontSize:字号大小
- fontWeight:字重粗细
- fontStyle:斜体/正常
- fill:填充颜色
- align:对齐方式
- letterSpacing:字间距
- lineHeight:行高
- wordWrap:自动换行
- wordWrapWidth:换行宽度
颜色系统
系统支持多种颜色表示方式,包括十六进制颜色代码和数字格式。在字幕剪辑中,颜色配置被组织为嵌套结构,支持默认颜色、活跃颜色、背景颜色等多个维度的独立控制。
渐变填充
文本填充除了支持纯色外,还支持渐变色填充。渐变通过起点坐标(x0, y0)和终点坐标(x1, y1)定义方向,通过颜色数组和对应的比例值定义渐变色分布。
与 Studio 的集成
添加和移除剪辑
Studio 类提供了 addClip 和 removeClip 方法用于管理项目中的剪辑。添加的剪辑会被自动编排到时间轴上,并根据其时间属性决定播放顺序。
// 剪辑添加示例
const text = new Text("Hello World");
text.duration = 5e6; // 5秒(微秒单位)
await studio.addClip(text);
// 移除剪辑
await studio.removeClip(text.id);
时间轴控制
Studio 负责管理整个项目的时间轴配置,包括总时长、帧率等参数。每个添加到 Studio 的剪辑都会继承项目级的时间轴设置,同时保留覆盖这些设置的能力。
扩展机制
自定义特效
系统提供了扩展接口允许开发者添加自定义特效。通过实现 EffectClip 的扩展接口,可以注册新的预设动画效果,这些效果会自动出现在可用的特效列表中。
自定义过渡
类似地,开发者可以通过扩展 TransitionClip 添加新的过渡效果。新的过渡效果需要提供对应的 GLSL 着色器代码和 Uniform 参数定义,完成注册后即可通过名称在项目中使用。
总结
剪辑系统是 OpenVideo 项目的基础模块,通过精心设计的类层次结构和接口规范,实现了多类型媒体的统一管理。该系统提供了从基础的视频、音频剪辑到高级的字幕、特效、过渡等多种功能,覆盖了视频编辑的核心需求。
系统的架构设计充分考虑了扩展性和性能要求,通过基于 WebCodecs 和 WebGL 的底层实现,确保了视频处理的效率和跨平台兼容性。事件驱动的设计模式使得渲染进度的监控和错误处理变得简单可靠。
来源:https://github.com/openvideodev/openvideo / 项目说明书
视频与音频剪辑
视频与音频剪辑是 openvideo 项目的核心功能模块,负责管理和处理各类媒体资源。该模块基于 WebCodecs API 实现硬件加速的视频解码与编码,支持从远程 URL 或本地文件系统加载媒体文件,并通过 PixiJS 渲染引擎实现高性能的 2D/3D 合成。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
视频与音频剪辑是 openvideo 项目的核心功能模块,负责管理和处理各类媒体资源。该模块基于 WebCodecs API 实现硬件加速的视频解码与编码,支持从远程 URL 或本地文件系统加载媒体文件,并通过 PixiJS 渲染引擎实现高性能的 2D/3D 合成。
剪辑系统采用面向对象的设计模式,定义了统一的 BaseClip 基类,所有具体剪辑类型(如 Video、Audio、Image、Text、Caption)都继承自该基类。这种设计确保了统一的接口规范和一致的渲染行为。
核心类架构
类继承体系
graph TD
A[IClip 接口] --> B[BaseClip]
B --> C[Video]
B --> D[Audio]
B --> E[Image]
B --> F[Text]
B --> G[Caption]
B --> H[Placeholder]
C -.->|实现| I[IPlaybackCapable]
J[Compositor] --> K[渲染引擎]
C --> K
D --> K
E --> K
F --> K
G --> K主要类说明
| 类名 | 职责 | 源码位置 |
|---|---|---|
Video | 视频解码、帧提取、缩略图生成 | video-clip.ts:1-100 |
Audio | 音频解码、音量控制 | audio-clip.ts |
Image | 静态图像加载与渲染 | image-clip.ts |
Text | 文本渲染与排版 | text-clip.ts |
Caption | 字幕样式与动画 | caption-clip.ts |
Video 剪辑详解
基本用法
Video 类是视频处理的核心组件,支持从 URL 异步加载 MP4 文件,并提供精确的帧级控制。
// 从 URL 加载视频
const videoClip = await Video.fromUrl('https://example.com/video.mp4', {
x: 0,
y: 0,
width: 1920,
height: 1080,
});
// 设置时间轴位置
videoClip.set({
display: {
from: 150, // 起始帧
to: 450, // 结束帧(30fps 下为 10 秒)
},
});
构造函数参数
constructor(
source: OPFSToolFile | ReadableStream<Uint8Array> | MPClipCloneArgs,
opts: IMP4ClipOpts = {},
src?: string,
)
| 参数 | 类型 | 说明 | 默认值 | ||
|---|---|---|---|---|---|
source | `OPFSToolFile \ | ReadableStream \ | MPClipCloneArgs` | 视频数据源 | 必需 |
opts | IMP4ClipOpts | 配置选项 | {} | ||
src | string | 视频源 URL | "" |
配置选项 (IMP4ClipOpts)
interface IMP4ClipOpts {
audio?: boolean | { volume: number };
__unsafe_hardwareAcceleration__?: HardwarePreference;
}
| 选项 | 类型 | 说明 | |
|---|---|---|---|
audio | `boolean \ | { volume: number }` | 音频开关或音量配置(0-1) |
__unsafe_hardwareAcceleration__ | HardwarePreference | 硬件加速偏好设置 |
资料来源:video-clip.ts:50-70
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
type | "Video" | 剪辑类型标识 |
src | string | 视频源 URL |
audio | boolean | 是否启用音频 |
volume | number | 音频音量(0-1) |
ready | Promise<void> | 加载完成状态 |
_meta | object | 元数据(duration, width, height) |
帧数据处理流程
sequenceDiagram
participant User as 用户
participant Video as Video 类
participant ResourceManager as ResourceManager
participant Compositor as Compositor
User->>Video: fromUrl(url)
Video->>ResourceManager: getReadableStream(url)
ResourceManager-->>Video: ReadableStream
Video->>Video: write(localFile, stream)
Video->>Video: await ready
Video->>Compositor: 渲染帧
Compositor-->>User: 显示视频视频帧数据经过以下处理流程:
- 加载阶段:通过
ResourceManager.getReadableStream()获取可读流 - 存储阶段:写入本地 OPFS 文件系统
- 解码阶段:使用 WebCodecs VideoDecoder 进行 MP4 解析
- 渲染阶段:通过 Compositor 和 PixiJS 渲染到画布
Audio 剪辑
音频配置
音频剪辑支持灵活的音量控制和播放参数:
// 启用音频
const video = await Video.fromUrl('video.mp4', {
audio: true,
});
// 音量控制
const videoWithVolume = await Video.fromUrl('video.mp4', {
audio: {
volume: 0.5, // 50% 音量
},
});
// 禁用音频
const videoNoAudio = await Video.fromUrl('video.mp4', {
audio: false,
});
音频参数处理逻辑如下:
this.audio = typeof this.opts.audio === "boolean"
? this.opts.audio
: true;
this.volume = typeof opts.audio === "object" && "volume" in opts.audio
? opts.audio.volume
: ((opts as any).volume ?? 1);
资料来源:video-clip.ts:75-90
Image 剪辑
图像加载
Image 类支持从 URL 或 data URI 加载静态图像:
// 从 URL 加载
const image = await ImageClip.fromUrl('https://example.com/image.png');
// 设置位置和尺寸
const imageWithOpts = await ImageClip.fromUrl('image.png', {
x: 100,
y: 200,
width: 800,
height: 600,
});
支持的图像格式
| 格式 | MIME 类型 | 支持情况 |
|---|---|---|
| PNG | image/png | 完全支持 |
| JPEG | image/jpeg | 完全支持 |
| WebP | image/webp | 完全支持 |
| SVG | image/svg+xml | 需测试 |
| Base64 | data URI | 完全支持 |
剪辑通用属性
所有剪辑类型共享以下核心属性:
| 属性 | 类型 | 说明 | 适用剪辑 | ||
|---|---|---|---|---|---|
id | string | 唯一标识 | 全部 | ||
src | string | 资源路径 | Video, Audio, Image | ||
left | number | X 坐标 | 全部 | ||
top | number | Y 坐标 | 全部 | ||
width | number | 宽度 | 全部 | ||
height | number | 高度 | 全部 | ||
angle | number | 旋转角度 | 全部 | ||
zIndex | number | 层级 | 全部 | ||
opacity | number | 透明度 | 全部 | ||
flip | `"horizontal" \ | "vertical" \ | null` | 翻转方向 | 全部 |
duration | number | 时长(微秒) | 全部 | ||
playbackRate | number | 播放速率 | 全部 |
资料来源:json-serialization.ts:15-50
时间轴与显示配置
display 属性
display: {
from: number; // 起始帧
to: number; // 结束帧
};
// 设置 5 秒时长的剪辑
text.duration = 5e6; // 5000000 微秒
trim 属性(可选)
trim?: {
from: number; // 裁剪起始点
to: number; // 裁剪结束点
};
特效与动画
特效配置
effects?: Array<{
id: string;
key: string;
startTime: number;
duration: number;
targets?: number[];
}>;
动画配置
animation?: {
keyFrames: Record<string, Partial<{
x: number;
y: number;
w: number;
h: number;
angle: number;
opacity: number;
}>>;
opts: {
duration: number;
delay?: number;
iterCount?: number;
};
};
资料来源:json-serialization.ts:50-75
JSON 序列化
BaseClipJSON 接口
interface BaseClipJSON {
id?: string;
name?: string;
metadata?: Record<string, any>;
effects?: Array<{...}>;
src: string;
display: {
from: number;
to: number;
};
playbackRate: number;
duration: number;
left: number;
top: number;
width: number;
height: number;
angle: number;
zIndex: number;
opacity: number;
flip: "horizontal" | "vertical" | null;
trim?: { from: number; to: number };
transition?: ITransitionInfo;
style?: any;
locked?: boolean;
colorAdjustment?: ColorAdjustment;
animation?: {...};
animations?: Array<{...}>;
}
导出示例
import { JsonSerialization } from 'openvideo';
const projectJson = JsonSerialization.toJSON(studio);
console.log(JSON.stringify(projectJson, null, 2));
在 Studio 中使用剪辑
添加剪辑到时间轴
const studio = new Studio({
canvas: document.getElementById('canvas'),
width: 1920,
height: 1080,
});
await studio.ready;
// 添加视频剪辑
const video = await Video.fromUrl('video.mp4');
await studio.addClip(video);
// 添加图像剪辑
const image = await ImageClip.fromUrl('image.png');
await studio.addClip(image);
移除剪辑
await studio.removeClip(video.id);
替换剪辑
await studio.timeline.replaceClipsBySource(
'old-video.mp4',
async (oldClip) => {
return await Video.fromUrl('new-video.mp4');
}
);
资料来源:studio.spec.ts:30-60
工作流程图
graph LR
A[加载媒体] --> B[解析 MP4/图像]
B --> C[创建 Clip 实例]
C --> D[添加到 Studio]
D --> E[配置 Timeline]
E --> F[Compositor 渲染]
F --> G[输出视频]
H[JSON 序列化] -.-> C
I[反序列化] -.-> C最佳实践
1. 异步加载
始终使用 await 等待资源加载完成:
const video = await Video.fromUrl('video.mp4');
await studio.addClip(video);
await studio.ready;
2. 资源清理
使用完剪辑后及时销毁释放内存:
video.destroy();
studio.destroy();
3. 缩略图生成
使用 ThumbnailOpts 生成视频缩略图:
const thumbnails = await video.getThumbnails({
start: 0,
end: video.duration,
step: 300, // 每 10 秒一帧(30fps)
});
4. 批量替换
使用 replaceClipsBySource 进行批量替换:
await studio.timeline.replaceClipsBySource(
'old.mp4',
async (oldClip) => Video.fromUrl('new.mp4')
);
总结
openvideo 的视频与音频剪辑模块提供了完整的媒体处理能力,支持从 URL 加载各类媒体资源,并通过统一的接口规范实现灵活的剪辑管理。开发者可以通过继承 BaseClip 创建自定义剪辑类型,通过 Compositor 实现高性能渲染,并通过 JSON 序列化实现项目持久化存储。该模块充分利用 WebCodecs API 和 PixiJS 渲染引擎,为 Web 环境下的专业视频编辑提供了坚实的技术基础。
资料来源:[video-clip.ts:50-70]()
文字与字幕剪辑
文字与字幕剪辑是 OpenVideo 视频编辑器中的核心功能模块,负责处理视频中的文字渲染和字幕显示。该模块包含两个主要组件:TextClip(文字剪辑) 和 CaptionClip(字幕剪辑),它们共享底层的 PixiJS 渲染引擎,但面向不同的使用场景。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
文字与字幕剪辑是 OpenVideo 视频编辑器中的核心功能模块,负责处理视频中的文字渲染和字幕显示。该模块包含两个主要组件:TextClip(文字剪辑) 和 CaptionClip(字幕剪辑),它们共享底层的 PixiJS 渲染引擎,但面向不同的使用场景。
TextClip 适用于静态或动态文字叠加,如标题、水印、动态文字效果;CaptionClip 则专注于字幕功能,支持逐字高亮、词动画、SRT 格式解析等高级特性。
资料来源:packages/openvideo/src/clips/text-clip.ts:1-50
架构设计
组件关系图
graph TD
A[Studio] --> B[TextClip]
A --> C[CaptionClip]
B --> D[SplitBitmapText]
C --> E[Word-Level Animation]
B --> F[PixiJS Container]
C --> F
D --> G[CanvasTextMetrics]
F --> H[PixiJS Renderer]
I[SRT Parser] --> C
J[FontManager] --> B
J --> C核心职责划分
| 组件 | 职责 | 继承关系 |
|---|---|---|
TextClip | 基础文字渲染,支持 stroke、shadow、wordWrap | ClipBase |
CaptionClip | 字幕渲染,支持逐词动画、颜色状态 | ClipBase |
SplitBitmapText | 文字分词渲染单元 | PIXI.Text |
FontManager | 字体加载与管理 | - |
SRTParser | SRT 字幕格式解析 | - |
资料来源:packages/openvideo/src/clips/text-clip.ts:1-30
TextClip 文字剪辑
功能特性
TextClip 提供了丰富的文字渲染能力,支持以下特性:
- 字体样式:fontFamily、fontSize、fontWeight、fontStyle
- 文本装饰:stroke(描边)、dropShadow(阴影)
- 布局控制:align、wordWrap、wordWrapWidth、lineHeight、letterSpacing
- 颜色填充:支持纯色和渐变填充
- 文字分词:将文本拆分为独立的单词进行渲染
资料来源:packages/openvideo/src/clips/text-clip.ts:100-180
渲染流程
sequenceDiagram
participant User as 用户
participant TextClip as TextClip
participant Container as PixiJS Container
participant WordTexts as SplitBitmapText[]
User->>TextClip: 设置文本内容
TextClip->>TextClip: 解析样式选项
TextClip->>TextClip: 拆分单词数组
TextClip->>TextClip: 计算行布局
TextClip->>Container: 创建/更新容器
Loop 每个单词
TextClip->>WordTexts: 创建 SplitBitmapText
Container->>WordTexts: 添加到容器
end
TextClip->>Container: 应用变换和动画样式配置
TextClip 支持从多种来源读取样式配置,包括剪辑自身的选项和外部传入的样式对象:
interface ITextOpts {
// 基础样式
text?: string;
fontFamily?: string;
fontSize?: number;
fontWeight?: string | number;
fontStyle?: 'normal' | 'italic';
fill?: number | string | FillGradient;
// 布局
align?: 'left' | 'center' | 'right';
wordWrap?: boolean;
wordWrapWidth?: number;
lineHeight?: number;
letterSpacing?: number;
// 装饰
stroke?: string | { color: string; width: number; join?: string; cap?: string; miterLimit?: number };
strokeWidth?: number;
dropShadow?: {
color?: string;
alpha?: number;
blur?: number;
distance?: number;
angle?: number;
};
// 垂直对齐
verticalAlign?: 'top' | 'middle' | 'bottom';
}
资料来源:packages/openvideo/src/clips/text-clip.ts:80-150
文本布局算法
TextClip 使用智能换行算法来计算文字布局:
- 空格宽度计算:使用 CanvasTextMetrics 和 SplitBitmapText 两种方式计算空格宽度,取较大值
- 换行宽度判断:根据 wordWrapWidth 确定是否需要换行
- 行分配:将单词依次分配到各行,计算每行的总宽度和高度
- 垂直对齐处理:根据 verticalAlign 调整行在容器中的位置
// 行宽计算逻辑
const wrapWidth = style.wordWrap && style.wordWrapWidth > 0
? style.wordWrapWidth
: 1e5;
const lines: { words: SplitBitmapText[]; width: number; height: number }[] = [];
let currentLineWords: SplitBitmapText[] = [];
let currentLineWidth = 0;
资料来源:packages/openvideo/src/clips/text-clip.ts:200-280
CaptionClip 字幕剪辑
功能特性
CaptionClip 是专为字幕场景设计的剪辑类型,提供了比 TextClip 更丰富的字幕功能:
- 逐词动画:支持每个单词独立的动画效果
- 颜色状态:区分"已出现(appeared)"、"激活(active)"、"背景(background)"等状态
- 关键词高亮:支持特定关键词的颜色标记
- SRT 解析:内置 SRT 字幕文件解析器
- 保留关键词颜色:在动画过程中保持关键词颜色不变
资料来源:packages/openvideo/src/clips/caption-clip.ts:50-120
字幕颜色系统
CaptionClip 维护了一套完整的颜色状态系统:
| 状态 | 说明 | 配置键 |
|---|---|---|
| appeared | 已显示完成的单词 | colors.appeared |
| active | 当前正在高亮的单词 | colors.active |
| activeFill | 激活单词的填充色 | colors.activeFill |
| background | 未激活单词的颜色 | colors.background |
| keyword | 关键词的特殊颜色 | colors.keyword |
资料来源:packages/openvideo/src/clips/caption-clip.ts:80-110
颜色配置示例
const captionClip = new CaptionClip({
text: "Hello world",
colors: {
appeared: 0x888888, // 已显示文字灰色
active: 0xFFFFFF, // 当前高亮白色
activeFill: 0xFFFF00, // 高亮填充黄色
background: 0x333333, // 背景色深灰
keyword: 0x00FF00 // 关键词绿色
},
preserveKeywordColor: true // 保持关键词颜色
});
渐变填充支持
CaptionClip 支持使用 FillGradient 实现渐变文字效果:
if (this.opts.fill && typeof this.opts.fill === 'object' && this.opts.fill.type === 'gradient') {
const gradient = new FillGradient(
this.opts.fill.x0,
this.opts.fill.y0,
this.opts.fill.x1,
this.opts.fill.y1
);
this.opts.fill.colors.forEach(
({ ratio, color }: { ratio: number; color: string | number }) => {
const colorNumber = typeof color === 'number'
? color
: (parseColor(color) ?? 0xffffff);
gradient.addColorStop(ratio, colorNumber);
}
);
}
资料来源:packages/openvideo/src/clips/caption-clip.ts:150-200
SRT 字幕解析
SRTParser 模块
OpenVideo 提供了内置的 SRT 格式解析器,可以将标准 SRT 字幕文件转换为内部格式:
graph LR
A[SRT 文件] --> B[SRTParser]
B --> C[字幕片段数组]
C --> D[startTime, endTime]
C --> E[text]
D --> F[CaptionClip]解析流程
- 时间码解析:将
00:00:01,000 --> 00:00:05,000格式转换为微秒时间戳 - 文本提取:获取字幕片段中的文字内容
- 片段组合:将多个连续片段合并为单个字幕项
- 格式输出:输出符合 CaptionClip 输入格式的数据
资料来源:packages/openvideo/src/utils/srt-parser.ts
字体管理
FontManager
字体管理模块负责动态加载自定义字体,确保文字渲染使用正确的字形:
graph TD
A[TimelineModel] --> B[FontManager]
B --> C{字体已缓存?}
C -->|是| D[直接使用]
C -->|否| E[加载字体]
E --> F[FontFace API]
F --> G[添加到 document.fonts]
G --> D
D --> H[渲染文字]字体加载策略
FontManager 会扫描所有剪辑中的字体引用,统一进行加载:
// 检查普通文字样式中的字体
if (clip.style?.fontUrl || (clip as any).fontUrl) {
fontsToLoad.set(fontUrl, {
name: clip.style?.fontFamily || (clip as any).fontFamily || "CustomFont",
url: fontUrl,
});
}
// 检查字幕样式中的字体
if (clip.type === "Caption") {
const fontUrl = clip.style?.fontUrl || (clip as any).fontUrl;
if (fontUrl) {
fontsToLoad.set(fontUrl, { ... });
}
}
资料来源:packages/openvideo/src/studio/timeline-model.ts:200-260
动画预设
内置动画类型
OpenVideo 为文字和字幕剪辑提供了丰富的预设动画:
| 动画类型 | 说明 | 参数 |
|---|---|---|
| fadeIn | 淡入效果 | opacityInit, opacityEnd |
| slideIn | 滑入效果 | direction, distance |
| slideOut | 滑出效果 | direction, distance |
| pulse | 脉冲效果 | scaleInit, scaleEnd |
| blurIn | 模糊淡入 | blurInit, opacityInit |
动画配置示例
// 淡入动画
case "fadeIn":
return {
"0%": { opacity: opacityInit ?? 0, mirror: defaultMirror },
"100%": { opacity: opacityEnd ?? 1, mirror: defaultMirror },
};
// 滑入动画
case "slideIn": {
const direction = normalized?.direction || "left";
const distance = normalized?.distance || 300;
return {
"0%": {
x: direction === "left" ? -distance : direction === "right" ? distance : 0,
opacity: opacityInit ?? 0,
mirror: defaultMirror,
},
"100%": {
x: 0,
opacity: opacityEnd ?? 1,
mirror: defaultMirror,
},
};
}
资料来源:packages/openvideo/src/animation/presets.ts:50-120
JSON 序列化
序列化格式
TextClip 和 CaptionClip 都支持完整的 JSON 序列化,便于项目保存和加载:
interface CaptionDataJSON {
words?: string[]; // 单词数组
colors?: CaptionColorsJSON; // 颜色配置
appearedColor?: number; // 已显示颜色(向后兼容)
activeColor?: number; // 激活颜色(向后兼容)
activeFillColor?: number; // 激活填充色(向后兼容)
backgroundColor?: number; // 背景色(向后兼容)
isKeyWordColor?: number; // 关键词色(向后兼容)
bottomOffset?: number; // 底部偏移
videoWidth?: number; // 视频宽度
videoHeight?: number; // 视频高度
wordsPerLine?: number; // 每行词数
fontUrl?: string; // 字体 URL
preserveKeywordColor?: boolean; // 保留关键词颜色
style?: TextStyleJSON; // 文字样式
}
向后兼容性
CaptionClip 的序列化模块实现了向后兼容处理,支持旧版本的扁平化数据结构:
// 旧扁平结构(向后兼容)
if (json.appearedColor !== undefined) {
captionOpts.colors = captionOpts.colors || {};
captionOpts.colors.appeared = json.appearedColor;
}
// 新嵌套结构
if (this.originalOpts?.caption?.colors?.appeared !== undefined) {
this.opts.appeared = this.originalOpts.caption.colors.appeared;
}
资料来源:packages/openvideo/src/clips/caption-clip.ts:300-400
使用示例
创建文字剪辑
import { Studio, Text } from 'openvideo';
const studio = new Studio({
canvas: document.getElementById('preview-canvas'),
width: 1280,
height: 720,
});
const title = new Text('Hello World');
title.duration = 5e6; // 5秒
title.fontSize = 48;
title.fontFamily = 'Arial';
title.fontWeight = 'bold';
title.fill = '#FFFFFF';
title.stroke = { color: '#000000', width: 2 };
title.dropShadow = {
color: '#000000',
alpha: 0.5,
blur: 4,
distance: 2,
angle: Math.PI / 4
};
await studio.addClip(title);
创建字幕剪辑
import { CaptionClip } from 'openvideo';
const caption = new CaptionClip({
text: 'This is a subtitle example',
fontSize: 32,
fontFamily: 'Microsoft YaHei',
colors: {
appeared: 0x888888,
active: 0xFFFFFF,
activeFill: 0xFFFF00,
background: 0x333333
},
wordAnimation: 'fadeIn',
verticalAlign: 'bottom',
bottomOffset: 50
});
await studio.addClip(caption);
从 SRT 文件加载
import { SRTParser } from 'openvideo';
const srtContent = await fetch('/path/to/subtitle.srt').then(r => r.text());
const parsed = SRTParser.parse(srtContent);
for (const segment of parsed) {
const caption = new CaptionClip({
startTime: segment.startTime,
endTime: segment.endTime,
text: segment.text,
fontSize: 28,
colors: {
appeared: 0xAAAAAA,
active: 0xFFFFFF,
activeFill: 0x00FF00
}
});
await studio.addClip(caption);
}
最佳实践
性能优化
- 避免频繁创建文字对象:TextClip 会为每个单词创建独立的 SplitBitmapText,过多的单词会影响性能
- 合理设置 wordWrapWidth:过小的换行宽度会导致行数增加,增加渲染负担
- 使用缓存的字体:确保字体只加载一次,避免重复加载
兼容性注意
- 使用标准字体:跨平台渲染时,优先使用系统自带字体
- 提供回退字体:在 fontFamily 中指定多个字体作为回退
- SRT 时间码格式:确保 SRT 文件使用标准格式
HH:MM:SS,mmm
相关模块
| 模块 | 文件路径 | 用途 |
|---|---|---|
| 核心剪辑 | src/clips/clip.ts | 剪辑基类 |
| 文字剪辑 | src/clips/text-clip.ts | 文字渲染实现 |
| 字幕剪辑 | src/clips/caption-clip.ts | 字幕渲染实现 |
| 字体管理 | src/utils/fonts.ts | 字体加载管理 |
| SRT 解析 | src/utils/srt-parser.ts | SRT 格式解析 |
| 动画预设 | src/animation/presets.ts | 内置动画效果 |
| 时间轴模型 | src/studio/timeline-model.ts | 时间轴数据管理 |
资料来源:[packages/openvideo/src/clips/text-clip.ts:1-50]()
资源管理
OpenVideo 的资源管理模块是整个视频处理工作流的基础组件,负责媒体资源的加载、存储、流式传输和生命周期管理。该模块由 ResourceManager 和 AssetManager 两个核心类构成,支持从远程 URL 获取资源并将数据持久化到本地 OPFS(Origin Private File System)文件系统。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
OpenVideo 的资源管理模块是整个视频处理工作流的基础组件,负责媒体资源的加载、存储、流式传输和生命周期管理。该模块由 ResourceManager 和 AssetManager 两个核心类构成,支持从远程 URL 获取资源并将数据持久化到本地 OPFS(Origin Private File System)文件系统。
资源管理的核心设计目标包括:
- 统一资源获取:提供一致的接口获取各类媒体资源(视频、音频、图片等)
- 本地缓存:通过 OPFS 实现资源的本地持久化存储,减少重复网络请求
- 流式处理:支持 ReadableStream 流式读取,适配大文件处理场景
- 异步操作:所有资源操作均为异步执行,确保主线程不被阻塞
资料来源:packages/openvideo/src/studio/resource-manager.ts:1-50
核心架构
类结构
┌─────────────────────────────────────────────────────────────┐
│ ResourceManager │
│ - getReadableStream(url): Promise<ReadableStream> │
│ - 静态方法,提供统一的资源获取入口 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AssetManager │
│ - 管理资源缓存和生命周期 │
│ - 提供资源加载、释放、状态追踪 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OPFS (Origin Private File System) │
│ - 本地文件系统存储 │
│ - 支持大文件持久化 │
└─────────────────────────────────────────────────────────────┘
资料来源:packages/openvideo/src/utils/asset-manager.ts:1-30
资源加载流程
graph TD
A[请求资源 URL] --> B{检查缓存}
B -->|命中缓存| C[从 OPFS 读取本地文件]
B -->|未命中| D[发起网络请求]
D --> E[获取 ReadableStream]
E --> F[写入 OPFS 本地文件]
F --> G[返回本地文件引用]
C --> G
G --> H[创建 Video/Audio/Image Clip]资料来源:packages/openvideo/src/studio/resource-manager.ts:20-40
ResourceManager
核心方法
ResourceManager 是静态工具类,提供资源获取的核心接口。
#### getReadableStream(url: string): Promise<ReadableStream<Uint8Array>>
从指定 URL 获取资源的可读流。
| 参数 | 类型 | 说明 |
|---|---|---|
| url | string | 资源 URL,支持 HTTP/HTTPS 协议 |
| 返回值 | 说明 |
|---|---|
| Promise<ReadableStream<Uint8Array>> | 资源的可读字节流 |
// 获取视频资源的流
const stream = await ResourceManager.getReadableStream('https://example.com/video.mp4');
资料来源:packages/openvideo/src/studio/resource-manager.ts:30-45
AssetManager
资源缓存机制
AssetManager 负责管理资源缓存状态和生命周期,确保内存资源得到正确释放。
资源类型支持
| 类型 | 说明 | 适用场景 |
|---|---|---|
| 视频资源 | 通过 Video Clip 加载的 MP4/WebM 等格式 | 视频轨道编辑 |
| 音频资源 | 独立的音频流 | 音频混合处理 |
| 图片资源 | PNG/JPG/WebP 等静态图像 | 封面、字幕背景 |
资料来源:packages/openvideo/src/utils/asset-manager.ts:25-40
Video Clip 中的资源集成
fromUrl 静态工厂方法
Video.fromUrl() 是创建视频剪辑的便捷入口,内部自动调用 ResourceManager 获取资源。
static async fromUrl(
url: string,
opts: {
x?: number;
y?: number;
width?: number;
height?: number;
} = {}
): Promise<Video>
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | 是 | 视频资源 URL |
| opts.x | number | 否 | 剪辑在画布中的 X 坐标 |
| opts.y | number | 否 | 剪辑在画布中的 Y 坐标 |
| opts.width | number | 否 | 剪辑显示宽度 |
| opts.height | number | 否 | 剪辑显示高度 |
// 加载远程视频并设置位置
const video = await Video.fromUrl('https://example.com/video.mp4', {
x: 100,
y: 50,
width: 1280,
height: 720
});
资料来源:packages/openvideo/src/clips/video-clip.ts:50-80
localFile 本地文件管理
视频资源下载后会存储在 OPFS 中,localFile 属性保存本地文件引用。
// Video Clip 构造函数中的本地文件初始化
const initByStream = async (s: ReadableStream) => {
await write(this.localFile, s);
return this.localFile;
};
this.localFile = isOTFile(source)
? source
: "localFileFinder"; // 使用 OPFS 存储
| 属性 | 类型 | 说明 |
|---|---|---|
| localFile | OPFSToolFile | OPFS 中的本地文件句柄 |
资料来源:packages/openvideo/src/clips/video-clip.ts:75-85
图像处理中的资源管理
图像源接口
ImgSource 类型用于统一不同来源的图像数据:
type ImgSource = ImageBitmap | HTMLImageElement |
HTMLCanvasElement | OffscreenCanvas |
VideoFrame;
Chromakey(色键抠像)资源处理
createChromakey 函数展示了图像资源的流式处理模式:
export const createChromakey = (
opts: Omit<IChromakeyOpts, 'keyColor'> & {
keyColor?: [number, number, number];
}
) => {
let canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
let gl: WebGLRenderingContext | null = null;
let keyColor = opts.keyColor;
let texture: WebGLTexture | null = null;
return async (imgSource: ImgSource) => {
if (canvas == null || gl == null || texture == null) {
if (keyColor == null) keyColor = getKeyColor(imgSource);
// 初始化 canvas 和 WebGL 上下文
}
// 更新纹理并返回处理后的 VideoFrame
};
};
| 组件 | 类型 | 生命周期 | |
|---|---|---|---|
| canvas | HTMLCanvasElement \ | OffscreenCanvas | 首次调用时创建 |
| gl | WebGLRenderingContext | 首次调用时初始化 | |
| texture | WebGLTexture | 按需创建和更新 |
资料来源:packages/openvideo/src/utils/chromakey.ts:20-60
渲染模板中的资源注入
OpenVideo 在服务端渲染场景中使用 Playwright 注入配置,HTML 模板通过 window.RENDER_CONFIG 获取渲染参数。
// 获取 JSON 配置
const jsonConfig = window.RENDER_CONFIG;
if (!jsonConfig) {
throw new Error('No render configuration provided');
}
// 从配置中提取设置
const settings = jsonConfig.settings || {};
const compositorOpts = {
width: settings.width || 1280,
height: settings.height || 720,
fps: settings.fps || 30,
bgColor: settings.bgColor || '#000000',
};
| 配置项 | 默认值 | 说明 |
|---|---|---|
| width | 1280 | 输出视频宽度 |
| height | 720 | 输出视频高度 |
| fps | 30 | 帧率 |
| bgColor | #000000 | 背景颜色 |
| videoCodec | - | 视频编码器 |
| bitrate | - | 比特率 |
| audio | true | 是否包含音频 |
资料来源:packages/openvideo/render.html:25-50
最佳实践
资源加载建议
- 使用 fromUrl 工厂方法:优先使用
Video.fromUrl()而非直接操作流,确保资源正确缓存 - 指定尺寸参数:在
fromUrl中指定width/height可避免布局抖动 - 检查资源就绪状态:调用
await clip.ready确保媒体元数据加载完成
内存管理
// 正确的异步初始化模式
const video = await Video.fromUrl('video.mp4');
await video.ready; // 等待元数据加载
await video.thumbnails(100); // 生成缩略图
异常处理
try {
const stream = await ResourceManager.getReadableStream(url);
if (!stream) {
throw new Error('Failed to get readable stream');
}
} catch (error) {
console.error('Resource loading failed:', error);
}
资料来源:packages/openvideo/src/clips/video-clip.ts:60-75
总结
OpenVideo 的资源管理模块通过 ResourceManager 和 AssetManager 提供了完整的资源获取和生命周期管理能力。结合 OPFS 本地存储和流式处理机制,该系统能够高效处理远程媒体资源,同时保持较低的内存占用。开发者应遵循工厂方法优先、异步操作必检、资源释放及时的原则,以确保应用的稳定性和性能。
资料来源:[packages/openvideo/src/studio/resource-manager.ts:1-50]()
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
下游已经要求复核,不能在页面中弱化。
用户安装前需要知道权限边界和敏感操作。
Pitfall Log / 踩坑日志
项目:openvideodev/openvideo
摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:能力坑 - 能力判断依赖假设。
1. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 建议检查:将假设转成下游验证清单。
- 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
- 证据:capability.assumptions | github_repo:1125747446 | https://github.com/openvideodev/openvideo | README/documentation is current enough for a first validation pass.
2. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
- 证据:evidence.maintainer_signals | github_repo:1125747446 | https://github.com/openvideodev/openvideo | last_activity_observed missing
3. 安全/权限坑 · 下游验证发现风险项
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:下游已经要求复核,不能在页面中弱化。
- 建议检查:进入安全/权限治理复核队列。
- 防护动作:下游风险存在时必须保持 review/recommendation 降级。
- 证据:downstream_validation.risk_items | github_repo:1125747446 | https://github.com/openvideodev/openvideo | no_demo; severity=medium
4. 安全/权限坑 · 存在安全注意事项
- 严重度:medium
- 证据强度:source_linked
- 发现:No sandbox install has been executed yet; downstream must verify before user use.
- 对用户的影响:用户安装前需要知道权限边界和敏感操作。
- 建议检查:转成明确权限清单和安全审查提示。
- 防护动作:安全注意事项必须面向用户前置展示。
- 证据:risks.safety_notes | github_repo:1125747446 | https://github.com/openvideodev/openvideo | No sandbox install has been executed yet; downstream must verify before user use.
5. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 建议检查:把风险写入边界卡,并确认是否需要人工复核。
- 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
- 证据:risks.scoring_risks | github_repo:1125747446 | https://github.com/openvideodev/openvideo | no_demo; severity=medium
6. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
- 防护动作:issue/PR 响应未知时,必须提示维护风险。
- 证据:evidence.maintainer_signals | github_repo:1125747446 | https://github.com/openvideodev/openvideo | issue_or_pr_quality=unknown
7. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
- 证据:evidence.maintainer_signals | github_repo:1125747446 | https://github.com/openvideodev/openvideo | release_recency=unknown
来源:Doramagic 发现、验证与编译记录