ios和android录屏

master
yineng.huang 2024-07-22 11:50:18 +08:00
parent fd88ef376a
commit 509a7278fc
23 changed files with 516 additions and 637 deletions

View File

@ -689,9 +689,9 @@ public class MobileDeviceConnection extends AbstractDeviceConnection {
@Override
public void startRecord() {
if ("0".equals(deviceInfo.getPlatform())) {
AndroidUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"));
AndroidUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"),deviceInfo.getOtherInfo().get("tenantId"));
} else {
IosUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"), deviceInfo.getResolution());
IosUtil.startRecord(this.remoteAddress, deviceInfo.getDeviceId(), deviceInfo.getOtherInfo().get("taskId"), deviceInfo.getOtherInfo().get("tenantId"),deviceInfo.getResolution());
}
}

View File

@ -589,6 +589,7 @@ public class DefaultExecThread implements AtuExecThread{
otherInfo = new HashMap<>();
}
otherInfo.put("taskId",task.getTaskId());
otherInfo.put("tenantId", task.getTenantId());
deviceConnection.getDeviceInfo().setOtherInfo(otherInfo);
//开启录屏
deviceConnection.startRecord();

View File

@ -55,10 +55,11 @@ public class AndroidUtil {
}
}
public static String startRecord(String remoteAddress,String deviceId,String taskId){
public static String startRecord(String remoteAddress,String deviceId,String taskId,String tenantId){
DebuggerDeviceInfo info = new DebuggerDeviceInfo();
info.setDeviceId(deviceId);
info.setPlatform("0");
info.setTenantId(tenantId);
logger.info("移动任务开启录屏任务id:"+taskId);
info.setTaskId(taskId);
logger.info("录屏参数:{}", JSON.toJSONString(info));

View File

@ -122,10 +122,11 @@ public class IosUtil {
}
public static String startRecord(String remoteAddress, String deviceId, String taskId, String resolution) {
public static String startRecord(String remoteAddress, String deviceId, String taskId, String tenantId,String resolution) {
DebuggerDeviceInfo info = new DebuggerDeviceInfo();
info.setDeviceId(deviceId);
info.setPlatform("1");
info.setTenantId(tenantId);
logger.info("移动任务开启录屏任务id:" + taskId);
info.setTaskId(taskId);
info.setResolution(resolution);

View File

@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.util.StringUtils;
import javax.websocket.Session;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
@ -46,6 +47,7 @@ public abstract class AbstractDeviceManager extends Thread implements DeviceMana
SpringUtils.getBean(DeviceConnectionService.class).deviceAgentDead(serial);
}
protected void publishDeviceOnlineToWebConnection(String deviceId) {
MessageHandler handleMessageThread = SpringUtils.getBean(DeviceConnectionService.class).getHandleMessageThread(deviceId);
if (null != handleMessageThread) {
@ -176,4 +178,6 @@ public abstract class AbstractDeviceManager extends Thread implements DeviceMana
brand.setBrandName(manufacturer.toUpperCase());
return brand;
}
public abstract void stopWebScreen(String udid, Session session);
}

View File

@ -1,15 +1,16 @@
package net.northking.cctp.upperComputer.deviceManager;
import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.AndroidDeviceInitThread;
import net.northking.cctp.upperComputer.driver.adb.Adb;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AndroidDeviceListener;
import net.northking.cctp.upperComputer.webSocket.thread.AndroidScreenResponseThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import javax.websocket.Session;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -73,6 +74,11 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
deviceSystemIsReady.put(serial, status);
}
@Override
public boolean getDeviceSystemStatus(String serial) {
return deviceSystemIsReady.get(serial);
}
@Override
public void shutdown() {
clearAndShutdownDevices();
@ -205,7 +211,7 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
for (AndroidScreenResponseThread screenResponseThread : screenMap.values()) {
logger.debug("上位机重启,中断残余获取屏幕线程");
if (null != screenResponseThread && screenResponseThread.isAlive() && !screenResponseThread.isInterrupted()) {
screenResponseThread.stopAndExit();
screenResponseThread.stopFetchPic();
}
}
screenMap.clear();
@ -226,4 +232,23 @@ public class AndroidDeviceManager extends AbstractDeviceManager {
return list;
}
@Override
public void offlineDevice(String serial) {
synchronized (onlineDeviceInitMap){
AndroidDeviceInitThread initThread = onlineDeviceInitMap.remove(serial);
if (null != initThread && initThread.isAlive() && !initThread.isInterrupted()) {
logger.info("设备【{}】初始化线程准备退出",serial);
initThread.exit();
}
}
}
@Override
public void stopWebScreen(String udid, Session session) {
AndroidScreenResponseThread androidScreenResponseThread = screenMap.get(udid);
if (null != androidScreenResponseThread) {
androidScreenResponseThread.stopWebSessionTransform(session);
}
}
}

View File

@ -2,6 +2,7 @@ package net.northking.cctp.upperComputer.deviceManager;
import net.northking.cctp.upperComputer.config.BuildWdaConfig;
import net.northking.cctp.upperComputer.deviceManager.common.PyMobileDevice;
import net.northking.cctp.upperComputer.deviceManager.screen.IosScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.deviceManager.thread.MacIosDeviceInitThread;
import net.northking.cctp.upperComputer.deviceManager.thread.WindowsAndLinuxIosDeviceInitThread;
@ -13,10 +14,10 @@ import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxd;
import net.northking.cctp.upperComputer.driver.usbmuxd.payload.usbmuxd.entity.DeviceConnectionDetail;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import net.northking.cctp.upperComputer.webSocket.thread.IosScreenResponseThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.Session;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -396,4 +397,12 @@ public class IOSDeviceManager extends AbstractDeviceManager {
public PyMobileDevice.SpecificAppleDeviceInfo getSpecificAppleDeviceInfo(String serial){
return pyMobileDeviceMap.get(serial);
}
@Override
public void stopWebScreen(String udid, Session session) {
IosScreenResponseThread iosScreenResponseThread = screenMap.get(udid);
if (null != iosScreenResponseThread) {
iosScreenResponseThread.stopWebSessionTransform(session);
}
}
}

View File

@ -1,4 +1,4 @@
package net.northking.cctp.upperComputer.webSocket.thread;
package net.northking.cctp.upperComputer.deviceManager.screen;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
@ -7,10 +7,9 @@ import net.northking.cctp.upperComputer.driver.agent.AndroidAgentSession;
import net.northking.cctp.upperComputer.driver.agent.command.ScreenStreamCommand;
import net.northking.cctp.upperComputer.driver.agent.command.WatchScreenStreamCommand;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CatchParam;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import javax.websocket.Session;
import java.io.DataInput;
@ -18,42 +17,35 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class AndroidScreenResponseThread extends Thread {
public class AndroidScreenResponseThread extends ImageScreenResponse {
private AndroidAgentSession asyncAgentSession;
private Session wsSession;
private AdbDevice adbDevice;
private CmdRequest screenOnRequest;
private CatchParam catchParam;
private byte[] lastData;
private boolean sendDeviceStatus = true;
private final Logger logger = LoggerFactory.getLogger(AndroidScreenResponseThread.class);
public AndroidScreenResponseThread(Session wsSession, AdbDevice adbDevice, CatchParam catchParam) {
this.wsSession = wsSession;
public AndroidScreenResponseThread(AdbDevice adbDevice) {
this.adbDevice = adbDevice;
this.catchParam = catchParam;
this.deviceId = adbDevice.getSerial();
setName("[" + adbDevice.getSerial() + "-response]");
}
public void setScreenOnRequest(CmdRequest screenOnRequest) {
this.screenOnRequest = screenOnRequest;
}
@Override
public void run() {
while (!isInterrupted()) {
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), messageMap, "设备掉线了");
if (!CollectionUtils.isEmpty(webSessions)) {
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
for (Session webSession : webSessions) {
if (webSession.isOpen()) {
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), messageMap, "设备掉线了");
}
}
}
AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(adbDevice.getSerial());
if (null == currentDevice) {
logger.warn("设备【{}】掉线了,等待设备准备好再打开图片流........", adbDevice.getSerial());
@ -74,15 +66,24 @@ public class AndroidScreenResponseThread extends Thread {
this.asyncAgentSession.send(screenStreamCommand);
} catch (IOException e) {
logger.error("创建设备【{}】agentSession出错。。。。。。", adbDevice.getSerial(), e);
Thread.currentThread().interrupt();
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.DISCONNECT);
SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
interrupt();
if (!CollectionUtils.isEmpty(webSessions)) {
//通知前端断开
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.DISCONNECT);
for (Session webSession : webSessions) {
if (webSession.isOpen()) {
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
}
}
}
this.asyncAgentSession.closeSilence();
break;
}
sendConnectInfo2Web();
// sendConnectInfo2Web();
DataInput dataInput = this.asyncAgentSession.getDataInput();
int receive = 1;
int send = 1;
while (!isInterrupted()) {
try {
int commandId = dataInput.readInt();
@ -94,22 +95,37 @@ public class AndroidScreenResponseThread extends Thread {
//将获取的最新帧进行缓存
if (null != screenPicContent && screenPicContent.length > 0) {
this.lastData = screenPicContent;
logger.info("设备【{}】收到图片:{}张,大小:{}",adbDevice.getSerial(),receive++,screenPicContent.length);
} else {
logger.warn("设备【{}】command读取最新帧为空!",adbDevice.getSerial());
logger.info("设备【{}】收到图片:{}张,大小为零",adbDevice.getSerial(),receive++);
continue;
}
} catch (IOException e) {
logger.error("读取数据出错了", e);
break;
}
//录屏
if (null != screenRecorder) {
screenRecorder.addImgToVideo(screenPicContent);
}
//有后端会话则给后端推送屏幕图片
if (wsSession != null && wsSession.isOpen()) {
SessionUtils.sendBinary(wsSession, screenPicContent);
if (sendDeviceStatus) {
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
sendDeviceStatus = false;
if (!CollectionUtils.isEmpty(webSessions)) {
for (Session webSession : webSessions) {
if (webSession.isOpen()) {
if (sendDeviceStatus) {
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
sendDeviceStatus = false;
}
logger.info("设备【{}】发送图片到微服务:{}张",adbDevice.getSerial(),send++);
SessionUtils.sendBinary(webSession, screenPicContent);
logger.info("设备【{}】发送图片到微服务完成:{}张",adbDevice.getSerial(),send);
} else {
logger.warn("推送到web端的session已经断开sessionId:{}",webSession.getId());
//todo:操作一下去掉webSessions里面的无效session
}
}
}
}
@ -129,58 +145,36 @@ public class AndroidScreenResponseThread extends Thread {
logger.warn("设备【{}】响应到上位机的屏幕线程退出..........................", adbDevice.getSerial());
}
public void sendConnectInfo2Web() {
//新连接需要先发送状态
if (null != screenOnRequest) { //只发第一次screen_on指令
//向前端推送之前缓存的最新帧
if (null != adbDevice) {
if (null != lastData && lastData.length > 0) {
SessionUtils.sendBinary(wsSession, lastData);
logger.debug("上位机发送缓存帧成功,大小:{}", lastData.length);
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
sendDeviceStatus = false;
} else {
logger.warn("设备【{}】缓存帧获取为空...", adbDevice.getSerial());
}
}
//向前端推送之前缓存的最新帧后发screenOn回复
SessionUtils.sendSuccessData(wsSession, screenOnRequest, null, "开启屏幕成功");
setScreenOnRequest(null);
}
}
// public void sendConnectInfo2Web() {
// //新连接需要先发送状态
// if (null != screenOnRequest) { //只发第一次screen_on指令
// //向前端推送之前缓存的最新帧
// if (null != adbDevice) {
// if (null != lastData && lastData.length > 0) {
// SessionUtils.sendBinary(wsSession, lastData);
// logger.debug("上位机发送缓存帧成功,大小:{}", lastData.length);
// Map<String, Object> result = new HashMap<>();
// result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
// SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
// sendDeviceStatus = false;
// } else {
// logger.warn("设备【{}】缓存帧获取为空...", adbDevice.getSerial());
// }
// }
// //向前端推送之前缓存的最新帧后发screenOn回复
// SessionUtils.sendSuccessData(wsSession, screenOnRequest, null, "开启屏幕成功");
// setScreenOnRequest(null);
// }
// }
public void stopAndExit() {
logger.warn("设备【{}】退出屏幕获取线程...", adbDevice.getSerial());
@Override
public void stopFetchPic() {
logger.warn("设备【{}】即将退出屏幕获取线程...", adbDevice.getSerial());
ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(false, catchParam.getRotation(), catchParam.getWidth(), catchParam.getHeight(), catchParam.getFrameRate(), catchParam.getQuality());
asyncAgentSession.send(screenStreamCommand);
asyncAgentSession.closeSilence();
this.lastData = null;
AndroidDeviceManager.getInstance().removeScreenThread(adbDevice.getSerial());
interrupt();
}
/**
*
*
* @param session
* @param catchParam
*/
public void startSendScreenToWeb(Session session, CatchParam catchParam) {
this.wsSession = session;
this.catchParam = catchParam;
// this.isNewConnect = true;
sendConnectInfo2Web();
}
/**
*
*/
public void stopSendPic2Web() {
this.wsSession = null;
// logger.debug("后端会话关联已置空!屏幕获取线程是否被中断:"+isInterrupted());
}
public void setSendDeviceStatus(boolean sendDeviceStatus) {
this.sendDeviceStatus = sendDeviceStatus;
}
}

View File

@ -1,4 +1,4 @@
package net.northking.cctp.upperComputer.webSocket.thread;
package net.northking.cctp.upperComputer.deviceManager.screen;
import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.constants.ResponseCmd;

View File

@ -0,0 +1,114 @@
package net.northking.cctp.upperComputer.deviceManager.screen;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CatchParam;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import javax.websocket.Session;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author : yineng.huang
* @date : 2024/7/18 17:48
*/
public abstract class ImageScreenResponse extends Thread{
private Logger logger = LoggerFactory.getLogger(IosScreenResponseThread.class);
protected List<Session> webSessions = new ArrayList<>(3); //与微服务之间的连接session传送屏幕的
protected ScreenRecorder screenRecorder;
protected boolean sendDeviceStatus = true;
protected Map<String,CmdRequest> allConnectionScreenOnRequestMap; //只是第一次连接需要,发送给前端代表连接成功
protected String deviceId;
protected CatchParam catchParam;
public void setCatchParam(CatchParam catchParam) {
this.catchParam = catchParam;
}
public void startRecordScreen(String tenantId, int width, int height, String taskId) {
logger.info("收到设备【{}】的录屏请求当前请求的taskId为{}",deviceId,taskId);
if (null != screenRecorder) {
logger.warn("上一次设备【{}】的录频还在跑呢上一次跑的taskId为{}",deviceId,screenRecorder.getTaskId());
String previousRecordPath = screenRecorder.endRecord();
logger.warn("设备【{}】上一次的录频结束taskId为{},上传的地址为:{}",deviceId,screenRecorder.getTaskId(),previousRecordPath);
}
logger.info("开启设备【{}】的录屏请求当前请求的taskId为{}",deviceId,taskId);
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String dateFormat = format.format(new Date());
String videoName = deviceId + "_" + dateFormat + ".mp4";
screenRecorder = new ScreenRecorder(deviceId, videoName, tenantId,taskId, width, height);
screenRecorder.startRecordVideo();
}
public String stopRecord() {
String result = null;
String taskId = null;
if (null != screenRecorder) {
result = screenRecorder.endRecord();
taskId = screenRecorder.getTaskId();
screenRecorder = null;
}
if (CollectionUtils.isEmpty(webSessions)) {
logger.info("设备【{}】在taskId:{}的录屏结束了,而且没有前端的连接,直接退出屏幕抓取的线程了.............", deviceId, taskId);
stopFetchPic();
} else {
logger.info("设备【{}】在taskId:{}的录屏结束了,但是还有前端的连接再用着,先不断开.............", deviceId, taskId);
}
return result;
}
public void startSendScreenToWeb(Session session, CatchParam catchParam) {
logger.info("设备【{}】当前连接了{}个web客户端...............",deviceId,this.webSessions.size());
if (this.webSessions.size() > 3) {
//通知前端断开连接,最多只能连接三个
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.DISCONNECT);
SessionUtils.sendMessageInitiative(session, ResponseCmd.DEVICE_STATUS, deviceId, messageMap, "最多支持三个客户端同时浏览手机");
return;
}
this.catchParam = catchParam;
this.webSessions.add(session);
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
SessionUtils.sendMessageInitiative(session, ResponseCmd.DEVICE_STATUS, deviceId, messageMap, "设备掉线了");
this.sendDeviceStatus = true;
}
public void setSendDeviceStatus(boolean sendDeviceStatus) {
this.sendDeviceStatus = sendDeviceStatus;
}
public void stopWebSessionTransform(Session session) {
logger.info("移除设备【{}】的web投屏连接,sessionId为{}...............",deviceId,session.getId());
webSessions.remove(session);
logger.info("设备【{}】的web投屏连接数还剩下{}...............",deviceId,webSessions.size());
if (webSessions.size() > 0) {
logger.info("设备【{}】还有其他人连着这个屏幕的,还保留一下这个线程............",deviceId);
} else {
if (null != screenRecorder) {
logger.info("设备【{}】还录着屏幕的,还保留一下这个线程............", deviceId);
} else {
logger.info("设备【{}】没有web端连接也没有录频退出............", deviceId);
stopFetchPic();
}
}
}
public void setScreenOnRequest(Session session, CmdRequest screenOnRequest) {
SessionUtils.sendSuccessData(session, screenOnRequest, null, "开启屏幕成功");
}
public abstract void stopFetchPic();
}

View File

@ -1,27 +1,22 @@
package net.northking.cctp.upperComputer.webSocket.thread;
package net.northking.cctp.upperComputer.deviceManager.screen;
import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
import net.northking.cctp.upperComputer.driver.usbmuxd.AppleDevice;
import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxd;
import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxdConnectFailedException;
import net.northking.cctp.upperComputer.driver.usbmuxd.payload.usbmuxd.entity.DeviceConnectionDetail;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CatchParam;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import javax.websocket.Session;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -30,7 +25,7 @@ import java.util.Map;
* @author : yineng.huang
* @date : 2023/10/17 16:41
*/
public class IosScreenResponseThread extends Thread {
public class IosScreenResponseThread extends ImageScreenResponse {
private Logger logger = LoggerFactory.getLogger(IosScreenResponseThread.class);
@ -53,32 +48,24 @@ public class IosScreenResponseThread extends Thread {
private PhoneEntity phone;
private Session webSession;
private long num = 1;
private CmdRequest screenOnRequest; //只是第一次连接需要,发送给前端代表连接成功
private UsbMuxd.ConnectOperator connectOperator;
private IosScreenRecordThread screenRecordThread;
private String recordingType = "0"; //录屏类型0-未录1-真机录屏2-自动化执行任务录屏
private boolean sendDeviceStatus = true;
private String currentTaskId;
// private IosScreenCompressHandleThread screenCompressHandle;
@Override
public void run() {
logger.info("即将开始拉取设备【{}】图片................................", phone.getUdid());
while (!isInterrupted()) {
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), messageMap, "设备掉线了");
if (!CollectionUtils.isEmpty(webSessions)) {
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
for (Session webSession : webSessions) {
if (webSession.isOpen()) {
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), messageMap, "设备掉线了");
}
}
}
UsbMuxd usbmuxd = new UsbMuxd();
connectOperator = getDeviceConnectOperator(usbmuxd);
if (connectOperator == null) {
@ -97,7 +84,7 @@ public class IosScreenResponseThread extends Thread {
try {
sleep(500);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
interrupt();
}
continue;
}
@ -151,8 +138,8 @@ public class IosScreenResponseThread extends Thread {
}
byte[] rawImageFileByteArray = chunkBuffer.toByteArray();
//这里是录屏
if (null != screenRecordThread && screenRecordThread.isAlive()) {
screenRecordThread.addImgToVideo(rawImageFileByteArray);
if (null != screenRecorder) {
screenRecorder.addImgToVideo(rawImageFileByteArray);
}
// if (compressQuality != 1f) {
//
@ -174,17 +161,23 @@ public class IosScreenResponseThread extends Thread {
// lastRawChunkSize = rawImageFileByteArray.length;
// }
if (null != this.webSession && this.webSession.isOpen()) {
SessionUtils.sendBinary(this.webSession, rawImageFileByteArray);
if (sendDeviceStatus) {
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), result, "设备连接失败");
sendDeviceStatus = false;
//有后端会话则给后端推送屏幕图片
if (!CollectionUtils.isEmpty(webSessions)) {
for (Session webSession : webSessions) {
if (webSession.isOpen()) {
if (sendDeviceStatus) {
Map<String, Object> result = new HashMap<>();
result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), result, "设备连接失败");
sendDeviceStatus = false;
}
SessionUtils.sendBinary(webSession, rawImageFileByteArray);
} else {
logger.warn("推送到web端的session已经断开sessionId:{}",webSession.getId());
//todo:操作一下去掉webSessions里面的无效session
}
}
}
// if (null != screenCompressHandle && screenCompressHandle.isAlive() && !screenCompressHandle.isInterrupted()) {
// screenCompressHandle.addData(rawImageFileByteArray);
// }
@ -227,10 +220,12 @@ public class IosScreenResponseThread extends Thread {
/**
* @param phone
*/
public IosScreenResponseThread(PhoneEntity phone, String currentTaskId) {
public IosScreenResponseThread(PhoneEntity phone) {
// screenCompressHandle = new IosScreenCompressHandleThread(phone.getUdid());
// screenCompressHandle.start();
this.phone = phone;
this.deviceId = phone.getUdid();
setName(phone.getUdid() + "拉取屏幕");
this.currentTaskId = currentTaskId;
}
private UsbMuxd.ConnectOperator getDeviceConnectOperator(UsbMuxd usbMuxd) {
@ -254,12 +249,6 @@ public class IosScreenResponseThread extends Thread {
return null;
}
public void setScreenOnRequest(CmdRequest screenOnRequest) {
if (!isInterrupted() && null != this.webSession && this.webSession.isOpen()) { //只发第一次screen_on指令
SessionUtils.sendSuccessData(this.webSession, screenOnRequest, null, "开启屏幕成功");
}
}
public void changeSize(int width, int height) {
// if (this.screenCompressHandle != null && this.screenCompressHandle.isAlive() && !this.screenCompressHandle.isInterrupted()) {
// this.screenCompressHandle.setScreenSize(width,height);
@ -272,30 +261,14 @@ public class IosScreenResponseThread extends Thread {
IosDeviceInitThread iosInitThread = IOSDeviceManager.getInstance().getIosInitThread(phone.getUdid());
if (null != iosInitThread && !iosInitThread.isInterrupted() && iosInitThread.isAlive()) {
logger.warn("设置手机【{}】推送图片的帧率:{}",phone.getUdid(),frameRate);
NKAgent nkAgent = iosInitThread.getNkAgent();
nkAgent.setMJPEGServerRate(frameRate);
iosInitThread.setMJPEGServerRate(frameRate);
} else {
logger.warn("设备【{}】已经不在线了............................");
}
}
/**
* 1.线退
* 2.退
*/
public void stopFetchPic() {
if (recordingType.equals("2")) { //直播退出
logger.warn("该设备【{}】处于自动化的录屏阶段,稍后自动化录屏完成之后再退出手机屏幕的获取。。。。。。", phone.getUdid());
this.webSession = null;
return;
}
logger.warn("该设备【{}】未处于自动化的录屏阶段,现在退出屏幕抓取。。。。。。", phone.getUdid());
String videoUrl = stopRecord();
if (null != videoUrl) {
Map<String, Object> result = new HashMap<>();
result.put("videoInfo", videoUrl);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.RECORD_FINISH, this.phone.getUdid(), result, "结束录屏成功");
}
logger.warn("设备【{}】即将退出屏幕的抓取。。。。。。", phone.getUdid());
if (null != connectOperator) {
try {
connectOperator.close();
@ -304,63 +277,8 @@ public class IosScreenResponseThread extends Thread {
}
}
IOSDeviceManager.getInstance().removeScreenThread(phone.getUdid());
this.webSession = null;
interrupt();
}
public void startRecordScreen(String tenantId, int width, int height,String recordType, String currentTaskId){
this.recordingType = recordType;
if (null != screenRecordThread && screenRecordThread.isAlive()) {
screenRecordThread.killRecord();
}
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String dateFormat = format.format(new Date());
String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4";
screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId,width, height, currentTaskId);
screenRecordThread.start();
}
//真机结束录屏
public String stopRecord() {
this.recordingType = "0";
if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
return null;
}
String result = screenRecordThread.endRecord();
screenRecordThread = null;
return result;
}
//自动化结束录屏
public String stopRecord(String tenantId,boolean isSave) {
this.recordingType = "0";
if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
return null;
}
String result = screenRecordThread.endRecord(tenantId,isSave);
JSONObject object = JSONObject.parseObject(result, JSONObject.class);
screenRecordThread = null;
String videoUrl = object.getString("videoUrl");
logger.debug("上传录屏的地址:{}", videoUrl);
if (this.webSession == null) {
logger.info("设备【{}】自动化录屏退出,同时退出设备的屏幕获取", phone.getUdid());
stopFetchPic();
} else {
logger.info("设备【{}】正链接前端的屏幕,稍后退出", phone.getUdid());
}
return videoUrl;
}
public void startSendScreenToWeb(Session session, CatchParam catchParam) {
this.webSession = session;
//通知前端开始连接
Map<String, Object> messageMap = new HashMap<>();
messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), messageMap, "设备掉线了");
this.sendDeviceStatus = true;
}
public void setSendDeviceStatus(boolean sendDeviceStatus) {
this.sendDeviceStatus = sendDeviceStatus;
}
}

View File

@ -1,8 +1,7 @@
package net.northking.cctp.upperComputer.webSocket.thread;
package net.northking.cctp.upperComputer.deviceManager.screen;
import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.SpringUtils;
@ -26,9 +25,9 @@ import java.util.Map;
* @author : yineng.huang
* @date : 2023/10/25 16:30
*/
public class IosScreenRecordThread extends Thread{
public class ScreenRecorder {
private Logger logger = LoggerFactory.getLogger(IosScreenRecordThread.class);
private Logger logger = LoggerFactory.getLogger(ScreenRecorder.class);
private FFmpegFrameRecorder recorder;
@ -48,12 +47,13 @@ public class IosScreenRecordThread extends Thread{
private int videoHeight;
private String currentTaskId;
private String taskId;
public IosScreenRecordThread(String udid, String videoName, String tenantId, int videoWidth, int videoHeight, String currentTaskId) {
public ScreenRecorder(String udid, String videoName, String tenantId,String taskId, int videoWidth, int videoHeight) {
this.udid = udid;
this.videoName = videoName;
this.tenantId = tenantId;
this.taskId = taskId;
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
this.filePath = UpperComputerManager.getInstance().getApplicationPath() + "/screenRecordTmp/";
@ -63,40 +63,35 @@ public class IosScreenRecordThread extends Thread{
}
//todo:这里注意改正linux的配置参数写文件
recorder = new FFmpegFrameRecorder(filePath + videoName, videoWidth, videoHeight);
// recorder.setFormat("mp4");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
// recorder.setVideoCodec(opencv_cudacodec.MPEG4);
recorder.setFrameRate(FRAME_RATE);
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
this.currentTaskId = currentTaskId;
}
@Override
public void run() {
public void startRecordVideo() {
try {
recorder.start();
} catch (FFmpegFrameRecorder.Exception e) {
logger.warn("录屏启动失败。。。。。。。", e);
}
while (!isInterrupted()) {
logger.info("ios设备【{}】录屏中。。。。。。。。。。。。。", udid);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
break;
}
}
logger.info("结束ios设备【{}】任务【{}】的录屏。。。。。。。", udid, videoName);
logger.info("设备【{}】已开启录屏.............................",udid);
}
public void addImgToVideo(byte[] imgData) {
try {
BufferedImage read = ImageIO.read(new ByteArrayInputStream(imgData));
recorder.record(converter.getFrame(read));
if (!recorder.isCloseOutputStream()) {
recorder.record(converter.getFrame(read));
}
} catch (IOException e) {
logger.warn("添加图片文件出现错误。。。。。",e);
}
}
public String endRecord() {
logger.info("准备结束设备【{}】在taskId:{}的视频录制.............",udid,taskId);
try {
recorder.stop();
} catch (FFmpegFrameRecorder.Exception e) {
@ -108,37 +103,10 @@ public class IosScreenRecordThread extends Thread{
logger.warn("释放录屏失败。。。。。。", e);
}
String result = uploadVideoToServer();
this.interrupt();
logger.info("设备【{}】在taskId:{}的视频录制已经结束,得到的视频地址为:{}.............",udid,taskId,result);
return result;
}
public String endRecord(Boolean save) {
try {
recorder.stop();
} catch (FFmpegFrameRecorder.Exception e) {
logger.warn("结束录屏失败。。。。。。", e);
}
try {
recorder.release();
} catch (FFmpegFrameRecorder.Exception e) {
logger.warn("释放录屏失败。。。。。。", e);
}
String result = null;
if (null != save && save) {
result = uploadVideoToServer();
}else {
logger.debug("录屏文件不保存!");
}
this.interrupt();
return result;
}
public String endRecord(String tenantId,Boolean save) {
this.tenantId = tenantId;
return endRecord(save);
}
private String uploadVideoToServer() {
Map<String, String> resultMap = new HashMap<>();
try {
@ -146,36 +114,22 @@ public class IosScreenRecordThread extends Thread{
logger.debug("[" +udid+ "]录屏停止,开始上传视屏文件");
String serverAddress = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
logger.info("开始上传ios视频文件");
Attachment upload = HttpUtils.upload(serverAddress + pubUploadPath, filePath + videoName, tenantId, currentTaskId, FileBusinessTypeEnum.MOBILE_TASK_VIDEO.getCode());
Attachment upload = HttpUtils.upload(serverAddress + pubUploadPath, filePath + videoName, tenantId);
if (null != upload && StringUtils.isNotBlank(upload.getId())) {
logger.info("文件上传成功返回id{}", upload.getId());
logger.debug("文件上传成功返回id{}", upload.getId());
resultMap.put("videoUrl", upload.getUrlPath());
resultMap.put("videoRef", videoWidth + "x" + videoHeight);
return JSON.toJSONString(resultMap);
}
} catch (Exception e) {
logger.warn("上传视频文件失败。。。。。。。。", e);
}finally {
}
//todo:上传完之后要删除
return null;
}
public void killRecord() {
try {
recorder.stop();
} catch (FFmpegFrameRecorder.Exception e) {
logger.warn("结束录屏失败。。。。。。", e);
}
try {
recorder.release();
} catch (FFmpegFrameRecorder.Exception e) {
logger.warn("释放录屏失败。。。。。。", e);
}
File file = new File(filePath + videoName);
if (file.exists()) {
file.delete();
}
interrupt();
public String getTaskId() {
return taskId;
}
}

View File

@ -431,6 +431,7 @@ public class AndroidDeviceInitThread extends Thread {
Map<String, Integer> sizeMap = new HashMap<>();
sizeMap.put("width", displayData.getWidth());
sizeMap.put("height", displayData.getHeight());
sizeMap.put("rotation", displayData.getRotation());
return sizeMap;
}
return null;

View File

@ -27,6 +27,8 @@ public abstract class IosDeviceInitThread extends Thread {
protected AppleDevice appleDevice;
private int frame = 12;
public IosDeviceInitThread(PhoneEntity phone, AppleDevice appleDevice) {
this.phone = phone;
this.appleDevice = appleDevice;
@ -45,8 +47,8 @@ public abstract class IosDeviceInitThread extends Thread {
nkAgentReady = nkAgent.getStatus();
logger.debug("设备【{}】的nkAgent连接完成结果{}", phone.getUdid(),nkAgentReady);
if (nkAgentReady) {
logger.debug("设置设备【{}】的帧数为:{}", phone.getUdid(), 25);
nkAgent.setMJPEGServerRate(25);
logger.debug("设置设备【{}】的帧数为:{}", phone.getUdid(), frame);
nkAgent.setMJPEGServerRate(frame);
} else {
logger.error("第{}次连接设备【{}】的agent不成功等待下一次......",reTryTime, phone.getUdid());
}
@ -91,5 +93,9 @@ public abstract class IosDeviceInitThread extends Thread {
return String.format("%s%d%d_%d-%d-%d", calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
}
public void setMJPEGServerRate(int frameRate){
this.frame = frameRate;
};
}

View File

@ -7,6 +7,8 @@ import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import net.northking.cctp.upperComputer.config.MobileProperty;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.AndroidDeviceInitThread;
import net.northking.cctp.upperComputer.driver.adb.Adb;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AdbTransport;
@ -18,9 +20,11 @@ import net.northking.cctp.upperComputer.entity.DebuggerDeviceInfo;
import net.northking.cctp.upperComputer.exception.AppiumException;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.service.thread.AndroidDeviceAllInfoThread;
import net.northking.cctp.upperComputer.service.thread.AndroidDeviceInfoByPackageThread;
import net.northking.cctp.upperComputer.utils.*;
import net.northking.cctp.upperComputer.webSocket.entity.CatchParam;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
@ -705,111 +709,172 @@ public class AndroidDebuggerServiceImpl extends AbstractDebuggerService {
private Map<String, Process> processMap = new HashMap<>();
// @Override
// public boolean startRecord(DebuggerDeviceInfo info) {
// logger.info("开启录屏");
// String taskId = info.getTaskId();
// if (StringUtils.isBlank(taskId)) {
// logger.warn("任务id必填");
// return false;
// }
// String videoPath = mobileProperty.getStfPath() + "/temp";
// Thread thread = new Thread(() -> {
// File file = new File(videoPath);
// if (!file.isDirectory()) {
// file.mkdirs();
// }
// logger.info("录屏前先kill掉所有scrcpy进程");
// String pullCmd = String.format("ps -ef|grep 'scrcpy -s %s -b 2M -Nr %s' |grep -v grep |awk '{print $2}' |xargs --no-run-if-empty kill -9", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
// String[] delCmd = {"/bin/sh", "-c", pullCmd};
// try {
// Process delProcess = Runtime.getRuntime().exec(delCmd, null, null);
// delProcess.waitFor();
// logger.info("录屏开启命令:" + pullCmd);
// delProcess.destroy();
// } catch (Exception e) {
// logger.error("删除scrcpy进程失败:", e);
// }
// String cmd = String.format("scrcpy -s %s -b 2M -Nr %s ", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
// logger.info(cmd);
//// if(props.getProperty("os.name").contains("Linux")){
//// }
// try {
// Process process = Runtime.getRuntime().exec(cmd);
// processMap.put(taskId, process);
// logger.info("开启scrcpy进程");
// } catch (IOException e) {
// logger.error("开启录屏异常:", e);
// }
// });
// thread.start();
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// logger.warn("线程终止。。。。。。", e);
// Thread.currentThread().interrupt();
// }
// return false;
// }
@Override
public boolean startRecord(DebuggerDeviceInfo info) {
logger.info("开启录屏");
String taskId = info.getTaskId();
logger.info("收到设备【{}】在任务【{}】开启录屏的请求............",info.getDeviceId(),taskId);
if (StringUtils.isBlank(taskId)) {
logger.warn("任务id必填");
logger.error("设备【{}】在录屏的时候任务id不存在",info.getDeviceId());
return false;
}
String videoPath = mobileProperty.getStfPath() + "/temp";
Thread thread = new Thread(() -> {
File file = new File(videoPath);
if (!file.isDirectory()) {
file.mkdirs();
}
logger.info("录屏前先kill掉所有scrcpy进程");
String pullCmd = String.format("ps -ef|grep 'scrcpy -s %s -b 2M -Nr %s' |grep -v grep |awk '{print $2}' |xargs --no-run-if-empty kill -9", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
String[] delCmd = {"/bin/sh", "-c", pullCmd};
try {
Process delProcess = Runtime.getRuntime().exec(delCmd, null, null);
delProcess.waitFor();
logger.info("录屏开启命令:" + pullCmd);
delProcess.destroy();
} catch (Exception e) {
logger.error("删除scrcpy进程失败:", e);
}
String cmd = String.format("scrcpy -s %s -b 2M -Nr %s ", info.getDeviceId(), videoPath + "/" + taskId + ".mp4");
logger.info(cmd);
try {
Process process = Runtime.getRuntime().exec(cmd);
processMap.put(taskId, process);
logger.info("开启scrcpy进程");
} catch (IOException e) {
logger.error("开启录屏异常:", e);
}
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
logger.warn("线程终止。。。。。。", e);
Thread.currentThread().interrupt();
AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(info.getDeviceId());
if (null == currentDevice) {
logger.error("设备【{}】当前不在线,无法录视频..........",info.getDeviceId());
return false;
}
return false;
logger.info("获取手机【{}】的实际宽高............................",info.getDeviceId());
AndroidDeviceInitThread androidInitThread = AndroidDeviceManager.getInstance().getAndroidInitThread(info.getDeviceId());
if (null == androidInitThread) {
throw new ParamMistakeException(String.format("手机【%s】已离线,请稍后再试!", info.getDeviceId()));
}
Map<String, Integer> screenSizeMap = androidInitThread.getRealScreenSize();
if (null == screenSizeMap) {
throw new ParamMistakeException(String.format("获取手机【%s】实际宽高失败,请稍后再试!", info.getDeviceId()));
}
CatchParam catchParam = new CatchParam();
catchParam.setRealHeight(screenSizeMap.get("height"));
catchParam.setRealWidth(screenSizeMap.get("width"));
catchParam.setWidth(screenSizeMap.get("width")/2);
catchParam.setHeight(screenSizeMap.get("width")/2);
catchParam.setFrameRate(30);
catchParam.setQuality(75);
catchParam.setRotation(screenSizeMap.get("rotation"));
logger.info("手机【{}】的实际宽:{}高:{}............................",info.getDeviceId() ,catchParam.getRealWidth(), catchParam.getRealHeight());
AndroidScreenResponseThread screenThread = AndroidDeviceManager.getInstance().getScreenThread(info.getDeviceId());
if (null == screenThread || screenThread.isInterrupted() || !screenThread.isAlive()) { //没人连手机
screenThread = new AndroidScreenResponseThread(currentDevice);
screenThread.setCatchParam(catchParam);
screenThread.start();
AndroidDeviceManager.getInstance().saveDeviceScreenThread(info.getDeviceId(),screenThread);
}
screenThread.startRecordScreen(info.getTenantId(), catchParam.getRealWidth(), catchParam.getRealHeight(),taskId);
return true;
}
// @Override
// public String endRecord(DebuggerDeviceInfo info) {
// String taskId = info.getTaskId();
// if (StringUtils.isBlank(taskId)) {
// logger.warn("任务id必填");
// return null;
// }
// String videoPath = null;
// String filePath = mobileProperty.getStfPath() + "/temp";
// logger.info("关闭scrcpy进程");
// try {
// Process process = processMap.remove(taskId);
// if (process != null) {
// process.destroy();
// process = null;
// }
// Thread.sleep(2000);
// } catch (Exception e) {
// logger.error("关闭录屏异常", e);
// return videoPath;
// }
// Boolean save = info.getSave();
// logger.info("上传视频文件");
// String fileName = filePath + "/" + taskId + ".mp4";
// File temp = new File(fileName);
// if (save) {
// try {
// //等待文件生成
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// logger.warn("线程终止。。。。。。", e);
// Thread.currentThread().interrupt();
// }
// if (!temp.exists()) {
// logger.warn("结束录屏失败:视频文件不存在,文件路径:{}", fileName);
// return videoPath;
// }
// try {
// //租户id
// String tenantId = info.getTenantId();
//// NKFile nkFile = simpleStorageService.upload(tenantId, temp);
//// if (nkFile != null && StringUtils.isNotBlank(nkFile.getId())) {
//// logger.debug("文件上传成功返回id{}", nkFile.getId());
//// String fileId = nkFile.getId();
//// videoPath = pathToId(tenantId,fileId);
//// }
// Attachment upload = HttpUtils.upload(mobileProperty.getServerAddr() + mobileProperty.getPublicUploadAddr(), temp.getAbsolutePath(), tenantId);
// if (null != upload && StringUtils.isNotBlank(upload.getId())) {
// logger.debug("文件上传成功返回id{}", upload.getId());
// videoPath = upload.getUrlPath();
// }
// } catch (Exception e) {
// logger.error("结束录屏失败,文件上传未成功:", e);
// return videoPath;
// }
// }
// try {
// temp.deleteOnExit();
// } catch (Exception e) {
// logger.error("删除文件失败:", e);
// }
// return videoPath;
// }
@Override
public String endRecord(DebuggerDeviceInfo info) {
String taskId = info.getTaskId();
if (StringUtils.isBlank(taskId)) {
logger.warn("任务id必填");
return null;
String result = null;
logger.info("收到设备【{}】在任务【{}】关闭录屏的请求............",info.getDeviceId(),info.getTaskId());
AndroidScreenResponseThread screenThread = AndroidDeviceManager.getInstance().getScreenThread(info.getDeviceId());
if (null != screenThread && screenThread.isAlive() && !screenThread.isInterrupted()) {
result = screenThread.stopRecord();
return result;
} else {
logger.info("设备【{}】在任务【{}】录屏不存在............",info.getDeviceId(),info.getTaskId());
}
String videoPath = null;
String filePath = mobileProperty.getStfPath() + "/temp";
logger.info("关闭scrcpy进程");
try {
Process process = processMap.remove(taskId);
if (process != null) {
process.destroy();
process = null;
}
Thread.sleep(2000);
} catch (Exception e) {
logger.error("关闭录屏异常", e);
return videoPath;
}
Boolean save = info.getSave();
logger.info("上传视频文件");
String fileName = filePath + "/" + taskId + ".mp4";
File temp = new File(fileName);
if (save) {
try {
//等待文件生成
Thread.sleep(3000);
} catch (InterruptedException e) {
logger.warn("线程终止。。。。。。", e);
Thread.currentThread().interrupt();
}
if (!temp.exists()) {
logger.warn("结束录屏失败:视频文件不存在,文件路径:{}", fileName);
return videoPath;
}
logger.info("视频文件路径:{},视频文件大小:{}", temp.getAbsolutePath(), temp.length());
try {
//租户id
String tenantId = info.getTenantId();
logger.info("开始上传视频文件,任务信息:{}android设备{}", JSON.toJSONString(info), info.getDeviceId());
Attachment upload = HttpUtils.upload(mobileProperty.getServerAddr() + mobileProperty.getPublicUploadAddr(), temp.getAbsolutePath(), tenantId, taskId, FileBusinessTypeEnum.MOBILE_TASK_VIDEO.getCode());
if (null != upload && StringUtils.isNotBlank(upload.getId())) {
logger.debug("文件上传成功返回id{}", upload.getId());
videoPath = upload.getUrlPath();
}
} catch (Exception e) {
logger.error("结束录屏失败,文件上传未成功:", e);
return videoPath;
}
}
try {
temp.deleteOnExit();
} catch (Exception e) {
logger.error("删除文件失败:", e);
}
return videoPath;
logger.info("设备【{}】在任务【{}】录屏保存的地址:{}............",info.getDeviceId(),info.getTaskId(),result);
return result;
}
@Override

View File

@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.appium.java_client.AppiumDriver;
import net.northking.cctp.upperComputer.config.MobileProperty;
import net.northking.cctp.upperComputer.deviceManager.screen.IosScreenResponseThread;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.DebuggerDeviceInfo;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
@ -17,7 +18,6 @@ import net.northking.cctp.upperComputer.service.thread.IOSDeviceInfoByPackageThr
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.ScreenShotUtils;
import net.northking.cctp.upperComputer.webSocket.thread.IosScreenResponseThread;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
@ -219,10 +219,13 @@ public class IosDebuggerServiceImpl extends AbstractDebuggerService {
return path;
}
@Override
public boolean startRecord(DebuggerDeviceInfo info) {
IosScreenResponseThread screenThread = IOSDeviceManager.getInstance().getScreenThread(info.getDeviceId());
logger.info("收到设备【{}】在任务【{}】开启录屏的请求............",info.getDeviceId(),info.getTaskId());
net.northking.cctp.upperComputer.deviceManager.screen.IosScreenResponseThread screenThread = IOSDeviceManager.getInstance().getScreenThread(info.getDeviceId());
String taskId = info.getTaskId();
String tenantId = info.getTenantId();
String resolution = info.getResolution();
String[] split = resolution.split("\\*");
int width = Integer.parseInt(split[0]);
@ -235,24 +238,27 @@ public class IosDebuggerServiceImpl extends AbstractDebuggerService {
}
if (null == screenThread || screenThread.isInterrupted() || !screenThread.isAlive()) { //没人连手机
PhoneEntity phoneEntity = IOSDeviceManager.getInstance().getPhoneEntity(info.getDeviceId());
screenThread = new IosScreenResponseThread(phoneEntity, taskId);
screenThread = new net.northking.cctp.upperComputer.deviceManager.screen.IosScreenResponseThread(phoneEntity);
screenThread.start();
IOSDeviceManager.getInstance().saveDeviceScreenThread(info.getDeviceId(),screenThread);
}
screenThread.startRecordScreen("default", width, height,"2", taskId);
screenThread.startRecordScreen(tenantId, width, height,taskId);
return true;
}
@Override
public String endRecord(DebuggerDeviceInfo info) {
String result = null;
logger.info("收到设备【{}】在任务【{}】关闭录屏的请求............",info.getDeviceId(),info.getTaskId());
IosScreenResponseThread screenThread = IOSDeviceManager.getInstance().getScreenThread(info.getDeviceId());
if (null != screenThread && screenThread.isAlive() && !screenThread.isInterrupted()) {
String tenantId = info.getTenantId();
//增加是否保存录屏文件的参数
String result = screenThread.stopRecord(tenantId,info.getSave());
result = screenThread.stopRecord();
return result;
} else {
logger.info("设备【{}】在任务【{}】录屏不存在............",info.getDeviceId(),info.getTaskId());
}
return null;
logger.info("设备【{}】在任务【{}】录屏保存的地址:{}............",info.getDeviceId(),info.getTaskId(),result);
return result;
}
@Override

View File

@ -2,6 +2,7 @@ package net.northking.cctp.upperComputer.webSocket.thread;
import net.northking.cctp.upperComputer.constants.KeyBoardCodeEnum;
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.screen.IosScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
import net.northking.cctp.upperComputer.driver.ios.command.data.UiNodeData;
@ -34,8 +35,6 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
protected IosFunctionByAgent function;
protected IosScreenResponseThread screenResponseThread;
protected GetNodeTreeThread nodeTreeThread;
protected StopWatch handleWatch = new StopWatch();
@ -160,7 +159,6 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
SessionUtils.sendSuccessData(session, request, uiNodeData, "获取节点树成功");
}
@Override
public void clearAndCloseDeviceAgentConnection() {
//通知前端设备现在处于掉线的状态,前端开始转圈
@ -170,7 +168,11 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
@Override
public void reConnectDeviceAgent() {
notifyDeviceOnlineToWeb();
screenResponseThread.setSendDeviceStatus(true); //发送连接成功消息
//发送前端重连成功消息的标志
IosScreenResponseThread screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
if (null != screenResponseThread) {
screenResponseThread.setSendDeviceStatus(true); //发送连接成功消息
}
}
@ -551,35 +553,6 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
return new Point(realX.floatValue(), realY.floatValue());
}
@Override
public void stopRecordScreen(CmdRequest request) {
String recordFileInfo = screenResponseThread.stopRecord();
if (recordFileInfo != null) {
Map<String, Object> result = new HashMap<>();
result.put("videoInfo", recordFileInfo);
SessionUtils.sendSuccessData(session, request, result, "结束录屏成功");
} else {
SessionUtils.sendFailureMessage(session, request, "该设备未开启录屏");
}
}
@Override
public void startRecordScreen(CmdRequest request) {
Map<String, Object> data = request.getData();
ParamCheck tenantIdCheck = ParamCheck.build().name("tenantId").type("string").length(32);
Map<String, Object> params = null;
try {
params = checkParam(data, false, tenantIdCheck);
} catch (Exception e) {
logger.error("参数校验失败!", e);
if (e instanceof ParamMistakeException) {
SessionUtils.sendFailureMessage(session, request, e.getMessage());
return;
}
}
screenResponseThread.startRecordScreen((String) params.get("tenantId"), catchParam.getRealWidth(), catchParam.getRealHeight(), "1", null);
SessionUtils.sendSuccessData(session, request, null, "开启录屏成功");
}
@Override
public void changeScreenQuality(CmdRequest request) {
@ -601,6 +574,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
int frameRateUse = (int) params.get("framerate");
catchParam.setFrameRate(frameRateUse);
logger.debug("改变屏幕质量=====>width{} height:{} quality:{} frameRate:{}", catchParam.getWidth(), catchParam.getHeight(), qualityUse, frameRateUse);
IosScreenResponseThread screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
screenResponseThread.changeQuality(qualityUse / 100f,frameRateUse);
}
@ -624,6 +598,7 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
int heightUse = (int) params.get("height");
catchParam.setHeight(heightUse);
logger.debug("改变宽高屏幕=====>width{} height:{} quality:{} frameRate:{}", widthUse, heightUse, catchParam.getQuality(), catchParam.getFrameRate());
IosScreenResponseThread screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
screenResponseThread.changeSize(widthUse, heightUse);
}
@ -657,25 +632,23 @@ public abstract class AbstractIosMessageHandlerThread extends AbstractMessageHan
int rotationUse = (int) params.get("rotation");
catchParam.setRotation(rotationUse);
logger.debug("开启屏幕=====>width{} height:{} quality:{} frameRate:{},rotation:{}", widthUse, heightUse, qualityUse, frameRateUse, rotationUse);
screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
IosScreenResponseThread screenResponseThread = IOSDeviceManager.getInstance().getScreenThread(phoneEntity.getUdid());
if (null == screenResponseThread || screenResponseThread.isInterrupted() || !screenResponseThread.isAlive()) {
//读取手机端响应线程
screenResponseThread = new IosScreenResponseThread(phoneEntity, null);
screenResponseThread = new IosScreenResponseThread(phoneEntity);
screenResponseThread.start();
screenResponseThread.startSendScreenToWeb(session, catchParam);
screenResponseThread.setScreenOnRequest(request);
screenResponseThread.setScreenOnRequest(session,request);
IOSDeviceManager.getInstance().saveDeviceScreenThread(phoneEntity.getUdid(), screenResponseThread);
} else {
screenResponseThread.startSendScreenToWeb(session, catchParam);
screenResponseThread.setScreenOnRequest(request);
screenResponseThread.setScreenOnRequest(session,request);
}
}
@Override
public void stopAndExit() {
if (null != screenResponseThread) {
screenResponseThread.stopFetchPic();
}
IOSDeviceManager.getInstance().stopWebScreen(phoneEntity.getUdid(),session);
if (null != iosPerfThread) {
iosPerfThread.stopThread();
}

View File

@ -60,11 +60,7 @@ public abstract class AbstractMessageHandler extends Thread implements MessageHa
changeScreenSize(request);
} else if (RequestCmd.SCREEN_QUALITY.equals(request.getCmd())) { //改变屏幕质量
changeScreenQuality(request);
} else if (RequestCmd.SCREEN_RECORD_ON.equals(request.getCmd())) {//开启录屏
startRecordScreen(request);
} else if (RequestCmd.SCREEN_RECORD_OFF.equals(request.getCmd())) { //结束录屏
stopRecordScreen(request);
} else if (RequestCmd.SCREEN_CAPTURE.equals(request.getCmd())) {//截图
} else if (RequestCmd.SCREEN_CAPTURE.equals(request.getCmd())) {//截图
screenShot(request);
} else if (RequestCmd.INPUT_TOUCH_DOWN.equals(request.getCmd())) { //鼠标摁下
touchDown(request);

View File

@ -6,6 +6,8 @@ import net.northking.cctp.upperComputer.constants.KeyBoardCodeEnum;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidVideoScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.AndroidDeviceInitThread;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AdbTransport;
@ -16,7 +18,6 @@ import net.northking.cctp.upperComputer.driver.agent.command.*;
import net.northking.cctp.upperComputer.driver.agent.command.data.PackageInfo;
import net.northking.cctp.upperComputer.driver.agent.command.protocol.ProtocolCommand;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
@ -50,8 +51,6 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
private String tenantId;
private AndroidScreenRecordThread screenRecordThread;
private AndroidLogThread androidLogThread;
private AndroidPerfThread androidPerfThread;
@ -68,8 +67,6 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
private boolean capsDown = false;
private AndroidScreenResponseThread responseThread;
private AndroidVideoScreenResponseThread videoScreenResponseThread;
private List<String> capsUpLetters = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
@ -154,7 +151,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
//上传到服务器
String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
String fileUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
String fileId = HttpUtils.doUpload(serverIp + fileUploadPath, tmpFile, tenantId,null, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
String fileId = HttpUtils.doUpload(serverIp + fileUploadPath, tmpFile, tenantId);
logger.info("上传文件到服务器完成:{}", fileId);
Map<String, Object> result = new HashMap<>();
result.put("fileName", fileName);
@ -325,7 +322,6 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
}
@Override
public void getNodeTreeOnce(CmdRequest request) {
@ -365,8 +361,10 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
throw new ExecuteException("设备还未准备好,请稍后再试");
}
notifyDeviceOnlineToWeb();
if(responseThread != null){
responseThread.setSendDeviceStatus(true); //发送连接成功消息
//发送前端重连成功消息的标志
AndroidScreenResponseThread screenThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
if (null != screenThread) {
screenThread.setSendDeviceStatus(true);
}
}
@ -1152,7 +1150,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
Attachment upload = null;
try {
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId,null,FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
} catch (Exception e) {
logger.debug("[{}]上传截图失败", adbDevice.getSerial(),e);
}
@ -1172,45 +1170,9 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
}
@Override
public void stopRecordScreen(CmdRequest request) {
if (screenRecordThread != null && screenRecordThread.isAlive()) {
String recordFilePath = screenRecordThread.stopRecord();
if (recordFilePath != null) {
Map<String, Object> result = new HashMap<>();
result.put("videoInfo", recordFilePath);
SessionUtils.sendSuccessData(session, request, result, "结束录屏成功");
} else {
SessionUtils.sendFailureMessage(session, request, "上传录屏失败");
}
screenRecordThread = null;
} else {
SessionUtils.sendFailureMessage(session, request, "未找到开启录屏信息");
}
}
@Override
public void startRecordScreen(CmdRequest request) {
Map<String, Object> data = request.getData();
ParamCheck tenantIdCheck = ParamCheck.build().name("tenantId").type("string").length(32);
Map<String, Object> params = null;
try {
params = checkParam(data, false, tenantIdCheck);
} catch (Exception e) {
logger.error("参数校验失败!", e);
if (e instanceof ParamMistakeException) {
SessionUtils.sendFailureMessage(session, request, e.getMessage());
return;
}
}
if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
screenRecordThread = new AndroidScreenRecordThread(adbDevice.getSerial(), (String) params.get("tenantId"), catchParam.getRealWidth(), catchParam.getRealHeight());
screenRecordThread.start();
} else {
logger.info("设备【{}】正在录屏。。。。。",serial);
}
SessionUtils.sendSuccessData(session, request, null, "开启录屏成功");
}
@Override
public void changeScreenQuality(CmdRequest request) {
@ -1232,6 +1194,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
int frameRateUse = (int) params.get("framerate");
catchParam.setFrameRate(frameRateUse);
logger.debug("改变屏幕质量=====>width{} height:{} quality:{} frameRate:{}", catchParam.getWidth(), catchParam.getHeight(), qualityUse, frameRateUse);
AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
if (null != responseThread) { //图片流
ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(true, 0, catchParam.getWidth(), catchParam.getHeight(), frameRateUse, qualityUse);
asyncSendCommand(screenStreamCommand);
@ -1264,6 +1227,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
int heightUse = (int) params.get("height");
catchParam.setHeight(heightUse);
logger.debug("改变宽高屏幕=====>width{} height:{} quality:{} frameRate:{}", widthUse, heightUse, catchParam.getQuality(), catchParam.getFrameRate());
AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
if (null != responseThread) { //图片流
ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(true, 0, widthUse, heightUse, catchParam.getFrameRate(), catchParam.getQuality());
asyncSendCommand(screenStreamCommand);
@ -1313,19 +1277,20 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
catchParam.setRotation(rotationUse);
if ("image".equals(type)) {
logger.debug("接收到开启屏幕指令=====>width{} height:{} quality:{} frameRate:{},rotation:{}", widthUse, heightUse, qualityUse, frameRateUse, rotationUse);
responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
if (null == responseThread || responseThread.isInterrupted() || !responseThread.isAlive()) {
//之前未开启过设备屏幕获取线程则新建
logger.debug("设备【{}】新建屏幕获取线程------",serial);
responseThread = new AndroidScreenResponseThread(session, adbDevice, catchParam);
responseThread.setScreenOnRequest(request);
responseThread = new AndroidScreenResponseThread(adbDevice);
responseThread.startSendScreenToWeb(session,catchParam);
responseThread.setScreenOnRequest(session,request);
responseThread.start();
AndroidDeviceManager.getInstance().saveDeviceScreenThread(adbDevice.getSerial(), responseThread);
} else {
//之前开启过则复用原屏幕获取线程
logger.debug("设备【{}】复用原屏幕获取线程=====",serial);
responseThread.setScreenOnRequest(request);
responseThread.startSendScreenToWeb(session,catchParam);
responseThread.setScreenOnRequest(session,request);
}
} else if ("video".equals(type)) {
//视频流专属的参数
@ -1413,9 +1378,7 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
@Override
public void stopAndExit() {
if (null != responseThread) {
responseThread.stopSendPic2Web();
}
AndroidDeviceManager.getInstance().stopWebScreen(adbDevice.getSerial(),session);
if (null != videoScreenResponseThread) {
videoScreenResponseThread.stopAndExit();
}
@ -1425,9 +1388,6 @@ public class AndroidMessageHandlerThread extends AbstractMessageHandler {
if (null != syncAgentSession) {
syncAgentSession.closeSilence();
}
if (null != screenRecordThread) {
screenRecordThread.stopRecord();
}
if (null != androidPerfThread) {
androidPerfThread.stopThread();
}

View File

@ -1,131 +0,0 @@
package net.northking.cctp.upperComputer.webSocket.thread;
import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.ProcessCmdUtils;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 线
*/
public class AndroidScreenRecordThread extends Thread{
private Logger logger = LoggerFactory.getLogger(AndroidScreenRecordThread.class);
private String serial;
private Process process;
private String recordFileName;
private String tenantId;
private int videoWidth;
private int videoHeight;
public AndroidScreenRecordThread(String serial,String tenantId,int width,int height){
this.serial = serial;
this.tenantId = tenantId;
this.videoWidth = width;
this.videoHeight = height;
setName("[" + serial+"]-record");
}
@Override
public void run() {
String applicationPath = UpperComputerManager.getInstance().getApplicationPath();
applicationPath = applicationPath.replace("\\","/");
File screenRecordPath = new File(applicationPath + "/screenRecordTmp/");
if (!screenRecordPath.exists()) {
screenRecordPath.mkdirs();
}
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String dateFormat = format.format(new Date());
String tmpPath = screenRecordPath.getAbsolutePath();
tmpPath = tmpPath.replace("\\","/");
recordFileName = tmpPath + "/" + serial + "_" + dateFormat + ".mp4";
List<String> cmdList = new ArrayList<>();
cmdList.add("scrcpy");
cmdList.add("-s");
cmdList.add(serial);
cmdList.add("-b");
cmdList.add("2M");
cmdList.add("-Nr");
cmdList.add(recordFileName);
logger.info("[" +serial+ "]录屏开启,时间:{}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
process = ProcessCmdUtils.execCmd(cmdList);
InputStreamReader ir = null;
LineNumberReader input = null;
try {
ir = new InputStreamReader(process.getInputStream(),"UTF-8");
input = new LineNumberReader(ir);
String line;
StringBuilder sb = new StringBuilder();
while ((line = input.readLine()) != null) {
logger.debug(line);
sb.append(line);
}
input.close();
} catch (Exception e) {
logger.warn("读取录屏输出失败",e);
}
logger.info("[" +serial+ "]录屏结束,时间:{}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public String stopRecord(){
if (null != process) {
process.destroy();
logger.debug("录屏进程是否存活:{}",process.isAlive());
//上传文件
logger.debug("[" +serial+ "]录屏停止,开始上传视屏文件");
String serverAddress = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
try {
if (StringUtils.isNotBlank(recordFileName)) {
File file = new File(recordFileName);
//文件存在才上传
if (null != file && file.exists()) {
logger.info("开始上传安卓视频文件设备Id{}", serial);
Attachment upload = HttpUtils.upload(serverAddress + pubUploadPath, recordFileName, tenantId, null, FileBusinessTypeEnum.MOBILE_TASK_VIDEO.getCode());
if (null != upload && StringUtils.isNotBlank(upload.getId())) {
logger.debug("文件上传成功返回id{}", upload.getId());
Map<String, String> resultMap = new HashMap<>();
resultMap.put("videoUrl", upload.getUrlPath());
resultMap.put("videoRef", videoWidth + "x" + videoHeight);
return JSON.toJSONString(resultMap);
}
}
}else {
logger.warn("[" +serial+ "]录屏文件路径为空");
}
}catch (Exception e) {
logger.error("上传录像文件异常,",e);
}
}
return null;
}
public void killRecord(){
if (null != process) {
process.destroy();
logger.debug("原录屏进程是否存活:",process.isAlive());
File file = new File(recordFileName);
if (file.exists()) {
file.delete();
}
}
}
}

View File

@ -2,7 +2,6 @@ package net.northking.cctp.upperComputer.webSocket.thread;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.upperComputer.constants.HandCommand;
import net.northking.cctp.upperComputer.constants.IosInputKeyBoardEnum;
import net.northking.cctp.upperComputer.constants.KeyBoardCodeEnum;
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
@ -10,13 +9,8 @@ import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.deviceManager.common.PyMobileDevice;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
import net.northking.cctp.upperComputer.driver.ios.command.data.DragXYData;
import net.northking.cctp.upperComputer.driver.ios.command.data.ForceTapXYData;
import net.northking.cctp.upperComputer.driver.ios.command.data.TypeKeysUiNodeData;
import net.northking.cctp.upperComputer.driver.ios.packet.EmptyCommandData;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
@ -41,6 +35,7 @@ public class IosMacMessageHandlerThread extends AbstractIosMessageHandlerThread
private final Logger logger = LoggerFactory.getLogger(IosMacMessageHandlerThread.class);
public IosMacMessageHandlerThread(PhoneEntity phoneEntity, Session session) throws ParamMistakeException {
super(phoneEntity, session);
}
@ -689,7 +684,7 @@ public class IosMacMessageHandlerThread extends AbstractIosMessageHandlerThread
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
Attachment upload = null;
try {
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_RECORD_SCREENSHOT.getCode());
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
} catch (Exception e) {
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
}
@ -712,9 +707,7 @@ public class IosMacMessageHandlerThread extends AbstractIosMessageHandlerThread
@Override
public void stopAndExit() {
if (null != screenResponseThread) {
screenResponseThread.stopFetchPic();
}
IOSDeviceManager.getInstance().stopWebScreen(phoneEntity.getUdid(),session);
if (null != iosPerfThread) {
iosPerfThread.stopThread();
}

View File

@ -10,7 +10,6 @@ import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
@ -20,7 +19,6 @@ import net.northking.cctp.upperComputer.utils.SpringUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import net.northking.cctp.upperComputer.webSocket.entity.ParamCheck;
import net.northking.cctp.upperComputer.webSocket.entity.Point;
import net.northking.cctp.upperComputer.webSocket.function.IosFunction;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -112,7 +110,6 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa
}
@Override
public void stopLogcat(CmdRequest request) {
Map<String, Object> data = request.getData();
@ -648,9 +645,7 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa
function.swipe(startX, startY, endX, endY);
} else { //点击
if (time < 2000) {//点击
logger.debug("设备【{}】准备请求点击坐标x:{},y:{}",phoneEntity.getUdid(),startX,startY);
function.click(startX, startY);
logger.debug("设备【{}】点击完成",phoneEntity.getUdid(),startX,startY);
} else { //长按
function.longPress(startX, startY);
}
@ -718,7 +713,7 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa
String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
Attachment upload = null;
try {
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId, null, FileBusinessTypeEnum.MOBILE_TASK_SCREENSHOT.getCode());
upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
} catch (Exception e) {
logger.debug("[{}]上传截图失败", phoneEntity.getUdid(), e);
}
@ -740,9 +735,7 @@ public class IosWindowsAndLinuxMessageHandlerThread extends AbstractIosMessageHa
@Override
public void stopAndExit() {
if (null != screenResponseThread) {
screenResponseThread.stopFetchPic();
}
IOSDeviceManager.getInstance().stopWebScreen(phoneEntity.getUdid(),session);
if (null != iosPerfThread) {
iosPerfThread.stopThread();
}

View File

@ -23,10 +23,6 @@ public interface MessageHandler {
public void changeScreenQuality(CmdRequest request);
public void startRecordScreen(CmdRequest request);
public void stopRecordScreen(CmdRequest request);
public void screenShot(CmdRequest request);
public void touchDown(CmdRequest request);