如何对嵌入式linux设备进行截图
说到截图,可能大家想到的是 Ctrl+Alt+A 这个QQ提供的非常好用的截图快捷键,好像截图是一个非常简单的功能,基本上所有的系统都会有嘛。
但对于嵌入式设备来说,截图这一功能其实并不是那么容易实现。嵌入式设备碎片化特别严重,运行的操作系统大多经过裁减,采用的显示方案也各有不同,要针对不同的设备进行适配。目前最好用最方便的方案是用ffmpeg来截图。
这里我会讲针对嵌入式Linux设备的四种截图方案:
- wayland下基于wayland debug protocol的 weston-screenshooter
- xwindow下基于framebuffer的 gsnap
- 通过ffmpeg来截图
- wayland下采用
kmsgrab
截图 - xwindow下采用
fbdev
截图
- wayland下采用
weston-screenshooter
较新的嵌入式硬件常采用wayland作为显示服务器。wayland对截图功能作了限制,适用于xwindow的截图方式在wayland下都不能用,不过wayland的官方实现weston中提供了一个截图工具weston-screenshooter。
下面我以某个采用weston作为显示后端的嵌入式设备说明如何使用weston-screenshooter来截图。
使用这一工具的前提是要开启wayland debug protocol
,要打开截图就要更改开机自启的脚本,修改其中有关weston的相关部分,再让weston以debug模式启动,具体就是在weston的启动参数上加上 --debug
。
之后就能调用 weston-screenshooter 程序来截图了,默认会把截图存放在运行weston-screenshooter时的工作目录,可以通过设置环境变量XDG_PICTURES_DIR
来设置要把截图放在哪个文件夹下。
这个工具使用起来比较麻烦,首先要开启wayland debug protocol
,这就要更改设备的启动参数。其次,用它截的图不能自定义名字,也不能直接通过流的形式传输。如果我们想对截取的图作二次处理时,每次获取图片的流程只能是:
- 亮屏(不亮屏不能截图)
- 截图
- 用
ls
和grep
在指定的目录中找到新截的图片 - 把图片拉取到电脑上或在设备内对图片进行处理
每次截图都需要用ls
和grep
去文件系统下查找最新的截图文件,导致了每次图片处理都很慢,也不太稳定。
gsnap
这是一个基于xwindow下framebuffer设备的截图方案,对于采用xwindow的设备可以采用这个方案,可以通过读取framebuffer获取截图。
它的源码地址为 https://github.com/luhuadong/gsnap
这个工具的截图质量不佳,也缺少资料,不建议使用。
ffmpeg
ffmpeg简直是音视频领域的霸主,什么都能做,基于它实现截图是我偶然想到的,以前尝试过用vlc实现过树莓派的实时图传,能够实现将树莓派上的摄像头图片的实时传输。
由此联想到vlc底层是ffmpeg,那我是不是可以试试用ffmpeg实现截图呢?
于是我去谷歌上搜了下 ”ffmpeg 截图“ 发现搜不到,再一想或许搜 “ffmpeg 屏幕录制” 可以,搜到了一些用ffmpeg录屏的方法。
ffmpeg 可以指定输入源,输入源可以是文件,可以是字节流,也可以是一个设备。用ffmpeg录屏其实就是通过指定ffmpeg的输入源为屏幕设备实现的。
ffmpeg支持很多设备,具体可以在 https://ffmpeg.org/ffmpeg-devices.html 查看。
kmsgrab
对于 wayland 显示服务器下的设备,可以通过 Kernel Mode Setting (KMS) 来获取 Direct Rendering Manager(DRM) 设备的framebuffer输出数据。
通过下面这串命令就能实现截图了,注意使用的ffmpeg在编译的时候要加上对kmsgrab
设备的支持,不然无法使用。
1 | ffmpeg -loglevel quiet -f kmsgrab -i - -vf 'hwdownload,format=bgr0' -b 600k -framerate 60 -frames:v 1 screenshot1.png |
-loglevel quiet
关闭ffmpeg自身的输出信息-f kmsgrab -i - -vf 'hwdownload,format=bgr0'
指定使用kmsgrab设备作为输入源-b 600k
设置输出的比特率为600k,不指定这个参数得到的截图会比较大-framerate 60
设置帧率为60,这个应该尽量往高设,会影响截图的速度-frames:v 1
只获取一帧数据,不加这个参数则会一直抓图,最后保存的是一个视频文件。
在实际的使用中,我需要由usb线通过adb连接设备,那么把截图保存为临时文件再拉下来的方案就有些低效了。ffmpeg支持把图片的数据输出到标准输出上,这样就不用通过文件中转,直接就能获取到截图了。
通过adb shell
调用设备中的ffmpeg 进行截图然后通过base64编码借助标准输出传输到电脑上的命令:
1 | adb shell "ffmpeg -loglevel quiet -f kmsgrab -i - -vf 'hwdownload,format=bgr0' -b 600k -framerate 60 -frames:v 1 -c:v png -f image2pipe - |base64"|base64 -d >abc.png |
fbdev
对于不使用wayland的设备,将输入源改成fbdev
设备就能截图了。
1 | ./ffmpeg -f fbdev -framerate 60 -i /dev/fb0 -frames:v 1 screenshot7.png |
录屏
如果想要实现录屏,只要把 -frames:v 1
去掉,就实现一直录制传输视频流了,通过ffplay可以解析这个视频流。
1 | adb shell "ffmpeg -loglevel quiet -f kmsgrab -i - -vf 'hwdownload,format=bgr0' -b 600k -f avi pipe:1 |base64 " |base64 -d |ffplay -fflags nobuffer -sync video - -vf transpose=1 |