You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

542 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <QDir>
#include <QMessageBox>
#include <QTimer>
#include "config.h"
#include "controller.h"
#include "devicemsg.h"
#include "decoder.h"
#include "device.h"
#include "filehandler.h"
#include "recorder.h"
#include "server.h"
#include "stream.h"
#include "videoform.h"
namespace qsc {
Device::Device(DeviceParams params, QObject *parent) : IDevice(parent), m_params(params)
{
if (!params.display && !m_params.recordFile) {
qCritical("not display must be recorded");
return;
}
if (params.display) {
m_decoder = new Decoder([this](int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV) {
for (const auto& item : m_deviceObservers) {
item->onFrame(width, height, dataY, dataU, dataV, linesizeY, linesizeU, linesizeV);
}
}, this);
m_fileHandler = new FileHandler(this);
m_controller = new Controller([this](const QByteArray& buffer) -> qint64 {
if (!m_server || !m_server->getControlSocket()) {
return 0;
}
return m_server->getControlSocket()->write(buffer.data(), buffer.length());
}, params.gameScript, this);
}
m_stream = new Stream([this](quint8 *buf, qint32 bufSize) -> qint32 {
auto videoSocket = m_server->getVideoSocket();
if (!videoSocket) {
return 0;
}
return videoSocket->subThreadRecvData(buf, bufSize);
}, this);
m_server = new Server(this);
if (m_params.recordFile && !m_params.recordPath.trimmed().isEmpty()) {
QString absFilePath;
QString fileDir(m_params.recordPath);
if (!fileDir.isEmpty()) {
QDateTime dateTime = QDateTime::currentDateTime();
QString fileName = dateTime.toString("_yyyyMMdd_hhmmss_zzz");
fileName = m_params.serial + fileName + "." + m_params.recordFileFormat;
QDir dir(fileDir);
absFilePath = dir.absoluteFilePath(fileName);
}
m_recorder = new Recorder(absFilePath, this);
}
initSignals();
}
Device::~Device()
{
Device::disconnectDevice();
}
void Device::setUserData(void *data)
{
m_userData = data;
}
void *Device::getUserData()
{
return m_userData;
}
void Device::registerDeviceObserver(DeviceObserver *observer)
{
m_deviceObservers.insert(observer);
}
void Device::deRegisterDeviceObserver(DeviceObserver *observer)
{
m_deviceObservers.erase(observer);
}
const QString &Device::getSerial()
{
return m_params.serial;
}
void Device::updateScript(QString script)
{
if (m_controller) {
m_controller->updateScript(script);
}
}
void Device::screenshot()
{
if (!m_decoder) {
return;
}
// screenshot
m_decoder->peekFrame([this](int width, int height, uint8_t* dataRGB32) {
saveFrame(width, height, dataRGB32);
});
}
void Device::showTouch(bool show)
{
AdbProcess *adb = new AdbProcess();
if (!adb) {
return;
}
connect(adb, &AdbProcess::adbProcessResult, this, [this](AdbProcess::ADB_EXEC_RESULT processResult) {
if (AdbProcess::AER_SUCCESS_START != processResult) {
sender()->deleteLater();
}
});
adb->setShowTouchesEnabled(getSerial(), show);
qInfo() << getSerial() << " show touch " << (show ? "enable" : "disable");
}
bool Device::isReversePort(quint16 port)
{
if (m_server && m_server->isReverse() && port == m_server->getParams().localPort) {
return true;
}
return false;
}
void Device::initSignals()
{
if (m_controller) {
connect(m_controller, &Controller::grabCursor, this, [this](bool grab){
for (const auto& item : m_deviceObservers) {
item->grabCursor(grab);
}
});
}
if (m_fileHandler) {
connect(m_fileHandler, &FileHandler::fileHandlerResult, this, [](FileHandler::FILE_HANDLER_RESULT processResult, bool isApk) {
QString tipsType = "";
if (isApk) {
tipsType = tr("install apk");
} else {
tipsType = tr("file transfer");
}
QString tips;
if (FileHandler::FAR_IS_RUNNING == processResult) {
tips = tr("wait current %1 to complete").arg(tipsType);
}
if (FileHandler::FAR_SUCCESS_EXEC == processResult) {
tips = tr("%1 complete, save in %2").arg(tipsType).arg(Config::getInstance().getPushFilePath());
}
if (FileHandler::FAR_ERROR_EXEC == processResult) {
tips = tr("%1 failed").arg(tipsType);
}
qInfo() << tips;
});
}
if (m_server) {
connect(m_server, &Server::serverStarted, this, [this](bool success, const QString &deviceName, const QSize &size) {
emit deviceConnected(success, m_params.serial, deviceName, size);
if (success) {
double diff = m_startTimeCount.elapsed() / 1000.0;
qInfo() << QString("server start finish in %1s").arg(diff).toStdString().c_str();
// init recorder
if (m_recorder) {
m_recorder->setFrameSize(size);
if (!m_recorder->open()) {
qCritical("Could not open recorder");
}
if (!m_recorder->startRecorder()) {
qCritical("Could not start recorder");
}
}
// init decoder
if (m_decoder) {
m_decoder->open();
}
// init decoder
m_stream->startDecode();
// recv device msg
connect(m_server->getControlSocket(), &QTcpSocket::readyRead, this, [this](){
if (!m_controller) {
return;
}
auto controlSocket = m_server->getControlSocket();
while (controlSocket->bytesAvailable()) {
QByteArray byteArray = controlSocket->peek(controlSocket->bytesAvailable());
DeviceMsg deviceMsg;
qint32 consume = deviceMsg.deserialize(byteArray);
if (0 >= consume) {
break;
}
controlSocket->read(consume);
m_controller->recvDeviceMsg(&deviceMsg);
}
});
// 显示界面时才自动息屏m_params.display
if (m_params.closeScreen && m_params.display && m_controller) {
m_controller->setScreenPowerMode(ControlMsg::SPM_OFF);
}
} else {
m_server->stop();
}
});
connect(m_server, &Server::serverStoped, this, [this]() {
disconnectDevice();
qDebug() << "server process stop";
});
}
if (m_stream) {
connect(m_stream, &Stream::onStreamStop, this, [this]() {
disconnectDevice();
qDebug() << "stream thread stop";
});
connect(m_stream, &Stream::getFrame, this, [this](AVPacket *packet) {
if (m_decoder && !m_decoder->push(packet)) {
qCritical("Could not send packet to decoder");
}
if (m_recorder && !m_recorder->push(packet)) {
qCritical("Could not send packet to recorder");
}
}, Qt::DirectConnection);
connect(m_stream, &Stream::getConfigFrame, this, [this](AVPacket *packet) {
if (m_recorder && !m_recorder->push(packet)) {
qCritical("Could not send config packet to recorder");
}
}, Qt::DirectConnection);
}
if (m_decoder) {
connect(m_decoder, &Decoder::updateFPS, this, [this](quint32 fps) {
for (const auto& item : m_deviceObservers) {
item->updateFPS(fps);
}
});
}
}
bool Device::connectDevice()
{
if (!m_server) {
return false;
}
// fix: macos cant recv finished signel, timer is ok
QTimer::singleShot(0, this, [this]() {
m_startTimeCount.start();
// max size support 480p 720p 1080p 设备原生分辨率
// support wireless connect, example:
//m_server->start("192.168.0.174:5555", 27183, m_maxSize, m_bitRate, "");
// only one devices, serial can be null
// mark: crop input format: "width:height:x:y" or "" for no crop, for example: "100:200:0:0"
Server::ServerParams params;
params.serverLocalPath = m_params.serverLocalPath;
params.serverRemotePath = m_params.serverRemotePath;
params.serial = m_params.serial;
params.localPort = m_params.localPort;
params.maxSize = m_params.maxSize;
params.bitRate = m_params.bitRate;
params.maxFps = m_params.maxFps;
params.useReverse = m_params.useReverse;
params.lockVideoOrientation = m_params.lockVideoOrientation;
params.stayAwake = m_params.stayAwake;
params.crop = "";
params.control = true;
m_server->start(params);
});
return true;
}
void Device::disconnectDevice()
{
if (!m_server) {
return;
}
m_server->stop();
m_server = Q_NULLPTR;
if (m_stream) {
m_stream->stopDecode();
}
// server must stop before decoder, because decoder block main thread
if (m_decoder) {
m_decoder->close();
}
if (m_recorder) {
if (m_recorder->isRunning()) {
m_recorder->stopRecorder();
m_recorder->wait();
}
m_recorder->close();
}
emit deviceDisconnected(m_params.serial);
}
void Device::postGoBack()
{
if (!m_controller) {
return;
}
m_controller->postGoBack();
}
void Device::postGoHome()
{
if (!m_controller) {
return;
}
m_controller->postGoHome();
}
void Device::postGoMenu()
{
if (!m_controller) {
return;
}
m_controller->postGoMenu();
}
void Device::postAppSwitch()
{
if (!m_controller) {
return;
}
m_controller->postAppSwitch();
}
void Device::postPower()
{
if (!m_controller) {
return;
}
m_controller->postPower();
}
void Device::postVolumeUp()
{
if (!m_controller) {
return;
}
m_controller->postVolumeUp();
}
void Device::postVolumeDown()
{
if (!m_controller) {
return;
}
m_controller->postVolumeDown();
}
void Device::postCopy()
{
if (!m_controller) {
return;
}
m_controller->copy();
}
void Device::postCut()
{
if (!m_controller) {
return;
}
m_controller->cut();
}
void Device::setScreenPowerMode(bool open)
{
if (!m_controller) {
return;
}
ControlMsg::ScreenPowerMode mode{};
if (open) {
mode = ControlMsg::SPM_NORMAL;
} else {
mode = ControlMsg::SPM_OFF;
}
m_controller->setScreenPowerMode(mode);
}
void Device::expandNotificationPanel()
{
if (!m_controller) {
return;
}
m_controller->expandNotificationPanel();
}
void Device::collapsePanel()
{
if (!m_controller) {
return;
}
m_controller->collapsePanel();
}
void Device::postBackOrScreenOn(bool down)
{
if (!m_controller) {
return;
}
m_controller->postBackOrScreenOn(down);
}
void Device::postTextInput(QString &text)
{
if (!m_controller) {
return;
}
m_controller->postTextInput(text);
}
void Device::requestDeviceClipboard()
{
if (!m_controller) {
return;
}
m_controller->requestDeviceClipboard();
}
void Device::setDeviceClipboard(bool pause)
{
if (!m_controller) {
return;
}
m_controller->setDeviceClipboard(pause);
}
void Device::clipboardPaste()
{
if (!m_controller) {
return;
}
m_controller->clipboardPaste();
}
void Device::pushFileRequest(const QString &file, const QString &devicePath)
{
if (!m_fileHandler) {
return;
}
m_fileHandler->onPushFileRequest(getSerial(), file, devicePath);
}
void Device::installApkRequest(const QString &apkFile)
{
if (!m_fileHandler) {
return;
}
m_fileHandler->onInstallApkRequest(getSerial(), apkFile);
}
void Device::mouseEvent(const QMouseEvent *from, const QSize &frameSize, const QSize &showSize)
{
if (!m_controller) {
return;
}
m_controller->mouseEvent(from, frameSize, showSize);
}
void Device::wheelEvent(const QWheelEvent *from, const QSize &frameSize, const QSize &showSize)
{
if (!m_controller) {
return;
}
m_controller->wheelEvent(from, frameSize, showSize);
}
void Device::keyEvent(const QKeyEvent *from, const QSize &frameSize, const QSize &showSize)
{
if (!m_controller) {
return;
}
m_controller->keyEvent(from, frameSize, showSize);
}
bool Device::isCurrentCustomKeymap()
{
if (!m_controller) {
return false;
}
return m_controller->isCurrentCustomKeymap();
}
bool Device::saveFrame(int width, int height, uint8_t* dataRGB32)
{
if (!dataRGB32) {
return false;
}
QImage rgbImage(dataRGB32, width, height, QImage::Format_RGB32);
// save
QString absFilePath;
QString fileDir(m_params.recordPath);
if (fileDir.isEmpty()) {
qWarning() << "please select record save path!!!";
return false;
}
QDateTime dateTime = QDateTime::currentDateTime();
QString fileName = dateTime.toString("_yyyyMMdd_hhmmss_zzz");
fileName = Config::getInstance().getTitle() + fileName + ".png";
QDir dir(fileDir);
absFilePath = dir.absoluteFilePath(fileName);
int ret = rgbImage.save(absFilePath, "PNG", 100);
if (!ret) {
return false;
}
qInfo() << "screenshot save to " << absFilePath;
return true;
}
}