def setup_server(self): """ Setup maxtouch server and adb forward Returns: server process """ if self.server_proc: self.server_proc.kill() self.server_proc = None self.localport, deviceport = self.adb.setup_forward( "localabstract:maxpresent_{}".format) deviceport = deviceport[len("localabstract:"):] p = self.adb.start_shell( "app_process -Djava.class.path={0} /data/local/tmp com.netease.maxpresent.MaxPresent socket {1}" .format(self.path_in_android, deviceport)) nbsp = NonBlockingStreamReader(p.stdout, name="airtouch_server", auto_kill=True) line = nbsp.readline(timeout=5.0) if line is None: kill_proc(p) raise RuntimeError("airtouch setup timeout") if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately kill_proc(p) raise RuntimeError("airtouch server quit immediately") self.server_proc = p return p
def setup_server(self): """ Setup minitouch server and adb forward Returns: server process """ if self.server_proc: self.server_proc.kill() self.server_proc = None self.localport, deviceport = self.adb.setup_forward( "localabstract:minitouch_{}".format) deviceport = deviceport[len("localabstract:"):] if self.input_event: p = self.adb.start_shell( "/data/local/tmp/minitouch -n '{0}' -d '{1}' 2>&1".format( deviceport, self.input_event)) else: p = self.adb.start_shell( "/data/local/tmp/minitouch -n '{0}' 2>&1".format(deviceport)) nbsp = NonBlockingStreamReader(p.stdout, name="minitouch_server", auto_kill=True) while True: line = nbsp.readline(timeout=3.0) if line is None: kill_proc(p) self.adb.close_proc_pipe(p) raise RuntimeError("minitouch setup timeout") line = line.decode(get_std_encoding(sys.stdout)) # 识别出setup成功的log,并匹配出max_x, max_y m = re.search( "Type \w touch device .+ \((\d+)x(\d+) with \d+ contacts\) detected on .+ \(.+\)", line) if m: self.max_x, self.max_y = int(m.group(1)), int(m.group(2)) break else: self.max_x = 32768 self.max_y = 32768 # nbsp.kill() # 保留,不杀了,后面还会继续读取并pirnt if p.poll() is not None: # server setup error, may be already setup by others # subprocess exit immediately kill_proc(p) raise RuntimeError("minitouch server quit immediately") self.server_proc = p reg_cleanup(kill_proc, self.server_proc) return p
def start_recording(self, max_time=1800, bit_rate=None): """ Start screen recording Args: max_time: maximum screen recording time, default is 1800 bit_rate: bit rate value, 450000-8000000, default is None(6000000) Raises: RuntimeError: if any error occurs while setup the recording Returns: None if recording did not start, otherwise True """ if getattr(self, "recording_proc", None): raise AirtestError("recording_proc has already started") pkg_path = self.adb.path_app(YOSEMITE_PACKAGE) max_time_param = "-Dduration=%d" % max_time if max_time else "" # The higher the bitrate, the clearer the video, the default value is 6000000 bit_rate_param = "-Dbitrate=%d" % bit_rate if bit_rate else "" # The video size is square, compatible with horizontal and vertical screens p = self.adb.start_shell( 'CLASSPATH=%s exec app_process %s %s /system/bin %s.Recorder --start-record' % (pkg_path, max_time_param, bit_rate_param, YOSEMITE_PACKAGE)) nbsp = NonBlockingStreamReader(p.stdout, name="start_recording_" + str(id(self))) # 进程p必须要保留到stop_recording执行时、或是退出前才进行清理,否则会导致录屏进程提前终止 reg_cleanup(kill_proc, p) while True: line = nbsp.readline(timeout=5) if line is None: nbsp.kill() kill_proc(p) raise RuntimeError("start recording error") if six.PY3: line = line.decode("utf-8") # 如果上次录屏被强制中断,可能会导致无法开始下一次录屏,额外发一个停止录屏指令 if re.search("Record has already started", line): self.stop_recording(is_interrupted=True) continue m = re.match( "start result: Record start success! File path:(.*\.mp4)", line.strip()) if m: output = m.group(1) self.recording_proc = p self.recording_file = output nbsp.kill() return True
def teardown(self): """ Stop the server and client Returns: None """ if hasattr(self, "backend_stop_event"): self.backend_stop_event.set() self.backend_queue.put(None) if self.client: self.client.close() if self.server_proc: kill_proc(self.server_proc)
def _setup_stream_server(self, lazy=False): """ Setup minicap process on device Args: lazy: parameter `-l` is used when True Returns: adb shell process, non-blocking stream reader and local port """ localport, deviceport = self.adb.setup_forward( "localabstract:minicap_{}".format) deviceport = deviceport[len("localabstract:"):] other_opt = "-l" if lazy else "" params, display_info = self._get_params() if self.display_id: proc = self.adb.start_shell( "%s -d %s -n '%s' -P %dx%d@%dx%d/%d %s 2>&1" % tuple([self.CMD, self.display_id, deviceport] + list(params) + [other_opt]), ) else: proc = self.adb.start_shell( "%s -n '%s' -P %dx%d@%dx%d/%d %s 2>&1" % tuple([self.CMD, deviceport] + list(params) + [other_opt]), ) nbsp = NonBlockingStreamReader(proc.stdout, print_output=True, name="minicap_server", auto_kill=True) while True: line = nbsp.readline(timeout=5.0) if line is None: kill_proc(proc) raise RuntimeError("minicap server setup timeout") if b"Server start" in line: break if proc.poll() is not None: # minicap server setup error, may be already setup by others # subprocess exit immediately kill_proc(proc) raise RuntimeError("minicap server quit immediately") reg_cleanup(kill_proc, proc) self._stream_rotation = int(display_info["rotation"]) return proc, nbsp, localport
def get_frames(self): """ Get the screen frames Returns: None """ proc, nbsp, localport = self._setup_stream_server() s = SafeSocket() s.connect((self.adb.host, localport)) try: t = s.recv(24) except Exception as e: # 在部分手机上,可能连接可以成功建立,但是在开始获取数据时会报错 raise ScreenError(e) # javacap header LOGGING.debug(struct.unpack("<2B5I2B", t)) stopping = False reg_cleanup(s.close) while not stopping: s.send(b"1") # recv frame header, count frame_size if self.RECVTIMEOUT is not None: header = s.recv_with_timeout(4, self.RECVTIMEOUT) else: header = s.recv(4) if header is None: LOGGING.error("javacap header is None") # recv timeout, if not frame updated, maybe screen locked stopping = yield None else: frame_size = struct.unpack("<I", header)[0] frame_data = s.recv(frame_size) stopping = yield frame_data LOGGING.debug("javacap stream ends") s.close() nbsp.kill() kill_proc(proc) self.adb.remove_forward("tcp:%s" % localport)
def _get_stream(self, lazy=True): proc, nbsp, localport = self._setup_stream_server(lazy=lazy) s = SafeSocket() s.connect((self.adb.host, localport)) t = s.recv(24) # minicap header global_headers = struct.unpack("<2B5I2B", t) LOGGING.debug(global_headers) # check quirk-bitflags, reference: https://github.com/openstf/minicap#quirk-bitflags ori, self.quirk_flag = global_headers[-2:] if self.quirk_flag & 2 and ori in (1, 3): # resetup LOGGING.debug("quirk_flag found, going to resetup") stopping = True else: stopping = False yield stopping while not stopping: if lazy: s.send(b"1") # recv frame header, count frame_size if self.RECVTIMEOUT is not None: header = s.recv_with_timeout(4, self.RECVTIMEOUT) else: header = s.recv(4) if header is None: LOGGING.error("minicap header is None") # recv timeout, if not frame updated, maybe screen locked stopping = yield None else: frame_size = struct.unpack("<I", header)[0] frame_data = s.recv(frame_size) stopping = yield frame_data LOGGING.debug("minicap stream ends") s.close() nbsp.kill() kill_proc(proc) self.adb.remove_forward("tcp:%s" % localport)
def stop_recording(self, output="screen.mp4", is_interrupted=False): """ Stop screen recording Args: output: default file is `screen.mp4` is_interrupted: True or False. Stop only, no pulling recorded file from device. Raises: AirtestError: if recording was not started before Returns: None """ pkg_path = self.adb.path_app(YOSEMITE_PACKAGE) p = self.adb.start_shell( 'CLASSPATH=%s exec app_process /system/bin %s.Recorder --stop-record' % (pkg_path, YOSEMITE_PACKAGE)) p.wait() if self.recording_proc: kill_proc(self.recording_proc) self.recording_proc = None if is_interrupted: kill_proc(p) return for line in p.stdout.readlines(): if line is None: break if six.PY3: line = line.decode("utf-8") m = re.match("stop result: Stop ok! File path:(.*\.mp4)", line.strip()) if m: self.recording_file = m.group(1) kill_proc(p) self.adb.pull(self.recording_file, output) return True kill_proc(p) raise AirtestError("start_recording first")