【前端】上传视频,截取第一帧图片

使用input上传视频,获得视频的第一帧
参考:JavaScript获取视频的尺寸信息和第一帧图片 - 掘金 (juejin.cn)

尝试了新的做法,比下面的都快:

【webview Android】视频获取首帧为封面-优快云博客

简单方法

其实视频标签video默认会显示视频首帧。但是在某些浏览器上不会,比如在安卓的webview会显示为:

在这里插入图片描述

如果想让视频封面图为视频首帧:

技术栈svelte:

  //   获取视频首帧
  const getImgByVideo = (event: Event) => {
    let videoElement = event.currentTarget as HTMLVideoElement;

    // 播放渲染完首帧后获取
    videoElement.play();
    setTimeout(() => {
      videoElement.pause();
      let canvas = document.createElement('canvas');
      canvas.width = videoElement.videoWidth;
      canvas.height = videoElement.videoHeight;
      let ctx = canvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
        let posterUrl = canvas.toDataURL();
        videoElement.setAttribute('poster', posterUrl);
      }
    }, 100);
  };
 <video
   bind:this={videoRef[index].ref}
   on:canplay={getImgByVideo}
   on:ended={() => {
     videoRef[index].isPlay = false;
   }}
 >
   <source src={item.image} />
   <track kind="captions" />
 </video>

复杂方法

html:

<input
   bind:this={uploadRef}
   on:change={handleUpload}
   accept="video/*"
   type="file"
 />

视频类型校验:

const VIDEO_REG =
  /^(?:video\/avi|video\/mpeg|video\/mp4|video\/ogg|video\/webm|video\/x-ms-wmv|video\/x-msvideo)$/i;

handleUpload

  • 校验类型
  • 获得视频第一帧图片:调用getVideoImg方法
const handleUpload=(e)=>{
 const files = e.target.files[0];
 if(VIDEO_REG.test(files.type)){
	const videoImg = await getVideoImg(files);
  }
}

相关方法:getVideoImg

使用canvas实现:从一个视频文件中提取一帧图像,并将这个图像转换为一个可以在浏览器中使用的URL。

const getVideoImg = async (files: File): Promise<string> => {
  try {
    // 使用loadVideo函数加载视频文件,等待加载完成
    const video: HTMLVideoElement = await loadVideo(files);

    const canvasElem = document.createElement('canvas');

    // 设置canvas的宽度和高度与视频的宽度和高度相同
    canvasElem.width = video.videoWidth;
    canvasElem.height = video.videoHeight;

    // 获取canvas的2d渲染上下文,并在canvas上绘制视频的当前帧
    canvasElem
      .getContext('2d')
      ?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);

    const pngFile = await new Promise<string>((resolve, reject) => {
      // 将canvas的内容导出成一个blob文件
      canvasElem.toBlob((blob) => {
        if (blob) {
          // 成功创建了blob文件,将其转换为File对象,然后创建一个指向这个File对象的URL
          resolve(URL.createObjectURL(toThumbFile(blob)));
        } else {
          // 创建blob文件失败,拒绝Promise
          reject('');
        }
      }, 'image/png');
    });

    // 返回Promise的结果,即指向File对象的URL
    return pngFile;
  } catch (error) {
    // 如果在上述过程中发生错误,打印错误信息
    console.error(error);
  }
};

其中调用了loadVideo

const loadVideo = (file: File): Promise<HTMLVideoElement> => {
  return new Promise(function (resolve, reject) {
  
    const videoElem = document.createElement('video');
    const dataUrl = URL.createObjectURL(file);

    videoElem.onloadeddata = function () {
      // 当video元素的当前帧的数据可用时,将video元素作为Promise的结果
      resolve(videoElem);
    };

    // 当video元素发生错误时的处理函数
    videoElem.onerror = function () {
      reject('video error');
    };

    // 设置video元素的preload属性为'auto',这样video元素会预加载数据
    // 如果不设置这个属性,可能会导致截图为黑色图片的情况
    videoElem.setAttribute('preload', 'auto');

    // 设置video元素的src属性为指向File对象的URL
    videoElem.src = dataUrl;
  });
};


// 输入的Blob对象,文件名是'video_img.png'
const toThumbFile = (blob) => new File([blob], 'video_img.png');

调用:

videoImg = await getVideoImg(files);

得到的videoImg 可以在<img src={videoImg }/> 中显示。

不用videoImg 后,要使用URL.revokeObjectURL释放:URL.createObjectURL生成的链接以blob:开头。一般来说在 销毁 的生命周期调用。

if (/^blob:/.test(videoImg)) {
   URL.revokeObjectURL(videoImg);
 }
### 实现方案 为了在 UniApp 中获取并保存视频第一帧作为封面图,可以采用多种方式。一种有效的方法是通过调用云端服务提供商(如七牛云、阿里云)提供的 API 来完成此操作[^4]。 另一种本地解决方案是在前端页面加载完成后,在 HTML5 `<video>` 元素上使用 JavaScript 的 `canvas` 对象绘制当前播放时刻的画面,并将其转换为图像数据 URL 或者 Blob 文件对象以便进一步处理或上传到服务器[^2]。 下面展示一段基于 renderjs 技术栈的具体实现代码片段: ```javascript // 定义渲染函数用于捕获视频首帧 export default { data() { return { videoUrl: 'https://yun.job268.com/yun_video_0.6519106342375556.mp4', coverImageUrl: '' } }, methods: { async getFirstFrameAsCover() { const ctx = uni.createCanvasContext('first-frame-canvas') let videoElement = document.createElement("video"); videoElement.src = this.videoUrl; await new Promise((resolve, reject) => { videoElement.onloadedmetadata = () => resolve(); videoElement.onerror = (e) => reject(e); }); // 设置 canvas 尺寸匹配视频尺寸 const canvasWidth = videoElement.videoWidth; const canvasHeight = videoElement.videoHeight; // 创建临时画布元素 var tempCanvas = document.createElement('canvas'); tempCanvas.width = canvasWidth; tempCanvas.height = canvasHeight; var context = tempCanvas.getContext('2d'); // 绘制视频第一帧至画布 context.drawImage(videoElement, 0, 0, canvasWidth, canvasHeight); // 获取 base64 编码后的图片字符串 this.coverImageUrl = tempCanvas.toDataURL('image/jpeg', 0.9); // 更新视图中的封面图路径 setTimeout(() => {ctx.draw()}, 20) console.log(this.coverImageUrl) } } } ``` 上述代码展示了如何创建一个隐藏的 `<video>` 元素来加载目标视频资源,并利用 Canvas API 提取出其初始帧的内容,最后将提取出来的图像设置为应用内的封面显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值