|
美摄Web端SDK
3.15.1
|
美摄SDK致力于解决web视频开发的技术门槛,使仅有web界面开发经验的程序员,都可以开发出性能优异、渲染效果丰富的的视频录制、编辑功能。我们的优势体现在:
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp以开发环境为例
// vite.config.js
export default defineConfig({
plugins: [
...,
{
name: "configure-response-headers",
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
next();
});
},
},
],
...
})
如果没有MeisheSDK库,请先联系美摄商务获取sdk和授权:
<script src="https://alieasset.meishesdk.com/NvWasm/domain/3-15-1-release/2/NvStreamingSdk.js"></script>
npm i meishewasmloader --save
import { WASMLoader } from 'meishewasmloader';
function loader(){
return new Promise(async (resolve,reject)=>{
const wasmLoader = WASMLoader({
// 加载进度回调
showLoader: function (state) {},
// 失败回调
showError(errorText: string) {
reject()
},
// 成功回调
loadingFinished() {
function poll(){
if(Module !== undefined && Module.Meishe !== undefined && Module.Meishe.streamingContext !== undefined){
//加载成功
resolve()
}else{
setTimeout(()=>{
poll()
},500)
}
}
poll()
},
});
wasmLoader.loadEmscriptenModule("https://alieasset.meishesdk.com/NvWasm/domain/3-15-1-release/2/"); // 开始调用函数加载
})
}
// 授权验证的回调函数
nvStreamingContext.onWebRequestAuthFinish = (success) => {
if (!success) {
console.error('SDK鉴权失败');
} else {
console.error('SDK鉴权成功');
}
};
nvStreamingContext.verifySdkLicenseFile('鉴权地址')
NvsStreamingContext是美摄SDK的流媒体上下文类,可视作整个SDK框架的入口。
<canvas id="live-window" />
var canvas = document.getElementById("live-window");
canvas.width = canvas.clientWidth * window.devicePixelRatio;
canvas.height = canvas.clientHeight * window.devicePixelRatio;
NvsLiveWindow的宽高比应该是1:1,4:3,16:9,9:16等,最好与要编辑的图像宽高比保持一致。否则,预览的图像是被裁剪后的图像。
declare const NvsLiveWindowFillModeEnum: Readonly<{
//图像按比例均匀填充,必要时进行裁剪(默认模式)
PreserveAspectCrop: 0;
//图像均匀地缩放来适合窗口,没有裁剪
PreserveAspectFit: 1;
//图像被缩放来适合窗口
Stretch: 2;
}
对于三种填充模式,图片展示如下:



FS类是SDK的资源管理类,主要负责资源的加载、创建、删除等。通过FS.mkdir()、 FS.readFile()、 FS.writeFile()等方法管理资源。更多方法请参考
const response = await fetch('https://alieasset.meishesdk.com/editor/2022/07/05/video/afd62303-3492-4c31-b09c-1c56c63b46a2/afd62303-3492-4c31-b09c-1c56c63b46a2.m3u8');
const text = await response.text();
const path = `/afd62303-3492-4c31-b09c-1c56c63b46a2.m3u8`
FS.writeFile(path, text)
FS.mkdir('/m3u8')
const path = `/m3u8/afd62303-3492-4c31-b09c-1c56c63b46a2.m3u8`
FS.writeFile(path, text)
实现视频编辑的一般步骤:
const nvStreamingContext = nvsGetStreamingContextInstance();
//NvsLiveWindow初始化
const nvLiveWindow = nvStreamingContext.createLiveWindow("live-window");
//设置填充模式
nvLiveWindow.setFillMode(NvsLiveWindowFillModeEnum.PreserveAspectFit);
//连接时间线到流媒体窗口 nvStreamingContext.connectTimelineWithLiveWindow(timeline, nvLiveWindow);
// html
<canvas id="live-window" />
// js
const liveWindow = document.getElementById("live-window");
const width = 960;
const height = 540
liveWindow.style.height = height + 'px';
liveWindow.style.width = width + 'px';
liveWindow.height = height * (window.devicePixelRatio || 1);
liveWindow.width = width * (window.devicePixelRatio || 1);
// 时间线创建的标识
const TIMELINE_FLAGS =
NvsCreateTimelineFlagEnum.DontAddDefaultVideoTransition +
NvsCreateTimelineFlagEnum.ForceAudioSampleFormatUsed +
NvsCreateTimelineFlagEnum.RecordingUserOperation +
NvsCreateTimelineFlagEnum.SyncAudioVideoTrasitionInVideoTrack
/*创建时间线*/
nvTimeline = nvStreamingContext.createTimeline(
new NvsVideoResolution(width, height),
new NvsRational(25, 1),
new NvsAudioResolution(44100, 2),
TIMELINE_FLAGS,
);
createTimeline(videoRes: NvsVideoResolution, fps: NvsRational, audioRes: NvsAudioResolution, flags?: number): NvsTimeline;
一般情况下创建一条视频轨道,然后往轨道上添加图片或者视频素材。添加到轨道上的素材,我们称它为片段(clip)。图片和视频素材都是通过文件路径添加到轨道上的。 请注意:如果图片素材尺寸过大,需要降低图片尺寸,降低尺寸后的图片和创建timeline的分辨率的尺寸大小匹配最好。
const videoTrack = nvTimeline.appendVideoTrack();
const audioTrack = nvTimeline.appendAudioTrack();
const response = await fetch('https://alieasset.meishesdk.com/editor/2022/07/05/video/afd62303-3492-4c31-b09c-1c56c63b46a2/afd62303-3492-4c31-b09c-1c56c63b46a2.m3u8');
const text = await response.text();
const path = `/afd62303-3492-4c31-b09c-1c56c63b46a2.m3u8`
FS.writeFile(path, text)
const clip = videoTrack.appendClip(path);
预览效果参考视频定位预览
playbackTimeline(timeline: NvsTimeline, startTime: number, endTime: number, videoSizeMode: number, preload: boolean, flags: number): boolean;
let flags = 0;
nvStreamingContext.playbackTimeline(
nvTimeline, // 当前时间线对象
nowTime, // 开始时间
endTime, // 结束时间 -1 代表播放到结尾
NvsVideoPreviewSizeModeEnum.LiveWindowSize, // 视频大小模式
preload, // 是否预加载 设为true
(flags |= NvsPlaybackFlagEnum.BuddyHostOriginVideoFrame) // 播放标记
);
seekTimeline(timeline: NvsTimeline, timestamp: number, videoSizeMode: number, flags: number): boolean;
nvStreamingContext.seekTimeline(
nvTimeline, // 当前时间线对象
nowTime, // 开始时间
NvsVideoPreviewSizeModeEnum.LiveWindowSize, // 视频预览大小模式
(flags |= NvsSeekFlagEnum.BuddyHostOriginVideoFrame), // 定位标识
);
const clip = videoTrack.getClipByIndex(0); clip.changeTrimInPoint(trim_in, true); clip.changeTrimOutPoint(trim_out, true);
videoTrack.removeClip(0, false);
videoTrack.moveClip(0,3);
const assetUrl = https://alieasset.meishesdk.com/test/2023/07/10/image/0c3c43a5-f9f8-4223-84f2-2c35c535f104/0c3c43a5-f9f8-4223-84f2-2c35c535f104.m3u8 const response = await fetch(assetUrl); const text = await response.text(); const path = `/0c3c43a5-f9f8-4223-84f2-2c35c535f104.m3u8` FS.writeFile(path, text) videoTrack.appendClip2(path,0,8000000);
创建的时间线,添加的视频轨道和音频轨道,如果当前不再需要,则要进行移除。操作如下:
nvStreamingContext.removeTimeline(nvTimeline);
nvTimeline.removeVideoTrack(0);
nvTimeline.removeAudioTrack(0);
预览效果参考视频定位预览
const assetUrl = 'https://alieasset.meishesdk.com/test/2024/05/24/audio/6dc60190-c22b-4740-a299-3981d1a8c7ec/6dc60190-c22b-4740-a299-3981d1a8c7ec.m3u8' const audioTrack = nvTimeline.appendAudioTrack(); const response = await fetch(assetUrl); const text = await response.text(); const path = `/6dc60190-c22b-4740-a299-3981d1a8c7ec.m3u8` FS.writeFile(path, text) audioTrack.appendClip(path);
const clip = audioTrack.getClipByIndex(0); clip.changeTrimInPoint(trim_in, true); clip.changeTrimOutPoint(trim_out, true);
预览效果参考视频定位预览
添加,删除以及获取字幕都是在时间线(timeline)上进行执行的,调用addCaption(),可以参考SdkDemo示例的addCaption() 方法
const timelineCapion = nvTimeline.addCaption("Meishe SDK", 0, nvTimeline.getDuration(), captionStylePackageId,false);
captionStylePackageId是素材样式包id,添加字幕之前需要先安装字幕样式包,参考素材包管理, 试用字幕样式包好物种草
const fontUrl = 'https://alieasset.meishesdk.com/font/站酷酷黑体.ttf'
const response = await Axios.get(fontUrl, {
responseType: 'arraybuffer',
})
const fontInFS = '/' + fontUrl.split('/').pop()
await FS.writeFile(fontInFS, new Uint8Array(response.data))
// 对字幕做字体设置
caption.setFontByFilePath(fontInFS)
const nextCaption = nvTimeline.removeCaption(timelineCapion);
//获得时间线上的第一个字幕 const firstCaption = nvTimeline.getFirstCaption(); //获得时间线上的最后一个字幕 const lastCaption = nvTimeline.getLastCaption(); //获得时间线上的当前字幕的前一个字幕 const prevCaption = nvTimeline.getPrevCaption(currentCaption); //获得时间线上的当前字幕的后一个字幕 const nextCaption = nvTimeline.getNextCaption(currentCaption);
根据时间线上的位置获得字幕,返回保存当前位置字幕的List集合。获取的字幕列表排序规则如下:
const cpationList = nvTimeline.getCaptionsByTimelinePosition(1000000);
修改字幕属性可通过NvsTimelineCaption类的方法实现。获取字幕后,可设置字幕文本,颜色,加粗,斜体,描边等。
currentCaption.setText("Meishe SDK");
//改变入点 currentCaption.changeInPoint(1000000); //改变出点 currentCaption.changeOutPoint(5000000); //改变显示位置(入点和出点同时偏移offset值) currentCaption.movePosition(1000000);
预览效果参考视频定位预览
添加,删除以及获取动画贴纸也是在时间线(timeline)上进行执行的,可以参考SdkDemo示例的贴纸模块。
nvTimeline.addAnimatedSticker(0, nvTimeline.getDuration(), stickerId);stickerId是动画贴纸包Id,添加动画贴纸之前需要先安装动画贴纸包,参考素材包管理, 试用贴纸奶油蛋糕
const nextSticker = nvTimeline.removeAnimatedSticker(currentSticker);
//获取时间线上第一个动画贴纸 const firstSticker = nvTimeline.getFirstAnimatedSticker(); //获取时间线上最后一个动画贴纸 const lastSticker = nvTimeline.getLastAnimatedSticker(); //获取时间线当前动画贴纸的前一个动画贴纸 const prevSticker = nvTimeline.getPrevAnimatedSticker(currentSticker); //获取时间线当前动画贴纸的后一个动画贴纸 const nextSticker = nvTimeline.getNextAnimatedSticker(currentSticker);
根据时间线上的位置获得动画贴纸,返回保存当前位置动画贴纸对象的List集合。 获取的动画贴纸列表排序规则如下:
const stickerList = nvTimeline.getAnimatedStickersByTimelinePosition(1000000);
修改贴纸属性可通过NvsTimelineAnimatedSticker类的方法实现。获取贴纸后,可设置缩放值,水平翻转,旋转角度,平移等。
currentSticker.setScale(1.2);
currentSticker.setCenterPolarAngle(1.2);
//改变入点 currentSticker.changeInPoint(1000000); //改变出点 currentSticker.changeOutPoint(5000000); //改变显示位置(入点和出点同时偏移offset值) currentSticker.movePosition(1000000);
预览效果参考视频定位预览
片段时间线是在视频轨道上添加的, 可以参考sdkDemo的 addTimelineClip 方法。
const timeline = nvStreamingContext.createTimeline(
new NvsVideoResolution(width, height),
new NvsRational(25, 1),
new NvsAudioResolution(44100, 2),
TIMELINE_FLAGS,
);
const videoTrack = timeline.appendVideoTrack();
const clip = videoTrack.appendClip(filePath);
const timelineClip = videoTrack.addTimelineClip(timeline,0);
addTimelineClip(timeline: NvsTimeline, inPoint: number): NvsVideoClip;
预览效果参考视频定位预览
视频转场包括内嵌式转场和包裹式转场。设置视频内嵌转场:参考setBuiltinTransition
videoTrack.setBuiltinTransition(0,transitionName);
videoTrack.setPackagedTransition(1,transitionId);transitionId是视频包式转场Id,添加视频包式转场之前需要先安装视频包式转场,参考素材包管理,试用转场三维旋转03
预览效果参考视频定位预览
在视频后续编辑中,经常会使用到几种特效,分别是视频特效(NvsVideoFx),音频特效(NvsAudioFx),时间线视频特效(NvsTimelineVideoFx)。
视频特效是使用在视频片段上的,每个视频片段可以添加若干个视频特效。视频特效包括内嵌视频特效,包裹视频特效,美颜特效。
videoClip.appendBuiltinFx(fxName);
videoClip.appendPackagedFx(fxPackageId);fxPackageId是视频特效包Id,添加视频特效包之前需要先安装视频特效包,参考素材包管理 试用转场垂直镜像
移除视频特效包括移除指定索引值特效和移除所有视频特。
videoClip.removeFx(0);
videoClip.removeAllFx();
音频特效是使用在音频片段上的,每个音频片段可以添加若干个音频特效。
audioClip.appendFx(fxName);
audioClip.removeFx(0);
时间线视频特效是使用在时间线上的一种特效,包含内嵌特效和包裹特效。时间线上可以添加若干个时间线视频特效。
添加时间线特效:
nvTimeline.addBuiltinTimelineVideoFx(1000000,5000000,fxName);
nvTimeline.addPackagedTimelineVideoFx(1000000,5000000,fxPackageId);fxPackageId是视频特效包Id,添加时间线特效包之前需要先安装时间线特效,参考素材包管理
//获取时间线上第一个时间线视频特效 const firstTimelineFx = nvTimeline.getFirstTimelineVideoFx(); //获取时间线上最后一个时间线视频特效 const lastTimelineFx = nvTimeline.getLastTimelineVideoFx(); //获取时间线上某个时间线视频特效的前一个时间线视频特效 const prevTimelineFx = nvTimeline.getPrevTimelineVideoFx(currentTimelineFx); //获取时间线上某个时间线视频特效的下一个时间线视频特效 const nextTimelineFx = nvTimeline.getNextTimelineVideoFx(currentTimelineFx);
根据时间线上的位置获得时间线视频特效,返回当前位置时间线视频特效对象的数组。获取的时间线视频特效数组排序规则如下:
const timelineFxArray = nvTimeline.getTimelineVideoFxByTimelinePosition(2000000);
//改变入点 currentTimelineFx.changeInPoint(1000000); //改变出点 currentTimelineFx.changeOutPoint(5000000); //改变显示位置(入点和出点同时偏移offset值) currentTimelineFx.movePosition(1000000);预览效果参考视频定位预览
美摄SDK提供了丰富的素材库,包括动画贴纸,主题,字幕样式,转场等。素材包可以从网络上下载,或者由美摄SDK项目组提供,用户可以根据需要选择使用这些素材包。美摄SDK通过NvsAssetPackageManager类对这些素材包进行管理,可以安装,卸载,获取素材包的状态,版本号等。 详细参考NvsAssetPackageManager
import axios from 'axios';
const packageUrl = "https://qasset.meishesdk.com/material/pu/animatedsticker/A1509C3D-7F5C-43CB-96EE-639ED7616BB7/A1509C3D-7F5C-43CB-96EE-639ED7616BB7.1.animatedsticker";
const response = await axios.get(packageUrl, { responseType: 'arraybuffer' });
const packageFile = '/' + packageUrl.split('/').pop();
await FS.writeFile(packageFile, new Uint8Array(response.data));
let packageLicenseFile = '';
let packageType = NvsAssetPackageTypeEnum.AnimatedSticker;
const assetPackageManager = nvsGetStreamingContextInstance().getAssetPackageManager();
assetPackageManager.onFinishAssetPackageInstallation = (assetPackageId, assetPackageFilePath, assetPackageType, error) => {};
assetPackageManager.installAssetPackage(packageFile, packageLicenseFile, packageType);
const error = nvStreamingContext.getAssetPackageManager().uninstallAssetPackage(stickerId,NvsAssetPackageTypeEnum.AnimatedSticker);
美摄SDK提供了很多回调接口,如果要查询采集设备状态,采集录制状态,视频播放状态,文件生成状态,资源包安装状态等,则必须在创建NvsStreamingContext对象后设置回调并实现对应的回调方法。参考nvStreamingContext
nvStreamingContext.onPlaybackTimelinePosition = (timeline, position) => {
console.log(`当前播放位置${position}`);
}
web端为了编辑效率,并没有使用原始视频进行编辑,而是采用了低分辨率的视频,合成视频需要调用后端的接口生成视频,可以参考sdkDemo中exportVideo()方法。
如果您的项目是ts版本,一般需要ts支持。您可以通过以下方式获得sdk.d.ts文件:
首先通过文档中的SDK示例地址将文件下载到本地,下方代码中是放置到根文件public/static/wasm文件夹下,名称为NvStreamingSdk.js,您可以根据您的项目实际情况而定,注意下方代码cmd第一行地址要与其保持一致(该地址是相对于终端运行的地址,注意区分)
import process from 'child_process';
const exec = process.exec;
const cmd = `\
tsc public/static/wasm/NvStreamingSdk.js \
--target es2015 \
--module commonjs \
--declarationMap true \
--declaration true \
--allowJs true \
--emitDeclarationOnly true \
--outFile types/sdk.d.ts
`;
exec(cmd, function (error, stdout, stderr) {
console.log(error, stdout, stderr);
});
node ./xx/xx/declare.js
... "include": ["src", "types"], ...
更多功能,请参考:https://www.meishesdk.com/cloudedit,会有详细介绍。