#include #include #include #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; } }