如何对嵌入式linux设备进行截图

IMG_6526.jpg

说到截图,可能大家想到的是 Ctrl+Alt+A 这个QQ提供的非常好用的截图快捷键,好像截图是一个非常简单的功能,基本上所有的系统都会有嘛。

但对于嵌入式设备来说,截图这一功能其实并不是那么容易实现。嵌入式设备碎片化特别严重,运行的操作系统大多经过裁减,采用的显示方案也各有不同,要针对不同的设备进行适配。目前最好用最方便的方案是用ffmpeg来截图。

这里我会讲针对嵌入式Linux设备的四种截图方案:

  • wayland下基于wayland debug protocolweston-screenshooter
  • xwindow下基于framebuffergsnap
  • 通过ffmpeg来截图
    • wayland下采用 kmsgrab 截图
    • xwindow下采用 fbdev 截图

weston-screenshooter

较新的嵌入式硬件常采用wayland作为显示服务器。wayland对截图功能作了限制,适用于xwindow的截图方式在wayland下都不能用,不过wayland的官方实现weston中提供了一个截图工具weston-screenshooter

下面我以某个采用weston作为显示后端的嵌入式设备说明如何使用weston-screenshooter来截图。

使用这一工具的前提是要开启wayland debug protocol,要打开截图就要更改开机自启的脚本,修改其中有关weston的相关部分,再让westondebug模式启动,具体就是在weston的启动参数上加上 --debug

之后就能调用 weston-screenshooter 程序来截图了,默认会把截图存放在运行weston-screenshooter时的工作目录,可以通过设置环境变量XDG_PICTURES_DIR来设置要把截图放在哪个文件夹下。

这个工具使用起来比较麻烦,首先要开启wayland debug protocol,这就要更改设备的启动参数。其次,用它截的图不能自定义名字,也不能直接通过流的形式传输。如果我们想对截取的图作二次处理时,每次获取图片的流程只能是:

  1. 亮屏(不亮屏不能截图)
  2. 截图
  3. lsgrep在指定的目录中找到新截的图片
  4. 把图片拉取到电脑上或在设备内对图片进行处理

每次截图都需要用lsgrep去文件系统下查找最新的截图文件,导致了每次图片处理都很慢,也不太稳定。

gsnap

这是一个基于xwindowframebuffer设备的截图方案,对于采用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

江达小记