小程序端视频剪辑的春天来了?

您所在的位置:网站首页 视频制作插件 小程序端视频剪辑的春天来了?

小程序端视频剪辑的春天来了?

2023-10-31 02:22| 来源: 网络整理| 查看: 265

前言-吐槽篇

在这个短视频遍地的时代,小程序端视频剪辑功能似乎一直是缺失状态,前段时间发布了一个视频编辑器的接口,试了下感觉也很鸡肋。也有在社区看到前辈们试水小程序音视频合成初探,研究了一番,其中涉及到几个重要的api:1-视频解码器,将视频解码,获得帧数据绘制到canvas上;2-音视频合成器,负责向原视频中添加音频;3-画面录制器,最后通过画面录制器导出成视频。这里必须要吐槽下小程序的文档,简约派代表,能省略的绝不多写一句,翻一下上述api文档便可石锤,几乎全靠猜,这么复杂的接口也不提供个demo。曾尝试按照上述思路做一些复杂的视频剪辑功能,比如图片视频合成一个,单单解决音频错位的问题就搞得头秃,可以说是步履维艰……

正文开始

最近发现小程序发布了一款视频剪辑插件 - 微剪,看了下官方提供的Demo还挺有趣的,相比于上述API功能更完善,感觉小程序一直在探索新姿势,真香啊。

小程序:是的,我啥都能做[手动狗头]

本文前半部分旨在结合上述插件,输出一个简单的视频剪辑小程序,感兴趣(不想动手只想白嫖)的同学也可以直接在小程序中搜索「微剪插件演示」,感受一下插件的功能;后半部分着重介绍下插件的一些核心思想和高级用法。

插件接入

微剪以插件的形式提供给开发者,不能单独使用,所以我们必须要先有个小程序主体(不会吧阿sir,这都想白嫖我?如何注册小程序请自行百度好伐)。插件的接入流程也比较简单,官方文档 写的比较清楚,这里就不啰嗦了。目前好像处于公测阶段,可以免费申请使用。

代码开发

上述准备工作做完后,我们就可以在代码中使用微剪插件了,插件暴露了两个组件clip和export,clip是入口组件,export为出口组件,其间的流程完全被插件接管,开发者无需关心也无法干涉(有点霸道总裁的味道了啊),通过一系列简单的配置后就可以看到插件的全貌了。【2020/09/23日 删除】

【2020/09/23日 新增】最新版插件提供两种开发模式,暂称为自动模式(仅通过少许配置即可)和手动模式(利用插件暴露的组件自定义开发,1.3.0版本新增)。先介绍第一种方式,按照下述步骤走一遍,应该就能看到插件的全貌了,问题不大。

step1:app.json 引入插件配置

参考这里:小程序插件配置

{ "plugins": { "myPlugin": { // 自定义插件名,后面会用到 "version": "1.3.0", // 最新版本号即可 "provider": "wx76f1d77827f78beb" // 插件ID,固定值 } } } step2:页面中添加插件

新建一个页面,在页面的json文件中引入插件暴露的clip组件

{ "usingComponents": { "my-clip": "plugin://myPlugin/clip" } }

my-clip: 自定义组件名字,在wxml中使用;

myPlugin: 引入插件的时候,在app.json中声明的插件名;

clip: 固定值,官方暴露的组件名称。

wxml文件:

js文件:

Page({ data: { settings: { common: { videoMaxDuration: 30, // 小程序限制最多拍摄30秒 chooseMaxDuration: 1000, // 选择视频的默认时长限制 clipMaxDuration: 60, // 裁切时长的默认限制 } } }, })

settings有众多配置项,具体可参考官方文档;不设置也可以,插件提供了一套默认的配置项。

PS:这里得吐槽下插件的主题机制,感觉很繁琐。跟平时写vue组件等通过样式覆盖的方案不同,而是通过一系列配置项进行配置,这也意味着只能修改配置项涉及的UI,其他的一律无法修改。这里有个疑问,为什么不采用外部样式类的方案呢?感觉更灵活啊。

到这里,应该就可以在手机上看到插件的全貌了,截取了几张图如下:

step3:导出视频

点击上图的「下一步」按钮,就可以导出视频到相册中了。插件本身也支持自定义导出,下面简单介绍下用法:

step3-1:新增导出页

创建一个自定义的导出页,在导出页中引入插件提供的导出组件即可。

json文件:

{ "usingComponents": { "my-export": "plugin://myPlugin/export" } }

wxml文件:

自定义导出界面部分 自定义导出按钮

然后在js文件中处理导出成功回调即可,导出组件提供了一系列属性,包括水印,导出进度等,具体导出数据结构及属性,可参考官方文档。

step3-2:配置导出页路径

新增上述导出页后,还需要通知插件导出页路径。还记得上文提到的 my-clip 组件中的settings属性吗,此为插件的配置项入口,在里面配置下即可。

data: { settings: { common: { videoMaxDuration: 30, // 小程序限制最多拍摄30秒 chooseMaxDuration: 1000, // 选择视频的默认时长限制 clipMaxDuration: 60, // 裁切时长的默认限制 exportPagePath: '/pages/export/export' // 自定义导出页面地址 } } }

事已至此,通过开发工具的预览功能便可以在手机中看到自定义的导出页了。

【2020/09/23日 新增】

高级模式(1.3.0版本新增)

还记得上文提到的简单(自动)模式,我们只用到了插件暴露的两个组件,clip和export,难道插件只暴露了两个组件吗?就这?就这???

插件从1.3.0版本开始,开放了更多的组件出来,也提供了一种称为【高级接入】的方式,接下来让我们一探究竟:

高级模式需要先了解插件的数据机制,即下文提到的Track驱动模式,在此基础上可以结合插件暴露的组件进行自定义创作。

标准的数据驱动模式

高级模式,即文章开头提到的手动模式,遵循一套标准的Track数据结构,此处引用官方文档的一张图和介绍

什么是Track?

track顾名思义为轨道,播放器支持多种轨道,如:媒体(视频 或者 图片), 音乐, 特效, 滤镜, 文字 等。 每一种类型的轨道对应不同类型的track,可以简单的理解track就是轨道。

什么是Clip?

clip是轨道中的素材片段。举个例子: 你需要编辑三段视频,那么你需要将三个视频片段加入媒体轨道中,那么这里的每个视频片段就对应不同的clip,每个clip的属性会不相同,如: 视频时长,开始结束时间等。

上述官方定义看得有点懵?大白话简单理解,画面播放的时候,我们在某一时刻可以看到(听到)很多元素,比如原始画面的一帧,特效的一部分(几个小心心),滤镜,文字以及音乐等,Track就是这些元素的数据载体,不同的元素由不同类型的Track管理,所以Track是个数组。

随着时间轴的推移,我们又可以看到不同的视频、图片、特效切换效果等等,比如由图片A切换到图片B,再切换到C、由特效A切换到特效B等,这个时候就涉及到clip的概念,clip可以简单理解为上述Track的一个属性,里面存放着原始素材的信息(基本信息,展示时长等),比如上述🌰,负责媒体的Track的clips就得包含图片A、图片B以及图片C的信息,负责特效的Track的clips就得包含特效A和特效B的信息。

官方提供了详细的Track以及Clip的数据接口定义,具体请参考官方文档 数据结构文档 部分,这里只做核心思想解释。

积木已有,创作在你

最新版本(1.3.0)暴露的几个核心组件:

wj-player:插件的核心播放器组件,对应的上述简单模式截图里的播放视频的容器; wj-camera:相机组件,可以拍摄视频,也可以从相册选择视频、图片,截图里的图片、视频就是从这来的; wj-clipper:裁切器组件,上述截图中可以看到视频一帧一帧画面的那部分,就是用这个组件实现的。

上述组件即插件实现的核心部分,还有个文字组件(wj-textEditor、向视频中添加文字)和导出组件(wj-export、与自动模式的导出组件基本相同)使用比较简单,每个组件都有各自的属性和方法,具体请参考高级组件API文档。结合标准的Track数据驱动,理论上可以搭配出与插件功能完全一致的视频剪辑体验,当然如果自动模式可以满足业务需求,那白嫖它,做条闲鱼不好吗?如果业务需求需要个性化,可以考虑使用上述手动模式,自力更生,丰衣足食。

光说不练假把式

下面通过一个简单的 相册集小demo 演示一下上述组件的用法,可以下载小程序片段到本地运行(注:由于插件的使用需要先申请资格,所以请填写一个有使用权限的小程序AppID)。主要使用了三个组件,wj-camera(收集素材)、wj-player(展示素材和特效)、wj-export(导出视频),通过camera页面从相册选择图片,然后搭配插件内置的一些特效输出到player页面中预览效果,最后通过export页面导出。

核心代码如下:

camera 页面

camera.wxml

随机特效 卡点视频(6图)

camera.js

// pages/camera/camare.js Page({ data:{ _media:[] }, onMediaChanged(e){ this.data._media = e.detail.track }, onClickDefaultBtn(){ global.testMedia = JSON.parse(JSON.stringify(this.data._media)) }, // 【万有引力】卡点设置 onClickPointBtn(){ let durationList = [2.6, 2.6, 2.6, 2.8, 2.8, 2.6], startAt = 0 let copyMedia = JSON.parse(JSON.stringify(this.data._media)) copyMedia.clips.forEach((item,index) => { // 更新卡点视频图片播放时长 let duration = durationList[index] item.section.end = duration item.section.duration = duration item.startAt = startAt startAt += duration }); copyMedia.duration = startAt global.testMedia = copyMedia } }) player 页面

player.wxml

// pages/player/player.js Page({ data: { styleConfig: { height: 1000, width: 750 }, type: 'default' }, onLoad: function (option) { this.setData({ type: option.type }) }, // 生命周期函数--监听页面初次渲染完成 onReady: function () { this.bindPlayer() }, // 绑定player bindPlayer() { this.player = this.selectComponent("#player"); }, // 设置player组件的数据 async onPlayerReady() { let tracks = [] if (this.data.type === 'default') { tracks = this.getDefaultTrack() } else { tracks = this.getWYYLTrack() } this.player.updateData(tracks) // 更新player的数据 global.testExportTracks = tracks // 存储导出页数据 }, // 随机特效 getDefaultTrack() { let startAt = -3, { TRACK_TYPES, CLIP_TYPES, Clip, ClipSection, Track } = global['wj-types'] let mediaTrack = global.testMedia // 获取camera组件输出的media轨道 let effectTrack = new Track({ // 新建一个effect轨道 type: TRACK_TYPES.EFFECT, clips: [] }) let effectList = this.player.getEffects(); // 获取player内置的特效 let randomEffects = new Array(mediaTrack.clips.length).fill(null).map((item, index) => { startAt += 3 return new Clip({ id: `effect-${index}`, type: CLIP_TYPES.EFFECT, key: this._getRandomEffect(effectList).key, section: new ClipSection({ start: 0, end: 3 }), startAt: startAt }) }) effectTrack.clips = randomEffects // 更新effect轨道的clips数据 return [mediaTrack, effectTrack] }, // 【万有引力】卡点特效 getWYYLTrack() { let { TRACK_TYPES, CLIP_TYPES, Clip, ClipSection, Track } = global['wj-types'] let mediaTrack = global.testMedia // 获取camera组件输出的media轨道 let effectTrack = new Track({ // 新建一个effect轨道 type: TRACK_TYPES.EFFECT, clips: [] }) // 定制卡点特效 let effectList = [ { key: 'Swing', duration: 2.6, startAt: 0 }, { key: 'SoulOut', duration: 0.6, startAt: 2.6 }, { key: 'Shining', duration: 2, startAt: 3.2 }, { key: 'SoulOut', duration: 0.6, startAt: 5.2 }, { key: 'Blink', duration: 2, startAt: 5.8 }, { key: 'SoulOut', duration: 0.6, startAt: 7.8 }, { key: 'LightCircle', duration: 2.2, startAt: 8.4 }, { key: 'SoulOut', duration: 0.6, startAt: 10.6 }, { key: 'FlowingLight', duration: 2.2, startAt: 11.2 }, { key: 'SoulOut', duration: 0.6, startAt: 13.4 }, { key: 'Heart', duration: 2, startAt: 14 }, ] let randomEffects = new Array(mediaTrack.clips.length * 2 - 1).fill(null).map((item, index) => { return new Clip({ id: `effect-${index}`, type: CLIP_TYPES.EFFECT, key: effectList[index].key, section: new ClipSection({ start: 0, end: effectList[index].duration }), startAt: effectList[index].startAt }) }) effectTrack.clips = randomEffects // 更新effect轨道的clips数据 let musicTrack = new Track({ // 新建一个music轨道 type: TRACK_TYPES.MUSIC, clips: [] }) let bgMusic = new Clip({ id: 'music', type: CLIP_TYPES.MUSIC, info: { tempFilePath: "https://imgcache.qq.com/operation/dianshi/other/wanyouyinli.c7f973d906d9b8a3e7db90a90a7874d01454614b.mp3", }, section: new ClipSection({ start: 0, end: 1000 }), startAt: 0 }) musicTrack.clips = [bgMusic] return [mediaTrack, effectTrack, musicTrack] }, // 跳转至导出页 goExport() { wx.navigateTo({ url: '../export2/export' }) }, // 获取随机特效 _getRandomEffect(effectList = []) { let index = Math.floor(Math.random() * effectList.length) return effectList[index] } }) export 页面

export.wxml

导出视频

export.js

// pages/export2/export.js Page({ data: { exportTracks: [] }, // 设置导出数据 onExportReady() { this.setData({ exportTracks: global.testExportTracks }) }, // 导出成功回调 onExportSuccess(e){ let res = e.detail; wx.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success: res => { wx.showToast({ title: "已保存至相册" }); } }); } })

camera和export页面比较简单,这里就不啰嗦了。主要说下player页面的实现思路,player的Track数据暂时只涉及到了三种:media类型、effect类型以及music类型(卡点模式用),其他的暂未使用。

随机模式:针对每张图随机获得一个特效key(wj-player组件内置了一些特效,直接使用即可),然后设置其时长与media类型的clip时长一致即可(默认是3s)。 卡点模式:根据音乐节奏调整了下每张照片的展示时长。

这样一个简单的照片集应用就做出来啦,so easy,妈妈再也不用担心我的发际线了。

卡点模式最终的成果物 点此预览

PS:使用过程中发现 wj-player 组件和 wj-export 组件无法在同一个页面中使用,不知道是不是我的姿势不对,有待后续验证;还有就是素材切换的时候没有转场动画,显得有些僵硬;另外看了下,特效倒是挺多的,但是没办法一键输出卡点视频,缺少一些常见的视频模板,自己做卡点视频特别费劲,要微调好久……

自动挡虽然好开,但是真正的赛车手都用手动挡[手动狗头结束]

总结

作为小程序端的一款视频剪辑工具,麻雀虽小五脏俱全,基础的功能是齐全的。个人使用下来整体体验还不错,接入也比较简单,官方配套文档也很完善,也期待官方后续可以提供更丰富的功能、组件。

最后,对视频剪辑小程序感兴趣的同学不防申请插件尝试一下,在官方基础api让人如此头秃的情况下,使用插件或许是个不错的选择。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3