opendx是一款基于 appium 的自动化测试平台。支持web端控制手机,整个架构设计非常优秀,可以根据公司业务,进行二次开发使用。
由于华为手机高版本无法使用appium自带的录制功能。所以opendx添加了scrcpy录制功能,缺点是需要在agent端安装scrcpy,且windows无法支持录制。由于公司大多数同学使用windows,导致启动的agent在高版本华为手机上无法录制视频,在用例结束后难以排查问题。
ScrcpyVideoRecorder类代码如下(示例):
package com.daxiang.core.mobile.android.scrcpy;
import com.daxiang.core.Device;
import com.daxiang.core.mobile.android.AndroidDevice;
import com.daxiang.utils.Terminal;
import com.daxiang.utils.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Created by jiangyitao.
*/
@Slf4j
public class ScrcpyVideoRecorder {
private String mobileId;
private String videoName;
private boolean isRecording = false;
private CountDownLatch countDownLatch;
private Device device;
private FileChannel fc;
private Scrcpy scrcpy;
private static final String H264 = ".h264";
private static final String MP4 = ".mp4";
public ScrcpyVideoRecorder(String mobileId,Device device) {
this.mobileId = mobileId;
this.device = device;
}
public synchronized void start() {
if (isRecording) {
return;
}
countDownLatch = new CountDownLatch(1);
scrcpy = ((AndroidDevice) device).getScrcpy();
try {
videoName = UUIDUtil.getUUID();
fc = new FileOutputStream(videoName+H264).getChannel();
scrcpy.start(imgData -> {
try {
fc.write(imgData);
} catch (IOException e) {
}
});
isRecording = true;
} catch (Exception e) {
isRecording = false;
throw new RuntimeException(e);
}finally {
countDownLatch.countDown();
}
}
/**
* 1. kill scrcpy server来停止录制视频是最优方案。但大多数安卓Mobile只能通过ps(非ps -ef)获取到scrcpy server进程,
* 此时的进程名为app_process, appium在Mobile里运行的进程也是app_process,所以可能会误杀appium在Mobile里运行的进程,不采用该方法
* 2. ExecuteWatchdog.destroyProcess()会导致最后一部分视频无法写入,
* 因为运行在pc的scrcpy进程被直接干掉,无法写入最终的视频,导致获取到破损的视频
* 3. 在非windows操作系统下,scrcpy收到kill信号后,会写入最后一部分视频,目前采用该方法
* 4. 无法在windows上使用,windows taskkill和ExecuteWatchdog.destroyProcess()一样
*/
public synchronized File stop() throws IOException {
if (!isRecording) {
throw new IllegalStateException("video is not in recording");
}
log.info("[{}]stop record video: {}", mobileId, videoName);
scrcpy.stop();
if (fc != null){
fc.close();
}
String killScrcpyCmd = String.format("ffmpeg -f h264 -i %s.h264 -y -vcodec copy %s.mp4", videoName,videoName);
Terminal.execute(killScrcpyCmd);
try {
// 等待视频写入完成,最多等3min
countDownLatch.await(3, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
FileUtils.deleteQuietly(new File(videoName+H264));
log.info("[{}]video: {} recording complete", mobileId, videoName);
isRecording = false;
return new File(videoName+MP4);
}
}
AndroidDevice类代码如下(示例):
@Override
public void startRecordingScreen() {
if (canUseAppiumRecordVideo) {
try {
AndroidStartScreenRecordingOptions androidOptions = new AndroidStartScreenRecordingOptions();
// Since Appium 1.8.2 the time limit can be up to 1800 seconds (30 minutes).
androidOptions.withTimeLimit(Duration.ofMinutes(30));
androidOptions.withBitRate(Integer.parseInt(App.getProperty("androidRecordVideoBitRate")) * 1000000); // default 4000000
((AndroidDriver) driver).startRecordingScreen(androidOptions);
return;
} catch (Exception e) {
log.warn("[{}]无法使用appium录制视频", getId(), e);
canUseAppiumRecordVideo = false;
}
}
if (scrcpyVideoRecorder == null) {
scrcpyVideoRecorder = new ScrcpyVideoRecorder(getId(),this);
}
try {
scrcpyVideoRecorder.start();
}catch (Exception e){
log.warn("[{}]无法使用scrcpy录制视频", getId(), e);
}
}
修改以上两个类后,agent端不需要安装scrcpy,windows也支持录制视频