如果是单纯的想裁剪视频的话,微信小程序提供了一个非常便捷的API openVideoEditor 如果要更换音频轨道,或者在为视频增加什么样式,就会分别获取到视频的视频轨道和音频轨道和视频的每一帧 首先是获取视频的每一帧
1·获取视频的帧
微信小程序提供了 createVideoDecoder API用来解析视频的帧数,虽然我用的时候总是感觉丢帧,但目前也没找到别的办法
步骤 |
---|
要获取视频的帧,首先要知道一个视频有多少帧 | 通过wx.getVideoInfo获取到视频信息包括fps和duration | 我们可以通过上面获取的信息获取视频总共有多少帧数(fps*duration) |
下面我们来获取视频的帧,并生成每一帧的图片,会用到 createOffscreenCanvas这个API用来创建一个离屏画布
首先如果是选择视频的话可以直接获取到视频信息,如果是网络链接的要通过 wx.downloadFile 把视频下拉获取到本地的临时链接 可以是网络视频 也可以是自己的视频,下面的案例是我本地的视频,视频时长不宜太长,不然小程序容易崩掉
关键逻辑处理在getFrameData 这个方法中,大致意思:获取的帧的数据是一个数组里面存储着图片的像素值
如果是1px*1px的图像那这个数组的长度就为4[0,0,0,255]代表着一个黑色不透明的像素点 ,图像就是由这么一个一个的像素组成,我们可以通过canvasAPI把这些像素绘制出来并且导出
需要注意的是,如果导入像素点的数量和画布实际的绘制大小有出入会导致错误,就像上面说的一样,如果你要把[0,0,0,255]绘制到两个像素这明显是不合理的
Page({
data:{
"audioUrl": "https://dl.stream.qqmusic.qq.com/C4000026gMkr1L9tKN.m4a?guid=7840612806&vkey=4696DBD6111F38EC3D75D0F4FDF769B3736068A795CF585BA18DF3A4BCD8F83D0662B54D70F64934AF74A34C9A354AFF0AA4660CCE4C66F0&uin=&fromtag=120032",
videoUrl: "https://yun-live.oss-cn-shanghai.aliyuncs.com/video/20201225/2EbJdXN5ZG.mp4",
videoInfo: {},
canvasWidth: 0,
canvasHeight: 0,
fps: 0,
duration: 0,
imageList: []
},
onLoad() {
this.videoDecoderStart()
},
async videoDecoderStart() {
var {tempFilePath} = await wx.chooseVideo()
console.log(tempFilePath)
var videoInfo = await wx.getVideoInfo({
src: tempFilePath,
})
this.setData({
videoInfo: videoInfo,
canvasWidth: videoInfo.width,
canvasHeight: videoInfo.height,
duration: videoInfo.duration,
fps: videoInfo.fps
}, () => {
this.videoDecoder = wx.createVideoDecoder()
const {
canvas,
context
} = this.initOffscreenCanvas(this.data.canvasWidth, this.data.canvasWidth)
this.videoDecoder.on("start", () => {
this.videoDecoder.seek(0)
this.timer = setInterval(() => {
this.getFrameData(canvas, context)
}, 300);
})
this.videoDecoder.on("seek", () => {})
this.videoDecoder.on("stop", () => {})
this.videoDecoder.start({
source: tempFilePath
})
})
},
getFrameData(canvas, context) {
var func = (() => {
var imageVideoData = this.videoDecoder.getFrameData()
if (imageVideoData) {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
context.clearRect(0, 0, this.data.canvasWidth, this.data.canvasHeight)
var imgData = context.createImageData(this.data.canvasWidth, this.data.canvasHeight)
var clampedArray = new Uint8ClampedArray(imageVideoData.data)
for (var i = 0; i < clampedArray.length; i++) {
imgData.data[i] = clampedArray[i];
}
context.putImageData(imgData, 0, 0)
this.data.imageList.push(canvas.toDataURL())
this.setData({
imageList: this.data.imageList
}, () => {
canvas.requestAnimationFrame(func)
})
}
})
func()
},
getTempPath(url){
return new Promise((r,j) => {
wx.downloadFile({
url: url,
success:r,
fail:j
})
})
},
initOffscreenCanvas(canvasWidth,canvasHeight){
const canvas = wx.createOffscreenCanvas({
type: '2d',
width: canvasWidth,
height: canvasHeight
})
return {
canvas,
context: canvas.getContext('2d')
}
},
})
2·展示视频的帧
获取到视频的帧之后,当然就要展示出来,下面是我展示的一些样式以及代码
代码部分
|