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

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

相关推荐
ruanno_top10 小时前
倒计时该用 deadline 还是每秒减一?我把两种写法都跑了一遍
vue.js
徐小夕14 小时前
下载量超 30 万的开源智能体 Prime Agent:它把自己从 TypeScript 重写成了 Rust
前端·vue.js·github
toooooop815 小时前
CRMEB 故障排查笔记:安全网关只放行 GET/POST 怎么办
前端·javascript·vue.js·thinkphp6·crmeb
软件技术NINI15 小时前
基于SpringBoot和Vue的项目评审系统
javascript·vue.js·spring boot·后端·spring·毕业设计·毕设
FRing031 天前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷1 天前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码
小郎君6582 天前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
yilaisai2 天前
前端开发遇到的一些细节问题整理
vue.js
华玥作者2 天前
Vue 3 + TypeScript + uni-app 组件库推荐:hy-app(华玥组件库)
vue.js·typescript·uni-app
阿琳a_2 天前
b-map-viewer:面向 Vue 3 的地图可视化 SDK,从接入到展示再到交互分析
前端·javascript·vue.js·gis·数据可视化·地图