使用minicap+python实现录屏功能

一: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/,解压后获得项目源码。
使用minicap+python实现录屏功能_第1张图片
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版本进行分类的。
使用minicap+python实现录屏功能_第2张图片
3,获取手机cpu架构,sdk版本
cpu版本:adb shell getprop ro.product.cpu.abi
sdk版本:adb sehll getprop ro.build.version.sdk
如图,可以看到cpu的架构是arm64-v8a,sdk版本是26
使用minicap+python实现录屏功能_第3张图片
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)目录下。
使用minicap+python实现录屏功能_第4张图片
使用minicap+python实现录屏功能_第5张图片
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/ 即可看到手机投屏
使用minicap+python实现录屏功能_第6张图片
七:通过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进行截图效率大大提升了。
使用minicap+python实现录屏功能_第7张图片
使用minicap+python实现录屏功能_第8张图片

八:总结。
1,在启动minicap过程中,出现没有权限的问题,安卓手机在没有root的情况下是没有权限对系统文件进行读写操作的,但是可通过adb命令进行权限赋予。adb shell chmod 777
使用minicap+python实现录屏功能_第9张图片
2,一个简单的基于minicap的录屏功能就基本完成了,后续将基于minicap进行实时录屏操作,并加入minitouch进行点击操作,期待能实现更多更多的功能!

你可能感兴趣的:(python,Airtest,软件测试)