前端对视频自动抽帧生成预览轴,给一个URL即可

最近出于个人兴趣,正在开发一个这样的视频剪辑器,等开发得差不多了,可能会开源出来。今天主要讲一讲下面轨道中的视频预览是如何实现的。

方案

  1. 获取视频时长,计算出轨道中的表示视频的这一个Item应该画多长。这里有一个比例尺的概念,比例尺是一个数字,表示当前界面上用多少px来表示1s。比如我这个视频是30s,比例尺是10,那么这个item 的宽度就是 30*10 = 300px

  2. 计算出每一帧的宽度。视频有横屏的,有竖屏的,但不管比例如何,我们总是要在Y轴的维度画满整个轨道。也就有了下面这个公式。

  3. 有了每一帧的宽度(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
  1. -i demo.mp4 输入视频
  2. -b:v 20K 把画面码率限制在20K
  3. -r 5 帧率设为每秒5帧
  4. -an 去掉音轨
  5. -vf scale=-2:100 视频高度压到100px,宽度自适应
  6. ./demo-thumb.mp4 输出视频

压缩后效果明显,看下图。

相关推荐
烂蜻蜓20 分钟前
前端已死?什么是前端
开发语言·前端·javascript·vue.js·uni-app
谢尔登1 小时前
Vue 和 React 的异同点
前端·vue.js·react.js
祈澈菇凉6 小时前
Webpack的基本功能有哪些
前端·javascript·vue.js
yanglamei19628 小时前
基于Python+Django+Vue的旅游景区推荐系统系统设计与实现源代码+数据库+使用说明
vue.js·python·django
流烟默8 小时前
vue和微信小程序处理markdown格式数据
前端·vue.js·微信小程序
菲力蒲LY9 小时前
vue 手写分页
前端·javascript·vue.js
zpjing~.~10 小时前
vue 父组件和子组件中v-model和props的使用和区别
前端·javascript·vue.js
bin915310 小时前
DeepSeek 助力 Vue 开发:打造丝滑的 键盘快捷键(Keyboard Shortcuts)
前端·javascript·vue.js·计算机外设·ecmascript·deepseek
格式化小拓10 小时前
在vue2中操作数组,如何保证其视图的响应式
前端·javascript·vue.js
陈小于10 小时前
vue从入门到精通(十一):条件渲染
前端·javascript·vue.js