惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
G
Google Developers Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
B
Blog RSS Feed
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
Stack Overflow Blog
Stack Overflow Blog
雷峰网
雷峰网
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Fortinet All Blogs
L
LangChain Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
T
The Blog of Author Tim Ferriss
B
Blog
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
I
InfoQ
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

张鑫旭-鑫空间-鑫生活

1分钟速度了解text-align match-parent声明 « 张鑫旭-鑫空间-鑫生活 光标的形状也能设置了,就是CSS caret-shape属性 « 张鑫旭-鑫空间-鑫生活 pretext与文字四面环绕效果的实现 « 张鑫旭-鑫空间-鑫生活 JSON.rawJSON方法的作用是什么? « 张鑫旭-鑫空间-鑫生活 如何使用CSS判断鼠标从哪个方向进入元素? « 张鑫旭-鑫空间-鑫生活 借助mediabunny纯JS实现视频水印、剪裁、合成等功能 « 张鑫旭-鑫空间-鑫生活 务必谨慎使用JS WeakRef弱引用 « 张鑫旭-鑫空间-鑫生活 CSS六边形头像的实现与蜂巢布局 « 张鑫旭-鑫空间-鑫生活 CSS corner-shape与背景底纹技术 « 张鑫旭-鑫空间-鑫生活 浅学WebTransport API:下一代Web双向通信技术 « 张鑫旭-鑫空间-鑫生活 HTML dialog元素新支持了closedBy属性 « 张鑫旭-鑫空间-鑫生活 HTML interestfor属性与悬停popover交互效果 « 张鑫旭-鑫空间-鑫生活 CSS3模拟window7炫酷界面效果展示 « 张鑫旭-鑫空间-鑫生活 图片旋转+剪裁js插件(兼容各浏览器) « 张鑫旭-鑫空间-鑫生活 纯js无图片无外链CSS图片幻灯片播放插件 « 张鑫旭-鑫空间-鑫生活 CSS border三角、圆角图形生成技术简介 « 张鑫旭-鑫空间-鑫生活 CSS “渐进增强”在web制作中常见应用举例 « 张鑫旭-鑫空间-鑫生活 让IE6/IE7/IE8浏览器支持CSS3属性 « 张鑫旭-鑫空间-鑫生活 jQuery – 高亮动态显示页面HTML代码插件 « 张鑫旭-鑫空间-鑫生活 翻译 – 逐渐消失的Flash网站 « 张鑫旭-鑫空间-鑫生活 CSS reset的重新审视 – 避免样式重置 « 张鑫旭-鑫空间-鑫生活 新浪微博插入话题后部分文字选中的js实现 « 张鑫旭-鑫空间-鑫生活 JavaScript实现http地址自动检测并添加URL链接 « 张鑫旭-鑫空间-鑫生活 CSS实现兼容性的渐变背景(gradient)效果 « 张鑫旭-鑫空间-鑫生活 CSS content内容生成技术以及应用 « 张鑫旭-鑫空间-鑫生活 CSS gradient渐变之webkit核心浏览器下的使用 « 张鑫旭-鑫空间-鑫生活 CSS渐变之CSS3 gradient在Firefox3.6下的使用 « 张鑫旭-鑫空间-鑫生活 区分IE8/IE7/IE6及其他浏览器-CSS “\9″hack « 张鑫旭-鑫空间-鑫生活 CSS实现跨浏览器兼容性的盒阴影效果 « 张鑫旭-鑫空间-鑫生活 CSS3&HTML5各浏览器支持情况一览表 « 张鑫旭-鑫空间-鑫生活
使用JS快速获取video视频任意位置的缩略图 « 张鑫旭-鑫空间-...
张鑫旭,zhangxinxu · 2024-01-15 · via 张鑫旭-鑫空间-鑫生活

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。

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

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

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

感谢阅读,欢迎。

紫霞仙子

本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11103

(本篇完)