Add: Faster screenshot methods ADB_nc and aScreenCap_nc

- Allow config names when creating Device instances
- Add "Ultra Fast" level in performance test
This commit is contained in:
LmeSzinc 2022-03-23 21:20:01 +08:00
parent 5951d05f90
commit 5ba2f5c5ac
16 changed files with 254 additions and 60 deletions

View File

@ -1424,8 +1424,10 @@
"Benchmark": { "Benchmark": {
"Benchmark": { "Benchmark": {
"AdbScreenshot": true, "AdbScreenshot": true,
"AdbncScreenshot": true,
"Uiautomator2Screenshot": true, "Uiautomator2Screenshot": true,
"AscreencapScreenshot": true, "AscreencapScreenshot": true,
"AscreencapncScreenshot": true,
"AdbClick": true, "AdbClick": true,
"Uiautomator2Click": true, "Uiautomator2Click": true,
"MinitouchClick": true, "MinitouchClick": true,

View File

@ -24,8 +24,10 @@
"value": "ADB", "value": "ADB",
"option": [ "option": [
"ADB", "ADB",
"ADB_nc",
"uiautomator2", "uiautomator2",
"aScreenCap" "aScreenCap",
"aScreenCap_nc"
] ]
}, },
"ControlMethod": { "ControlMethod": {
@ -6386,6 +6388,10 @@
"type": "checkbox", "type": "checkbox",
"value": true "value": true
}, },
"AdbncScreenshot": {
"type": "checkbox",
"value": true
},
"Uiautomator2Screenshot": { "Uiautomator2Screenshot": {
"type": "checkbox", "type": "checkbox",
"value": true "value": true
@ -6394,6 +6400,10 @@
"type": "checkbox", "type": "checkbox",
"value": true "value": true
}, },
"AscreencapncScreenshot": {
"type": "checkbox",
"value": true
},
"AdbClick": { "AdbClick": {
"type": "checkbox", "type": "checkbox",
"value": true "value": true

View File

@ -19,7 +19,7 @@ Emulator:
option: [cn, en, jp, tw] option: [cn, en, jp, tw]
ScreenshotMethod: ScreenshotMethod:
value: ADB value: ADB
option: [ADB, uiautomator2, aScreenCap] option: [ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc]
ControlMethod: ControlMethod:
value: minitouch value: minitouch
option: [ADB, uiautomator2, minitouch, Hermit] option: [ADB, uiautomator2, minitouch, Hermit]
@ -494,8 +494,10 @@ OpsiDaemon:
RepairShip: true RepairShip: true
Benchmark: Benchmark:
AdbScreenshot: true AdbScreenshot: true
AdbncScreenshot: true
Uiautomator2Screenshot: true Uiautomator2Screenshot: true
AscreencapScreenshot: true AscreencapScreenshot: true
AscreencapncScreenshot: true
AdbClick: true AdbClick: true
Uiautomator2Click: true Uiautomator2Click: true
MinitouchClick: true MinitouchClick: true

View File

@ -21,7 +21,7 @@ class GeneratedConfig:
Emulator_Serial = '127.0.0.1:5555' Emulator_Serial = '127.0.0.1:5555'
Emulator_PackageName = 'com.bilibili.azurlane' Emulator_PackageName = 'com.bilibili.azurlane'
Emulator_Server = 'cn' # cn, en, jp, tw Emulator_Server = 'cn' # cn, en, jp, tw
Emulator_ScreenshotMethod = 'ADB' # ADB, uiautomator2, aScreenCap Emulator_ScreenshotMethod = 'ADB' # ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc
Emulator_ControlMethod = 'minitouch' # ADB, uiautomator2, minitouch, Hermit Emulator_ControlMethod = 'minitouch' # ADB, uiautomator2, minitouch, Hermit
Emulator_ScreenshotDedithering = False Emulator_ScreenshotDedithering = False
@ -359,8 +359,10 @@ class GeneratedConfig:
# Group `Benchmark` # Group `Benchmark`
Benchmark_AdbScreenshot = True Benchmark_AdbScreenshot = True
Benchmark_AdbncScreenshot = True
Benchmark_Uiautomator2Screenshot = True Benchmark_Uiautomator2Screenshot = True
Benchmark_AscreencapScreenshot = True Benchmark_AscreencapScreenshot = True
Benchmark_AscreencapncScreenshot = True
Benchmark_AdbClick = True Benchmark_AdbClick = True
Benchmark_Uiautomator2Click = True Benchmark_Uiautomator2Click = True
Benchmark_MinitouchClick = True Benchmark_MinitouchClick = True

View File

@ -61,6 +61,7 @@ class ManualConfig:
module.device module.device
""" """
FORWARD_PORT_RANGE = (20000, 21000) FORWARD_PORT_RANGE = (20000, 21000)
REVERSE_SERVER_PORT = 7903
ASCREENCAP_FILEPATH_LOCAL = './bin/ascreencap' ASCREENCAP_FILEPATH_LOCAL = './bin/ascreencap'
ASCREENCAP_FILEPATH_REMOTE = '/data/local/tmp/ascreencap' ASCREENCAP_FILEPATH_REMOTE = '/data/local/tmp/ascreencap'
MINITOUCH_FILEPATH_REMOTE = '/data/local/tmp/minitouch' MINITOUCH_FILEPATH_REMOTE = '/data/local/tmp/minitouch'

View File

@ -292,17 +292,19 @@
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
"name": "Screenshot Method", "name": "Screenshot Method",
"help": "Speed: aScreenCap > uiautomator2 ~= ADB\nADB is recommended", "help": "Speed: aScreenCap_nc > ADB_nc >>> aScreenCap > uiautomator2 ~= ADB\nRun Tools - Performance Test to find the fastest method",
"ADB": "ADB ", "ADB": "ADB ",
"uiautomator2": "uiautomator2 ", "ADB_nc": "ADB_nc",
"aScreenCap": "aScreenCap " "uiautomator2": "uiautomator2",
"aScreenCap": "aScreenCap",
"aScreenCap_nc": "aScreenCap_nc"
}, },
"ControlMethod": { "ControlMethod": {
"name": "Control Method", "name": "Control Method",
"help": "Speed: minitouch > Hermit >>> uiautomator2 ~= ADB\nminitouch is recommended, hermit is not recommended unless on vmos", "help": "Speed: minitouch > Hermit >>> uiautomator2 ~= ADB\nminitouch is recommended, hermit is not recommended unless on vmos",
"ADB": "ADB ", "ADB": "ADB",
"uiautomator2": "uiautomator2 ", "uiautomator2": "uiautomator2",
"minitouch": "minitouch ", "minitouch": "minitouch",
"Hermit": "Hermit" "Hermit": "Hermit"
}, },
"ScreenshotDedithering": { "ScreenshotDedithering": {
@ -1846,6 +1848,10 @@
"name": "Test ADB Screenshot", "name": "Test ADB Screenshot",
"help": "" "help": ""
}, },
"AdbncScreenshot": {
"name": "Test ADB_nc Screenshot",
"help": ""
},
"Uiautomator2Screenshot": { "Uiautomator2Screenshot": {
"name": "Test Uiautomator2 Screenshot", "name": "Test Uiautomator2 Screenshot",
"help": "" "help": ""
@ -1854,6 +1860,10 @@
"name": "Test aScreenCap Screenshot", "name": "Test aScreenCap Screenshot",
"help": "" "help": ""
}, },
"AscreencapncScreenshot": {
"name": "Test aScreenCap_nc Screenshot",
"help": ""
},
"AdbClick": { "AdbClick": {
"name": "Test ADB Click", "name": "Test ADB Click",
"help": "" "help": ""

View File

@ -294,8 +294,10 @@
"name": "Emulator.ScreenshotMethod.name", "name": "Emulator.ScreenshotMethod.name",
"help": "Emulator.ScreenshotMethod.help", "help": "Emulator.ScreenshotMethod.help",
"ADB": "ADB", "ADB": "ADB",
"ADB_nc": "ADB_nc",
"uiautomator2": "uiautomator2", "uiautomator2": "uiautomator2",
"aScreenCap": "aScreenCap" "aScreenCap": "aScreenCap",
"aScreenCap_nc": "aScreenCap_nc"
}, },
"ControlMethod": { "ControlMethod": {
"name": "Emulator.ControlMethod.name", "name": "Emulator.ControlMethod.name",
@ -1846,6 +1848,10 @@
"name": "Benchmark.AdbScreenshot.name", "name": "Benchmark.AdbScreenshot.name",
"help": "Benchmark.AdbScreenshot.help" "help": "Benchmark.AdbScreenshot.help"
}, },
"AdbncScreenshot": {
"name": "Benchmark.AdbncScreenshot.name",
"help": "Benchmark.AdbncScreenshot.help"
},
"Uiautomator2Screenshot": { "Uiautomator2Screenshot": {
"name": "Benchmark.Uiautomator2Screenshot.name", "name": "Benchmark.Uiautomator2Screenshot.name",
"help": "Benchmark.Uiautomator2Screenshot.help" "help": "Benchmark.Uiautomator2Screenshot.help"
@ -1854,6 +1860,10 @@
"name": "Benchmark.AscreencapScreenshot.name", "name": "Benchmark.AscreencapScreenshot.name",
"help": "Benchmark.AscreencapScreenshot.help" "help": "Benchmark.AscreencapScreenshot.help"
}, },
"AscreencapncScreenshot": {
"name": "Benchmark.AscreencapncScreenshot.name",
"help": "Benchmark.AscreencapncScreenshot.help"
},
"AdbClick": { "AdbClick": {
"name": "Benchmark.AdbClick.name", "name": "Benchmark.AdbClick.name",
"help": "Benchmark.AdbClick.help" "help": "Benchmark.AdbClick.help"

View File

@ -292,17 +292,19 @@
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
"name": "模拟器截图方案", "name": "模拟器截图方案",
"help": "速度: aScreenCap > uiautomator2 ~= ADB\n建议选 ADB", "help": "速度: aScreenCap_nc > ADB_nc >>> aScreenCap > uiautomator2 ~= ADB\n运行 工具 - 性能测试 以寻找最快的方案",
"ADB": "ADB ", "ADB": "ADB",
"uiautomator2": "uiautomator2 ", "ADB_nc": "ADB_nc",
"aScreenCap": "aScreenCap " "uiautomator2": "uiautomator2",
"aScreenCap": "aScreenCap",
"aScreenCap_nc": "aScreenCap_nc"
}, },
"ControlMethod": { "ControlMethod": {
"name": "模拟器控制方案", "name": "模拟器控制方案",
"help": "速度: minitouch > Hermit >>> uiautomator2 ~= ADB\n建议选minitouch不建议选Hermit除非在vmos下", "help": "速度: minitouch > Hermit >>> uiautomator2 ~= ADB\n建议选minitouch不建议选Hermit除非在vmos下",
"ADB": "ADB ", "ADB": "ADB",
"uiautomator2": "uiautomator2 ", "uiautomator2": "uiautomator2",
"minitouch": "minitouch ", "minitouch": "minitouch",
"Hermit": "Hermit" "Hermit": "Hermit"
}, },
"ScreenshotDedithering": { "ScreenshotDedithering": {
@ -1846,6 +1848,10 @@
"name": "测试 ADB 截图", "name": "测试 ADB 截图",
"help": "" "help": ""
}, },
"AdbncScreenshot": {
"name": "测试 ADB_nc 截图",
"help": ""
},
"Uiautomator2Screenshot": { "Uiautomator2Screenshot": {
"name": "测试 uiautomator2 截图", "name": "测试 uiautomator2 截图",
"help": "" "help": ""
@ -1854,6 +1860,10 @@
"name": "测试 aScreenCap 截图", "name": "测试 aScreenCap 截图",
"help": "" "help": ""
}, },
"AscreencapncScreenshot": {
"name": "测试 aScreenCap_nc 截图",
"help": ""
},
"AdbClick": { "AdbClick": {
"name": "测试 ADB 点击", "name": "测试 ADB 点击",
"help": "" "help": ""

View File

@ -292,10 +292,12 @@
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
"name": "模擬器截圖方案", "name": "模擬器截圖方案",
"help": "速度: aScreenCap > uiautomator2 ~= ADB\n建議選 ADB", "help": "速度: aScreenCap_nc > ADB_nc >>> aScreenCap > uiautomator2 ~= ADB\n運行 工具 - 性能測試 以尋找最快的方案",
"ADB": "ADB", "ADB": "ADB",
"ADB_nc": "ADB_nc",
"uiautomator2": "uiautomator2", "uiautomator2": "uiautomator2",
"aScreenCap": "aScreenCap" "aScreenCap": "aScreenCap",
"aScreenCap_nc": "aScreenCap_nc"
}, },
"ControlMethod": { "ControlMethod": {
"name": "模擬器控制方案", "name": "模擬器控制方案",
@ -1846,6 +1848,10 @@
"name": "測試 ADB 截圖", "name": "測試 ADB 截圖",
"help": "" "help": ""
}, },
"AdbncScreenshot": {
"name": "測試 ADB 截圖",
"help": "Benchmark.AdbncScreenshot.help"
},
"Uiautomator2Screenshot": { "Uiautomator2Screenshot": {
"name": "測試 uiautomator2 截圖", "name": "測試 uiautomator2 截圖",
"help": "" "help": ""
@ -1854,6 +1860,10 @@
"name": "測試 aScreenCap 截圖", "name": "測試 aScreenCap 截圖",
"help": "" "help": ""
}, },
"AscreencapncScreenshot": {
"name": "測試 aScreenCap_nc 截圖",
"help": ""
},
"AdbClick": { "AdbClick": {
"name": "測試 ADB 點擊", "name": "測試 ADB 點擊",
"help": "" "help": ""

View File

@ -68,6 +68,8 @@ class Benchmark(DaemonBase, CampaignUI):
if not isinstance(cost, (float, int)): if not isinstance(cost, (float, int)):
return Text(cost, style="bold bright_red") return Text(cost, style="bold bright_red")
if cost < 0.12:
return Text('Ultra Fast', style="bold bright_green")
if cost < 0.25: if cost < 0.25:
return Text('Very Fast', style="bright_green") return Text('Very Fast', style="bright_green")
if cost < 0.45: if cost < 0.45:
@ -132,10 +134,14 @@ class Benchmark(DaemonBase, CampaignUI):
data = [] data = []
if self.config.Benchmark_AdbScreenshot: if self.config.Benchmark_AdbScreenshot:
data.append(['ADB', self.benchmark_test(self.device.screenshot_adb)]) data.append(['ADB', self.benchmark_test(self.device.screenshot_adb)])
if self.config.Benchmark_AdbncScreenshot:
data.append(['ADB_nc', self.benchmark_test(self.device.screenshot_adb_nc)])
if self.config.Benchmark_Uiautomator2Screenshot: if self.config.Benchmark_Uiautomator2Screenshot:
data.append(['uiautomator2', self.benchmark_test(self.device.screenshot_uiautomator2)]) data.append(['uiautomator2', self.benchmark_test(self.device.screenshot_uiautomator2)])
if self.config.Benchmark_AscreencapScreenshot: if self.config.Benchmark_AscreencapScreenshot:
data.append(['aScreenCap', self.benchmark_test(self.device.screenshot_ascreencap)]) data.append(['aScreenCap', self.benchmark_test(self.device.screenshot_ascreencap)])
if self.config.Benchmark_AscreencapncScreenshot:
data.append(['aScreenCap_nc', self.benchmark_test(self.device.screenshot_ascreencap_nc)])
screenshot = data screenshot = data
data = [] data = []

View File

@ -1,23 +1,27 @@
import logging import logging
import os import os
import re import re
import socket
import subprocess import subprocess
import time import time
import adbutils import adbutils
import uiautomator2 as u2 import uiautomator2 as u2
from adbutils import AdbClient, AdbDevice, ForwardItem from adbutils import AdbClient, AdbDevice, ForwardItem, ReverseItem, AdbTimeout
from deploy.utils import poor_yaml_read, DEPLOY_CONFIG from deploy.utils import poor_yaml_read, DEPLOY_CONFIG
from module.base.decorator import cached_property from module.base.decorator import cached_property
from module.base.utils import ensure_time from module.base.utils import ensure_time
from module.config.config import AzurLaneConfig from module.config.config import AzurLaneConfig
from module.device.method.utils import possible_reasons, random_port, del_cached_property from module.device.method.utils import recv_all, possible_reasons, random_port, del_cached_property
from module.exception import RequestHumanTakeover from module.exception import RequestHumanTakeover
from module.logger import logger from module.logger import logger
class Connection: class Connection:
config: AzurLaneConfig
serial: str
adb_binary_list = [ adb_binary_list = [
'./bin/adb/adb.exe', './bin/adb/adb.exe',
'./toolkit/Lib/site-packages/adbutils/binaries/adb.exe', './toolkit/Lib/site-packages/adbutils/binaries/adb.exe',
@ -27,10 +31,14 @@ class Connection:
def __init__(self, config): def __init__(self, config):
""" """
Args: Args:
config (AzurLaneConfig): config (AzurLaneConfig, str): Name of the user config under ./config
""" """
logger.hr('Device') logger.hr('Device')
self.config = config if isinstance(config, str):
self.config = AzurLaneConfig(config, task=None)
else:
self.config = config
self.serial = str(self.config.Emulator_Serial) self.serial = str(self.config.Emulator_Serial)
if "bluestacks4-hyperv" in self.serial: if "bluestacks4-hyperv" in self.serial:
self.serial = self.find_bluestacks4_hyperv(self.serial) self.serial = self.find_bluestacks4_hyperv(self.serial)
@ -192,6 +200,50 @@ class Connection:
result = self.adb.shell(cmd, timeout=10, **kwargs) result = self.adb.shell(cmd, timeout=10, **kwargs)
return result return result
@cached_property
def reverse_server(self):
"""
Setup a server on Alas, access it from emulator.
This will bypass adb shell and be faster.
"""
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._server_port = self.adb_reverse(f'tcp:{self.config.REVERSE_SERVER_PORT}')
server.bind(('127.0.0.1', self._server_port))
server.listen(5)
logger.info(f'Reverse server listening on {self._server_port}')
return server
def adb_shell_nc(self, cmd, timeout=5, chunk_size=262144):
"""
Args:
cmd (list):
timeout (int):
chunk_size (int): Default to 262144
Returns:
bytes:
"""
# <command> | nc 127.0.0.1 {port}
cmd += ['|', 'nc', '127.0.0.1', self.config.REVERSE_SERVER_PORT]
# Server start listening
server = self.reverse_server
server.settimeout(timeout)
# Client send data, waiting for server accept
_ = self.adb_shell(cmd, stream=True)
try:
# Server accept connection
conn, conn_port = server.accept()
except socket.timeout:
raise AdbTimeout('reverse server accept timeout')
# Server receive data
data = recv_all(conn, chunk_size=chunk_size)
# Server close connection
conn.close()
return data
def adb_exec_out(self, cmd, serial=None): def adb_exec_out(self, cmd, serial=None):
cmd.insert(0, 'exec-out') cmd.insert(0, 'exec-out')
return self.adb_command(cmd, serial) return self.adb_command(cmd, serial)
@ -234,6 +286,27 @@ class Connection:
self.adb.forward(forward.local, forward.remote) self.adb.forward(forward.local, forward.remote)
return port return port
def adb_reverse(self, remote):
port = 0
for reverse in self.adb.reverse_list():
if reverse.remote == remote and reverse.local.startswith('tcp:'):
if not port:
logger.info(f'Reuse reverse: {reverse}')
port = int(reverse.local[4:])
else:
logger.info(f'Remove redundant forward: {reverse}')
self.adb_forward_remove(reverse.local)
if port:
return port
else:
# Create new reverse
port = random_port(self.config.FORWARD_PORT_RANGE)
reverse = ReverseItem(f'tcp:{port}', remote)
logger.info(f'Create reverse: {reverse}')
self.adb.reverse(reverse.local, reverse.remote)
return port
def adb_forward_remove(self, local): def adb_forward_remove(self, local):
""" """
Equivalent to `adb -s <serial> forward --remove <local>` Equivalent to `adb -s <serial> forward --remove <local>`
@ -248,6 +321,20 @@ class Connection:
c.send_command(list_cmd) c.send_command(list_cmd)
c.check_okay() c.check_okay()
def adb_reverse_remove(self, local):
"""
Equivalent to `adb -s <serial> reverse --remove <local>`
Args:
local (str): Such as 'tcp:2437'
"""
with self.adb_client._connect() as c:
c.send_command(f"host:transport:{self.serial}")
c.check_okay()
list_cmd = f"reverse:killforward:{local}"
c.send_command(list_cmd)
c.check_okay()
def adb_push(self, local, remote): def adb_push(self, local, remote):
""" """
Args: Args:
@ -297,6 +384,7 @@ class Connection:
del_cached_property(self, 'hermit_session') del_cached_property(self, 'hermit_session')
del_cached_property(self, 'minitouch_builder') del_cached_property(self, 'minitouch_builder')
del_cached_property(self, 'reverse_server')
def install_uiautomator2(self): def install_uiautomator2(self):
""" """

View File

@ -1,4 +1,5 @@
import re import re
from functools import wraps
import cv2 import cv2
import numpy as np import numpy as np
@ -6,12 +7,13 @@ from adbutils.errors import AdbError
from lxml import etree from lxml import etree
from module.device.connection import Connection from module.device.connection import Connection
from module.device.method.utils import possible_reasons, handle_adb_error, RETRY_TRIES, RETRY_DELAY from module.device.method.utils import recv_all, possible_reasons, handle_adb_error, RETRY_TRIES, RETRY_DELAY
from module.exception import ScriptError, RequestHumanTakeover from module.exception import ScriptError, RequestHumanTakeover
from module.logger import logger from module.logger import logger
def retry(func): def retry(func):
@wraps(func)
def retry_wrapper(self, *args, **kwargs): def retry_wrapper(self, *args, **kwargs):
""" """
Args: Args:
@ -101,15 +103,28 @@ class Adb(Connection):
def screenshot_adb(self): def screenshot_adb(self):
stream = self.adb_shell(['screencap', '-p'], stream=True) stream = self.adb_shell(['screencap', '-p'], stream=True)
content = b"" content = recv_all(stream)
while True:
chunk = stream.read(4096)
if not chunk:
break
content += chunk
return self.__process_screenshot(content) return self.__process_screenshot(content)
@retry
def screenshot_adb_nc(self):
data = self.adb_shell_nc(['screencap'])
if len(data) < 100:
logger.warning(f'Unexpected screenshot: {data}')
# Load data
header = np.frombuffer(data[0:12], dtype=np.uint32)
channel = 4 # screencap sends an RGBA image
width, height, _ = header # Usually to be 1280, 720, 1
image = np.frombuffer(data, dtype=np.uint8)
shape = image.shape[0]
image = image[shape - width * height * channel:].reshape(height, width, channel)
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
return image
@retry @retry
def click_adb(self, x, y): def click_adb(self, x, y):
self.adb_shell(['input', 'tap', x, y]) self.adb_shell(['input', 'tap', x, y])

View File

@ -5,7 +5,7 @@ from adbutils.errors import AdbError
from module.base.utils import * from module.base.utils import *
from module.device.connection import Connection from module.device.connection import Connection
from module.device.method.utils import handle_adb_error, RETRY_TRIES, RETRY_DELAY from module.device.method.utils import recv_all, handle_adb_error, RETRY_TRIES, RETRY_DELAY
from module.exception import ScriptError, RequestHumanTakeover from module.exception import ScriptError, RequestHumanTakeover
from module.logger import logger from module.logger import logger
@ -106,28 +106,17 @@ class AScreenCap(Connection):
raise AscreencapError(text) raise AscreencapError(text)
return byte_array[self.__bytepointer:] return byte_array[self.__bytepointer:]
def _ascreencap_execute(self):
stream = self.adb_shell([self.config.ASCREENCAP_FILEPATH_REMOTE, '--pack', '2', '--stdout'], stream=True)
content = b""
while True:
chunk = stream.read(4096)
if not chunk:
break
content += chunk
return self.__process_screenshot(content)
def __load_screenshot(self, screenshot, method): def __load_screenshot(self, screenshot, method):
if method == 0: if method == 0:
pass return screenshot
elif method == 1: elif method == 1:
screenshot = screenshot.replace(b'\r\n', b'\n') return screenshot.replace(b'\r\n', b'\n')
elif method == 2: elif method == 2:
screenshot = screenshot.replace(b'\r\r\n', b'\n') return screenshot.replace(b'\r\r\n', b'\n')
else: else:
raise ScriptError(f'Unknown method to load screenshots: {method}') raise ScriptError(f'Unknown method to load screenshots: {method}')
def __uncompress(self, screenshot):
raw_compressed_data = self._ascreencap_reposition_byte_pointer(screenshot) raw_compressed_data = self._ascreencap_reposition_byte_pointer(screenshot)
# See headers in: # See headers in:
@ -158,6 +147,7 @@ class AScreenCap(Connection):
for method in self.__screenshot_method_fixed: for method in self.__screenshot_method_fixed:
try: try:
result = self.__load_screenshot(screenshot, method=method) result = self.__load_screenshot(screenshot, method=method)
result = self.__uncompress(result)
self.__screenshot_method_fixed = [method] + self.__screenshot_method self.__screenshot_method_fixed = [method] + self.__screenshot_method
return result return result
except lz4.block.LZ4BlockError: except lz4.block.LZ4BlockError:
@ -173,11 +163,12 @@ class AScreenCap(Connection):
def screenshot_ascreencap(self): def screenshot_ascreencap(self):
stream = self.adb_shell([self.config.ASCREENCAP_FILEPATH_REMOTE, '--pack', '2', '--stdout'], stream=True) stream = self.adb_shell([self.config.ASCREENCAP_FILEPATH_REMOTE, '--pack', '2', '--stdout'], stream=True)
content = b"" content = recv_all(stream)
while True:
chunk = stream.read(4096)
if not chunk:
break
content += chunk
return self.__process_screenshot(content) return self.__process_screenshot(content)
@retry
def screenshot_ascreencap_nc(self):
data = self.adb_shell_nc([self.config.ASCREENCAP_FILEPATH_REMOTE, '--pack', '2', '--stdout'])
return self.__uncompress(data)

View File

@ -7,7 +7,7 @@ from module.base.decorator import cached_property
from module.base.timer import Timer from module.base.timer import Timer
from module.base.utils import random_rectangle_point, point2str from module.base.utils import random_rectangle_point, point2str
from module.device.method.adb import Adb from module.device.method.adb import Adb
from module.device.method.utils import HierarchyButton, handle_adb_error, del_cached_property, RETRY_TRIES, RETRY_DELAY from module.device.method.utils import HierarchyButton, handle_adb_error, RETRY_TRIES, RETRY_DELAY
from module.exception import RequestHumanTakeover from module.exception import RequestHumanTakeover
from module.logger import logger from module.logger import logger

View File

@ -4,6 +4,7 @@ import socket
import uiautomator2 as u2 import uiautomator2 as u2
from lxml import etree from lxml import etree
from adbutils import _AdbStreamConnection, AdbTimeout
from module.base.decorator import cached_property from module.base.decorator import cached_property
from module.logger import logger from module.logger import logger
@ -34,6 +35,34 @@ def random_port(port_range):
return new_port return new_port
def recv_all(stream, chunk_size=4096) -> bytes:
"""
Args:
stream:
chunk_size:
Returns:
bytes:
Raises:
AdbTimeout
"""
if isinstance(stream, _AdbStreamConnection):
stream = stream.conn
try:
fragments = []
while 1:
chunk = stream.recv(chunk_size)
if chunk:
fragments.append(chunk)
else:
break
return b''.join(fragments)
except socket.timeout:
raise AdbTimeout('adb read timeout')
def possible_reasons(*args): def possible_reasons(*args):
""" """
Show possible reasons Show possible reasons

View File

@ -26,6 +26,16 @@ class Screenshot(Adb, WSA, Uiautomator2, AScreenCap):
_last_save_time = {} _last_save_time = {}
image: np.ndarray image: np.ndarray
@cached_property
def screenshot_methods(self):
return {
'ADB': self.screenshot_adb,
'ADB_nc': self.screenshot_adb_nc,
'uiautomator2': self.screenshot_uiautomator2,
'aScreenCap': self.screenshot_ascreencap,
'aScreenCap_nc': self.screenshot_ascreencap_nc,
}
@timer @timer
def screenshot(self): def screenshot(self):
""" """
@ -36,13 +46,11 @@ class Screenshot(Adb, WSA, Uiautomator2, AScreenCap):
self._screenshot_interval.reset() self._screenshot_interval.reset()
for _ in range(2): for _ in range(2):
method = self.config.Emulator_ScreenshotMethod method = self.screenshot_methods.get(
if method == 'aScreenCap': self.config.Emulator_ScreenshotMethod,
self.image = self.screenshot_ascreencap() self.screenshot_adb
elif method == 'uiautomator2': )
self.image = self.screenshot_uiautomator2() self.image = method()
else:
self.image = self.screenshot_adb()
if self.config.Emulator_ScreenshotDedithering: if self.config.Emulator_ScreenshotDedithering:
# This will take 40-60ms # This will take 40-60ms