tav-media
v0.3.2
Published
Cross platform media editing framework
Downloads
24
Readme
TAVMedia
TAVMedia 是一个跨平台的音视频编辑框架,支持在移动端、桌面端、浏览器端和服务器端使用。
Web 版使用指南
浏览器支持
TAVMedia 目前支持 Chrome 75+, Safari 12+.
安装
在 Web 工程中使用 TAVMedia,您可以直接使用 NPM 将 TAVMedia 安装到您的项目中
npm install tav-media
初始化 tav-media
import { initializeWasm, TAVWasmOptions } from 'tav-media';
const env: TAVWasmOptions = {
/**
* 可选,指定 wasm 文件在服务端的路径。如果不指定,则使用 `./tav-media-wasm.wasm`
*/
locateFile: wasmFileName => `/node_modules/tav-media/bin/wasm/${wasmFileName}`,
/**
* 可选,后续资源的 path 为相对路径时,会自动添加此前缀
*/
baseUrl: '/',
};
initializeWasm(env).then(async () => {
// 调用业务初始化代码
});
License 指引
License 鉴权
在 TAVMedia SDK 中提供了鉴权的对应的 API ,可以参考使用:
/**
* 验证授权信息
*/
async function authorize() {
const authResult = await TAVLicense.Auth(
// License url 或者 license 内容的 ArrayBuffer
'assets/tav_media.license',
// AppId
'your_appid_here',
// License secret
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
);
if (authResult !== OAuthErrorCode.OK) {
// 根据验证结果进行进一步处理
console.warn('授权失败,使用 TAVMedia 水印试用版');
}
}
initializeWasm(env).then(async () => {
// 加载 TAVMedia wasm 后验证 license
await authorize();
});
基础功能
使用 TAVMedia 渲染视频
TAVMedia Web 版封装了 TAVMediaView
类, 封装了 TAVSurface
, TAVVideoReader
, TAVAudioReader
的创建和调用过程,
提供了适合 Web 播放使用的 API。视频图像渲染到指定的 canvas,音频会直接通过浏览器输出,暂时不支持读取每帧的音频合成数据。
// 创建一个 MovieClip 对象
const ghost = await MovieAsset.MakeFromPath('assets/ghost.mp4');
const movie = await MovieClip.MakeFromAsset(ghost);
movie.duration = ghost.duration;
// 创建一个 TAVMediaView,将图像渲染到 id 为 stage 的 canvas
const view = TAVMediaView.MakeFromHtmlCanvas('#stage');
// 播放视频
await view.setMedia(movie);
view.play();
同时播放多个媒体
通过将多个媒体添加到 Composition 的方式,可以在场景中同时播放多个视频和特效。例如下面的视频转场的例子。
// 创建主时间轴
const root = new Composition();
root.width = 1280;
root.height = 720;
root.duration = 3_000_000;
// 添加第一个视频
const ghost = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(ghost);
movie1.duration = ghost.duration;
movie1.startTime = 0;
// 添加第二个视频
const dog = await MovieAsset.MakeFromPath('video-640x360-2.mp4');
const movie2 = await MovieClip.MakeFromAsset(dog);
movie2.duration = dog.duration;
movie2.startTime = 800_000;
// 添加转场
const zcAsset = await PAGAsset.MakeFromPath('zc.pag');
const zc = await PAGEffect.MakeFromAsset(zcAsset);
zc.startTime = 800_000;
zc.duration = zcAsset.duration;
// 将两个视频作为转场效果的输入,在转场播放过程中,将根据设计师
// 指定的方式展示两个视频切换的效果。
zc.addInput(movie1);
zc.addInput(movie2);
// 添加到主时间轴
root.addClip(movie1);
root.addClip(movie2);
root.addClip(zc);
// 播放
await view.setMedia(root);
view.play();
层级结构
显示控制
Matrix 和 CropRect 控制位移缩放旋转裁切
const asset = await MovieAsset.MakeFromPath('video-640x360.mp4');
const clip = await MovieClip.MakeFromAsset(asset);
clip.duration = 3_000_000;
const matrix = new Matrix();
matrix.postTranslate(-asset.width / 2, -asset.height / 2);
matrix.postScale(2, 2);
matrix.postTranslate(asset.width / 2, asset.height / 2);
const cropRect = Rect.MakeXYWH(asset.width / 4, asset.height / 4, asset.width / 2, asset.height / 2);
clip.matrix = matrix;
clip.cropRect = cropRect;
Opacity
opacity是不透明度,通过设置opacity可以控制Movie的透明度。取值范围是0.0-1.0。0是完全透明,1是完全不透明。
const clip = await MovieClip.MakeFromAsset(asset);
clip.opacity = 0.5;
时间控制
通过 Clip 和 Composition 排布时间轴
const ghost = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(ghost);
movie1.startTime = 1_000_000;
movie1.duration = 4_000_000;
// 创建一个分辨率为 720 * 1280 的Composition
// contentStartTime 为 1_000_000,表示从视频的第1秒开始播放
// contentDuration 为 10_000_000,表示播放 10 秒
const composition = await Composition.Make(720, 1280, 2_000_000, 10_000_000);
composition.duration = 10_000_000;
composition.addClip(movie1);
通过 MovieClip 裁剪资源片段
const ghost = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(ghost, 1_000_000, 4_000_000);
movie1.startTime = 0;
movie1.duration = 4_000_000;
变速
// 通过 Movie 直接变速
const totalDuration = 10_000_000;
const asset = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie = await MovieClip.MakeFromAsset(asset, 0, totalDuration);
movie.startTime = 0;
movie.duration = totalDuration / 2;
// 通过 Composition 进行变速
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1, 0, totalDuration);
movie1.startTime = 0;
movie1.duration = totalDuration;
const composition = await Composition.Make(640, 360, 0, totalDuration);
composition.addClip(movie1);
composition.startTime = 0;
composition.duration = totalDuration / 2;
定格
// 通过 Movie 定格
const totalDuration = 10_000_000;
const asset = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie = await MovieClip.MakeFromAsset(asset, 1_000_000, 0);
movie.startTime = 0;
movie.duration = totalDuration;
// 通过 Composition 定格
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1, 0, totalDuration);
movie1.startTime = 0;
movie1.duration = totalDuration;
const composition = await Composition.Make(640, 360, 1_000_000, 0);
composition.addClip(movie1);
composition.startTime = 0;
composition.duration = totalDuration;
音频播放
TAVMedia的音频播放
const totalDuration = 10_000_000;
const path = 'hoaprox.mp3';
const asset = await AudioAsset.MakeFromPath(path);
const audio = await AudioClip.MakeFromAsset(asset, 0, totalDuration);
audio.duration = totalDuration;
const effect = await AudioVolumeEffect.MakeFIFOEffect(audio, 1.0, 3_000_000, 3_000_000);
effect.startTime = 0;
effect.duration = totalDuration;
添加效果
TransformEffect
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.startTime = 0;
movie1.duration = asset1.duration;
const transformEffect = TransformEffect.MakeTransformEffect();
const keyframes: NoBlankArray<Keyframe> = [
TAVKeyframe.MakeLinear(0, 1_000_000, 0, 90),
];
transformEffect.transform2D = {
rotation: TAVProperty.MakeAnimatableProperty(keyframes),
}
transformEffect.addInput(movie1);
TAVColorTuningEffect
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.startTime = 0;
movie1.duration = asset1.duration;
const colorTuningEffect = ColorTuningEffect.MakeColorTuningEffect();
const keyframes: NoBlankArray<Keyframe> = [
TAVKeyframe.MakeLinear(0, 1_000_000, -50, 50),
];
// 设置饱和度
colorTuningEffect.colorTuning = {
saturation: TAVProperty.MakeAnimatableProperty(keyframes),
}
colorTuningEffect.addInput(movie1);
LUTEffect
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.startTime = 0;
movie1.duration = asset1.duration;
const lutEffect = await LUTEffect.MakeFromPath('lut.png');
lutEffect.startTime = 0;
lutEffect.duration = 1_000_000;
lutEffect.strength = 0.5;
lutEffect.addInput(movie1);
ChromaMatting
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.startTime = 0;
movie1.duration = asset1.duration;
const chromaMattingEffect = ChromaMattingEffect.Make();
chromaMattingEffect.chromaMattingConfig = {
intensity: 0.2,
shadow: 0.5,
selectedColor: {
red: 0,
green: 255,
blue: 0,
alpha: 255,
},
}
chromaMattingEffect.startTime = 0;
chromaMattingEffect.duration = 1_000_000;
chromaMattingEffect.addInput(movie1);
PAGEffect
const asset1 = await MovieAsset.MakeFromPath('video-640x360.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.startTime = 0;
movie1.duration = asset1.duration;
const pagAsset = await PAGAsset.MakeFromPath('fw.pag');
const pagEffect = await PAGEffect.MakeFromAsset(pagAsset);
// numImgs 表示该 PAG File 最大支持的替换图层数量
const numImgs = pagEffect.numImages;
// 添加 movie1 作为 effect 的输入,
// scaleMode 可以设置如何缩放 movie1 以适应 PAG 图层的大小
// editableIndex 表示 PAG File 中的第几个可替换图层
pagEffect.addInput(movie1, ScaleMode.LetterBox, 0);
pagEffect.startTime = 0;
// 这边可以根据需求设置成任意时长,例子中设置成文件时长
pagEffect.duration = pagAsset.duration;
const movieDuration = 3_000_000;
const asset1 = await MovieAsset.MakeFromPath('1.mp4');
const movie1 = await MovieClip.MakeFromAsset(asset1);
movie1.duration = movieDuration;
const asset2 = await MovieAsset.MakeFromPath('2.mp4');
const movie2 = await MovieClip.MakeFromAsset(asset2);
movie2.duration = movieDuration;
const pagAsset = await PAGAsset.MakeFromPath('zc.pag');
const pagEffect = await PAGEffect.MakeFromAsset(pagAsset);
const numImgs = pagEffect.numImages;
// 分别设置 movie1 和 movie2 替换 PAG file 中的第一个替换图层和第二个替换图层
// 替换哪两个替换图层需要根据 PAG 文件来决定,通常情况下 input0 对应 editableIndex0,input1 对应 editableIndex1
// 也可以通过 `addInput` 的 `editableIndex` 参数来指定
pagEffect.addInput(movie1);
pagEffect.addInput(movie2);
// 这边可以根据需求设置成任意时长,例子中设置成文件时长
pagEffect.duration = pagAsset.duration;
// 设置转场和 movie2 的开始时间为 movie1 的时长减去 PAG File 的时长,
// 这样 PAG Effect 能够将效果作用到 movie1 和 movie2 之间
movie2.startTime = movie1.duration - pagEffect.duration;
pagEffect.startTime = movie2.startTime;