最近出于个人兴趣,正在开发一个这样的视频剪辑器,等开发得差不多了,可能会开源出来。今天主要讲一讲下面轨道中的视频预览是如何实现的。
方案
-
获取视频时长,计算出轨道中的表示视频的这一个
Item
应该画多长。这里有一个比例尺的概念,比例尺是一个数字,表示当前界面上用多少px来表示1s。比如我这个视频是30s,比例尺是10,那么这个item
的宽度就是 30*10 = 300px -
计算出每一帧的宽度。视频有横屏的,有竖屏的,但不管比例如何,我们总是要在Y轴的维度画满整个轨道。也就有了下面这个公式。
-
有了每一帧的宽度(px),再结合比例尺,可以算出步长,也就是我们应该隔几秒取一帧。
frameWidth / scale
相关API
视频时长
video 的时长、宽高信息属于视频的元信息,可以在onloadedmetadata
事件后取得。
javascript
export default class Video extends Item {
...
getMetaInfo(url) {
return new Promise((resolve) => {
const video = document.createElement('video')
video.src = url
video.onloadedmetadata = () => {
this.meta = {
videoHeight: video.videoHeight,
videoWidth: video.videoWidth,
duration: video.duration
}
resolve(this.meta)
}
})
}
...
}
贴图
把视频帧画在canvas上用drawImage方法,直接看MDN吧,我就不赘述了。
具体实现
相关代码
javascript
function drawFrames() {
canvas.value.width = props.data.duration * props.data.timeline.scale
ctx = canvas.value.getContext('2d')
if (!video) {
video = document.createElement('video')
video.style.display = 'none' // Hide the video element
video.src = props.data.thumb
video.muted = true
document.body.appendChild(video)
}
video.addEventListener(
'loadedmetadata',
() => {
drawNextFrame(0)
},
{ once: true }
)
drawNextFrame(0)
video.removeEventListener('seeked', drawFrame)
video.addEventListener('seeked', drawFrame)
}
function drawFrame() {
const frameWidth = (props.data.track.heightInPx / props.data.height) * props.data.width
const left = video.currentTime * props.data.timeline.scale
const step = frameWidth / props.data.timeline.scale
ctx.drawImage(video, left, 0, frameWidth, props.data.track.heightInPx)
drawNextFrame(step)
}
function drawNextFrame(step) {
if (0 === step) {
video.currentTime = step
}
if (video.currentTime < video.duration) {
video.currentTime += step
}
}
视频太大怎么办
如果一个视频太大,比如有1G,你会发现预览轴渲染得很慢,这是显而易见的,因为画帧要把视频实实在在的加载到那个地方。
我这边使用的方案是在服务器端,为每个视频生成一个thumb版的视频。这个thumb版的视频画面分辨率 / 帧率 / 码率 ,都压得非常小,并且把音轨去掉,一条ffmpeg
命令就搞定。
bash
ffmpeg -i demo.mp4 -b:v 20K -r 5 -an -vf scale=-2:100 ./demo-thumb.mp4
- -i demo.mp4 输入视频
- -b:v 20K 把画面码率限制在20K
- -r 5 帧率设为每秒5帧
- -an 去掉音轨
- -vf scale=-2:100 视频高度压到100px,宽度自适应
- ./demo-thumb.mp4 输出视频
压缩后效果明显,看下图。