2

使用JS快速获取video视频任意位置的缩略图

 7 months ago
source link: https://www.zhangxinxu.com/wordpress/2024/01/js-get-video-thumb-poster/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11103 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

封面图,坠落的天使

一、先看效果

您可以狠狠地点击这里:JS每隔一秒提取视频缩略图demo

例如,选择本地视频后:

选择本地视频缩略图生成示意

输入在线的视频文件地址后,注意,该地址要允许跨域,也可以看到缩略图出现了,前后也就几秒钟的时间,还是挺快的。

在线地址缩略图

二、如何实现?

方法一,对视频帧进行完全解码,然后进行提取,相关实现可以参考“mp4box.js加WebCodecs 解码MP4视频帧并渲染”一文。

不过此方法对资源占用较大,而缩略图仅仅是视频的部分帧信息,有些高射炮打蚊子,不划算。

方法二,利用video元素进行绘制。

具体描述为:

希望绘制哪一帧视频,就让视频跳到这一帧的画面,然后使用canvas直接绘制该video元素。

不过具体实现的时候还是有不少细节需要注意的。

  1. 视频的src地址必须是本地地址,而不能是在线地址,否则可能会有绘制异常的问题,例如,你要绘制最后1秒的缩略图,在线视频往往当前画面资源是未加载的,你再去绘制,就会有很长的等待时间,画面就可能是黑色的。
  2. video.currentTime = xxx可以让视频定位到具体的时间,但是此过程并不是即时的,需要使用 timeupdate 事件进行监控,例如:
    video.addEventListener('timeupdate', () => {
       // 绘制当前video
    });
    video.currentTime = 1;

为了方便大家的使用与学习,对于缩略图的提取,我直接弄了个方法,以1秒为间隔,批量返回视频的缩略图,大家可以直接使用,源码如下:

const handleGetVideoThumb = function (url, options = {}) {
    if (typeof url != 'string') {
        return;    
    }
    // 默认参数
    const defaults = {
        onLoading: () => {},
        onLoaded: () => {},
        onFinish: (arr) => {}
    };
    
    const params = Object.assign({}, defaults, options);
    
    // 基于视频元素绘制缩略图,而非解码视频
    const video = document.createElement('video');
    // 静音
    video.muted = true;
    
    // 绘制缩略图的canvas画布元素
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d', {
        willReadFrequently: true
    });
    
    // 绘制缩略图的标志量
    let isTimeUpdated = false;
    // 几个视频事件
    // 1. 获取视频尺寸
    video.addEventListener('loadedmetadata', () => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        
        // 开始执行绘制
        draw();
    });
    // 2. 触发绘制监控
    video.addEventListener('timeupdate', () => {
        isTimeUpdated = true;
    });
    
    // 获取视频数据
    params.onLoading();
    // 请求视频地址,如果是本地文件,直接执行
    if (/^blob:|base64,/i.test(url)) {
        video.src = url;    
    } else {
        fetch(url).then(res => res.blob()).then(blob => {
            params.onLoaded();
            // 赋予视频
            video.src = URL.createObjectURL(blob);
        });
    }
    
    // 绘制方法
    const draw = () => {
        const arrThumb = [];
        const duration = video.duration;
        let seekTime = 0.1;

        const loop = () => {
            if (isTimeUpdated) {                
                context.clearRect(0, 0, canvas.width, canvas.height);
                context.drawImage(video, 0, 0, canvas.width, canvas.height);

                canvas.toBlob(blob => {
                    arrThumb.push(URL.createObjectURL(blob));

                    seekTime += 1;

                    if (seekTime > duration) {
                        params.onFinish(arrThumb);

                        return;
                    }

                    step();
                }, 'image/jpeg');

                return;
            }
            // 监控状态
            requestAnimationFrame(loop);
        }
        
        // 逐步绘制,因为currentTime修改生效是异步的
        const step = () => {
            isTimeUpdated = false;
            video.currentTime = seekTime;
            
            loop();
        }

        step();
    }
};

语法

handleGetVideoThumb(url, {
  onLoading: () => {},
  onLoaded: () => {},
  onFinish: (arr) => {}
})

其中,最主要的可选参数就是 onFinish 方法,其支持一个参数,为数组,里面就是1:1和原始视频尺寸一样的缩略图的 blob 地址,以秒为间隔。

更具体的使用可以参考demo页面的代码示意。

如果你希望获得某一具体时间的缩略图,可以参考本文提供的方法,稍加改造就可以。

例如将 seekTime 设置为你需要的时间,只绘制一次就执行params.onFinish方法。

三、有感而发

在之前,我一直以为<video>元素和<audio>元素一样,是个比较鸡肋的特性,不能应对复杂的需求。

最近发现,我的认识浅薄了,video还是可以的,不然不会只有Web Audio API而没有Web Video API。

琢磨了下,说不定试试绘制也是可以的,可能会有点延时偏差,应该好解决,再研究研究吧。

好吧,差不多就说这么多吧。

年底了,赶书稿,就不多啰嗦了。

感谢阅读,欢迎。

紫霞仙子

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK