使用input上传视频,获得视频的第一帧
参考:JavaScript获取视频的尺寸信息和第一帧图片 - 掘金 (juejin.cn)
尝试了新的做法,比下面的都快:
【webview Android】视频获取首帧为封面-优快云博客
简单方法
其实视频标签video默认会显示视频首帧。但是在某些浏览器上不会,比如在安卓的webview会显示为:
如果想让视频封面图为视频首帧:
技术栈svelte:
// 获取视频首帧
const getImgByVideo = (event: Event) => {
let videoElement = event.currentTarget as HTMLVideoElement;
// 播放渲染完首帧后获取
videoElement.play();
setTimeout(() => {
videoElement.pause();
let canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
let ctx = canvas.getContext('2d');
if (ctx) {
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
let posterUrl = canvas.toDataURL();
videoElement.setAttribute('poster', posterUrl);
}
}, 100);
};
<video
bind:this={videoRef[index].ref}
on:canplay={getImgByVideo}
on:ended={() => {
videoRef[index].isPlay = false;
}}
>
<source src={item.image} />
<track kind="captions" />
</video>
复杂方法
html:
<input
bind:this={uploadRef}
on:change={handleUpload}
accept="video/*"
type="file"
/>
视频类型校验:
const VIDEO_REG =
/^(?:video\/avi|video\/mpeg|video\/mp4|video\/ogg|video\/webm|video\/x-ms-wmv|video\/x-msvideo)$/i;
handleUpload
:
- 校验类型
- 获得视频第一帧图片:调用
getVideoImg
方法
const handleUpload=(e)=>{
const files = e.target.files[0];
if(VIDEO_REG.test(files.type)){
const videoImg = await getVideoImg(files);
}
}
相关方法:getVideoImg
。
使用canvas实现:从一个视频文件中提取一帧图像,并将这个图像转换为一个可以在浏览器中使用的URL。
const getVideoImg = async (files: File): Promise<string> => {
try {
// 使用loadVideo函数加载视频文件,等待加载完成
const video: HTMLVideoElement = await loadVideo(files);
const canvasElem = document.createElement('canvas');
// 设置canvas的宽度和高度与视频的宽度和高度相同
canvasElem.width = video.videoWidth;
canvasElem.height = video.videoHeight;
// 获取canvas的2d渲染上下文,并在canvas上绘制视频的当前帧
canvasElem
.getContext('2d')
?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const pngFile = await new Promise<string>((resolve, reject) => {
// 将canvas的内容导出成一个blob文件
canvasElem.toBlob((blob) => {
if (blob) {
// 成功创建了blob文件,将其转换为File对象,然后创建一个指向这个File对象的URL
resolve(URL.createObjectURL(toThumbFile(blob)));
} else {
// 创建blob文件失败,拒绝Promise
reject('');
}
}, 'image/png');
});
// 返回Promise的结果,即指向File对象的URL
return pngFile;
} catch (error) {
// 如果在上述过程中发生错误,打印错误信息
console.error(error);
}
};
其中调用了loadVideo
:
const loadVideo = (file: File): Promise<HTMLVideoElement> => {
return new Promise(function (resolve, reject) {
const videoElem = document.createElement('video');
const dataUrl = URL.createObjectURL(file);
videoElem.onloadeddata = function () {
// 当video元素的当前帧的数据可用时,将video元素作为Promise的结果
resolve(videoElem);
};
// 当video元素发生错误时的处理函数
videoElem.onerror = function () {
reject('video error');
};
// 设置video元素的preload属性为'auto',这样video元素会预加载数据
// 如果不设置这个属性,可能会导致截图为黑色图片的情况
videoElem.setAttribute('preload', 'auto');
// 设置video元素的src属性为指向File对象的URL
videoElem.src = dataUrl;
});
};
// 输入的Blob对象,文件名是'video_img.png'
const toThumbFile = (blob) => new File([blob], 'video_img.png');
调用:
videoImg = await getVideoImg(files);
得到的videoImg 可以在<img src={videoImg }/>
中显示。
不用videoImg 后,要使用URL.revokeObjectURL
释放:URL.createObjectURL
生成的链接以blob:
开头。一般来说在 销毁 的生命周期调用。
if (/^blob:/.test(videoImg)) {
URL.revokeObjectURL(videoImg);
}