引言

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)

api

模仿 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()

完整代码


↙↙↙阅读原文可查看相关链接,并与作者交流