Skip to main content

WEB 端视频时间轴预览图的实现思路

·231 words·2 mins
Table of Contents

时间轴预览图实现
#

简单看一下 bilibili 的实现。在鼠标首次移动到时间轴上时,获取到一个 avif 格式的雪碧图,它包含了鼠标所指位置附近的预览画面。在鼠标沿着时间滑动的时候,会根据判断是否需要请求新的雪碧图

时长 23:40 秒的视频,拉取的雪碧图一共两张,每张是 10x10 的布局。应该有一个数据是让前端拿到视频的预览信息,可以定位到预览图在哪个 URL 的几行几列,后端则负责雪碧图的生成

FFmpeg 生成雪碧图
#

我们来看一下通过 FFmpeg 如何在后端生成雪碧图。

测试视频还是使用 https://www.h264info.com/clips.html 中的 “The Simpsons Movie – 720p Trailer”,下载后重命名为 input.mp4

按照时间切割
#

通过指定 fps=1 ,可以每秒取一帧。tile=10x10 告诉 FFmpeg 我们的雪碧图格式是什么样子的。-q:v 20 来控制生成图的质量,这个参数越小质量则越高。我们可以发现 bilibili 的雪碧图 URL 后面有一个 50q 的参数,这个应该是质量 loss 50% 的意思。如果用 OSS 的话,一般是支持动态拿到不同质量的图的

$ ffmpeg -i input.mp4 -vf "fps=1,scale=320:-1,tile=10x10" -q:v 20 time-linear-%02d.jpg

一共生成了 136 张图,效果如下

因为 1 秒一个图,一张雪碧图的时间范围是 1 分 40 秒,这种对于短视频来说还好。但对于长视频来说就太多了

关键帧
#

关键帧可以有效的避免生成雪碧图数量过多的问题

$ ffmpeg -skip_frame nokey -i input.mp4 -vsync vfr -vf "scale=320:-1,tile=10x10" -q:v 20 key-frame-%02d.jpg

效果如下

这里一共生成了 48 张图,最后是一张黑色的。如果使用这种,那么还需要告诉前端时间的信息,这点可以通过加入 showinfo 来 grep 到 FFmpeg 截取日志

$ ffmpeg -skip_frame nokey -i input.mp4 -vsync vfr -vf "showinfo,scale=320:-1,tile=10x10" -q:v 20 key-frame-%02d.jpg 2>&1 | grep "showinfo" | grep "duration_time"

部分日志如下

[Parsed_showinfo_0 @ 0x7e2d0c003d40] n:   0 pts:   1001 pts_time:0.0417083 duration:   1001 duration_time:0.0417083 fmt:yuv420p cl:left sar:0/1 s:1280x544 i:P iskey:1 type:I checksum:C627E3A8 plane_checksum:[1A3FA0BC B3F301D6 734F4116] mean:[93 108 91] stdev:[26.4 4.2 7.7]
[Parsed_showinfo_0 @ 0x7e2d0c003d40] n:   1 pts: 141141 pts_time:5.880875 duration:   1001 duration_time:0.0417083 fmt:yuv420p cl:left sar:0/1 s:1280x544 i:P iskey:1 type:I checksum:2AB765A2 plane_checksum:[10FCA9F3 0A0E6B5A 6F755046] mean:[91 121 132] stdev:[75.5 8.3 8.6]
[Parsed_showinfo_0 @ 0x7e2d0c003d40] n:   2 pts: 256256 pts_time:10.677333 duration:   1001 duration_time:0.0417083 fmt:yuv420p cl:left sar:0/1 s:1280x544 i:P iskey:1 type:I checksum:0D78B354 plane_checksum:[CFC05B20 A4B5EE5F C5ED69C6] mean:[64 123 133] stdev:[28.0 5.4 5.7]

这里的 pts_time 就是时间戳信息,n 则是小图的索引。有了两个数据就可以确定时间轴上应该显示的图片了

转场
#

转场则是另一个手段

$ ffmpeg -i input.mp4 -filter:v "select='gt(scene,0.2)',showinfo,scale=320:-1,tile=10x10" -vsync vfr -q:v 20  -y scene-%02d.jpg

通过 select 滤镜来筛选场景变化。gt(scene,0.25) 表示筛选出场景变化程度大于 0.2 的帧。这个参数可以自己进行调整

时间信息还是通过 showinfo 在日志中提取即可

Related

FFmpeg 封装字幕
·425 words·2 mins
FFmpeg 视频水印实战
·303 words·2 mins
FFmpeg 抽帧指南
·420 words·2 mins