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

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

相关推荐
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
会发光的猪。7 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客7 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
周全全8 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
ZwaterZ8 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
码农六六8 小时前
vue3封装Element Plus table表格组件
javascript·vue.js·elementui
徐同保8 小时前
el-table 多选改成单选
javascript·vue.js·elementui