update:视频传输和控制指令改为两个不同的socket videosocket负责视频接收 controlsocket负责指令发送

This commit is contained in:
Barry 2019-06-18 20:25:38 +08:00
parent 7220d91ed3
commit b44f78f19b
18 changed files with 181 additions and 125 deletions

View file

@ -6,16 +6,16 @@ class QScrcpyEvent : public QEvent
{
public:
enum Type {
DeviceSocket = QEvent::User + 1,
VideoSocket = QEvent::User + 1,
Control,
};
QScrcpyEvent(Type type) : QEvent(QEvent::Type(type)){}
};
// DeviceSocketEvent
class DeviceSocketEvent : public QScrcpyEvent
// VideoSocketEvent
class VideoSocketEvent : public QScrcpyEvent
{
public:
DeviceSocketEvent() : QScrcpyEvent(DeviceSocket){}
VideoSocketEvent() : QScrcpyEvent(VideoSocket){}
};
#endif // QSCRCPYEVENT_H

View file

@ -1,7 +1,7 @@
#include <QCoreApplication>
#include "controller.h"
#include "devicesocket.h"
#include "videosocket.h"
#include "controlevent.h"
Controller::Controller(QObject* parent) : QObject(parent)
@ -14,9 +14,9 @@ Controller::~Controller()
}
void Controller::setDeviceSocket(DeviceSocket *deviceSocket)
void Controller::setControlSocket(QTcpSocket* controlSocket)
{
m_deviceSocket = deviceSocket;
m_controlSocket = controlSocket;
}
void Controller::postControlEvent(ControlEvent *controlEvent)
@ -51,8 +51,8 @@ bool Controller::sendControl(const QByteArray &buffer)
return false;
}
qint32 len = 0;
if (m_deviceSocket) {
len = m_deviceSocket->write(buffer.data(), buffer.length());
if (m_controlSocket) {
len = m_controlSocket->write(buffer.data(), buffer.length());
}
return len == buffer.length() ? true : false;
}

View file

@ -4,7 +4,7 @@
#include <QObject>
#include <QPointer>
class DeviceSocket;
class QTcpSocket;
class ControlEvent;
class Controller : public QObject
{
@ -13,7 +13,7 @@ public:
Controller(QObject* parent = Q_NULLPTR);
virtual ~Controller();
void setDeviceSocket(DeviceSocket* deviceSocket);
void setControlSocket(QTcpSocket* controlSocket);
void postControlEvent(ControlEvent* controlEvent);
void test(QRect rc);
@ -24,7 +24,7 @@ private:
bool sendControl(const QByteArray& buffer);
private:
QPointer<DeviceSocket> m_deviceSocket;
QPointer<QTcpSocket> m_controlSocket;
};
#endif // CONTROLLER_H

View file

@ -10,9 +10,9 @@ InputConvertBase::~InputConvertBase()
}
void InputConvertBase::setDeviceSocket(DeviceSocket *deviceSocket)
void InputConvertBase::setControlSocket(QTcpSocket *controlSocket)
{
m_controller.setDeviceSocket(deviceSocket);
m_controller.setControlSocket(controlSocket);
}
void InputConvertBase::sendControlEvent(ControlEvent *event)

View file

@ -20,7 +20,7 @@ public:
virtual void wheelEvent(const QWheelEvent* from, const QSize& frameSize, const QSize& showSize) = 0;
virtual void keyEvent(const QKeyEvent* from, const QSize& frameSize, const QSize& showSize) = 0;
void setDeviceSocket(DeviceSocket* deviceSocket);
void setControlSocket(QTcpSocket* controlSocket);
void sendControlEvent(ControlEvent* event);
private:

View file

@ -1,7 +1,6 @@
#include <QDebug>
#include <QTimer>
#include <QThread>
#include <QSize>
#include <QTimerEvent>
#include <QCoreApplication>
#include <QFileInfo>
@ -17,25 +16,30 @@ Server::Server(QObject *parent) : QObject(parent)
connect(&m_workProcess, &AdbProcess::adbProcessResult, this, &Server::onWorkProcessResult);
connect(&m_serverProcess, &AdbProcess::adbProcessResult, this, &Server::onWorkProcessResult);
connect(&m_serverSocket, &QTcpServer::newConnection, this, [this](){
m_deviceSocket = dynamic_cast<DeviceSocket*>(m_serverSocket.nextPendingConnection());
QString deviceName;
QSize deviceSize;
if (m_deviceSocket->isValid() && readInfo(deviceName, deviceSize)) {
// we don't need the server socket anymore
// just m_deviceSocket is ok
m_serverSocket.close();
// we don't need the adb tunnel anymore
disableTunnelReverse();
m_tunnelEnabled = false;
emit connectToResult(true, deviceName, deviceSize);
connect(&m_serverSocket, &QTcpServer::newConnection, this, [this](){
QTcpSocket* tmp = m_serverSocket.nextPendingConnection();
if (dynamic_cast<VideoSocket*>(tmp)) {
m_videoSocket = dynamic_cast<VideoSocket*>(tmp);
if (!m_videoSocket->isValid() || !readInfo(m_deviceName, m_deviceSize)) {
stop();
emit connectToResult(false);
}
} else {
stop();
emit connectToResult(false, deviceName, deviceSize);
m_controlSocket = tmp;
if (m_controlSocket && m_controlSocket->isValid()) {
// we don't need the server socket anymore
// just m_videoSocket is ok
m_serverSocket.close();
// we don't need the adb tunnel anymore
disableTunnelReverse();
m_tunnelEnabled = false;
emit connectToResult(true, m_deviceName, m_deviceSize);
} else {
stop();
emit connectToResult(false);
}
stopAcceptTimeoutTimer();
}
stopAcceptTimeoutTimer();
});
}
@ -159,7 +163,7 @@ bool Server::connectTo()
return false;
}
if (!m_tunnelForward && !m_deviceSocket) {
if (!m_tunnelForward && !m_videoSocket) {
startAcceptTimeoutTimer();
return true;
}
@ -167,39 +171,49 @@ bool Server::connectTo()
// device server need time to start
// TODO:电脑配置太低的话这里有可能时间不够导致连接太早安卓监听socket还没有建立
// 后续研究其他办法
// wait for devices server start
QTimer::singleShot(1000, this, [this](){
QString deviceName;
QSize deviceSize;
bool success = false;
m_deviceSocket = new DeviceSocket();
// wait for devices server start
m_deviceSocket->connectToHost(QHostAddress::LocalHost, m_localPort);
if (!m_deviceSocket->waitForConnected(1000)) {
// video socket
m_videoSocket = new VideoSocket();
m_videoSocket->connectToHost(QHostAddress::LocalHost, m_localPort);
if (!m_videoSocket->waitForConnected(1000)) {
stop();
qWarning("connect to server failed");
qWarning("video socket connect to server failed");
emit connectToResult(false, "", QSize());
return false;
}
if (QTcpSocket::ConnectedState == m_deviceSocket->state()) {
if (QTcpSocket::ConnectedState == m_videoSocket->state()) {
// connect will success even if devices offline, recv data is real connect success
// because connect is to pc adb server
m_deviceSocket->waitForReadyRead(1000);
m_videoSocket->waitForReadyRead(1000);
// devices will send 1 byte first on tunnel forward mode
QByteArray data = m_deviceSocket->read(1);
QByteArray data = m_videoSocket->read(1);
if (!data.isEmpty() && readInfo(deviceName, deviceSize)) {
success = true;
} else {
qWarning("connect to server read device info failed");
qWarning("video socket connect to server read device info failed");
success = false;
}
} else {
qWarning("connect to server failed");
m_deviceSocket->deleteLater();
m_videoSocket->deleteLater();
success = false;
}
// control socket
m_controlSocket = new QTcpSocket();
m_controlSocket->connectToHost(QHostAddress::LocalHost, m_localPort);
if (!m_controlSocket->waitForConnected(1000)) {
stop();
qWarning("control socket connect to server failed");
emit connectToResult(false, "", QSize());
return false;
}
if (success) {
// we don't need the adb tunnel anymore
disableTunnelForward();
@ -221,16 +235,25 @@ void Server::timerEvent(QTimerEvent *event)
}
}
DeviceSocket* Server::getDeviceSocket()
VideoSocket* Server::getVideoSocket()
{
return m_deviceSocket;
return m_videoSocket;
}
QTcpSocket *Server::getControlSocket()
{
return m_controlSocket;
}
void Server::stop()
{
if (m_deviceSocket) {
m_deviceSocket->close();
m_deviceSocket->deleteLater();
if (m_videoSocket) {
m_videoSocket->close();
m_videoSocket->deleteLater();
}
if (m_controlSocket) {
m_controlSocket->close();
m_controlSocket->deleteLater();
}
// ignore failure
m_serverProcess.kill();
@ -270,7 +293,7 @@ bool Server::startServerByStep()
// client listens and the server connects to the client. That way, the
// client can listen before starting the server app, so there is no need to
// try to connect until the server socket is listening on the device.
m_serverSocket.setMaxPendingConnections(1);
m_serverSocket.setMaxPendingConnections(2);
if (!m_serverSocket.listen(QHostAddress::LocalHost, m_localPort)) {
qCritical(QString("Could not listen on port %1").arg(m_localPort).toStdString().c_str());
m_serverStartStep = SSS_NULL;
@ -300,11 +323,11 @@ bool Server::startServerByStep()
bool Server::readInfo(QString &deviceName, QSize &size)
{
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
if (m_deviceSocket->bytesAvailable() <= (DEVICE_NAME_FIELD_LENGTH + 4)) {
m_deviceSocket->waitForReadyRead(300);
if (m_videoSocket->bytesAvailable() <= (DEVICE_NAME_FIELD_LENGTH + 4)) {
m_videoSocket->waitForReadyRead(300);
}
qint64 len = m_deviceSocket->read((char*)buf, sizeof(buf));
qint64 len = m_videoSocket->read((char*)buf, sizeof(buf));
if (len < DEVICE_NAME_FIELD_LENGTH + 4) {
qInfo("Could not retrieve device information");
return false;

View file

@ -3,9 +3,10 @@
#include <QObject>
#include <QPointer>
#include <QSize>
#include "tcpserver.h"
#include "devicesocket.h"
#include "videosocket.h"
#include "adbprocess.h"
class Server : public QObject
@ -27,13 +28,14 @@ public:
bool start(const QString& serial, quint16 localPort, quint16 maxSize, quint32 bitRate, const QString& crop, bool sendFrameMeta = false);
bool connectTo();
DeviceSocket* getDeviceSocket();
VideoSocket* getVideoSocket();
QTcpSocket* getControlSocket();
void stop();
signals:
void serverStartResult(bool success);
void connectToResult(bool success, const QString &deviceName, const QSize &size);
void connectToResult(bool success, const QString &deviceName = "", const QSize &size = QSize());
void onServerStop();
private slots:
@ -62,7 +64,8 @@ private:
QString m_serial = "";
AdbProcess m_serverProcess;
TcpServer m_serverSocket; // only used if !tunnel_forward
QPointer<DeviceSocket> m_deviceSocket = Q_NULLPTR;
QPointer<VideoSocket> m_videoSocket = Q_NULLPTR;
QPointer<QTcpSocket> m_controlSocket = Q_NULLPTR;
quint16 m_localPort = 0;
bool m_tunnelEnabled = false;
bool m_tunnelForward = false; // use "adb forward" instead of "adb reverse"
@ -71,6 +74,8 @@ private:
quint32 m_bitRate = 0;
QString m_crop = "";
quint32 m_acceptTimeoutTimer = 0;
QString m_deviceName = "";
QSize m_deviceSize = QSize();
SERVER_START_STEP m_serverStartStep = SSS_NULL;
};

View file

@ -1,9 +1,9 @@
HEADERS += \
$$PWD/devicesocket.h \
$$PWD/server.h \
$$PWD/tcpserver.h
$$PWD/tcpserver.h \
$$PWD/videosocket.h
SOURCES += \
$$PWD/devicesocket.cpp \
$$PWD/server.cpp \
$$PWD/tcpserver.cpp
$$PWD/tcpserver.cpp \
$$PWD/videosocket.cpp

View file

@ -1,5 +1,5 @@
#include "tcpserver.h"
#include "devicesocket.h"
#include "videosocket.h"
TcpServer::TcpServer(QObject *parent) : QTcpServer(parent)
{
@ -13,7 +13,16 @@ TcpServer::~TcpServer()
void TcpServer::incomingConnection(qintptr handle)
{
DeviceSocket *socket = new DeviceSocket();
socket->setSocketDescriptor(handle);
addPendingConnection(socket);
if (m_isVideoSocket) {
VideoSocket *socket = new VideoSocket();
socket->setSocketDescriptor(handle);
addPendingConnection(socket);
// next is control socket
m_isVideoSocket = false;
} else {
QTcpSocket *socket = new QTcpSocket();
socket->setSocketDescriptor(handle);
addPendingConnection(socket);
}
}

View file

@ -12,6 +12,9 @@ public:
protected:
virtual void incomingConnection(qintptr handle);
private:
bool m_isVideoSocket = true;
};
#endif // TCPSERVER_H

View file

@ -3,21 +3,21 @@
#include <QDebug>
#include "qscrcpyevent.h"
#include "devicesocket.h"
#include "videosocket.h"
DeviceSocket::DeviceSocket(QObject *parent) : QTcpSocket(parent)
VideoSocket::VideoSocket(QObject *parent) : QTcpSocket(parent)
{
connect(this, &DeviceSocket::readyRead, this, &DeviceSocket::onReadyRead);
connect(this, &DeviceSocket::aboutToClose, this, &DeviceSocket::quitNotify);
connect(this, &DeviceSocket::disconnected, this, &DeviceSocket::quitNotify);
connect(this, &VideoSocket::readyRead, this, &VideoSocket::onReadyRead);
connect(this, &VideoSocket::aboutToClose, this, &VideoSocket::quitNotify);
connect(this, &VideoSocket::disconnected, this, &VideoSocket::quitNotify);
}
DeviceSocket::~DeviceSocket()
VideoSocket::~VideoSocket()
{
quitNotify();
}
qint32 DeviceSocket::subThreadRecvData(quint8 *buf, qint32 bufSize)
qint32 VideoSocket::subThreadRecvData(quint8 *buf, qint32 bufSize)
{
// this function cant call in main thread
Q_ASSERT(QCoreApplication::instance()->thread() != QThread::currentThread());
@ -31,7 +31,7 @@ qint32 DeviceSocket::subThreadRecvData(quint8 *buf, qint32 bufSize)
m_dataSize = 0;
// post event
DeviceSocketEvent* getDataEvent = new DeviceSocketEvent();
VideoSocketEvent* getDataEvent = new VideoSocketEvent();
QCoreApplication::postEvent(this, getDataEvent);
// wait
@ -43,16 +43,16 @@ qint32 DeviceSocket::subThreadRecvData(quint8 *buf, qint32 bufSize)
return m_dataSize;
}
bool DeviceSocket::event(QEvent *event)
bool VideoSocket::event(QEvent *event)
{
if (event->type() == QScrcpyEvent::DeviceSocket) {
if (event->type() == QScrcpyEvent::VideoSocket) {
onReadyRead();
return true;
}
return QTcpSocket::event(event);
}
void DeviceSocket::onReadyRead()
void VideoSocket::onReadyRead()
{
QMutexLocker locker(&m_mutex);
if (m_buffer && 0 < bytesAvailable()) {
@ -67,7 +67,7 @@ void DeviceSocket::onReadyRead()
}
}
void DeviceSocket::quitNotify()
void VideoSocket::quitNotify()
{
m_quit = true;
QMutexLocker locker(&m_mutex);

View file

@ -1,17 +1,17 @@
#ifndef DEVICESOCKET_H
#define DEVICESOCKET_H
#ifndef VIDEOSOCKET_H
#define VIDEOSOCKET_H
#include <QEvent>
#include <QTcpSocket>
#include <QMutex>
#include <QWaitCondition>
class DeviceSocket : public QTcpSocket
class VideoSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit DeviceSocket(QObject *parent = nullptr);
virtual ~DeviceSocket();
explicit VideoSocket(QObject *parent = nullptr);
virtual ~VideoSocket();
qint32 subThreadRecvData(quint8* buf, qint32 bufSize);
@ -32,4 +32,4 @@ private:
bool m_quit = false;
};
#endif // DEVICESOCKET_H
#endif // VIDEOSOCKET_H

View file

@ -4,7 +4,7 @@
#include "compat.h"
#include "stream.h"
#include "decoder.h"
#include "devicesocket.h"
#include "videosocket.h"
#include "recorder.h"
#define BUFSIZE 0x10000
@ -199,9 +199,9 @@ static qint32 readRawPacket(void *opaque, quint8 *buf, qint32 bufSize) {
return AVERROR_EOF;
}
void Stream::setDeviceSocket(DeviceSocket* deviceSocket)
void Stream::setVideoSocket(VideoSocket* videoSocket)
{
m_deviceSocket = deviceSocket;
m_videoSocket = videoSocket;
}
void Stream::setRecoder(Recorder *recorder)
@ -214,8 +214,8 @@ qint32 Stream::recvData(quint8* buf, qint32 bufSize)
if (!buf) {
return 0;
}
if (m_deviceSocket) {
qint32 len = m_deviceSocket->subThreadRecvData(buf, bufSize);
if (m_videoSocket) {
qint32 len = m_videoSocket->subThreadRecvData(buf, bufSize);
return len;
}
return 0;
@ -223,7 +223,7 @@ qint32 Stream::recvData(quint8* buf, qint32 bufSize)
bool Stream::startDecode()
{
if (!m_deviceSocket) {
if (!m_videoSocket) {
return false;
}
m_quit.store(0);

View file

@ -12,7 +12,7 @@ extern "C"
#include "libavformat/avformat.h"
}
class DeviceSocket;
class VideoSocket;
class Recorder;
class Decoder;
class Stream : public QThread
@ -38,7 +38,7 @@ public:
static void deInit();
void setDecoder(Decoder* vb);
void setDeviceSocket(DeviceSocket* deviceSocket);
void setVideoSocket(VideoSocket* deviceSocket);
void setRecoder(Recorder* recorder);
qint32 recvData(quint8* buf, qint32 bufSize);
bool startDecode();
@ -52,7 +52,7 @@ protected:
void run();
private:
QPointer<DeviceSocket> m_deviceSocket;
QPointer<VideoSocket> m_videoSocket;
QAtomicInteger<qint8> m_quit;
// for recorder

View file

@ -171,11 +171,11 @@ void VideoForm::initSignals()
}
// init decoder
m_stream.setDeviceSocket(m_server->getDeviceSocket());
m_stream.setVideoSocket(m_server->getVideoSocket());
m_stream.startDecode();
// init controller
m_inputConvert.setDeviceSocket(m_server->getDeviceSocket());
m_inputConvert.setControlSocket(m_server->getControlSocket());
}
});

View file

@ -16,16 +16,20 @@ public final class DesktopConnection implements Closeable {
private static final String SOCKET_NAME = "qtscrcpy";
private final LocalSocket socket;
private final InputStream inputStream;
private final FileDescriptor fd;
private final LocalSocket videoSocket;
private final FileDescriptor videoFd;
private final LocalSocket controlSocket;
private final InputStream controlInputStream;
private final ControlEventReader reader = new ControlEventReader();
private DesktopConnection(LocalSocket socket) throws IOException {
this.socket = socket;
inputStream = socket.getInputStream();
fd = socket.getFileDescriptor();
private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException {
this.videoSocket = videoSocket;
this.controlSocket = controlSocket;
controlInputStream = controlSocket.getInputStream();
videoFd = videoSocket.getFileDescriptor();
}
private static LocalSocket connect(String abstractName) throws IOException {
@ -34,35 +38,47 @@ public final class DesktopConnection implements Closeable {
return localSocket;
}
private static LocalSocket listenAndAccept(String abstractName) throws IOException {
LocalServerSocket localServerSocket = new LocalServerSocket(abstractName);
try {
return localServerSocket.accept();
} finally {
localServerSocket.close();
}
}
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
LocalSocket socket;
LocalSocket videoSocket;
LocalSocket controlSocket;
if (tunnelForward) {
socket = listenAndAccept(SOCKET_NAME);
// send one byte so the client may read() to detect a connection error
socket.getOutputStream().write(0);
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
try {
videoSocket = localServerSocket.accept();
// send one byte so the client may read() to detect a connection error
videoSocket.getOutputStream().write(0);
try {
controlSocket = localServerSocket.accept();
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
} finally {
localServerSocket.close();
}
} else {
socket = connect(SOCKET_NAME);
videoSocket = connect(SOCKET_NAME);
try {
controlSocket = connect(SOCKET_NAME);
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
}
DesktopConnection connection = new DesktopConnection(socket);
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
Size videoSize = device.getScreenInfo().getVideoSize();
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
return connection;
}
public void close() throws IOException {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
videoSocket.shutdownInput();
videoSocket.shutdownOutput();
videoSocket.close();
controlSocket.shutdownInput();
controlSocket.shutdownOutput();
controlSocket.close();
}
@SuppressWarnings("checkstyle:MagicNumber")
@ -78,17 +94,17 @@ public final class DesktopConnection implements Closeable {
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
IO.writeFully(fd, buffer, 0, buffer.length);
IO.writeFully(videoFd, buffer, 0, buffer.length);
}
public FileDescriptor getFd() {
return fd;
public FileDescriptor getVideoFd() {
return videoFd;
}
public ControlEvent receiveControlEvent() throws IOException {
ControlEvent event = reader.next();
while (event == null) {
reader.readFrom(inputStream);
reader.readFrom(controlInputStream);
event = reader.next();
}
return event;

View file

@ -25,7 +25,7 @@ public final class Server {
try {
// synchronous
screenEncoder.streamScreen(device, connection.getFd());
screenEncoder.streamScreen(device, connection.getVideoFd());
} catch (IOException e) {
// this is expected on close
Ln.d("Screen streaming stopped");

Binary file not shown.