小工具知识网

当前位置: 首页 > 行业知识 > javascript视频截帧的四种方式

行业知识

javascript视频截帧的四种方式

2026-10-26 16:23:43 来源:互联网转载

前端视频截帧的四种方式

  • 1. 阿里云后缀参数
  • 2.本地文件截取预览图
  • 3.网络视频截帧(不推荐)
  • 4.采用`ffmpeg`插件截帧(推荐)

    1. 阿里云后缀参数

    • 其实OSS本身提供了视频截帧功能,OSS提供的视频截帧功能和OSS图片服务功能使用的方式是类似的,都是通过传入x-oss-process参数的方式来实现的。最简单的使用方式就是直接在public的object的url后面拼接处理字符串。比如如下的url:http://a-image-demo.oss-cn-qingdao.aliyuncs.com/demo.mp4?x-oss-process=video/snapshot,t_10000,m_fast
    • 注意
      • 当前仅支持对视频编码格式为H264的视频文件进行视频截帧。
      • OSS当前没有默认保存视频截帧的操作,视频截帧的图片需手动下载到本地。
      • 如果需要对private的object调用视频截图,需要使用SDK进行鉴权,接口和OSS图片服务的调用接口一致。
      • 图片处理除了能够处理已经存在的图片内容,还能够截取出视频中的指定位置产生图片,完成视频截帧。
        参数描述取值范围
        t截图时间单位ms,[0,视频时长]
        w截图宽度,如果指定为0则自动计算像素值:[0,视频宽度]
        h截图高度,如果指定为0则自动计算,如果w和h都为0则输出为原视频宽高像素值:[0,视频高度]
        m截图模式,不指定则为默认模式,根据时间精确截图,如果指定为fast则截取该时间点之前的最近的一个关键帧枚举值:fast
        f输出图片格式枚举值:jpg、png
        • 示例
          • 找到视频5s处的内容,输出为宽为800像素,高自动计算的png格式图片http://a-image-demo.oss-cn-qingdao.aliyuncs.com/demo.mp4?x-oss-process=video/snapshot,t_5000,m_fast,w_800,f_png

            详见文档:阿里云oss视频上传后,如何获取视频封面

            2.本地文件截取预览图

            获取本地视频文件封面,实现本地预览功能,支持指定秒数

            1. 首先将本地文件通过URL.createObjectURL(file.raw)得到视频url本地地址
            2. 动态创建一个video标签,并利用onloadeddata 方法设置具体时间点,并添加监听事件addEventListener
            3. 然后利用canvas绘制成图片
            4. 输出base64
            • 使用
               GetVideoCover({         url: URL.createObjectURL(fileList[0].raw),         time: 2,         success: (res) => {         //封面地址res.base64         //this.videocover=res.base64         }});
              • 关键代码

                定义GetVideoCover.js

                // 压缩图片function clipAndCompressCover({ media, currentWidth, currentHeight, success }) {    // eslint-disable-next-line no-unused-vars    const that = this    const canvas = document.createElement('canvas')    const area = canvas.getContext('2d')    const currentScale = currentWidth / currentHeight    const targetScale = 750 / 420    let targetWidth = 0    let targetHeight = 0    let clipWidth = 0    let clipHeight = 0    let quality = 0.95 // 不要用1,会额外增大base64大小。    // // 根据视频宽高,决定截图大小    // if (currentScale >= targetScale) {    //     targetHeight = currentHeight > 420 ? 420 : currentHeight    //     targetWidth = targetHeight * currentScale    // } else {    //     targetWidth = currentWidth > 750 ? 750 : currentWidth    //     targetHeight = targetWidth / currentScale    // }    // clipWidth = targetWidth > 750 ? 750 : targetWidth    // clipHeight = targetHeight > 420 ? 420 : currentHeight    canvas.width = currentWidth    canvas.height = currentHeight    area.drawImage(        media,        0, 0,        currentWidth,        currentHeight,    )    var handler = function() {        const base64 = canvas.toDataURL('image/jpeg', quality)        getMediaSize({ src: base64 }).then(response => {          success && success(base64)        })    }    handler()}// 绘制视频封面图片const GetVideoCover = ({ url, time, success }) => {        const video1 = document.createElement('video')        video1.src = url        video1.style.cssText = 'position:fixed; top:0; left:-100%; visibility:hidden'        video1.onloadeddata = function() {                const currentTime = time // 截图时间点                video1.addEventListener('timeupdate', function() {                    // console.log('video1', video1.videoWidth, video1.videoHeight)                    clipAndCompressCover({                        media: video1,                        currentWidth: video1.videoWidth,                        currentHeight: video1.videoHeight,                        success: function(base64) {                            video1.remove(video1)                            success({                                base64                            })                        }                    })                })                video1.currentTime = currentTime  {        const xhr = new XMLHttpRequest()        // eslint-disable-next-line no-unused-vars        xhr.onreadystatechange = _ => {            if (xhr.readyState === 4) {                if (xhr.status >= 200 && xhr.status = 0) {        byteString = atob(base64.split(',')[1])    } else {        byteString = unescape(base64.split(',')[1])    }    const mimeString = base64.split(',')[0].split(':')[1].split(';')[0]    const ia = new Uint8Array(byteString.length)    for (let i = 0; i  

                3.网络视频截帧(不推荐)

                1. 从后台拿到视频url以及相关配置(宽,高,比例,时长),本地创建video的获取方式存在某些视频格式有问题或者帧率不对,导致无法拿到相关配置的问题,所以可以跟后台协商,将相关配置在拿视频时一起回传过来后,提前计算好帧率,创建好canvas数组容器。
                2. 视频添加监听函数,当视频开始播放时,就创建一个canvas将当前帧绘制出来,将canvas转化为图片地址,再推入准备好的canvas数组。
                3. 处理完后,添加键盘监听事件,在html中用标签遍历上一步处理好的canvas数组,直接展示绘制好的canvas,这样用户可以通过键盘的上下左右箭头预览视频的每一帧的画面,也可以根据项目添加滚动条或者输入框做预览帧交互。
                4. 缺点很大,超慢,原视频分辨率越大,处理的越慢,一个8s的视频要处理40s,而且浏览器很容易崩。
                • 关键代码
                      
                  //给视频添加监听函数initVideoplayer() {      if (!this.videoPlayer && this.$refs.video) {        this.videoPlayer = this.$refs.video;       // 视频播放 -- 点击播放        this.videoPlayer.addEventListener("play", e => {          let { frame, duration } = this.videoCig;          this.timer = setInterval(() => {            this.no += 1;            if (this.no > (duration - 1) * frame) {              clearInterval(this.timer);              this.timer = null;              //   this.listenKeyDown(); //视频帧全部截取完毕再添加            } else {              this.cutPicture(null, this.no);            }          }, 1000 / frame);        });        // 视频播放 -- 播放结束        this.videoPlayer.addEventListener("ended", e => {          console.log("视频播放结束");        });      }    }    //截取当前帧的图片    cutPicture(item, index) {      let { width, height } = this.videoCig;      var v = item        ? document.getElementById("img" + index)        : document.querySelector("video");      let canvas = document.getElementById("myCanvas" + index);      var ctx = canvas.getContext("2d");      ctx.drawImage(v, 0, 0, width, height);      let copyImg = ctx.getImageData(0, 0, width, height);     var oGrayImg = canvas.toDataURL("image/jpeg");      this.canvasList.splice(index, 1, copyImg);      if (index == 0) {      //将首帧设为封面         this.video.cover = oGrayImg;      }    }

                  4.采用ffmpeg插件截帧(推荐)

                  • 需引入ffmpeg插件,详见文章vue2+vite利用ffmpeg实现纯前端视频剪切

                  • 可指定开始帧率、输出张数、尺寸、格式

                  • 缺点是加载该插件需要一定时间

                  • 优点是相比第三种视频播放+canvas的方法要快很多,二十多秒的视频几秒钟就处理完了,也不用担心浏览器崩的问题,除非你的视频文件很长。

                  • 参考文档: FFmpeg基本命令使用 、FFmpeg官网、 FFmpeg——在Vue项目中使用FFmpeg(安装、配置、使用)

                  • 关键代码

                     // 上传视频后解析视频帧    async getVideoFrames() {      try {        showLoading();        let { name, file, duration, ccbl } = this.ffVideo;        ffmpeg.FS("writeFile", name, await FFmpeg.fetchFile(file));        // 计算每秒需要抽的帧数        let step = Math.ceil(10 / duration),          allNum = Math.floor(step * duration);      await ffmpeg.run(          "-i",          name,//视频文件名          "-r",          `${step}`,//每秒抽取帧数          "-ss",          "0",//从0帧开始          "-vframes",          `${allNum}`,//所需截取总张数          "-f",          "image2",//图片格式          "-s",          `88*${Math.ceil((88 * ccbl[1]) / ccbl[0])}`, //图片尺寸需要整数,此处按比例计算高          "image-%02d.png"//图片命名        );        // ffmpeg -i 2.mp4 -r 1  -ss 0 -vframes 5 -f image2 -s 352x240 image-%02d.jpeg        for (let i = 0; i                                                 

上一篇:揭秘Android点菜系统源码,深度解析与实现之道!

下一篇:Win10许可证全解析,重点知识汇总,轻松掌握!