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

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

相关推荐
2401_857600952 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600952 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL3 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
轻口味3 小时前
Vue.js 核心概念:模板、指令、数据绑定
vue.js
2402_857583493 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js
java_heartLake4 小时前
Vue3之性能优化
javascript·vue.js·性能优化
ddd君317745 小时前
组件的声明、创建、渲染
vue.js
前端没钱5 小时前
从 Vue 迈向 React:平滑过渡与关键注意点全解析
前端·vue.js·react.js
顽疲6 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
羊小猪~~6 小时前
前端入门之VUE--ajax、vuex、router,最后的前端总结
前端·javascript·css·vue.js·vscode·ajax·html5