自动化录制屏幕有很多用途,比如生成教学视频、生成产品文档,等等。对比人工,自动化有很多好处:
- 避免创作者的设备和环境问题(比如邻居装修、麦克风不好等)
- 避免创作者的语言、发音问题(比如普通话不标准、不会说某种语言)
- 录制环境出现变化,可以方便的重录(比如换个背景图,界面有升级)
- 就像写博客一样,任何时候,拿出电脑或者手机都能编辑一段
所以目前研究这方面应用的很多,我厂也是。我近期就投入大量时间在这项工作上面,现在终于有所成果,写篇博客分享一下。
0. 准备环境
首先推荐大家使用 Linux。Linux 开源,有很多开源免费的工具可以完成各种操作,不仅可以录屏,还可以很容易地模拟各种用户操作,给我们留下大量开发空间。
建议选择有图形界面的 Linux 发行版,我尝试过 fedora 33 和 Debian 10 树莓派版,都很容易配置。如果使用纯命令行版本,然后自己完成安装图形界面,比如 gnome,再完成剩下来的配置,会很麻烦。
然后记得把系统更新到最新版,以规避可能遇到的问题。
1. 配置 vncserver
如果只能在主屏录制,这个产品的实用性就会大打折扣。所以我们选择用 vncserver 创建虚拟屏幕,然后在虚拟屏幕上完成录制。如果需要的话,也可以随时用 vnc viewer 之类的软件连上 VNC 实时查看效果,非常方便。
有些系统自带 vncserver,比如 Debian 10 树莓派,那就不用安装。我们选用 fedora 33,需要手动安装,这里推荐 TigerVNC,安装使用都很方便:
sudo dnf install tigervnc
安装完成后,使用:
vncserver :5 -geometry 1280x720
就可以创建虚拟显示器了。其中,:5
是显示器 id,可以顺延,比如 :6
、:7
、甚至 :99
,至于上限在哪里我暂时不知道。-geometry 1280x720
是设定显示器分辨率为 1280×720。
另外,还可以使用下面的命令查看和关闭显示器:
vncserver -list
vncserver -kill :5
1.1 测试
配置完成之后,可以用 Firefox 测试一下效果。
# 安装 firefox
sudo dnf install firefox
# 在指定虚拟显示器打开 firefox
DISPLAY=:5 firefox https://cn.bing.com
# 截图,可能需要安装 xwd
xwd -root -display :5 > screen.xwd
# 转换成 png,可能需要安装 ImageMagick
convert screen.xwd screen.png
然后把图片下载到本地,或者启动一个 http 服务器就能看到了。
1.2 关闭桌面
默认的 VNC server 会启动桌面,此时可能会要求我们登录什么的。我们在这套系统当中并不需要桌面,只要有显示器即可,所以可以修改 ~/.vnc/xstartup
禁用:
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# 把下面这行注释掉
# exec /etc/X11/xinit/xinitr
2. 使用 FFmpeg 捕获屏幕内容
使用 FFmpeg 录屏比较简单,将输入源设置为指定显示器即可,format
是 x11grab
,命令大体如下:
ffmpeg -y -f x11grab -video_size 1280x720 -framerate 30 -i :5.0+0,0 -c:v h264 a.mp4
其中,
-y
意思是自动覆盖前面生成的视频:5.0+0,0
是使用刚才创建的:5
显示器,用它的0
号桌面,启动位置是0,0
即左上角-f x11grab
是使用 X server 抓取格式,Linux 下的图形界面一版是基于这个系统
注意,上面这条命令里参数的顺序很重要,否则可能遇到 Protocol not found
等错误。
3. 使用 node.js 驱动
最后只要用 node.js 的 child_process.spawn()
功能调用上面的命令即可。这段代码属于公司,我就不贴了,主要分享几点经验教训:
- 要用
spawn
,因为录制过程中我们需要用输出来判断录制状态,exec
这种只能在结束时提供输出的没法用 - FFmpeg 会把输出输出到
stderr
,理由不明,不过记得要用stderr
来检查 - 录像完成,如果在命令行,按
q
或者ctrl+C
都可以停止录像,并开始封装视频文件。在 node.js 里,我们可以调用cp.kill('SIGINT')
。注意,调用之后,FFmpeg 子进程并没有立刻结束,它要把前面的录像进行封装,这个过程也是需要时间的,所以如果你接下来还要对视频文件进行操作,应该等待子进程彻底结束 - 判断录像开始的依据,我目前用的是:输出里包含
Output #0, mp4, to 'a.mp4'
- 判断录像结束,视频已经生成的依据,我用的是
cp.on('exit', onExit)
,然后在onExit
里处理。注意,其它情况导致 ffpmeg 子进程退出时也会触发这个函数,所以我们必须检查code
。此时,在我的机器上,code
是 255,表示它是用户手动中止的,可以当作判断依据。
总结
剩下来的内容基本就是怎么驱动图形界面程序运行了。一般来说用 puppeteer 比较好,可以很容易的跟 node.js 联动,我厂的 showman 也是基于这个方案来实现的,最后贴一段视频,大家看下效果:
欢迎吐槽,共同进步