作者:gete | 来源:互联网 | 2023-08-31 08:07
B站视频卡片预览
打开浏览器控制台,当鼠标指向某个视频卡片时,会加载一张大图,这张图是当前视频许多帧的合集,所以视频预览的本质是预览图片,鼠标滑动时改变大图在容器的位置,就
B站视频卡片预览
打开浏览器控制台,当鼠标指向某个视频卡片时,会加载一张大图,这张图是当前视频许多帧的合集,所以视频预览的本质是预览图片,鼠标滑动时改变大图在容器的位置,就能实现这种效果。
视频预览实现
1、准备视频素材
下载地址:https://share.weiyun.com/Ps99dLfl
2、生成预览图
ffmpeg -i anoyi.mp4 -vf select='not(mod(n,60))',scale=200:-1,tile=10x1 -frames:v 1 preview.png
ffmpeg 命令解释说明:
-
select='not(mod(n,60))'
:每60帧取1帧
-
scale=200:-1
:按宽度200等比缩放
-
tile=10x1
:取10帧合成1帧,排列顺序是 10×1(这里合成一行是为了方便后续前端计算)
如果做成和B站一样的形式,可以使用如下命令:
ffmpeg -i anoyi.mp4 -vf select='not(mod(n,60))',scale=200:-1,tile=4x3:nb_frames=10 -frames:v 1 preview_4x3_10.png
服务端源码参考(基于ffmpeg-python):
def get_scaled_preview_frame_by_frame(file_path: str, start, end, interval, count, width):
"""
获取缩放的视频帧
:param file_path: 视频地址
:param start: 起始帧数,默认值:0
:param end: 结束帧数,默认值:1
:param width: 缩放宽度,默认值:720
:param interval: 帧数间隔,默认值:1
:param count: 帧的数量,默认值:1
:return:
"""
out, err = (
ffmpeg
.input(file_path)
.filter('select', f'gte(n, {start})*lt(n, {end})')
.filter('select', f'not(mod(n, {interval}))')
.filter('scale', width, -1)
.filter('tile', f'{count}x1')
.output('pipe:', vframes=1, format='image2', vcodec='mjpeg', **{'q:v': 0})
.run(capture_stdout=True)
)
return out, err
3、前端实现
.video-bg {
height:100%; width: 100%; background-image: url(preview.png); background-repeat: no-repeat; background-position: 0 0;
}
// 节流与防抖
let ms = 200;
let lastEvent = Date.now() - ms;
const mouseMove = (event) => {
if (Date.now() - lastEvent >= ms) {
let offset = 200 * parseInt(event.offsetX / 20);
let style = `background-position: -${offset}px 0;`;
document.getElementById('preview').setAttribute('style', style);
lastEvent = Date.now();
}
}
const mouseOut = () => {
document.getElementById('preview').setAttribute('style', `background-position: 0 0;`);
}
4、效果预览
参考文档
- https://ffmpeg.org/ffmpeg-filters.html#tile-1