从Android设备获取实时截屏(adb)

前两篇文章已经把如何控制android设备的输入讲了,这一篇就是如何获取输出,通过adb的方式

原理

framebuffer获取流程

AdbClient和AdbServer都是运行在PC上的,AdbDaemon运行在android设备上。那framebuffer倒底是个啥?

帧缓冲(frame buffer)是Linux视频系统的核心概念,因此先了解一下他的功能。

因为视频适配器可能基于不同的硬件体系架构,较高内核层和应用程序的实现可能会因视频卡的不同而不同,这会导致在使用不同视频卡的时需要采用不同的方案。随之而来的低可移植性和冗余的代码需要大量的投入和维护开销。帧缓冲的概念解决了这个问题,它进行了一般化的抽象并规定编程接口,从而开发人员可以以与平台无关的方式编写应用层和较高内核层程序。因此,内核的帧缓冲接口允许应用程序与底层图形硬件的变化无关,如果应用和显示器驱动程序遵循帧缓冲接口,应用程序不用改变就可以在不同类型的视频硬件上运行。 (引用链接)

我来简单的解释一下,其实就是当前时间和之前几个帧的屏幕图像信息(每个像素点的颜色,分辨率等等)。

java实现

我们来看看android系统里面对每一帧图像信息的定义

//ddmlibsrccomandroidddmlibDevice.java

public RawImage getScreenshot()

throws TimeoutException, AdbCommandRejectedException, IOException {

return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this);

}

//ddmlibsrccomandroidddmlibAdbHelper.java

/**

* Retrieve the frame buffer from the device.

* @throws TimeoutException in case of timeout on the connection.

* @throws AdbCommandRejectedException if adb rejects the command

* @throws IOException in case of I/O error on the connection.

*/

static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device)

throws TimeoutException, AdbCommandRejectedException, IOException {

RawImage imageParams = new RawImage();

byte[] request = formAdbRequest("framebuffer:"); //$NON-NLS-1$

byte[] nudge = {

0

};

byte[] reply;

SocketChannel adbChan = null;

try {

adbChan = SocketChannel.open(adbSockAddr);

adbChan.configureBlocking(false);

// if the device is not -1, then we first tell adb we're looking to talk

// to a specific device

setDevice(adbChan, device);

write(adbChan, request);

AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);

if (resp.okay == false) {

throw new AdbCommandRejectedException(resp.message);

}

// first the protocol version.

reply = new byte[4];

read(adbChan, reply);

ByteBuffer buf = ByteBuffer.wrap(reply);

buf.order(ByteOrder.LITTLE_ENDIAN);

int version = buf.getInt();

// get the header size (this is a count of int)

int headerSize = RawImage.getHeaderSize(version);

// read the header

reply = new byte[headerSize * 4];

read(adbChan, reply);

buf = ByteBuffer.wrap(reply);

buf.order(ByteOrder.LITTLE_ENDIAN);

// fill the RawImage with the header

if (imageParams.readHeader(version, buf) == false) {

Log.e("Screenshot", "Unsupported protocol: " + version);

return null;

}

Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="

+ imageParams.size + ", width=" + imageParams.width

+ ", height=" + imageParams.height);

write(adbChan, nudge);

reply = new byte[imageParams.size];

read(adbChan, reply);

imageParams.data = reply;

} finally {

if (adbChan != null) {

adbChan.close();

}

}

return imageParams;

}

上面两段源码取自于ddmlib库,展示了AdbClient需要如何去和AdbServer通信去获取framebuffer当前帧,PS:当前adb自带的一些工具都还没有实现该功能,如果我们想实现该功能,很简单,建立一个java程序,引用ddmlib和一些其他的库,去调用即可(getScreenshot()),怎么调用?请看《Android Adb调试功能漫谈》

buf.order(ByteOrder.LITTLE_ENDIAN);

注意上面这一句,涉及到一个大字节序和小字节序的问题,详情请见大字节序与小字节序详解。

由于现在大部分民用cpu都是小字节序,所以我在adb.exe翻译成c代码时,这一段没有实现。也希望有兴趣的朋友把大小字节序转换的函数实现,网上也有部分代码参考哦。

adb.exe 实现

这里是一个代码翻译过程,不多啰嗦,看代码

//outScreen由函数里面申请,外面用完了必须要释放内存,因为协议里面带有长度

int getScreen(char* inSerial, unsigned char** outScreen, int* outWidth, int* outHeight)

{

int fd = 0;

int rel = 0;

int dbgRel = 0;

char buf[10240];

int version = -1;

int headerSize = 0;

unsigned char* retScreen;

int* cousor;

unsigned char* cCousor;

unsigned char nudge[] = {0};

//图片的信息

int bpp;

int size;

int width;

int height;

int red_offset;

int red_length;

int blue_offset;

int blue_length;

int green_offset;

int green_length;

int alpha_offset;

int alpha_length;

transport_type ttype = kTransportAny;

int server_port = DEFAULT_ADB_PORT;

adb_set_transport(ttype, inSerial);

adb_set_tcp_specifics(server_port);

fd = adb_connect("framebuffer:"); //+

if(fd < 0) {

return 0;

//read_finished(fd);

//adb_close(fd);

}

//开始接收数据

if (readx(fd,buf,4) == 0){

version = *(int*)(buf);

//printf("version:%d

",version);

}else{

fprintf(stderr,"err recv size");

return 0;

}

switch (version)

{

case 1:

headerSize = 12;

break;

case 16:

headerSize = 3;

break;

default:

break;

}

if (headerSize == 0)

{

adb_close(fd);

return 0;

}

retScreen = (unsigned char*)malloc(headerSize*4);

if (readx(fd,retScreen,headerSize*4) != 0)

{

fprintf(stderr,"err recv size");

adb_close(fd);

return 0;

}

cousor = (int*)retScreen;

if (version == 1)

{

bpp = *cousor;cousor++;

size = *cousor;cousor++;

width = *cousor;cousor++;

height = *cousor;cousor++;

red_offset = *cousor;cousor++;

red_length = *cousor;cousor++;

blue_offset = *cousor;cousor++;

blue_length = *cousor;cousor++;

green_offset = *cousor;cousor++;

green_length = *cousor;cousor++;

alpha_offset = *cousor;cousor++;

alpha_length = *cousor;

}

else if(version == 16)

{

bpp = 16;

// read actual values.

size = *cousor;cousor++;

width = *cousor;cousor++;

height = *cousor;cousor++;

// create default values for the rest. Format is 565

red_offset = 11;

red_length = 5;

green_offset = 5;

green_length = 6;

blue_offset = 0;

blue_length = 5;

alpha_offset = 0;

alpha_length = 0;

}

else

{

fprintf(stderr,"unsupport version");

}

//printf("bpp:%d width:%d high:%d size:%d

",bpp,width,height,size);

adb_write(fd,nudge,sizeof nudge);

retScreen = (unsigned char*)realloc(retScreen,size);

if (readx(fd,retScreen,size) != 0)

{

fprintf(stderr,"err recv size should be:%d

",size);

adb_close(fd);

return 0;

}

* outScreen = retScreen;

* outWidth = width;

* outHeight = height;

//write png

//write_PNG(retScreen,"d:\a.png",width,height);

//free(retScreen);

adb_close(fd);

return 1;

}

使用该函数就可以把当前帧的framebuffer取过来,进行处理。C代码?怎么用?请见《让Adb.exe支持Monkey》

framebuffer格式

那把framebuffer取下来之后怎么处理呢?这是一个问题。

对字节流进行处理

typedef struct RGBA

{

RGBA()

{

R = 0;

G = 0;

B = 0;

A = 0;

}

RGBA(BYTE pR, BYTE pG, BYTE pB, BYTE pA)

{

R = pR;

G = pG;

B = pB;

A = pA;

}

BYTE R;

BYTE G;

BYTE B;

BYTE A;

}*PRGBA;

这是一个像素点的RGBA信息,A是Alpha通道,与透明度相关,RGB就不解释了。一个像素是占用4个字节,按顺序是R,G,B,A信息。整个字节流是一个二维数组,可以根据framebuffer里面的元数组取得长和宽,这样就可以精确的读取到每一个像素点的信息。

用opencv进行处理

//传入 framebuffer, frameWidth, frameHeight

...

try

{

Size size(frameWidth, frameHeight);

//这里创建不会复制数据,只会创建一个头部

Mat ref(size, CV_8UC4, framebuffer);

cvtColor(ref, ref, CV_BGRA2RGB);

ret = ...(function)(Mat ref)

}

catch (Exception* e)

{

fprintf(stderr, "Function execute err:%s

", e->what());

ret = false;

}

if (freeFramebuffer)

free(framebuffer);

...

现在的图形处理如果还不用opencv可就有点out了哦 :)

后记

经过实测,framebuffer是无压缩的,有些分辨率高的设备取到的framebuffer非常大,从设备传送到AdbClient耗时会比较大,5秒左右的也有,如果追求极限是可以在android侧制作一个程序先获取framebuffer再压缩,再传送到AdbClient,就会快很多,有兴趣的朋友可以交流。

framebuffer获取的原理和格式都已经清楚了,其实已经可以做非常多的自由发挥了。下一篇是啥?怎么用opencv做一些处理?或者用tesseract做一些有意思的东东?

ddms java 截图_从Android设备获取实时截屏相关推荐

  1. 电脑如何长截屏截图_您的意见很重要-截屏技术调查

    电脑如何长截屏截图 Hi Dear Reader, I'd like you to SOUND OFF. 嗨,亲爱的读者,我想请你声音清醒. As you may know, part of my j ...

  2. 双击背面截图_苹果怎么设置背面双击截屏

    展开全部 ios14怎么双击背面截屏e68a84e8a2ad3231313335323631343130323136353331333433656638?怎么双击背部截图?这个里面有很多的操作都很方便 ...

  3. android获取activity截图,Android Activity 不能被截屏的解决方法

    Android Activity 不能被截屏的解决方法 在Activity 添加即可 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECU ...

  4. Android设备获取USB扫码枪扫描的内容与可能遇到的问题解决

    这篇文章主要给大家介绍了关于Android设备获取扫码枪扫描内容的方法,以及在开发中可能会遇到的问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们 ...

  5. 【IoT最佳实践】设备获取实时天气DEMO代码解读

    [摘要] 之前,我们曾为您介绍如何实现设备实时获取天气信息,本文将为您从代码逻辑层面解读该实时天气应用的DEMO. 本文承接[IoT最佳实践]设备获取实时天气信息,为您解读实时天气DEMO的代码逻辑, ...

  6. 【IoT最佳实践】设备获取实时天气信息

    [摘要] 物联网智能设备可以通过物联网平台统一获取实时天气信息,本文提供一个实现该功能的DEMO和其使用方法. 场景说明 随着技术的发展,物联网设备也越来越智能化,例如可以显示当地的实时天气. 在物联 ...

  7. Java通达信接口如何实现获取实时股票数据?

    Java通达信接口如何实现获取实时股票数据? 一般有三种方式: 网页爬虫.采用爬虫去爬取目标网页的股票数据,去相关网站或技术论坛上找一下别人写的爬虫集成到项目中. 请求第三方API.会有专门的公司(例 ...

  8. 苹果xr怎么截屏_苹果怎么截图?教你各种iPhone机型截屏方法

    苹果手机怎么截图?刚刚换手机的用户往往会遇到一个难题:新手机怎么截屏?很多手机上都有自带的截图功能,但是刚换手机的小伙伴可能找不到截图的方法,为了帮助大家更熟悉自己的新手机,今天帮大家整理了各种型号i ...

  9. shell 截屏 android,Android 使用Shell脚本截屏并自动传到电脑上

    Android 使用Shell脚本截屏并自动传到电脑上 Android设备用久了,截屏是个麻烦事.更麻烦的是通过qq传到电脑上,倒腾半天.其实用adb命令就可以截屏,然后写个pull的语句就可以拉到电 ...

最新文章

  1. [Eclipse]代码已被写入关于如何切换到unix在新行
  2. 混合云存储开启企业上云新路径--阿里云混合云备份容灾方案发布 1
  3. pypthon3精要(16)-enumerate
  4. python的特征提取实验一_在opencv3中使用ORB进行特征提取实验-Python版
  5. linux进程莫名其妙被kill,Linux运行程序时,程序进程莫名退出(被杀死)
  6. java变量类型概念_java变量类型
  7. python 读写tiff文件
  8. matlab在故障诊断中的应用,Matlab在发动机故障诊断中的应用研究
  9. Mac终端远程连接历史记录怎么清除
  10. MPI编程(3)—点对点通信(阻塞式MPI_Send/MPI_Recv和非阻塞式MPI_Isend/MPI_Irecv)
  11. 批量下载ERA5数据(Python+IDM)
  12. 【ACWing】587. 吃蛋糕
  13. 【天猫】双十一活动策划书;保守估计500万销售额,货值表
  14. 遇到maven私服下载过慢或者卡死的情况
  15. 计算机网络Wireshark实验-棋歌教学网
  16. 3孔融分梨 4分 函数c语言,孔融分梨有绝招
  17. 企业管理应具备哪些软件
  18. MATLAB unwrap应用
  19. Flutter Scaffold
  20. 英语口语294之每日十句口语

热门文章

  1. 2021年焊工(初级)考试报名及焊工(初级)模拟试题
  2. 同步消息和异步消息的区别
  3. Apache BookKeeper 简介
  4. IntelliJ IDEA 如何下载安装插件
  5. 【Oracle】关于索引的那些事
  6. 微信小程序富文本标签 rich-text 图片自适应大小问题
  7. 【记录】Ubuntu已连接网络但无法上网解决方法
  8. 西安面试第一天面试问题总结
  9. Js获取上传文件的绝对路径时总是的到C:\fakepath\+文件名称 解决方案
  10. 机器学习系列--数据预处理