🍉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  // 二进制数据可用于上传
        });
      });
    };
  });
}

效果

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学3 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端