js获取在线视频的第一帧作为封面

最近碰到一个需求,要求获取在线视频文件的第一帧画面作为视频的封面图片,项目中使用的是 xgplayer 播放器,封面 poster 要求传封面的 url。
思路:用 canvas 绘出画面 - 转成 base64 - base64 转成 File - 获取 File 的本地预览地址

获取 base64 格式的封面

这里 video 元素需添加 autoplay 属性,否则在一些浏览器上获取到的画面是黑的。还有就是先把 video 元素添加到 body 标签再移除,有时候会碰到视频在后台播放,用户根本没有可操作的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export const getVideoBase64 = (url: string) =>
new Promise(function (resolve) {
let dataURL = ''
let video = document.createElement('video')
video.setAttribute('crossOrigin', 'anonymous') //处理跨域
video.setAttribute('src', url)
video.setAttribute('width', 880 + '')
video.setAttribute('height', 495 + '')
video.setAttribute('autoplay', 'autoplay')
document.body.appendChild(video)
video.addEventListener('loadeddata', function () {
let canvas = document.createElement('canvas'),
width = width, //canvas的尺寸和图片一样
height = height
canvas.width = video.width
canvas.height = video.height
canvas.getContext('2d')!.drawImage(video, 0, 0, width, height) //绘制canvas
dataURL = canvas.toDataURL('image/jpeg') //转换为base64
document.body.removeChild(video)
resolve(dataURL)
})
})

base64 格式转成 File,再由 File 生成本地预览链接

之所以要这样做是因为 xgplayer 要求设置的封面 poster 是一个 url,否则直接吧 base64 格式的数据设置为 image 标签的 src 属性即可显示封面图。当然我们也可以把这个操作放在上传视频的时候,生成的封面直接传给后端存起来,就不用每次都生成本地预览链接。本地预览链接只在本地生效,且是在缓存里,也只能用于预览封面了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export const base64ImgtoFile = (dataurl: string, filename = 'file') => {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.\*?);/)![1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})

export const getLocalPreviewUrl = (file: File) => URL.createObjectURL(file)

export const getOnlineVideoCover = async (url: string) =>
getLocalPreviewUrl(base64ImgtoFile((await getVideoBase64(url)) as string))

image-20220316143409091

本文由adiynil原创编写,转载请尽量保留版权网址,感谢您的理解与分享!