美摄Web端SDK
3.12.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/13/NvEffectSdk.js"></script>
npm i meishewasmloader --save
import { WASMLoader } from 'meishewasmloader'; const wasmLoader = WASMLoader({ // 加载进度回调 showLoader: function (state) {}, // 失败回调 showError(errorText: string) {}, // 成功回调 loadingFinished() {}, }); wasmLoader.loadEmscriptenModule("https://alieasset.meishesdk.com/NvWasm/domain/13/"); // 开始调用函数加载
// 授权验证的回调函数 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.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;
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()方法。
更多功能,请参考:https://www.meishesdk.com/cloudedit,会有详细介绍。