minicap介绍

minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示。

stf自己写了一个工具叫minicap用来替代原生的screencap,这个工具是stf框架的依赖工具之一,最近手头上的项目刚好由于帧率卡顿需要优化,刚好来在testerhome社区看到对STF的介绍,WEB 端批量移动设备管理控制工具 STF 的环境搭建和运行

minicap工具是用NDK开发的,属于Android的底层开发,该工具分为两个部分,一个是动态连接库.so文件,一个是minicap可执行文件。但不是通用的,因为CPU架构的不同分为不同的版本文件,STF提供的minicap文件根据CPU 的ABI分为如下4种:

arm64-v8aarmeabi-v7a,x86,x86_64 架构。而minicap.so文件在这个基础上还要分为不同的sdk版本。这些都可以从Github地址:链接地址下载而来

结构树目录.
├── bin
│   ├── arm64-v8a
│   │   ├── minicap
│   │   └── minicap-nopie
│   ├── armeabi-v7a
│   │   ├── minicap
│   │   └── minicap-nopie
│   ├── x86
│   │   ├── minicap
│   │   └── minicap-nopie
│   └── x86_64
│       ├── minicap
│       └── minicap-nopie
└── shared├── android-10│   └── armeabi-v7a│       └── minicap.so├── android-14│   ├── armeabi-v7a│   │   └── minicap.so│   └── x86│       └── minicap.so ├── android-15 │ ├── armeabi-v7a │ │ └── minicap.so │ └── x86 │ └── minicap.so ├── android-16 │ ├── armeabi-v7a │ │ └── minicap.so │ └── x86 │ └── minicap.so ├── android-17 │ ├── armeabi-v7a │ │ └── minicap.so │ └── x86 │ └── minicap.so ├── android-18 │ ├── armeabi-v7a │ │ └── minicap.so │ └── x86 │ └── minicap.so ├── android-19 │ ├── armeabi-v7a │ │ └── minicap.so │ └── x86 │ └── minicap.so ├── android-21 │ ├── arm64-v8a │ │ └── minicap.so │ ├── armeabi-v7a │ │ └── minicap.so │ ├── x86 │ │ └── minicap.so │ └── x86_64 │ └── minicap.so ├── android-22 │ ├── arm64-v8a │ │ └── minicap.so │ ├── armeabi-v7a │ │ └── minicap.so │ ├── x86 │ │ └── minicap.so │ └── x86_64 │ └── minicap.so ├── android-9 │ └── armeabi-v7a │ └── minicap.so └── android-M ├── arm64-v8a │ └── minicap.so ├── armeabi-v7a │ └── minicap.so ├── x86 │ └── minicap.so └── x86_64 └── minicap.so

准备对应文件

a、查看CPU架构(adb shell getprop ro.product.cpu.abi)及查看android版本level(adb shell getprop ro.build.version.sdk)

b、根据上面获取的信息,将适合设备的可执行文件和.so文件push到手机的/data/local/tmp目录下,或者在STF框架的源码下找到vendor/minicap文件夹下

c、adb shell进入到目录下chmod 777 minicap

d、测试一下minicap是否可用:(-P后面跟的参数为你屏幕的尺寸)

  adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x1920@1080x1920/0 -t

安装运行环境

a、安装nodejs:

  查看版本号:node -v

b、安装运行依赖 ws和express包

  npm install ws –g

  npm install express -g

启动手机端服务

就是启动了一个socket服务器

adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x1920@1080x1920/0

本地端口转发

a、跟上面的socket服务通信,首先我们要将本地的端口映射到minicap工具上,端口随意:

  adb forward tcp:1717 localabstract:minicap

b、输入 node app.js 回车启动服务:

  控制台显示  Listening on port 9002 表示启动成功

c、浏览器打开本地 localhost:9002  链接地址,查看

获取信息

然后使用命令nc localhost 1717来与minicap通信,然后你会发现好多乱码。官方提供了一个demo来看效果,在minicap项目下的example目录

但是这些信息是有规则的,只是我们无法实际查看。但是我们做的工具需要用java来获得该信息,所以弄懂这些格式是很有必要的,结果分析后得出这些信息分3部分

Banner模块(第一部分)

这一部分的信息只在连接后,只发送一次,是一些汇总信息,一般为24个16进制字符,每一个字符都表示不同的信息:

位置 信息
0 版本
1 该Banner信息的长度,方便循环使用
2,3,4,5 相加得到进程id号
6,7,8,9 累加得到设备真实宽度
10,11,12,13 累加得到设备真实高度
14,15,16,17 累加得到设备的虚拟宽度
18,19,20,21 累加得到设备的虚拟高度
22 设备的方向
23 设备信息获取策略

携带图片大小信息和图片二进制信息模块(第二部分)

得到上面的Banner部分处理完成后,以后不会再发送Banner信息,后续只会发送图片相关的信息。那么接下来就接受图片信息了,第一个过来的图片信息的前4个字符不是图片的二进制信息,而是携带着图片大小的信息,我们需要累加得到图片大小。这一部分的信息除去前四个字符,其他信息也是图片的实际二进制信息,比如我们接受到的信息长度为n,那么4~(n-4)部分是图片的信息,需要保存下来。

只携带图片二进制信息模块(第三部分)

每一个变化的界面都会有上面的[携带图片大小信息和图片二进制信息模块],当得到大小后,或许发送过来的数据都是要组装成图片的二进制信息,知道当前屏幕的数据发送完成。 
有2种方式可以看出来图片组装完成了:

  • 又遇到第二部分
  • 设定大小的数据已经装满了

java的实现:

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.Stack; import java.util.concurrent.ConcurrentLinkedQueue; import javax.imageio.ImageIO; import org.apache.log4j.Logger; import com.android.ddmlib.AdbCommandRejectedException; import com.android.ddmlib.CollectingOutputReceiver; import com.android.ddmlib.IDevice; import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace; import com.android.ddmlib.ShellCommandUnresponsiveException; import com.android.ddmlib.SyncException; import com.android.ddmlib.TimeoutException; import com.wuba.utils.DirStructureUtil; import com.wuba.utils.TimeUtil; /** * @date 2015年8月12日 上午11:02:53 */ public class MiniCapUtil { private Logger LOG = Logger.getLogger(MiniCapUtil.class); // CPU架构的种类 public static final String ABIS_ARM64_V8A = "arm64-v8a"; public static final String ABIS_ARMEABI_V7A = "armeabi-v7a"; public static final String ABIS_X86 = "x86"; public static final String ABIS_X86_64 = "x86_64"; private Queue<byte[]> dataQueue = new ConcurrentLinkedQueue<byte[]>(); private Banner banner = new Banner(); private static final int PORT = 1717; private IDevice device; private String REMOTE_PATH = "/data/local/tmp"; private String ABI_COMMAND = "ro.product.cpu.abi"; private String SDK_COMMAND = "ro.build.version.sdk"; private String MINICAP_BIN = "minicap"; private String MINICAP_SO = "minicap.so"; private String MINICAP_CHMOD_COMMAND = "chmod 777 %s/%s"; private String MINICAP_WM_SIZE_COMMAND = "wm size"; private String MINICAP_START_COMMAND = "LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %s@%s/0"; private boolean isRunning = false; public MiniCapUtil(IDevice device) { this.device = device; init(); } /** * 将minicap的二进制和.so文件push到/data/local/tmp文件夹下,启动minicap服务 */ private void init() { String abi = device.getProperty(ABI_COMMAND); String sdk = device.getProperty(SDK_COMMAND); File minicapBinFile = new File(DirStructureUtil.getMinicapBin(), abi + File.separator + MINICAP_BIN); File minicapSoFile = new File(DirStructureUtil.getMinicapSo(), "android-" + sdk + File.separator + abi + File.separator + MINICAP_SO); try { // 将minicap的可执行文件和.so文件一起push到设备中 device.pushFile(minicapBinFile.getAbsolutePath(), REMOTE_PATH + File.separator + MINICAP_BIN); device.pushFile(minicapSoFile.getAbsolutePath(), REMOTE_PATH + File.separator + MINICAP_SO); executeShellCommand(String.format(MINICAP_CHMOD_COMMAND, REMOTE_PATH, MINICAP_BIN)); // 端口转发 device.createForward(PORT, "minicap", DeviceUnixSocketNamespace.ABSTRACT); // 获取设备屏幕的尺寸 String output = executeShellCommand(MINICAP_WM_SIZE_COMMAND); String size = output.split(":")[1].trim(); final String startCommand = String.format(MINICAP_START_COMMAND, size, size); // 启动minicap服务 new Thread(new Runnable() { @Override public void run() { LOG.info("minicap服务器启动"); executeShellCommand(startCommand); } }).start(); } catch (SyncException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (AdbCommandRejectedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private String executeShellCommand(String command) { CollectingOutputReceiver output = new CollectingOutputReceiver(); try { device.executeShellCommand(command, output, 0); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (AdbCommandRejectedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ShellCommandUnresponsiveException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return output.getOutput(); } public void startScreenListener() { isRunning = true; new Thread(new ImageConverter()).start(); new Thread(new ImageBinaryFrameCollector()).start(); } public void stopScreenListener() { isRunning = false; } private synchronized void createImageFromByte(byte[] binaryData) { InputStream in = new ByteArrayInputStream(binaryData); try { BufferedImage bufferedImage = ImageIO.read(in); ImageIO.write(bufferedImage, "jpg", new File("screen.jpg")); } catch (IOException e) { e.printStackTrace(); } } // java合并两个byte数组 private static byte[] byteMerger(byte[] byte_1, byte[] byte_2) { byte[] byte_3 = new byte[byte_1.length + byte_2.length]; System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); return byte_3; } private static byte[] subByteArray(byte[] byte1, int start, int end) { byte[] byte2 = new byte[end - start]; System.arraycopy(byte1, start, byte2, 0, end - start); return byte2; } class ImageBinaryFrameCollector implements Runnable { private Socket socket; @Override public void run() { LOG.debug("图片二进制数据收集器已经开启"); // TODO Auto-generated method stub InputStream stream = null; DataInputStream input = null; try { socket = new Socket("localhost", PORT); stream = socket.getInputStream(); input = new DataInputStream(stream); while (isRunning) { byte[] buffer; int len = 0; while (len == 0) { len = input.available(); } buffer = new byte[len]; input.read(buffer); dataQueue.add(buffer); } } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null && socket.isConnected()) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (stream != null) { try { stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } LOG.debug("图片二进制数据收集器已关闭"); } } class ImageConverter implements Runnable { private int readBannerBytes = 0; private int bannerLength = 2; private int readFrameBytes = 0; private int frameBodyLength = 0; private byte[] frameBody = new byte[0]; @Override public void run() { LOG.debug("图片生成器已经开启"); long start = System.currentTimeMillis(); while (isRunning) { byte[] binaryData = dataQueue.poll(); if (binaryData == null) continue; int len = binaryData.length; for (int cursor = 0; cursor < len;) { int byte10 = binaryData[cursor] & 0xff; if (readBannerBytes < bannerLength) { cursor = parserBanner(cursor, byte10); } 

转载于:https://www.cnblogs.com/xiand/p/6724399.html

minicap_工具使用相关推荐

  1. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  2. Redis 笔记(16)— info 指令和命令行工具(查看内存、状态、客户端连接数、监控服务器、扫描大key、采样服务器、执行批量命令等)

    Info 命令返回关于 Redis 服务器的各种信息和统计数值.通过给定可选的参数 section ,可以让命令只返回某一部分的信息. 1. 显示模块 server : 一般 Redis 服务器信息, ...

  3. Go 学习笔记(81)— Go 性能分析工具 pprof

    Go 语言工具链中的 go pprof 可以帮助开发者快速分析及定位各种性能问题,如 CPU消耗 .内存分配及阻塞分析 .具体作用如下: 性能分析首先需要使用 runtime.pprof 包嵌入到待分 ...

  4. etcd 笔记(02)— etcd 安装(apt 或 yum 安装 、二进制包安装、Docker 安装 etcd、etcd 前端工具etcdkeeper)

    1. 使用 apt 或 yum 安装 etcd 命令如下: sudo apt-get install etcd 或者 sudo yum install etcd 这样安装的缺点是:安装的 etcd 版 ...

  5. 网络安全工具:Nmap

    使用Nmap工具实现对目标主机A.B(IP地址可自行设置)的扫描. 1. 对目标主机A.B进行全扫描. (1)命令:nmap -sT 192.138.58.2 nmap -sT 192.168.58. ...

  6. java日期转化工具类

    package com.rest.ful.utils;import java.text.DateFormat; import java.text.ParseException; import java ...

  7. java数据类型相互转换工具类

    package com.rest.ful.utils;import java.util.ArrayList; import java.util.HashMap; import java.util.Li ...

  8. 论文阅读工具ReadPaper

    对于搞科研的同学们来说,看论文是要经历的第一关,尤其是要读好多篇论文的时候,更是着实令人头大. 这不,最近无意中发现了个在线论文阅读网站:readpaper.com,号称「论文阅读笔记神器,硕博科研学 ...

  9. dbeaver数据库工具

    20220114 ctrl+ '+' 字符放大快捷键 ctrl+ '-' 字符减小快捷键 20220111 C:\Users\hz\AppData\Roaming\DBeaverData\worksp ...

最新文章

  1. C# 集合类(三):Stack
  2. STM32 的 BOOT 概述
  3. CVS代码库管理安装配置
  4. iOS之“支付宝支付”开发流程
  5. 参数化登陆防止SQL注入攻击
  6. Latente Wärme
  7. SharedObject使用:在FluorineFx.net与Flex中使用共享对象维护在线用户列表实例
  8. 电脑怎么开护眼模式_绿色电脑桌面、手机护眼模式真的可以护眼?
  9. 在linux配置端口映射,Linux 配置端口映射
  10. SSM SpringBoot vue高校实训管理系统
  11. 解决python写入文件数据不全的问题
  12. 两个PDF比较标出差异_找出两份Word文档差异,你加班2小时完成,同事只用1分钟搞定...
  13. python 实现人脸采集 训练 与人脸识别
  14. 英语单词学习-词根词缀记忆思维导图
  15. ntp校时器(网络对时服务器)自动化系统技术应用方案
  16. vue项目的简体繁体切换
  17. Intel (Altera) LVDS
  18. 软考高级 真题 2012年下半年 信息系统项目管理师 案例分析
  19. 程序猿如何练习用英语讲好一个笑话?
  20. Java 线程池 ThreadPoolExecutor 八种拒绝策略浅析

热门文章

  1. Redis专题-缓存穿透、缓存雪崩、缓存击穿
  2. 的 while循环_十八、Python图解while循环
  3. 【干货】运维,你是青铜还是王者?
  4. linux怎么删除端口转发,linux使用rinetd快速实现端口转发
  5. 移植 Python 量化交易 TA-Lib 库到函数计算
  6. android view padding,记一次tablayout的tabView偷偷自带padding的问题
  7. python中tushare数据可以导出嘛_Python与交易策略分析tushare/baostock库介绍(附代码)...
  8. 如何取回服务器上的文件网页设计,毕业设计(论文)-基于内容中心网络开发平台的文件分享精选.docx...
  9. java斗破苍穹游戏阵容,斗破苍穹手游竞技场阵容搭配解析 最强阵容你知道吗
  10. springboot 引入jdbc驱动_Spring Boot:企业常用的 Starter以及实现