Android framebuffer 截屏原理

24 篇文章 0 订阅

原帖地址:http://www.ctoandroid.com/?p=56

项目的原因需要将android设备的屏幕截图,并以流媒体的形式传输。
万事开头难,android截屏网上有很多种方法,但是大多数只是在应用内截屏,使用view提供的方法得到,但是这显然太有局限性了。
后面找到可以使用读取framebuffer实现截屏,下面这篇文章是我在学习的过程中找到的一篇相当不错的文章,贴在此处。

1.
首先让我们来说说Android的屏幕是怎么显示出来的。 众所周知,Android也是linux派生出来的,因此Android的显示机制用的是Linux一样的机制:Framebuffer
FrameBuffer是一种机制。他提共接口将显示设备抽象为帧缓冲区。用户可以将它看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,这种机制是把屏幕上的每个点映射成一段线性内存空间,程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。例如如果你想把一张bitmap图片显示到屏幕上去,你只要解析bitmap之后把数据bit copy进framebuffer,屏幕就会立刻显示出来。一般Linux的framebuffer 对应/dev/fb0这个字符设备文件。 Android稍微改了改,放在/dev/graphics/fb0下。

2.
知道了Android的显示机制,和android framebuffer的文件位置,那么截屏就可以变得非常高效率了:将framebuffer里的显示区读出来就行了。但是读出来还是有很多细节的, 你知道这里面存了多少帧? 你知道图像以什么格式存在里面的?

3.
关于这个问题,Android不同机型还真不同,我在网上找到过一篇文章说的是G1机型的,G1的framebuffer一次存2帧, 帧的格式是RGB565,也就是说一个点的颜色由Red Blue Green 一共16个bit, 2 bytes形成。而对于N1, 经过摸索, 实际上framebuffer里存了3帧, 颜色格式是BGR32, 一共4bytes, RGBA各8bit顺序排列,注意不是ARGB, A:透明度。

以上是原理,知道原理,那么人人都能用命令行作截屏了。不需要用DDMS里的驱动帮你截屏。具体动手步骤可以参看如下
> adb shell
>$ su
>$ cat /dev/graphics/fb0 /sdcard/frame.raw
>$ exit
上面是进去手机用命令把framebuffer原始数据直接导出来到一个文件里去, 因为访问/dev/graphics/fb0需要系统权限,这也是为什么程序实现截屏都需要申请系统权限的原因。
>adb pull /sdcard/frame.raw frame.raw
这样就把这个文件从手机里拷贝出来到本地电脑了。接下来从这原始数据里取出1帧。Ubuntu下有很好的命令行可以直接把文件按raw data裁剪。
>dd bs=1920 count=800 if=frame.raw of=xxx.raw
上面这条命令的意思是把输入文件frame.raw 一次读1920bytes, 一共读800次, 读完后写出来到xxx.raw文件里去。 为什么是1920? 因为N1屏幕分辨率宽480像素,上面原理里解释过,N1的颜色格式一个像素要占4bytes的颜色存储,所以480 * 4 = 1920,所以1920就相当于一次读一行的像素出来,读800次,大家就好理解了,因为N1屏幕高800像素。
我们这个命令就相当于把framebuffer中正好800X480一屏幕的数据截了出来。

接下来就用解码工具按照颜色格式将这1帧数据转换成看图工具可以支持的格式了,这里我采用png格式。
> ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt bgr32 -s 480X800 -i xxx.raw -f image2 -vcodec png frame-%d.png
上面命令很简单,注意的一个参数是-pix_fmt bgr32颜色格式千变万化,rgb32, rgb565, yuv等等等等,找不到正确的格式参数你就怎么也解不对这张图片,颜色会很诡异。我也是试了一会才自己找到的,没有去找相关的文档。不过还好,因为大致知道应该是RBGA某种排列,按照排列组合大家也可以算出来一共24种组合,另外可以猜测RGB肯定会是一块的,不至于A插到他们中间某个位置,所以就剩12钟组合了,试了一会就出来了。
打开输出来的文件,截图就好了

自己动手的步骤有了,程序实现自然也就不难了。 剩下的就是截屏的时候响应哪两个组合键阿,播放咔嚓声阿,这些杂事了。

=========================这里有一个C程序实现了,请参考(未验证)

public class ScreenShot {

    /**
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws InterruptedException {    
        try {
            //分辨率大小,后续可以通过代码来获取到当前的分辨率
            int xResolution = 320;
            int yResolution = 480;
            //执行adb命令,把framebuffer中内容保存到fb1文件中
             Runtime.getRuntime().exec("adb pull /dev/graphics/fb0 C:/fb1");
             //等待几秒保证framebuffer中的数据都被保存下来,如果没有保存完成进行读取操作会有IO异常
             Thread.sleep(15000);
             //读取文件中的数据
             InputStream in = (InputStream)new FileInputStream("C:/fb1");
             DataInput frameBuffer = new LittleEndianDataInputStream(in);
             
             BufferedImage screenImage = new BufferedImage(
                     xResolution, yResolution, BufferedImage.TYPE_INT_ARGB);
                 int[] oneLine = new int[xResolution];
                for (int y = 0; y < yResolution; y++) {
                    //从frameBuffer中计算出rgb值
                    convertToRgba32(frameBuffer, oneLine);
                    //把rgb值设置到image对象中
                    screenImage.setRGB(0, y, xResolution, 1, oneLine, 0, xResolution);
                }
                Closeables.closeQuietly(in);
                
                ByteArrayOutputStream rawPngStream = new ByteArrayOutputStream();
                try {
                      if (!ImageIO.write(screenImage, "png", rawPngStream)) {
                        throw new RuntimeException(
                            "This Java environment does not support converting to PNG.");
                      }
                    } catch (IOException exception) {
                      // This should never happen because rawPngStream is an in-memory stream.
                     System.out.println("IOException=" + exception);
                    }
                byte[] rawPngBytes = rawPngStream.toByteArray();
                String base64Png = new Base64Encoder().encode(rawPngBytes);
                
                File screenshot = OutputType.FILE.convertFromBase64Png(base64Png);
                System.out.println("screenshot==" + screenshot.toString());
                screenshot.renameTo(new File("C:\\screenshottemp.png"));
                
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e);
        }
    }
    
    
    public static void convertToRgba32(DataInput frameBuffer, int[] into) {
        try {
            for (int x = 0; x < into.length; x++) {
                try{
                int rgb = frameBuffer.readShort() & 0xffff;
                int red = rgb >> 11;
                red = (red << 3) | (red >> 2);
                int green = (rgb >> 5) & 63;
                green = (green << 2) | (green >> 4);
                int blue = rgb & 31;
                blue = (blue << 3) | (blue >> 2);
                into[x] = 0xff000000 | (red << 16) | (green << 8) | blue;
                }catch (EOFException e){
                    System.out.println("EOFException=" + e);
                }
              }
        } catch (IOException exception) {
            System.out.println("convertToRgba32Exception=" + exception);
      }
    }
    
}


  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
Android framebuffer是指在Android系统中用于显示图像的一种技术。它使用framebuffer设备文件来管理显示屏的像素数据,并通过SurfaceFlinger作为屏幕合成引擎将图像写入到framebuffer中。在Android中,framebuffer设备文件通常位于/dev/graphics/fb0或/dev/graphics/fb1等路径下。打开可用的framebuffer设备是操作framebuffer的主要步骤之一。 SurfaceFlinger负责管理来自各个窗口的Surface对象,并将它们写入到framebuffer中。它使用前buffer来合成图像,后buffer来绘制图像。一旦绘制完成,Android通过页翻转操作,交换Y轴坐标的偏移量,选择不同的buffer。如果虚拟Y轴分辨率大于实际Y轴分辨率,说明framebuffer可以直接使用双缓冲机制。否则,后buffer需要复制到前buffer,这可能导致页交换延迟。为了提高系统性能,framebuffer驱动最好提供双缓冲机制。 总结来说,Android framebuffer是一种用于显示图像的技术,在Android系统中使用framebuffer设备文件来管理显示屏的像素数据,并通过SurfaceFlinger作为屏幕合成引擎将图像写入到framebuffer中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Android Framebuffer介绍及使用](https://blog.csdn.net/wx_962464/article/details/77943919)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值