前端对视频自动抽帧生成预览轴,给一个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 输出视频

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

相关推荐
梵得儿SHI8 小时前
Vue 项目实战与性能优化全攻略:从代码、渲染到首屏,一站式解决卡顿慢加载
前端·vue.js·性能优化·vite·前端面试·前端优化·首屏优化
一 乐10 小时前
幼儿园管理系统|基于springboot + vue幼儿园管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·幼儿园管理系统
云水一下10 小时前
Vue.js从零到精通系列(七):高级特性实战——Teleport、异步组件、自定义指令与TypeScript深度结合
前端·vue.js·typescript
qq43569470110 小时前
Vue05
前端·vue.js
英勇无比的消炎药10 小时前
收藏备用TinyVue开发高频踩坑问题合集
vue.js
英勇无比的消炎药11 小时前
体积瘦身TinyVue打包优化与按需加载实践
vue.js·前端工程化
英勇无比的消炎药11 小时前
一套代码多端运行TinyVue响应式开发
vue.js·响应式设计
SwJieJie11 小时前
Webpack vs Vite 构建工程化实战(Vue 项目深度解析)
前端·vue.js·webpack·node.js
云水一下11 小时前
Vue.js从零到精通系列(八):项目实战——构建一个完整的电商后台管理系统
前端·javascript·vue.js
Csvn11 小时前
Vue3 响应式陷阱:解构赋值后页面不动了?Proxy 的"隐形成员"在搞鬼
前端·vue.js