0.3.2 • Published 2 years ago

tav-media v0.3.2

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

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;
0.3.2

2 years ago

0.3.1

2 years ago

0.3.0

2 years ago

0.2.26

2 years ago