视频文件中如何提取画面帧?

前言

在很多视频网站,上传视频后,会让用户选取视频的某一帧的画面作为视频的封面。

那么这个功能是怎么实现的,本文就介绍下如何通过选择的视频生成某一帧的画面。

1. 前置知识

实践之前先了解下需要用到的方法及事件。

1.1 createObjectURL

URL.createObjectURL()  静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的 URL 对象表示指定的 File 对象或 Blob 对象。

例如:通过URL.createObjectURL 生成临时链接地址
视频文件中如何提取画面帧?

将地址在浏览器中打开,即可访问本地资源,页面关闭就失效
视频文件中如何提取画面帧?

当我们使用input标签进行选择视频文件时,如何传递给video标签使用,就需要createObjectURL方法创建一个临时的链接,提供给video使用.

1.2 HTMLMediaElement: canplay事件

canplay 事件在终端可以播放媒体文件时(但估计还没有加载足够的数据来播放媒体直到其结束,即后续可能需要停止以进一步缓冲内容)被触发。

当video标签有地址视频,就可以指定视频的时间及播放,此时就需要对应的事件进行监听。canplay事件保证video正常触发,这样才能给下一个阶段使用。

1.2 HTMLCanvasElement.toBlob方法

HTMLCanvasElement.toBlob()  方法创造 Blob 对象,用以展示 canvas 上的图片;这个图片文件可以被缓存或保存到本地(由用户代理自行决定)。

使用canvas从video元素中获取画面帧,使用toBlob方法得到blob对象,用以保存上传。

2. 具体实现

2.1 实现思路

具体实现过程:

  1. 文件选择: 用户通过<input type="file">标签选择了一个视频文件。
  2. 事件监听: 为这个input元素添加了onchange事件监听器,这样当用户选择文件后,就能触发对应的处理函数。
  3. 创建临时链接: 在处理函数中,通过URL.createObjectURL(file)为选中的视频文件创建一个临时的URL,这个URL可以直接用于<video>元素的src属性,以便在页面上播放视频。
  4. 视频准备: 设置<video>元素的currentTime属性来指定要捕获的帧的时间点,并监听canplay事件以确保视频已准备好可以播放。
  5. 捕获帧: 当视频准备好后,使用<canvas>元素和绘图API来绘制视频帧。这通常涉及到将<video>元素绘制到<canvas>上,然后利用<canvas>toBlob方法将当前帧转换为一个Blob对象。
  6. 处理结果toBlob方法返回的是一个Promise,它解析为一个包含捕获帧数据的Blob对象。这个Blob对象可以用于多种用途,比如上传到服务器、预览或保存到本地。
  7. 预览和上传: 一旦得到了Blob对象,就可以使用它创建一个新的临时URL来预览捕获的帧,或者上传到服务器。

2.1 代码实现

定义页面结构:

<input type="file" id="ipt" />

具体逻辑实现:

const inputNode = document.getElementById('ipt')
inputNode.onchange = async (e) => {
        const file = e.target.files[0]
        const res = await captureFrame(file)
       
        const img = document.createElement('img')
        img.src = res.url
        document.body.appendChild(img)
}

// 使用canvas绘制
function drawVideo(videoNode) {
    return new Promise((resolve, reject) => {
        const cvs = document.createElement('canvas');
        const ctx = cvs.getContext('2d')
        cvs.width = videoNode.videoWidth
        cvs.height = videoNode.videoHeight
        ctx.drawImage(videoNode, 0, 0, cvs.width, cvs.height)
        cvs.toBlob(blob => {
            resolve({
                blob,
                url: URL.createObjectURL(blob)
            })
        })
    })
}



// 视频 使用video -> 画入canvas -> 提取图片
function captureFrame(videoFile, time = 0) {
    return new Promise(resolve => {
        const videoNode = document.createElement('video')
        videoNode.currentTime = time
        videoNode.muted = true // 静音 可以播放
        videoNode.autoplay = true
        // 是什么,能够读取本地的数据的url,开头是blob
        videoNode.src = URL.createObjectURL(videoFile)
        // 需要时间准备好时间进行播放即可
        videoNode.oncanplay = async () => {
            const res = await drawVideo(videoNode)
            resolve(res)
        }
    })

}

效果如下:
视频文件中如何提取画面帧?

3. 总结

最后总结一下,就是通过file对象生成临时链接,提供给视频的video,进行播放设置,canvas直接进行绘制转换,就可以得到每一帧的画面。

如有错误,请指正O^O!

原文链接:https://juejin.cn/post/7352079398072746047 作者:一诺滚雪球

(0)
上一篇 2024年3月31日 上午10:11
下一篇 2024年3月31日 上午10:21

相关推荐

发表回复

登录后才能评论