FFmpeg - 模仿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
preview 10x1
ffmpeg 命令解释说明:
select='not(mod(n\,60))'
:每60帧取1帧scale=200:-1
:按宽度200等比缩放tile=10x1
:取10帧合成1帧,排列顺序是 10x1(这里合成一行是为了方便后续前端计算)
如果做成和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
preview 4x3
服务端源码参考(基于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、前端实现
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Video Preview</title> <style> .video-bg { height:100%; width: 100%; background-image: url(preview.png); background-repeat: no-repeat; background-position: 0 0; } </style></head><body style="margin:0; padding:0"> <div style="height: 100vh; width:100%;display: flex; align-items: center; justify-content:center"> <div style="width: 200px; height:113px; position: relative; cursor: pointer;" onmousemove="mouseMove(event)" onmouseout="mouseOut()"> <div class="video-bg" id="preview"></div> </div> </div></body><script> // 节流与防抖 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;`); }</script></html>
4、效果预览
参考文档
https://ffmpeg.org/ffmpeg-filters.html#tile-1
作者:Anoyi
链接:https://www.jianshu.com/p/9d2abebd4f35