问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

定格动态:如何用前端实现视频帧截图

创作时间:
作者:
@小白创作中心

定格动态:如何用前端实现视频帧截图

引用
1
来源
1.
https://juejin.cn/post/7375871100524888115

在这样一个图像化极其重要的时代,从视频中提取精彩瞬间,即视频帧截图的技术,已成为前端开发中的一个亮点。JavaScript作为网页动态效果和交互的主力军,其在视频处理领域能力逐渐被挖掘和重视,尤其是视频帧截图技术的应用,为网站和应用程序提供了更为丰富和直观的用户体验。

在一些媒体网站上传作品后,通常都会让你选择视频中的某个画面作为封面,这个封面可以吸引观众的注意力,让他们对你的视频产生兴趣。那么,如何实现视频帧截图呢?本文将介绍如何使用JavaScript来实现这一功能。

前端实现视频帧截图大体流程如下:

选择视频文件定格帧

选择本地视频文件

<input type="file">  
const input = document.querySelector('input');
input.onchange = async (e) => {
const file = e.target.files[0];
console.log(file);
}  

创建一个video标签,把视频文件放到video标签中。使用 createObjectURL 方法把视频文件对象转为一个url。

const video = document.createElement("video");
video.src = URL.createObjectURL(file);
// 设置视频自动播放
video.autoplay = true;
// 设置视频播放的时间点
video.currentTime = 10;  

使用 createObjectURL 创建的URL是一个blob:开头的临时路径,这个路径可以在浏览器中直接访问,访问到的内容就是上传的视频文件。当页面关闭后,此路径也随之失效。

通过 currentTime 设置需要截图的时间点,由于创建的video标签未加到页面中,所以这里设置了自动播放后,不会继续播放10s后的内容,而是停留在10s的位置。

绘制视频帧

现在视频停留在了指定帧的位置,接下来就通过 canvas来绘制当前帧。

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = vdo.videoWidth;
canvas.height = vdo.videoHeight;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
document.body.appendChild(canvas);  

转为图片地址

将canvas内容转为一个可用img标签展示的url地址,这个过程需要两步:

  • 使用 toBlob 方法创造一个Blob对象
  • 使用 createObjectURL 方法把对象转为可访问的URL
function drawVideo(vdo) {
return new Promise((resolve) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = vdo.videoWidth;
canvas.height = vdo.videoHeight;
ctx.drawImage(vdo, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
resolve({
blob,
url: URL.createObjectURL(blob)
});
})
})
}  

由于 toBlob 是采用回调函数的方式调用,所以封装drawVideo 方法返回为一个promise。

从上图可以看到抖音平台下面展示的预览图同样也是以blob:开头的临时地址。

截图全流程

当video标签设置url后,不能马上截图,视频加载需要一个过程,要等视频可以播放后再截图。

video.oncanplay = async () => {
const frame = await drawVideo(video);
}  

封装完整的截图方法,由于 oncanplay 同样是一个异步方法,这里也需要返回一个promise

function captureFrame(file, time) {
return new Promise((resolve) => {
const video = document.createElement("video");
video.autoplay = true;
video.currentTime = time;
video.src = URL.createObjectURL(file);
video.oncanplay = async () => {
const frame = await drawVideo(video);
resolve(frame);
}
})
}  

获取视频时长,按照每1秒的间隔截一张图

const video = document.createElement("video");
video.src = URL.createObjectURL(file);
// 视频加载完成后,才能获取到视频的时长
video.onloadedmetadata = async () => {
for (let index = 0; index < video.duration; index++) {
const frame = await captureFrame(file, index);
// 将截图加入到页面中展示
const img = document.createElement("img");
img.src = frame.url;
img.style.width = "100px";
document.body.appendChild(img);
}
};  

尽管视频帧截图技术带来了许多便利,但在实际应用过程中也面临着一些挑战:

  • 性能问题:处理大量或高分辨率视频时的性能消耗较大,可能影响页面加载速度和用户体验。解决方案包括优化图片尺寸、使用异步加载技术、以及在服务器端进行视频处理等。
  • 浏览器兼容性:不同浏览器对视频解码和渲染的支持程度不同,可能导致在某些浏览器上无法正确显示视频帧截图。解决这一问题的方法包括使用广泛支持的编解码器和格式,以及提供替代的媒体格式。
  • 用户体验:如何让用户方便快捷地选择和保存截图是一个需要解决的问题。提供直观的用户界面和明确的操作指导是提升用户体验的关键。

随着前端技术的不断进步,未来视频帧截图技术将变得更加高效和用户友好。Web Assembly和FFmpeg等技术的发展有望进一步提升前端处理视频的能力,使得视频帧截图更加迅速和精确。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号