一:minicap简介:
minicap属于STF框架的一个工具,可以高速截图、同步手机屏幕至浏览器等功能,经过试验,截同一个屏幕的一张图,使用adb shell screencap命令进行截图速度为2.9秒,而minicap仅为0.8秒,效率高很多,这在基于图像识别的自动化测试中,起到至关重要的作用,假如你要做一个类似于腾讯的wetest网测平台,那么或许你可以尝试一下结合minicap工具实现屏幕同步功能。
二:minicap获取途径:
1,通过下载源码进行编译,源码地址:https://github.com/openstf/minicap,编译环境为NDK+make,有兴趣的朋友可以参考https://www.cnblogs.com/qiangayz/p/9580389.html进行尝试,但本人觉得windows的环境设置有点麻烦,就选择了其他现成的minicap进行操作。,
2,airtest源码项目进行minicap获取,airtest是网易开发的一款开源项目, 它里面集成了minicap和minitouch等工具,使用这些工具我们只需要到相应的目录去查找即可,下面介绍通过使用airtest上的minicap实现录屏功能。
三:将airtest上的minicap及minicap.so文件push到手机固定的目录当中去。
1,下载Airtest IDE,官方网站http://airtest.netease.com/,解压后获得项目源码。
2,使用pycharm打开项目,进入AirtestIDE_2020-01-21_py3_win64\airtest\core\android\static\stf_libs,如图可以看到,stf_libs目录下有着各种minicap版本的父目录。它是根据手机CPU架构(arm64-v8a、armeabi-v7a,x86,x86_64)和sdk版本进行分类的。
3,获取手机cpu架构,sdk版本
cpu版本:adb shell getprop ro.product.cpu.abi
sdk版本:adb sehll getprop ro.build.version.sdk
如图,可以看到cpu的架构是arm64-v8a,sdk版本是26
4,在airtest上找到相应的minicap可执行文件和minicap.so文件。minicap在(AirtestIDE_2020-01-21_py3_win64\airtest\core\android\static\stf_libs\arm64-v8a)目录下,而minicap.so在(airtest\core\android\static\stf_libs\minicap-shared\aosp\libs\android-27\arm64-v8a)目录下。
5,push minicap,minicap.so到手机中
adb push minicap /data/local/tmp
adb push minicap.so /data/local/tmp
6,对它们进行赋权
adb shell chmod 777 /data/local/tmp/minicap
adb shell chmod 777 /data/local/tmp/minicap.so
四:获取手机分辨率后测试并且启动minicap服务
1,屏幕分辨率:adb shell wm size
2,测试是否可运行:adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x2160@1080x2160/0 -t
3,启动minicap服务:adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x2160@1080x2160/0
-P 后面的参数格式:{RealWidth}x{RealHeight}@{VirtualWidth}x{VirtualHeight}/{Orientation}
Orientation可以理解为手机的旋转角度,可选参数为 0 | 90 | 180 | 270
五. 端口映射,把minicap映射到1717端口,也可以是其他端口。**
1,adb forward tcp:1717 localabstract:minicap
六,进行测试
1,minicap源码里面有example/文件夹下就是一个minicap提供的测试服务,由node.js搭建的服务端
所以启动需要本地有node.js环境,具体实现大概为开启socket连接,监听1717端口,然后再开一个websocket把监听到数据发给前端页面启动方式为:node app.js PORD=9002
pord为指定前端页面端口,
2,打开浏览器访问:http://localhost:9002/ 即可看到手机投屏
七:通过python实现录屏:
1,执行下面代码,可以看到在当前目录下会有手机截屏图片生成。
# coding: utf8
import socket
import sys
import time
import struct
from collections import OrderedDict
class Banner:
def __init__(self):
self.__banner = OrderedDict(
[('version', 0),
('length', 0),
('pid', 0),
('realWidth', 0),
('realHeight', 0),
('virtualWidth', 0),
('virtualHeight', 0),
('orientation', 0),
('quirks', 0)
])
def __setitem__(self, key, value):
self.__banner[key] = value
def __getitem__(self, key):
return self.__banner[key]
def keys(self):
return self.__banner.keys()
def __str__(self):
return str(self.__banner)
class Minicap(object):
def __init__(self, host, port, banner):
self.buffer_size = 4096
self.host = host
self.port = port
self.banner = banner
def connect(self):
try:
self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except :
print('2')
sys.exit(1)
self.__socket.connect((self.host, self.port))
def on_image_transfered(self, data):
file_name = str(time.time()) + '.jpg'
with open(file_name, 'wb') as f:
for b in data:
f.write(b)
def consume(self):
readBannerBytes = 0
bannerLength = 24
readFrameBytes = 0
frameBodyLength = 0
data = []
while True:
try:
chunk = self.__socket.recv(self.buffer_size)
except:
print('11')
sys.exit(1)
cursor = 0
buf_len = len(chunk)
while cursor < buf_len:
if readBannerBytes < bannerLength:
map(lambda i, val: self.banner.__setitem__(self.banner.keys()[i], val),
[i for i in range(len(self.banner.keys()))], struct.unpack("<2b5ibB", chunk))
cursor = buf_len
readBannerBytes = bannerLength
print(self.banner)
elif readFrameBytes < 4:
frameBodyLength += (struct.unpack('B', chunk[cursor])[0] << (readFrameBytes * 8)) >> 0
cursor += 1
readFrameBytes += 1
else:
print("frame length:{0} buf_len:{1} cursor:{2}".format(frameBodyLength, buf_len, cursor))
# pic end
if buf_len - cursor >= frameBodyLength:
data.extend(chunk[cursor:cursor + frameBodyLength])
self.on_image_transfered(data)
cursor += frameBodyLength
frameBodyLength = readFrameBytes = 0
data = []
else:
data.extend(chunk[cursor:buf_len])
frameBodyLength -= buf_len - cursor
readFrameBytes += buf_len - cursor
cursor = buf_len
if '__main__' == __name__:
print('开始')
mc = Minicap('localhost', 1717, Banner())
mc.connect()
mc.consume()
2,将图片帧转换为视频,执行下面代码:
import cv2
import glob
import os
from datetime import datetime
def frames_to_video(fps, save_path, frames_path, max_index):
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
videoWriter = cv2.VideoWriter(save_path, fourcc, fps, (960, 544))
imgs = glob.glob(frames_path + "/*.jpg")
frames_num = len(imgs)
for i in range(max_index):
if os.path.isfile("%s/%d.jpg" % (frames_path, i)):
frame = cv2.imread("%s/%d.jpg" % (frames_path, i))
videoWriter.write(frame)
videoWriter.release()
return
if __name__ == '__main__':
t1 = datetime.now()
frames_to_video(22, "result.mp4", 'face_recog_frames', 1000)
t2 = datetime.now()
print("耗时 :", (t2 - t1))
print("完成了 !!!")
3,结果,可以看到当前项目目录有一个result.mp4生成,耗时也是相当的短,可以看到通过minicap进行截图效率大大提升了。
八:总结。
1,在启动minicap过程中,出现没有权限的问题,安卓手机在没有root的情况下是没有权限对系统文件进行读写操作的,但是可通过adb命令进行权限赋予。adb shell chmod 777
2,一个简单的基于minicap的录屏功能就基本完成了,后续将基于minicap进行实时录屏操作,并加入minitouch进行点击操作,期待能实现更多更多的功能!