FrameBuffer中获取Android屏幕截图

16 篇文章 0 订阅
引子
 
我们知道,DDMS可以很容易的获取 Android 手机 的屏幕截图,那么它是怎么做到的呢? 
 
其实,android手机上有一个叫做FrameBuffer的设备,图像信息都是通过FrameBuffer写到手机屏幕上去的。因此可以通过读取此设备中的数据来获取当前正在显示的图像。当然DDMS也是这么做到的。
 
FrameBuffer 对应的设备文件就是/dev/graphics/fb0。因此我们可以通过下面的代码读取屏幕图像数据。其中传入的参数fd为一个文件描述符,也可以是 socket描述符。这样我们就可以把从fb中读取的屏幕图像信息传递给我们自己的应用,从而获取手机屏幕信息。
 
void framebuffer_service(int fd)
{
    struct fb_var_screeninfo vinfo;
    int fb, offset;
    char x[256];
 
    struct fbinfo fbinfo;
    unsigned i, bytespp;
 
    fb = open("/dev/graphics/fb0", O_RDONLY);
    if(fb < 0) goto done;
 
    if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
    fcntl(fb, F_SETFD, FD_CLOEXEC);
 
    bytespp = vinfo.bits_per_pixel / 8;
 
    fbinfo.version = DDMS_RAWIMAGE_VERSION;
    fbinfo.bpp = vinfo.bits_per_pixel;
    fbinfo.size = vinfo.xres * vinfo.yres * bytespp;
    fbinfo.width = vinfo.xres;
    fbinfo.height = vinfo.yres;
    fbinfo.red_offset = vinfo.red.offset;
    fbinfo.red_length = vinfo.red.length;
    fbinfo.green_offset = vinfo.green.offset;
    fbinfo.green_length = vinfo.green.length;
    fbinfo.blue_offset = vinfo.blue.offset;
    fbinfo.blue_length = vinfo.blue.length;
    fbinfo.alpha_offset = vinfo.transp.offset;
    fbinfo.alpha_length = vinfo.transp.length;
 
    /* HACK: for several of our 3d cores a specific alignment
     * is required so the start of the fb may not be an integer number of lines
     * from the base.  As a result we are storing the additional offset in
     * xoffset. This is not the correct usage for xoffset, it should be added
     * to each line, not just once at the beginning */
    offset = vinfo.xoffset * bytespp;
 
    offset += vinfo.xres * vinfo.yoffset * bytespp;
    printf("offset %d/n", offset);
 
    if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
 
    lseek(fb, offset, SEEK_SET);
    for(i = 0; i < fbinfo.size; i += 256) {
      if(readx(fb, &x, 256)) goto done;
      if(writex(fd, &x, 256)) goto done;
    }
 
    if(readx(fb, &x, fbinfo.size % 256)) goto done;
    if(writex(fd, &x, fbinfo.size % 256)) goto done;
 
done:
    if(fb >= 0) close(fb);
    close(fd);
}
(evilcode)
 

简单介绍:

截屏一般有三种方法:

1.           直接获取到一个view,然后通过ViewCache来获取一个bitmap对象,然后将bitmap对象写到图像文件。

2.           ddmslib截屏,ddmslibandroid内置的一个库,在pc端的截屏都是使用的这种方式,但在网上找不到使用这种方法的资料。

3.           framebuffer截屏,网上流传的都是这种方法,不过只有root后的设备能使用

根据各种方法的特点,我们选择的是framebuffer的方式,View的方式也简单介绍一下。由于截屏需要一定的处理时间,而且需要在后台运行,用service来实现是比较好的选择。                          

 

View截屏的简单实现

使用view截屏的话首先要获取当前的界面的view

Activity中获取view是非常容易的,但是仅限于当前Activityview,这种方法截屏是有限制的,截取的图像没有状态栏。在Activity中截屏的代码如下:

取得bitmap

View view = v.getRootView();

view.setDrawingCacheEnabled(true);

view.buildDrawingCache();

Bitmap bitmap = view.getDrawingCache();

 

然后将bitmap保存为png格式:

    FileOutputStream out new FileOutputStream(file_name);

    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);

 

framebuffer截屏的实现

framebufferlinux内核对显示的最底层驱动。在一般的linux文件系统中,通过/dev/fb0设备文件来提供给应用程序对framebuffer进行读写的访问。这里,如果有多个显示设备,就将依次出现fb1,fb2,…等文件。而在我们所说的android系统中,这个设备文件被放在了/dev/graphics/fb0,而且往往只有这一个。

 

这种方法使用的最为普遍,linux系统中经常使用这种方法实现截屏,一般步骤是:

1.      读取framebuffer

2.      Framebuffer转换为bitmap

3.      bitmap生成图像文件

 

读取framebuffer

android上使用这种方法第一个难题是获取framebuffer,因为默认的配置中framebuffer的读取权限是“root”,而Apk的权限最高只能提升到“system”,framework工作的权限也是“system”,也因此网上提供的截屏软件都只能在root过的手机上使用。对拥有源码的人来说,最简单的方法是直接改变framebuffer的权限,普通用户也有权限读取framebuffer

然后就可以通过读文件的方式直接读取framebuffer,利用如下代码打开一个指向framebuffer的输入流就可以读取了。

    public static InputStream getInputStream() throws Exception {

       FileInputStream buf = new  FileInputStream(

new File("/dev/graphics/fb0"));

       return buf;

}//get the InputStream from framebuffer

 

framebufferbitmap的转换

framebuffer的数据是直接送入显示设备的,这些数据没有文件头,而且由于framebuffer读到数据跟显示方式关系很大,在不同设备上framebuffer的大小和数据格式不一样,读取前确定framebuffer的数据格式。

       获取屏幕大小:

       DisplayMetrics metrics = new DisplayMetrics();

    WindowManager WM = (WindowManager) mContext

                  .getSystemService(Context.WINDOW_SERVICE);

    Display display = WM.getDefaultDisplay();

    display.getMetrics(metrics);

    int height = metrics.heightPixels; //屏幕高

    int with = metrics.widthPixels    //屏幕的宽

 

    获取显示方式

    int pixelformat = display.getPixelFormat();

    PixelFormat localPixelFormat1 = new PixelFormat();

    PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1);

    int deepth = localPixelFormat1.bytesPerPixel;//位深

 

pixelformat代表的意义是在PixelFormat.java定义的:

    public static final int RGBA_8888   = 1;

    public static final int RGBX_8888   = 2;

    public static final int RGB_888     = 3;

    public static final int RGB_565     = 4;

 

    public static final int RGBA_5551   = 6;

    public static final int RGBA_4444   = 7;

    public static final int A_8          = 8;

    public static final int L_8          = 9;

    public static final int LA_88        = 0xA;

    public static final int RGB_332     = 0xB;

      

pixelformat是下面进行判断处理的依据,根据pixelformat计算出实际的深度。

一般

       Framebuffer大小 height* with* deepth*2

之所以“*2”,是因为Framebuffer包含两帧画面,我们使用任何一帧都可以。

 

piex = new byte[height * with * deepth];

InputStream stream = getInputStream(

new File("/dev/graphics/fb0"));

       DataInputStream dStream = new DataInputStream(stream);

       dStream.readFully(piex);

              这样framebuffer的数据就被写进了piex

piex 生成bitmap

       bitmap = Bitmap.createBitmap(datamwidthmheightconfig);

        data:像素数据,data的一个元素表示一个像素,所以这里不能直接使用piex,必须经过转换。

              mwidthmheight:图像大小

       config:图像格式,可以取以下数值

        Bitmap.Config.ALPHA_8     (2),

        Bitmap.Config.RGB_565     (4),

        Bitmap.Config.ARGB_4444   (5),

        Bitmap.Config.ARGB_8888   (6);

 

bitmap保存为png格式

这是跟上面一样的

    FileOutputStream out new FileOutputStream(file_name);

    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);

 

这样截取的一幅图片就被保存到了固定位置。

 

快捷键的添加

    由于快捷键是全局的,所以要在PhoneWindowManager中添加

public int interceptKeyBeforeQueueing(

long whenNanos, int action, int flags,

int keyCode, int scanCode, int policyFlags,

boolean isScreenOn)

 

这个函数是在按键事件进入消息队列之前调用的,所以先与所有app相应事件。但是在这个函数对于每个按键事件是单独处理的,组合键需要我们自己来识别。

下图分别是power键和volumedown键的按键处理,黄色区域为新增加代码。由于现在power键的长按和短按都是有事件响应的,所以设计的关键在于必须在长按和短按的间隙处理截屏事件,而且截屏后保证power的长按不在响应。

 

 

service设计:

这个服务的功能比较单一,只在启动时响应请求就可以,不需要驻留在后台,也不需要调用接口,所以不需要AIDL,也不需要远程连接。

客户端,使用intent启动服务

Intent intent = new Intent();

intent.setAction("com.android.ScreenServer.SHOT");

mContext.startService(intent);

在应用程序端

       声明权限使用SD

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

       Intentfilter过滤事件

    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

        <service

            android:name="shotservice" >

            <intent-filter >

                <action android:name="com.android.ScreenServer.SHOT"/>

            </intent-filter>  

        </service>

</application>

 

程序代码:

    public void onStart(Intent intent, int startId) {

       // TODO Auto-generated method stub

       super.onStart(intent, startId);

       mContext this;

       。。。

       使用framebuffer截屏

       。。。

}

这样截屏结束后,Service的生命周期就结束了,系统会自动销毁Service

 
  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Android framebuffer是指在Android系统用于显示图像的一种技术。它使用framebuffer设备文件来管理显示屏的像素数据,并通过SurfaceFlinger作为屏幕合成引擎将图像写入到framebuffer。在Androidframebuffer设备文件通常位于/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 ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值