🍉jym,取 视频 的 某帧,pick pick pick🎨

前言

在一些视频站点,往往会有这么一个功能,就是你上传了一个视频过后,他可以帮你自动的生成封面。

许多视频平台都提供智能封面生成功能------当用户上传视频后,系统能自动分析视频内容并提取关键帧作为封面候选,让用户可以选择最合适的画面作为视频封面。这项功能的核心技术在于如何高效地从视频流中提取每一帧图像。

可以在这个视频里边选择某一帧,来作为这个视频的封面。那么涉及一个需求:如何根据一个视频,来得到它里边每一帧的画面。

抖音创作平台管理后台就有做啊(所有pc视频管理后台,抖音啊、淘宝啊、然后西瓜🍉),给大家截图看一眼,类似这种:

正文

举个例子,选择一个文件,根据这个视频文件,把那些一帧一帧的画面,给做一个图片显示出来。

可以通过以下步骤构建视频帧提取功能:

  1. 文件选择交互

    • 在页面中添加文件选择控件
    • 为控件绑定change事件监听器
    • 当用户选择视频文件后触发处理逻辑
  2. 视频帧处理

    • 使用HTML5 Video元素和Canvas API
    • 通过视频时间轴逐帧捕获画面
    • 将捕获的帧转换为图像数据
js 复制代码
const inp = document.querySelector('input[type=file]');

inp.onchange = e => {
  const file = e.target.files[0]
  capptureFrame(file, 1) // 这个文件的第一秒的画面
}

// 辅助方法:capptureFrame。给一个file文件,给第几帧的画面,默认为0
function capptureFrame(file, time = 0) {
  return {
    url: '', // 图片的url地址,用于渲染
    blob: '' // 图片的二进制数据,用于上传到服务器
  }
}

通过e.target.files[0]拿到选择的文件。

根据这个选择的文件,来生成某一帧的画面,要写这么一个辅助方法,叫capptureFrame

具体实现

  • 首先 ,把这个video元素。

  • 接着 ,跳到这个video元素指定的时间点。

  • 然后 ,把它画到canvas里面。

为什么

因为,能够得到整个canvas里面画的二进制数据,以及,通过这个数据,生成一些UI和地址。

js 复制代码
function capptureFrame(file, time = 0) {
  const vdo = document.createElement('video');
  // 第一种:通过base64,给一个文件,把它转换成一个base64格式。如果用这个方法,base64转一个视频,会非常大。
  // 第二种:object url
  vdo.src = URL.createObjectURL(file);
  console.log(vdo.src);
}

object url(它是一个blob协议开头):

通过这样的地址,访问到本地计算机里边的一些资源,直接粘贴到地址栏blob:http://127.0.0.1:5500/f16b2abc-61f1-43d3-a0c8-c36b7e04c20f,可以直接访问该视频。

一旦网页关闭了,它就会跟着消失了,就失效了。

js 复制代码
// 辅助方法:capptureFrame。给一个file文件,给第几帧的画面,默认为0
function capptureFrame(file, time = 0) {
  const vdo = document.createElement("video");
  // 第一种:通过base64,给一个文件,把它转换成一个base64格式。如果用这个方法,base64转一个视频,会非常大。
  // 第二种:object url
  vdo.currentTime = time; // 设置视频的播放时间
  vdo.autoplay = true; // 设置视频自动播放,只是为了能够自动播出来。可能浏览器的自动播放策略会禁止它自动播放。
  vdo.src = URL.createObjectURL(file); // 设置视频的源
  console.log(vdo.src); // 打印视频的源
}

可能浏览器的自动播放策略会禁止它自动播放,那么自动播放可能会失败。

在浏览器中处理视频自动播放限制的最佳实践如下:给它自动静音,也不需要他的声音。

js 复制代码
vdo.muted = true;

画canvas

js 复制代码
vdo.oncanplay = () => {
  const cvs = document.createElement('canvas');
  cvs.width = vdo.videoWidth;
  cvs.height = vdo.videoHeight;
  const ctx = cvs.getContext('2d');
  ctx.drawImage(vdo, 0, 0, cvs.width, cvs.height);
  document.body.appendChild(cvs);
}

选择一个文件,看这个视频的第一秒的画面,是不是就画到页面上去了。

把这个canvas里边的东西导出,导出成一个图片地址,以及一个二进制对象。

canvas里边本身就有一个函数叫做toBlob,异步,传一个回调,把这个转换的一个二进制对象:

js 复制代码
cvs.toBlob((blob) => {
  const url = URL.createObjectURL(blob);
  console.log(url);
})

循环前10s

js 复制代码
inp.onchange = (e) => {
  const file = e.target.files[0];
  // captureFrame(file, 1); // 这个文件的第一秒的画面
  // 循环前面10s的画面
  for (let i = 0; i < 10; i++) {
    captureFrame(file, i).then((res) => {
      console.log(res);
      const img = document.createElement("img");
      img.src = res.url;
      document.body.appendChild(img);
    });
  }
};

全部代码

// 文件选择 和 事件处理

js 复制代码
const inp = document.querySelector('input[type=file]');

inp.onchange = e => {
  const file = e.target.files[0];
  // 提取第1秒的画面
  captureFrame(file, 1).then(res => {
    console.log(res);
  });
  
  // 或者提取前10秒的每一帧
  for (let i = 0; i < 10; i++) {
    captureFrame(file, i).then(res => {
      const img = document.createElement("img");
      img.src = res.url;
      document.body.appendChild(img);
    });
  }
};

// 帧提取函数

js 复制代码
function captureFrame(file, time = 0) {
  return new Promise((resolve, reject) => {
    const vdo = document.createElement("video");
    vdo.currentTime = time; // 设置要提取的时间点
    vdo.autoplay = true;    // 设置自动播放
    vdo.muted = true;       // 静音以避免自动播放限制
    vdo.src = URL.createObjectURL(file); // 设置视频源
    
    vdo.oncanplay = () => {
      const cvs = document.createElement("canvas");
      cvs.width = vdo.videoWidth;   // 设置canvas尺寸与视频一致
      cvs.height = vdo.videoHeight;
      const ctx = cvs.getContext("2d");
      ctx.drawImage(vdo, 0, 0, cvs.width, cvs.height); // 绘制视频帧
      
      // 将canvas内容转为Blob对象
      cvs.toBlob((blob) => {
        const url = URL.createObjectURL(blob);
        resolve({
          url,  // 图片URL可用于显示
          blob  // 二进制数据可用于上传
        });
      });
    };
  });
}

效果

相关推荐
fruge14 小时前
前端正则表达式实战合集:表单验证与字符串处理高频场景
前端·正则表达式
baozj14 小时前
🚀 手动改 500 个文件?不存在的!我用 AST 撸了个 Vue 国际化神器
前端·javascript·vue.js
用户40993225021215 小时前
为什么Vue 3的计算属性能解决模板臃肿、性能优化和双向同步三大痛点?
前端·ai编程·trae
海云前端115 小时前
Vue首屏加速秘籍 组件按需加载真能省一半时间
前端
蛋仔聊测试15 小时前
Playwright 中route 方法模拟测试数据(Mocking)详解
前端·python·测试
零号机15 小时前
使用TRAE 30分钟极速开发一款划词中英互译浏览器插件
前端·人工智能
疯狂踩坑人15 小时前
结合400行mini-react代码,图文解说React原理
前端·react.js·面试
Mintopia15 小时前
🚀 共绩算力:3分钟拥有自己的文生图AI服务-容器化部署 StableDiffusion1.5-WebUI 应用
前端·人工智能·aigc
街尾杂货店&15 小时前
CSS - transition 过渡属性及使用方法(示例代码)
前端·css
CH_X_M15 小时前
为什么在AI对话中选择用sse而不是web socket?
前端