欢迎访问我的博客首页。
配置好 ADB,可以使用下面的命令在安卓端保存截屏图像文件,并将图像文件拷贝到 PC 端。
adb shell screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png C:/
命令 adb shell screencap 不仅可以生成图像文件,还可以生成图像数据流,然后直接在 PC 端获取图像数据,效率更高。下面我们就获取屏幕图像数据,把图像缩小后,模拟点击操作,可以简单地在 PC 端操作手机。
import os
import cv2
import numpy as np
import subprocess as sp
def onMouseCallback(event, x, y, flags, param):
if event != cv2.EVENT_LBUTTONDOWN:
return
scale = param
os.system('adb shell input tap ' + str(x * scale) + ' ' + str(y * scale))
def screenshot(scale=4):
cv2.namedWindow('screenshot', cv2.WINDOW_AUTOSIZE)
cv2.setMouseCallback('screenshot', onMouseCallback, scale)
while True:
res = sp.Popen('adb shell screencap -p', shell=True, stdout=sp.PIPE)
out, _ = res.communicate()
# data = out.replace(b'\r\r\n', b'\n') # Android7 以下。
data = out.replace(b'\r\n', b'\n') # Android7 以上。
img = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)
img = cv2.resize(img, (img.shape[1] // scale, img.shape[0] // scale), interpolation=cv2.INTER_AREA)
cv2.imshow('screenshot', img)
if cv2.waitKey(2) & 0xFF == 27:
break
cv2.destroyAllWindows()
if __name__ == '__main__':
screenshot()
下面的命令可以实现录屏。原生安卓系统对 ADB 的支持很好,商用系统则不然。很多商用安卓系统不能使用 screenrecord 命令,报错如第二行。
adb shell screenrecord /sdcard/screen.mp4
/system/bin/sh: screenrecord: inaccessible or not found
和截图命令一样,录屏命令不仅可以输出视频文件,还可以输出视频数据流。对于支持录屏命令的安卓系统,可以使用下面的代码获取屏幕视频数据流。相比截屏命令,这种方式投屏的帧率有明显提升。
import cv2
import time
import numpy as np
import subprocess as sp
def screenrecord(scale=4):
# 1. 获取屏幕分辨率。
# noinspection PyBroadException
try:
tmp = sp.Popen('adb shell wm size', shell=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
resolution, _ = tmp.communicate()
wh = resolution.strip().decode().split(': ')[1].split('x')
except Exception as _:
print('ABD failed to connect devices, please run "adb devices" to find more error messages!')
return
# 2. 获取屏幕流。
screen_cmd = ['adb', 'exec-out', 'screenrecord', '--output-format=h264', '--size=' + wh[0] + 'x' + wh[1], '-']
screen_stream = sp.Popen(screen_cmd, stdout=sp.PIPE, universal_newlines=True)
ffmpeg_cmd = ['ffmpeg', '-i', '-', '-f', 'rawvideo', '-vcodec', 'bmp', '-vf', 'fps=5', '-']
ffmpeg_stream = sp.Popen(ffmpeg_cmd, stdin=screen_stream.stdout, stdout=sp.PIPE)
while True:
size_bytes = ffmpeg_stream.stdout.read(6)
if len(size_bytes) == 0:
print('Failed to find img!')
time.sleep(1)
continue
size = 0
for i in range(4):
size += size_bytes[i + 2] * 256 ** i
bmp_data = size_bytes + ffmpeg_stream.stdout.read(size - 6)
img = cv2.imdecode(np.frombuffer(bmp_data, dtype=np.uint8), 1)
img = cv2.resize(img, (img.shape[1] // scale, img.shape[0] // scale), interpolation=cv2.INTER_AREA)
cv2.imshow('screenrecord', img)
cv2.waitKey(1)
if __name__ == '__main__':
screenrecord()
该代码使用了 adb exec-out,要求 ADB 版本最低为 1.0.41。上面的代码先使用录屏命令 screenrecord 获取 h264 格式的屏幕视频数据流,再使用 ffmpeg 解码 h264 数据流,最后使用 OpenCV 逐帧显示解码数据。