airtest 中用 Yosemite 实现安卓端的录屏功能,但 windows 端并没有脚本运行过程的步骤视频,为了方便做 PC 端自动化测试时查 bug 简单一点,我就自己用 OpenCV 撸了个录屏。
原理比较简单,就是开一条子线程,不停截图,最后导出一个 MP4 即可
def Record(self, bbox):
import time
import airtest.core.win.screen as screen
threading.current_thread.name = 'recording'
while True:
if not self.m_bRecording:
break
self.m_Lock.acquire()
print('Recording...', (bbox[2] - bbox[0], bbox[3] - bbox[1]))
try:
im = aircv.crop_image(screen.screenshot(None), bbox)
self.video.write(numpy.array(im)) # 将img convert ndarray
except:
pass
self.m_Lock.release()
time.sleep(INTERVAL)
模仿 airtest 里安卓部分来写
def start_recording(self, output):
rect = self.get_rect() # 获得分辨率
bbox = (ToEvenNum(rect.left), ToEvenNum(rect.top), ToEvenNum(rect.right), ToEvenNum(rect.bottom)) # 虚拟机中需要是偶数
# bbox = (rect.left, rect.top, rect.right, rect.bottom)
size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
width, heigh = size
self.video = cv2.VideoWriter(output, cv2.VideoWriter_fourcc(*'X264'), FPS, (width, heigh))
self.m_Lock = threading.Lock()
self.m_bRecording = True
self.LoopTimer = threading.Timer(INTERVAL, self.Record, [bbox])
self.LoopTimer.start()
def stop_recording(self, output):
self.m_Lock.acquire()
print('Record end')
self.m_bRecording = False
if self.LoopTimer:
self.LoopTimer.cancel()
self.LoopTimer = None
self.video.release()
self.m_Lock.release()
这里几个坑解释一下
1.start_recording 里 bbox 是截图时四个顶点的坐标,一开始在 PC 端搞的时候只要 bbox = (rect.left, rect.top, rect.right, rect.bottom) 就可以,后来 PC 数量有限换成了 win10 虚拟机怎么搞都不能截图,经过了一番调试发现把四个数都强转成偶数就可以了,至今没明白为什么。一个大迷。
2.cv2.VideoWriter_fourcc('X264') 这个解码器选的是'X264',其他也试过,要么输出的颜色不对,要么导出的 mp4 无法在 html 中显示
def InitVideoRecorder(oDevices):
if oDevices.__class__.__name__ == "Windows":
oDevices.m_Lock = threading.Lock()
if not hasattr(oDevices, 'start_recording'):
oDevices.start_recording = types.MethodType(start_recording, oDevices)
if not hasattr(oDevices, 'stop_recording'):
oDevices.stop_recording = types.MethodType(stop_recording, oDevices)
if not hasattr(oDevices, 'Record'):
oDevices.Record = types.MethodType(Record, oDevices)
一般在 AirtestCase 的 setUp 里开始录制,在 tearDown 里结束录制
也可以自己在脚本里适当的位置初始化后开始和结束
def setUp(self):
auto_setup(logdir=self._logdir)
self.RecordScreen()
def tearDown(self):
try:
output = os.path.join(self.logdir, "recording_0.mp4")
print(output)
self.m_oDev.stop_recording(output)
except:
traceback.print_exc()
@RetryFunc()
def RecordScreen(self): # 开始录屏
try:
return self.StartRecording()
except Exception as e:
try:
self.m_oDev.stop_recording(is_interrupted=True)
except:
pass
raise e
def StartRecording(self):
if device_platform(self.m_oDev) == 'Windows':
output = os.path.join(self.logdir, 'recording_0.mp4')
video.InitVideoRecorder(self.m_oDev)
self.m_oDev.start_recording(output)
else:
self.m_oDev.start_recording()