本文记录一种播放器进度条预览的实现方式,类似基于 vtt + 精灵图的实现,只不过是通过约定和计算公式确定所截取的雪碧图的小图位置,而不需要依赖 vtt 文件。
1. 从视频文件或播放列表按照一定时间间隔抓取图
抓取图的间隔决定了进度条预览的粒度,如果以 5s 为间隔取图,那么预览图就有一定跳跃性,最长可能要拖动进度条 5s 才会看到预览图有变化。如果每 1s 取一次图,预览时候效果就相对比较精细了,但没必要粒度太细。毕竟进度条的时间,一般不会显示毫秒数,拖动进度条的幅度一般也超过 1s。 以下命令使用 ffmpeg 从视频文件 540p30.mp4
中按 1s 时间间隔抓取宽为 213 像素,高为 120 像素(16:9, 维持原来的宽高比)的缩略图,并保存到 thumbs
目录中,文件名以 ‘thumb-‘ 开头,后面跟数字位数为 5 的整数,个位数字则前面补 4 个 0,依此类推,文件扩展名为 .jpg
.
1
ffmpeg -i 540p30.mp4 -vf fps=1/1 -s 213x120 thumbs/thumb-%05d.jpg
注意: 以上命令需要安装 ffmpeg
2. 拼接缩略图成精灵图(Sprite)
这一步将由第 1 步中生成的缩略图按照一定的行列数,按顺序拼接成精灵图以减少下载图片数量。这一步骤需要系统安装好 imagemagick
, 因为会用到其中的 montage
命令去拼接缩略图。这个过程已经有 shell 脚本去做,参考自 rokudev 的一个仓库里的脚本。不过我对这个脚本微调了一下,以适应本文描述的做法。主要涉及两点调整,一个是对应地调整格式化打印读取文件时候的表达式,以适配第 1 步中的改动,即 %03d
改成 %05d
因为考虑到,如果有时长 10 小时的视频,按 1s 间隔取图,就会有 36000 张图,文件名也会去到 thumb-35999.jpg
,只是为了”扩容“。 第二点改动是将最终拼接生成大图时候的文件名数字编码由从 1 开始改成从 0 开始,即在原来的控制这个数字编码的 shell 变量基础上减 1. 改动后的版本,放到了 GitHub Gist.
这个脚本的使用如下:
1
./gen_tiles.sh thumbs thumb images/pre 213x120 12 10
上面的命令表示从 thumbs
目录(即第 1 步中缩略图存放的位置),读取文件名前缀是 ‘thumb’ 的文件,按每一格大小是 213x120,12 列,10 行地拼接一张精灵图。第 1 张会是 images/pre_0.jpg
,第 2 张是 images/pre_1.jpg
依此类推,最后一张精灵图,如果剩余的缩略图的数量凑不够 120 个小格,那么后面剩下的格子会用黑色格子填充凑成一张 2556x1200 的大图。
3. 根据时间线上的位置去确定取哪张精灵图的哪个小格
先直接给出计算公式:
① 获取哪个精灵图: 时间线当前位置时间 / 120
例如:pos 是 12:55 就是 12 x 60 + 55 = 775 秒 775 / 120 = 6 所以应该去拿 xxx/images/pre_6.jpg 的精灵图
② 得到小图的 x 坐标: ((pos % 120) % 12) * 213
例如:
pos = 775
, 则x = (( 775 % 120) % 12) * 213 = 1491
③ 得到小图的 y 坐标: ((pos % 120) / 12) * 120
例如
pos = 775
, 则y = (( 775 % 120) / 12) * 120 = 480
④ 取小格缩略图: subImage(pre_6.jpg, 1491, 480, 213, 120),这是个假想的方法,不过一般编程语言都有类似的API,传入精灵图,x,y 坐标,以及图片宽高可以切出小图,即要放在时间线上的预览图。
总结
以上步骤采用的参数可以实际情况调整,例如取图的时间间隔,精灵图的行列数,缩略图的宽高都可以调整,但计算套路仍然适用。下一步可能会引入一个 manifest.json
或 meta.json
之类,用于定义取图时间间隔等参数,这样客户端就不用写固定的参数,可以更灵活一些,服务端资源更新客户端也能适应。 本文没有给出客户端将预览图放到时间线上的具体 UI 实现,只是介绍一种通用做法,具体实现留给客户端。