mirror of
https://github.com/barry-ran/QtScrcpy.git
synced 2025-09-04 16:46:20 +00:00
commit
bb43261872
30 changed files with 1364 additions and 1129 deletions
|
@ -7,7 +7,9 @@
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
#include "videosocket.h"
|
#include "videosocket.h"
|
||||||
|
|
||||||
Controller::Controller(QString gameScript, QObject *parent) : QObject(parent)
|
Controller::Controller(std::function<qint64(const QByteArray&)> sendData, QString gameScript, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_sendData(sendData)
|
||||||
{
|
{
|
||||||
m_receiver = new Receiver(this);
|
m_receiver = new Receiver(this);
|
||||||
Q_ASSERT(m_receiver);
|
Q_ASSERT(m_receiver);
|
||||||
|
@ -17,15 +19,6 @@ Controller::Controller(QString gameScript, QObject *parent) : QObject(parent)
|
||||||
|
|
||||||
Controller::~Controller() {}
|
Controller::~Controller() {}
|
||||||
|
|
||||||
void Controller::setControlSocket(QTcpSocket *controlSocket)
|
|
||||||
{
|
|
||||||
if (m_controlSocket || !controlSocket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_controlSocket = controlSocket;
|
|
||||||
m_receiver->setControlSocket(controlSocket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::postControlMsg(ControlMsg *controlMsg)
|
void Controller::postControlMsg(ControlMsg *controlMsg)
|
||||||
{
|
{
|
||||||
if (controlMsg) {
|
if (controlMsg) {
|
||||||
|
@ -33,6 +26,15 @@ void Controller::postControlMsg(ControlMsg *controlMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::recvDeviceMsg(DeviceMsg *deviceMsg)
|
||||||
|
{
|
||||||
|
if (!m_receiver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_receiver->recvDeviceMsg(deviceMsg);
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::test(QRect rc)
|
void Controller::test(QRect rc)
|
||||||
{
|
{
|
||||||
ControlMsg *controlMsg = new ControlMsg(ControlMsg::CMT_INJECT_TOUCH);
|
ControlMsg *controlMsg = new ControlMsg(ControlMsg::CMT_INJECT_TOUCH);
|
||||||
|
@ -236,8 +238,8 @@ bool Controller::sendControl(const QByteArray &buffer)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
qint32 len = 0;
|
qint32 len = 0;
|
||||||
if (m_controlSocket) {
|
if (m_sendData) {
|
||||||
len = static_cast<qint32>(m_controlSocket->write(buffer.data(), buffer.length()));
|
len = static_cast<qint32>(m_sendData(buffer));
|
||||||
}
|
}
|
||||||
return len == buffer.length() ? true : false;
|
return len == buffer.length() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
#ifndef CONTROLLER_H
|
#ifndef CONTROLLER_H
|
||||||
#define CONTROLLER_H
|
#define CONTROLLER_H
|
||||||
|
|
||||||
|
@ -9,15 +10,16 @@
|
||||||
class QTcpSocket;
|
class QTcpSocket;
|
||||||
class Receiver;
|
class Receiver;
|
||||||
class InputConvertBase;
|
class InputConvertBase;
|
||||||
|
class DeviceMsg;
|
||||||
class Controller : public QObject
|
class Controller : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Controller(QString gameScript = "", QObject *parent = Q_NULLPTR);
|
Controller(std::function<qint64(const QByteArray&)> sendData, QString gameScript = "", QObject *parent = Q_NULLPTR);
|
||||||
virtual ~Controller();
|
virtual ~Controller();
|
||||||
|
|
||||||
void setControlSocket(QTcpSocket *controlSocket);
|
|
||||||
void postControlMsg(ControlMsg *controlMsg);
|
void postControlMsg(ControlMsg *controlMsg);
|
||||||
|
void recvDeviceMsg(DeviceMsg *deviceMsg);
|
||||||
void test(QRect rc);
|
void test(QRect rc);
|
||||||
|
|
||||||
void updateScript(QString gameScript = "");
|
void updateScript(QString gameScript = "");
|
||||||
|
@ -62,9 +64,9 @@ private:
|
||||||
void postKeyCodeClick(AndroidKeycode keycode);
|
void postKeyCodeClick(AndroidKeycode keycode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<QTcpSocket> m_controlSocket;
|
|
||||||
QPointer<Receiver> m_receiver;
|
QPointer<Receiver> m_receiver;
|
||||||
QPointer<InputConvertBase> m_inputConvert;
|
QPointer<InputConvertBase> m_inputConvert;
|
||||||
|
std::function<qint64(const QByteArray&)> m_sendData = Q_NULLPTR;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONTROLLER_H
|
#endif // CONTROLLER_H
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QTcpSocket>
|
|
||||||
|
|
||||||
#include "devicemsg.h"
|
#include "devicemsg.h"
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
|
@ -9,34 +8,7 @@ Receiver::Receiver(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
Receiver::~Receiver() {}
|
Receiver::~Receiver() {}
|
||||||
|
|
||||||
void Receiver::setControlSocket(QTcpSocket *controlSocket)
|
void Receiver::recvDeviceMsg(DeviceMsg *deviceMsg)
|
||||||
{
|
|
||||||
if (m_controlSocket || !controlSocket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_controlSocket = controlSocket;
|
|
||||||
connect(controlSocket, &QTcpSocket::readyRead, this, &Receiver::onReadyRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Receiver::onReadyRead()
|
|
||||||
{
|
|
||||||
if (!m_controlSocket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (m_controlSocket->bytesAvailable()) {
|
|
||||||
QByteArray byteArray = m_controlSocket->peek(m_controlSocket->bytesAvailable());
|
|
||||||
DeviceMsg deviceMsg;
|
|
||||||
qint32 consume = deviceMsg.deserialize(byteArray);
|
|
||||||
if (0 >= consume) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_controlSocket->read(consume);
|
|
||||||
processMsg(&deviceMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Receiver::processMsg(DeviceMsg *deviceMsg)
|
|
||||||
{
|
{
|
||||||
switch (deviceMsg->type()) {
|
switch (deviceMsg->type()) {
|
||||||
case DeviceMsg::DMT_GET_CLIPBOARD: {
|
case DeviceMsg::DMT_GET_CLIPBOARD: {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
class QTcpSocket;
|
|
||||||
class DeviceMsg;
|
class DeviceMsg;
|
||||||
class Receiver : public QObject
|
class Receiver : public QObject
|
||||||
{
|
{
|
||||||
|
@ -12,16 +11,7 @@ public:
|
||||||
explicit Receiver(QObject *parent = Q_NULLPTR);
|
explicit Receiver(QObject *parent = Q_NULLPTR);
|
||||||
virtual ~Receiver();
|
virtual ~Receiver();
|
||||||
|
|
||||||
void setControlSocket(QTcpSocket *controlSocket);
|
void recvDeviceMsg(DeviceMsg *deviceMsg);
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onReadyRead();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void processMsg(DeviceMsg *deviceMsg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPointer<QTcpSocket> m_controlSocket;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RECEIVER_H
|
#endif // RECEIVER_H
|
||||||
|
|
|
@ -4,12 +4,31 @@
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "videobuffer.h"
|
#include "videobuffer.h"
|
||||||
|
|
||||||
Decoder::Decoder(VideoBuffer *vb, QObject *parent) : QObject(parent), m_vb(vb) {}
|
Decoder::Decoder(std::function<void(int, int, uint8_t*, uint8_t*, uint8_t*, int, int, int)> onFrame, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
Decoder::~Decoder() {}
|
, m_vb(new VideoBuffer())
|
||||||
|
, m_onFrame(onFrame)
|
||||||
bool Decoder::open(const AVCodec *codec)
|
|
||||||
{
|
{
|
||||||
|
m_vb->init();
|
||||||
|
connect(this, &Decoder::newFrame, this, &Decoder::onNewFrame, Qt::QueuedConnection);
|
||||||
|
connect(m_vb, &VideoBuffer::updateFPS, this, &Decoder::updateFPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder::~Decoder() {
|
||||||
|
m_vb->deInit();
|
||||||
|
delete m_vb;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Decoder::open()
|
||||||
|
{
|
||||||
|
// codec
|
||||||
|
AVCodec *codec = Q_NULLPTR;
|
||||||
|
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||||
|
if (!codec) {
|
||||||
|
qCritical("H.264 decoder not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// codec context
|
// codec context
|
||||||
m_codecCtx = avcodec_alloc_context3(codec);
|
m_codecCtx = avcodec_alloc_context3(codec);
|
||||||
if (!m_codecCtx) {
|
if (!m_codecCtx) {
|
||||||
|
@ -26,6 +45,10 @@ bool Decoder::open(const AVCodec *codec)
|
||||||
|
|
||||||
void Decoder::close()
|
void Decoder::close()
|
||||||
{
|
{
|
||||||
|
if (m_vb) {
|
||||||
|
m_vb->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_codecCtx) {
|
if (!m_codecCtx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,11 +121,12 @@ bool Decoder::push(const AVPacket *packet)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::interrupt()
|
void Decoder::peekFrame(std::function<void (int, int, uint8_t *)> onFrame)
|
||||||
{
|
{
|
||||||
if (m_vb) {
|
if (!m_vb) {
|
||||||
m_vb->interrupt();
|
return;
|
||||||
}
|
}
|
||||||
|
m_vb->peekRenderedFrame(onFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Decoder::pushFrame()
|
void Decoder::pushFrame()
|
||||||
|
@ -116,5 +140,16 @@ void Decoder::pushFrame()
|
||||||
// the previous newFrame will consume this frame
|
// the previous newFrame will consume this frame
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit onNewFrame();
|
emit newFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::onNewFrame() {
|
||||||
|
if (!m_onFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_vb->lock();
|
||||||
|
const AVFrame *frame = m_vb->consumeRenderedFrame();
|
||||||
|
m_onFrame(frame->width, frame->height, frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
|
||||||
|
m_vb->unLock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,24 +12,31 @@ class Decoder : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Decoder(VideoBuffer *vb, QObject *parent = Q_NULLPTR);
|
Decoder(std::function<void(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV)> onFrame, QObject *parent = Q_NULLPTR);
|
||||||
virtual ~Decoder();
|
virtual ~Decoder();
|
||||||
|
|
||||||
bool open(const AVCodec *codec);
|
bool open();
|
||||||
void close();
|
void close();
|
||||||
bool push(const AVPacket *packet);
|
bool push(const AVPacket *packet);
|
||||||
void interrupt();
|
void peekFrame(std::function<void(int width, int height, uint8_t* dataRGB32)> onFrame);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void updateFPS(quint32 fps);
|
||||||
|
|
||||||
|
private slots:
|
||||||
void onNewFrame();
|
void onNewFrame();
|
||||||
|
|
||||||
protected:
|
signals:
|
||||||
|
void newFrame();
|
||||||
|
|
||||||
|
private:
|
||||||
void pushFrame();
|
void pushFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoBuffer *m_vb = Q_NULLPTR;
|
VideoBuffer *m_vb = Q_NULLPTR;
|
||||||
AVCodecContext *m_codecCtx = Q_NULLPTR;
|
AVCodecContext *m_codecCtx = Q_NULLPTR;
|
||||||
bool m_isCodecCtxOpen = false;
|
bool m_isCodecCtxOpen = false;
|
||||||
|
std::function<void(int, int, uint8_t*, uint8_t*, uint8_t*, int, int, int)> m_onFrame = Q_NULLPTR;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DECODER_H
|
#endif // DECODER_H
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
#include "videobuffer.h"
|
#include "videobuffer.h"
|
||||||
|
#include "avframeconvert.h"
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#include "libavformat/avformat.h"
|
#include "libavformat/avformat.h"
|
||||||
#include "libavutil/avutil.h"
|
#include "libavutil/avutil.h"
|
||||||
|
#include "libavutil/imgutils.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoBuffer::VideoBuffer() {}
|
VideoBuffer::VideoBuffer(QObject *parent) : QObject(parent) {
|
||||||
|
connect(&m_fpsCounter, &FpsCounter::updateFPS, this, &VideoBuffer::updateFPS);
|
||||||
|
}
|
||||||
|
|
||||||
VideoBuffer::~VideoBuffer() {}
|
VideoBuffer::~VideoBuffer() {}
|
||||||
|
|
||||||
bool VideoBuffer::init(bool renderExpiredFrames)
|
bool VideoBuffer::init()
|
||||||
{
|
{
|
||||||
m_renderExpiredFrames = renderExpiredFrames;
|
|
||||||
m_decodingFrame = av_frame_alloc();
|
m_decodingFrame = av_frame_alloc();
|
||||||
if (!m_decodingFrame) {
|
if (!m_decodingFrame) {
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -57,6 +60,11 @@ void VideoBuffer::unLock()
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoBuffer::setRenderExpiredFrames(bool renderExpiredFrames)
|
||||||
|
{
|
||||||
|
m_renderExpiredFrames = renderExpiredFrames;
|
||||||
|
}
|
||||||
|
|
||||||
AVFrame *VideoBuffer::decodingFrame()
|
AVFrame *VideoBuffer::decodingFrame()
|
||||||
{
|
{
|
||||||
return m_decodingFrame;
|
return m_decodingFrame;
|
||||||
|
@ -99,9 +107,51 @@ const AVFrame *VideoBuffer::consumeRenderedFrame()
|
||||||
return m_renderingframe;
|
return m_renderingframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVFrame *VideoBuffer::peekRenderedFrame()
|
void VideoBuffer::peekRenderedFrame(std::function<void(int width, int height, uint8_t* dataRGB32)> onFrame)
|
||||||
{
|
{
|
||||||
return m_renderingframe;
|
if (!onFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock();
|
||||||
|
auto frame = m_renderingframe;
|
||||||
|
int width = frame->width;
|
||||||
|
int height = frame->height;
|
||||||
|
|
||||||
|
// create buffer
|
||||||
|
uint8_t* rgbBuffer = new uint8_t[width * height * 4];
|
||||||
|
AVFrame *rgbFrame = av_frame_alloc();
|
||||||
|
if (!rgbFrame) {
|
||||||
|
delete [] rgbBuffer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind buffer to AVFrame
|
||||||
|
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, rgbBuffer, AV_PIX_FMT_RGB32, width, height, 4);
|
||||||
|
|
||||||
|
// convert
|
||||||
|
AVFrameConvert convert;
|
||||||
|
convert.setSrcFrameInfo(width, height, AV_PIX_FMT_YUV420P);
|
||||||
|
convert.setDstFrameInfo(width, height, AV_PIX_FMT_RGB32);
|
||||||
|
bool ret = false;
|
||||||
|
ret = convert.init();
|
||||||
|
if (!ret) {
|
||||||
|
delete [] rgbBuffer;
|
||||||
|
av_free(rgbFrame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = convert.convert(frame, rgbFrame);
|
||||||
|
if (!ret) {
|
||||||
|
delete [] rgbBuffer;
|
||||||
|
av_free(rgbFrame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
convert.deInit();
|
||||||
|
av_free(rgbFrame);
|
||||||
|
unLock();
|
||||||
|
|
||||||
|
onFrame(width, height, rgbBuffer);
|
||||||
|
delete [] rgbBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBuffer::interrupt()
|
void VideoBuffer::interrupt()
|
||||||
|
@ -115,11 +165,6 @@ void VideoBuffer::interrupt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FpsCounter *VideoBuffer::getFPSCounter()
|
|
||||||
{
|
|
||||||
return &m_fpsCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoBuffer::swap()
|
void VideoBuffer::swap()
|
||||||
{
|
{
|
||||||
AVFrame *tmp = m_decodingFrame;
|
AVFrame *tmp = m_decodingFrame;
|
||||||
|
|
|
@ -3,22 +3,25 @@
|
||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "fpscounter.h"
|
#include "fpscounter.h"
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
typedef struct AVFrame AVFrame;
|
typedef struct AVFrame AVFrame;
|
||||||
|
|
||||||
class VideoBuffer
|
class VideoBuffer : public QObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VideoBuffer();
|
VideoBuffer(QObject *parent = Q_NULLPTR);
|
||||||
virtual ~VideoBuffer();
|
virtual ~VideoBuffer();
|
||||||
|
|
||||||
bool init(bool renderExpiredFrames = false);
|
bool init();
|
||||||
void deInit();
|
void deInit();
|
||||||
void lock();
|
void lock();
|
||||||
void unLock();
|
void unLock();
|
||||||
|
void setRenderExpiredFrames(bool renderExpiredFrames);
|
||||||
|
|
||||||
AVFrame *decodingFrame();
|
AVFrame *decodingFrame();
|
||||||
// set the decoder frame as ready for rendering
|
// set the decoder frame as ready for rendering
|
||||||
|
@ -32,12 +35,13 @@ public:
|
||||||
// unlocking m_mutex
|
// unlocking m_mutex
|
||||||
const AVFrame *consumeRenderedFrame();
|
const AVFrame *consumeRenderedFrame();
|
||||||
|
|
||||||
const AVFrame *peekRenderedFrame();
|
void peekRenderedFrame(std::function<void(int width, int height, uint8_t* dataRGB32)> onFrame);
|
||||||
|
|
||||||
// wake up and avoid any blocking call
|
// wake up and avoid any blocking call
|
||||||
void interrupt();
|
void interrupt();
|
||||||
|
|
||||||
FpsCounter *getFPSCounter();
|
signals:
|
||||||
|
void updateFPS(quint32 fps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void swap();
|
void swap();
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "avframeconvert.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "devicemsg.h"
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "filehandler.h"
|
#include "filehandler.h"
|
||||||
|
@ -12,12 +12,7 @@
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "videobuffer.h"
|
|
||||||
#include "videoform.h"
|
#include "videoform.h"
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#include "libavutil/imgutils.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::Device(DeviceParams params, QObject *parent) : QObject(parent), m_params(params)
|
Device::Device(DeviceParams params, QObject *parent) : QObject(parent), m_params(params)
|
||||||
{
|
{
|
||||||
|
@ -28,23 +23,36 @@ Device::Device(DeviceParams params, QObject *parent) : QObject(parent), m_params
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.display) {
|
if (params.display) {
|
||||||
m_vb = new VideoBuffer();
|
|
||||||
m_vb->init(params.renderExpiredFrames);
|
m_decoder = new Decoder([this](int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV) {
|
||||||
m_decoder = new Decoder(m_vb, this);
|
if (m_videoForm) {
|
||||||
|
m_videoForm->updateRender(width, height, dataY, dataU, dataV, linesizeY, linesizeU, linesizeV);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
m_fileHandler = new FileHandler(this);
|
m_fileHandler = new FileHandler(this);
|
||||||
m_controller = new Controller(params.gameScript, 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_videoForm = new VideoForm(params.framelessWindow, Config::getInstance().getSkin());
|
m_videoForm = new VideoForm(params.framelessWindow, Config::getInstance().getSkin());
|
||||||
m_videoForm->setDevice(this);
|
m_videoForm->setDevice(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stream = new Stream(this);
|
m_stream = new Stream([this](quint8 *buf, qint32 bufSize) -> qint32 {
|
||||||
if (m_decoder) {
|
auto videoSocket = m_server->getVideoSocket();
|
||||||
m_stream->setDecoder(m_decoder);
|
if (!videoSocket) {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return videoSocket->subThreadRecvData(buf, bufSize);
|
||||||
|
}, this);
|
||||||
|
|
||||||
m_server = new Server(this);
|
m_server = new Server(this);
|
||||||
if (!m_params.recordFileName.trimmed().isEmpty()) {
|
if (!m_params.recordFileName.trimmed().isEmpty()) {
|
||||||
m_recorder = new Recorder(m_params.recordFileName);
|
m_recorder = new Recorder(m_params.recordFileName);
|
||||||
m_stream->setRecoder(m_recorder);
|
|
||||||
}
|
}
|
||||||
initSignals();
|
initSignals();
|
||||||
startServer();
|
startServer();
|
||||||
|
@ -55,17 +63,23 @@ Device::~Device()
|
||||||
if (m_server) {
|
if (m_server) {
|
||||||
m_server->stop();
|
m_server->stop();
|
||||||
}
|
}
|
||||||
// server must stop before decoder, because decoder block main thread
|
|
||||||
if (m_stream) {
|
if (m_stream) {
|
||||||
m_stream->stopDecode();
|
m_stream->stopDecode();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_recorder) {
|
// server must stop before decoder, because decoder block main thread
|
||||||
delete m_recorder;
|
if (m_decoder) {
|
||||||
|
m_decoder->close();
|
||||||
}
|
}
|
||||||
if (m_vb) {
|
|
||||||
m_vb->deInit();
|
if (m_recorder) {
|
||||||
delete m_vb;
|
if (m_recorder->isRunning()) {
|
||||||
|
m_recorder->stopRecorder();
|
||||||
|
m_recorder->wait();
|
||||||
|
}
|
||||||
|
m_recorder->close();
|
||||||
|
delete m_recorder;
|
||||||
}
|
}
|
||||||
if (m_videoForm) {
|
if (m_videoForm) {
|
||||||
m_videoForm->close();
|
m_videoForm->close();
|
||||||
|
@ -107,14 +121,14 @@ void Device::updateScript(QString script)
|
||||||
|
|
||||||
void Device::onScreenshot()
|
void Device::onScreenshot()
|
||||||
{
|
{
|
||||||
if (!m_vb) {
|
if (!m_decoder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_vb->lock();
|
|
||||||
// screenshot
|
// screenshot
|
||||||
saveFrame(m_vb->peekRenderedFrame());
|
m_decoder->peekFrame([this](int width, int height, uint8_t* dataRGB32) {
|
||||||
m_vb->unLock();
|
saveFrame(width, height, dataRGB32);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::onShowTouch(bool show)
|
void Device::onShowTouch(bool show)
|
||||||
|
@ -242,16 +256,41 @@ void Device::initSignals()
|
||||||
// init recorder
|
// init recorder
|
||||||
if (m_recorder) {
|
if (m_recorder) {
|
||||||
m_recorder->setFrameSize(size);
|
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
|
// init decoder
|
||||||
m_stream->setVideoSocket(m_server->getVideoSocket());
|
|
||||||
m_stream->startDecode();
|
m_stream->startDecode();
|
||||||
|
|
||||||
// init controller
|
// recv device msg
|
||||||
if (m_controller) {
|
connect(m_server->getControlSocket(), &QTcpSocket::readyRead, this, [this](){
|
||||||
m_controller->setControlSocket(m_server->getControlSocket());
|
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)
|
// 显示界面时才自动息屏(m_params.display)
|
||||||
if (m_params.closeScreen && m_params.display && m_controller) {
|
if (m_params.closeScreen && m_params.display && m_controller) {
|
||||||
|
@ -270,24 +309,24 @@ void Device::initSignals()
|
||||||
deleteLater();
|
deleteLater();
|
||||||
qDebug() << "stream thread stop";
|
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 && m_vb) {
|
if (m_decoder) {
|
||||||
// must be Qt::QueuedConnection, ui update must be main thread
|
connect(m_decoder, &Decoder::updateFPS, m_videoForm, &VideoForm::updateFPS);
|
||||||
connect(
|
|
||||||
m_decoder,
|
|
||||||
&Decoder::onNewFrame,
|
|
||||||
this,
|
|
||||||
[this]() {
|
|
||||||
m_vb->lock();
|
|
||||||
const AVFrame *frame = m_vb->consumeRenderedFrame();
|
|
||||||
if (m_videoForm) {
|
|
||||||
m_videoForm->updateRender(frame);
|
|
||||||
}
|
|
||||||
m_vb->unLock();
|
|
||||||
},
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
connect(m_vb->getFPSCounter(), &::FpsCounter::updateFPS, m_videoForm, &VideoForm::updateFPS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,37 +391,13 @@ bool Device::isCurrentCustomKeymap()
|
||||||
return m_controller->isCurrentCustomKeymap();
|
return m_controller->isCurrentCustomKeymap();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::saveFrame(const AVFrame *frame)
|
bool Device::saveFrame(int width, int height, uint8_t* dataRGB32)
|
||||||
{
|
{
|
||||||
if (!frame) {
|
if (!dataRGB32) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create buffer
|
QImage rgbImage(dataRGB32, width, height, QImage::Format_RGB32);
|
||||||
QImage rgbImage(frame->width, frame->height, QImage::Format_RGB32);
|
|
||||||
AVFrame *rgbFrame = av_frame_alloc();
|
|
||||||
if (!rgbFrame) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind buffer to AVFrame
|
|
||||||
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, rgbImage.bits(), AV_PIX_FMT_RGB32, frame->width, frame->height, 4);
|
|
||||||
|
|
||||||
// convert
|
|
||||||
AVFrameConvert convert;
|
|
||||||
convert.setSrcFrameInfo(frame->width, frame->height, AV_PIX_FMT_YUV420P);
|
|
||||||
convert.setDstFrameInfo(frame->width, frame->height, AV_PIX_FMT_RGB32);
|
|
||||||
bool ret = false;
|
|
||||||
ret = convert.init();
|
|
||||||
if (!ret) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ret = convert.convert(frame, rgbFrame);
|
|
||||||
if (!ret) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
convert.deInit();
|
|
||||||
av_free(rgbFrame);
|
|
||||||
|
|
||||||
// save
|
// save
|
||||||
QString absFilePath;
|
QString absFilePath;
|
||||||
|
@ -396,7 +411,7 @@ bool Device::saveFrame(const AVFrame *frame)
|
||||||
fileName = Config::getInstance().getTitle() + fileName + ".png";
|
fileName = Config::getInstance().getTitle() + fileName + ".png";
|
||||||
QDir dir(fileDir);
|
QDir dir(fileDir);
|
||||||
absFilePath = dir.absoluteFilePath(fileName);
|
absFilePath = dir.absoluteFilePath(fileName);
|
||||||
ret = rgbImage.save(absFilePath, "PNG", 100);
|
int ret = rgbImage.save(absFilePath, "PNG", 100);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ public slots:
|
||||||
private:
|
private:
|
||||||
void initSignals();
|
void initSignals();
|
||||||
void startServer();
|
void startServer();
|
||||||
bool saveFrame(const AVFrame *frame);
|
bool saveFrame(int width, int height, uint8_t* dataRGB32);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// server relevant
|
// server relevant
|
||||||
|
@ -117,7 +117,6 @@ private:
|
||||||
QPointer<Controller> m_controller;
|
QPointer<Controller> m_controller;
|
||||||
QPointer<FileHandler> m_fileHandler;
|
QPointer<FileHandler> m_fileHandler;
|
||||||
QPointer<Stream> m_stream;
|
QPointer<Stream> m_stream;
|
||||||
VideoBuffer *m_vb = Q_NULLPTR;
|
|
||||||
Recorder *m_recorder = Q_NULLPTR;
|
Recorder *m_recorder = Q_NULLPTR;
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
|
|
|
@ -51,8 +51,15 @@ void Recorder::setFormat(Recorder::RecorderFormat format)
|
||||||
m_format = format;
|
m_format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Recorder::open(const AVCodec *inputCodec)
|
bool Recorder::open()
|
||||||
{
|
{
|
||||||
|
// codec
|
||||||
|
AVCodec* inputCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||||
|
if (!inputCodec) {
|
||||||
|
qCritical("H.264 decoder not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QString formatName = recorderGetFormatName(m_format);
|
QString formatName = recorderGetFormatName(m_format);
|
||||||
Q_ASSERT(!formatName.isEmpty());
|
Q_ASSERT(!formatName.isEmpty());
|
||||||
const AVOutputFormat *format = findMuxer(formatName.toUtf8());
|
const AVOutputFormat *format = findMuxer(formatName.toUtf8());
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
|
|
||||||
void setFrameSize(const QSize &declaredFrameSize);
|
void setFrameSize(const QSize &declaredFrameSize);
|
||||||
void setFormat(Recorder::RecorderFormat format);
|
void setFormat(Recorder::RecorderFormat format);
|
||||||
bool open(const AVCodec *inputCodec);
|
bool open();
|
||||||
void close();
|
void close();
|
||||||
bool write(AVPacket *packet);
|
bool write(AVPacket *packet);
|
||||||
bool startRecorder();
|
bool startRecorder();
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "decoder.h"
|
|
||||||
#include "recorder.h"
|
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "videosocket.h"
|
|
||||||
|
|
||||||
#define BUFSIZE 0x10000
|
#define BUFSIZE 0x10000
|
||||||
#define HEADER_SIZE 12
|
#define HEADER_SIZE 12
|
||||||
|
@ -13,7 +10,10 @@
|
||||||
|
|
||||||
typedef qint32 (*ReadPacketFunc)(void *, quint8 *, qint32);
|
typedef qint32 (*ReadPacketFunc)(void *, quint8 *, qint32);
|
||||||
|
|
||||||
Stream::Stream(QObject *parent) : QThread(parent) {}
|
Stream::Stream(std::function<qint32(quint8*, qint32)> recvData, QObject *parent)
|
||||||
|
: QThread(parent)
|
||||||
|
, m_recvData(recvData)
|
||||||
|
{}
|
||||||
|
|
||||||
Stream::~Stream() {}
|
Stream::~Stream() {}
|
||||||
|
|
||||||
|
@ -64,11 +64,6 @@ void Stream::deInit()
|
||||||
avformat_network_deinit(); // ignore failure
|
avformat_network_deinit(); // ignore failure
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::setDecoder(Decoder *decoder)
|
|
||||||
{
|
|
||||||
m_decoder = decoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
static quint32 bufferRead32be(quint8 *buf)
|
static quint32 bufferRead32be(quint8 *buf)
|
||||||
{
|
{
|
||||||
return static_cast<quint32>((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
|
return static_cast<quint32>((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
|
||||||
|
@ -81,31 +76,19 @@ static quint64 bufferRead64be(quint8 *buf)
|
||||||
return (static_cast<quint64>(msb) << 32) | lsb;
|
return (static_cast<quint64>(msb) << 32) | lsb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::setVideoSocket(VideoSocket *videoSocket)
|
|
||||||
{
|
|
||||||
m_videoSocket = videoSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::setRecoder(Recorder *recorder)
|
|
||||||
{
|
|
||||||
m_recorder = recorder;
|
|
||||||
}
|
|
||||||
|
|
||||||
qint32 Stream::recvData(quint8 *buf, qint32 bufSize)
|
qint32 Stream::recvData(quint8 *buf, qint32 bufSize)
|
||||||
{
|
{
|
||||||
if (!buf) {
|
if (!buf || !m_recvData) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (m_videoSocket) {
|
|
||||||
qint32 len = m_videoSocket->subThreadRecvData(buf, bufSize);
|
qint32 len = m_recvData(buf, bufSize);
|
||||||
return len;
|
return len;
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stream::startDecode()
|
bool Stream::startDecode()
|
||||||
{
|
{
|
||||||
if (!m_videoSocket) {
|
if (!m_recvData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
start();
|
start();
|
||||||
|
@ -114,9 +97,6 @@ bool Stream::startDecode()
|
||||||
|
|
||||||
void Stream::stopDecode()
|
void Stream::stopDecode()
|
||||||
{
|
{
|
||||||
if (m_decoder) {
|
|
||||||
m_decoder->interrupt();
|
|
||||||
}
|
|
||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,23 +120,6 @@ void Stream::run()
|
||||||
goto runQuit;
|
goto runQuit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder && !m_decoder->open(codec)) {
|
|
||||||
qCritical("Could not open m_decoder");
|
|
||||||
goto runQuit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_recorder) {
|
|
||||||
if (!m_recorder->open(codec)) {
|
|
||||||
qCritical("Could not open recorder");
|
|
||||||
goto runQuit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_recorder->startRecorder()) {
|
|
||||||
qCritical("Could not start recorder");
|
|
||||||
goto runQuit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_parser = av_parser_init(AV_CODEC_ID_H264);
|
m_parser = av_parser_init(AV_CODEC_ID_H264);
|
||||||
if (!m_parser) {
|
if (!m_parser) {
|
||||||
qCritical("Could not initialize parser");
|
qCritical("Could not initialize parser");
|
||||||
|
@ -192,16 +155,6 @@ void Stream::run()
|
||||||
av_parser_close(m_parser);
|
av_parser_close(m_parser);
|
||||||
|
|
||||||
runQuit:
|
runQuit:
|
||||||
if (m_recorder) {
|
|
||||||
if (m_recorder->isRunning()) {
|
|
||||||
m_recorder->stopRecorder();
|
|
||||||
m_recorder->wait();
|
|
||||||
}
|
|
||||||
m_recorder->close();
|
|
||||||
}
|
|
||||||
if (m_decoder) {
|
|
||||||
m_decoder->close();
|
|
||||||
}
|
|
||||||
if (m_codecCtx) {
|
if (m_codecCtx) {
|
||||||
avcodec_free_context(&m_codecCtx);
|
avcodec_free_context(&m_codecCtx);
|
||||||
}
|
}
|
||||||
|
@ -309,10 +262,7 @@ bool Stream::pushPacket(AVPacket *packet)
|
||||||
|
|
||||||
bool Stream::processConfigPacket(AVPacket *packet)
|
bool Stream::processConfigPacket(AVPacket *packet)
|
||||||
{
|
{
|
||||||
if (m_recorder && !m_recorder->push(packet)) {
|
emit getConfigFrame(packet);
|
||||||
qCritical("Could not send config packet to recorder");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,18 +294,7 @@ bool Stream::parse(AVPacket *packet)
|
||||||
|
|
||||||
bool Stream::processFrame(AVPacket *packet)
|
bool Stream::processFrame(AVPacket *packet)
|
||||||
{
|
{
|
||||||
if (m_decoder && !m_decoder->push(packet)) {
|
packet->dts = packet->pts;
|
||||||
return false;
|
emit getFrame(packet);
|
||||||
}
|
|
||||||
|
|
||||||
if (m_recorder) {
|
|
||||||
packet->dts = packet->pts;
|
|
||||||
|
|
||||||
if (!m_recorder->push(packet)) {
|
|
||||||
qCritical("Could not send packet to recorder");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,29 +10,24 @@ extern "C"
|
||||||
#include "libavformat/avformat.h"
|
#include "libavformat/avformat.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoSocket;
|
|
||||||
class Recorder;
|
|
||||||
class Decoder;
|
|
||||||
class Stream : public QThread
|
class Stream : public QThread
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Stream(QObject *parent = Q_NULLPTR);
|
Stream(std::function<qint32(quint8*, qint32)> recvData, QObject *parent = Q_NULLPTR);
|
||||||
virtual ~Stream();
|
virtual ~Stream();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool init();
|
static bool init();
|
||||||
static void deInit();
|
static void deInit();
|
||||||
|
|
||||||
void setDecoder(Decoder *decoder);
|
|
||||||
void setRecoder(Recorder *recorder);
|
|
||||||
void setVideoSocket(VideoSocket *deviceSocket);
|
|
||||||
qint32 recvData(quint8 *buf, qint32 bufSize);
|
|
||||||
bool startDecode();
|
bool startDecode();
|
||||||
void stopDecode();
|
void stopDecode();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void onStreamStop();
|
void onStreamStop();
|
||||||
|
void getFrame(AVPacket* packet);
|
||||||
|
void getConfigFrame(AVPacket* packet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run();
|
void run();
|
||||||
|
@ -41,12 +36,10 @@ protected:
|
||||||
bool processConfigPacket(AVPacket *packet);
|
bool processConfigPacket(AVPacket *packet);
|
||||||
bool parse(AVPacket *packet);
|
bool parse(AVPacket *packet);
|
||||||
bool processFrame(AVPacket *packet);
|
bool processFrame(AVPacket *packet);
|
||||||
|
qint32 recvData(quint8 *buf, qint32 bufSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<VideoSocket> m_videoSocket;
|
std::function<qint32(quint8*, qint32)> m_recvData = nullptr;
|
||||||
// for recorder
|
|
||||||
Recorder *m_recorder = Q_NULLPTR;
|
|
||||||
Decoder *m_decoder = Q_NULLPTR;
|
|
||||||
|
|
||||||
AVCodecContext *m_codecCtx = Q_NULLPTR;
|
AVCodecContext *m_codecCtx = Q_NULLPTR;
|
||||||
AVCodecParserContext *m_parser = Q_NULLPTR;
|
AVCodecParserContext *m_parser = Q_NULLPTR;
|
||||||
|
|
|
@ -41,6 +41,7 @@ void ToolForm::initStyle()
|
||||||
IconHelper::Instance()->SetIcon(ui->appSwitchBtn, QChar(0xf24d), 15);
|
IconHelper::Instance()->SetIcon(ui->appSwitchBtn, QChar(0xf24d), 15);
|
||||||
IconHelper::Instance()->SetIcon(ui->volumeUpBtn, QChar(0xf028), 15);
|
IconHelper::Instance()->SetIcon(ui->volumeUpBtn, QChar(0xf028), 15);
|
||||||
IconHelper::Instance()->SetIcon(ui->volumeDownBtn, QChar(0xf027), 15);
|
IconHelper::Instance()->SetIcon(ui->volumeDownBtn, QChar(0xf027), 15);
|
||||||
|
IconHelper::Instance()->SetIcon(ui->openScreenBtn, QChar(0xf06e), 15);
|
||||||
IconHelper::Instance()->SetIcon(ui->closeScreenBtn, QChar(0xf070), 15);
|
IconHelper::Instance()->SetIcon(ui->closeScreenBtn, QChar(0xf070), 15);
|
||||||
IconHelper::Instance()->SetIcon(ui->powerBtn, QChar(0xf011), 15);
|
IconHelper::Instance()->SetIcon(ui->powerBtn, QChar(0xf011), 15);
|
||||||
IconHelper::Instance()->SetIcon(ui->expandNotifyBtn, QChar(0xf103), 15);
|
IconHelper::Instance()->SetIcon(ui->expandNotifyBtn, QChar(0xf103), 15);
|
||||||
|
@ -220,3 +221,11 @@ void ToolForm::onControlStateChange(Device *device, Device::GroupControlState ol
|
||||||
Q_UNUSED(newState)
|
Q_UNUSED(newState)
|
||||||
updateGroupControl();
|
updateGroupControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToolForm::on_openScreenBtn_clicked()
|
||||||
|
{
|
||||||
|
if (!m_device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit m_device->setScreenPowerMode(ControlMsg::SPM_NORMAL);
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ private slots:
|
||||||
|
|
||||||
void onControlStateChange(Device *device, Device::GroupControlState oldState, Device::GroupControlState newState);
|
void onControlStateChange(Device *device, Device::GroupControlState oldState, Device::GroupControlState newState);
|
||||||
|
|
||||||
|
void on_openScreenBtn_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initStyle();
|
void initStyle();
|
||||||
void updateGroupControl();
|
void updateGroupControl();
|
||||||
|
|
|
@ -70,6 +70,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="openScreenBtn">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>open screen</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="closeScreenBtn">
|
<widget class="QPushButton" name="closeScreenBtn">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
|
|
@ -90,8 +90,8 @@ QRect VideoForm::getGrabCursorRect()
|
||||||
#if defined(Q_OS_WIN32)
|
#if defined(Q_OS_WIN32)
|
||||||
rc = QRect(ui->keepRatioWidget->mapToGlobal(m_videoWidget->pos()), m_videoWidget->size());
|
rc = QRect(ui->keepRatioWidget->mapToGlobal(m_videoWidget->pos()), m_videoWidget->size());
|
||||||
// high dpi support
|
// high dpi support
|
||||||
rc.setTopLeft(rc.topLeft() * m_videoWidget->devicePixelRatio());
|
rc.setTopLeft(rc.topLeft() * m_videoWidget->devicePixelRatioF());
|
||||||
rc.setBottomRight(rc.bottomRight() * m_videoWidget->devicePixelRatio());
|
rc.setBottomRight(rc.bottomRight() * m_videoWidget->devicePixelRatioF());
|
||||||
|
|
||||||
rc.setX(rc.x() + 10);
|
rc.setX(rc.x() + 10);
|
||||||
rc.setY(rc.y() + 10);
|
rc.setY(rc.y() + 10);
|
||||||
|
@ -109,8 +109,8 @@ QRect VideoForm::getGrabCursorRect()
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX)
|
||||||
rc = QRect(ui->keepRatioWidget->mapToGlobal(m_videoWidget->pos()), m_videoWidget->size());
|
rc = QRect(ui->keepRatioWidget->mapToGlobal(m_videoWidget->pos()), m_videoWidget->size());
|
||||||
// high dpi support -- taken from the WIN32 section and untested
|
// high dpi support -- taken from the WIN32 section and untested
|
||||||
rc.setTopLeft(rc.topLeft() * m_videoWidget->devicePixelRatio());
|
rc.setTopLeft(rc.topLeft() * m_videoWidget->devicePixelRatioF());
|
||||||
rc.setBottomRight(rc.bottomRight() * m_videoWidget->devicePixelRatio());
|
rc.setBottomRight(rc.bottomRight() * m_videoWidget->devicePixelRatioF());
|
||||||
|
|
||||||
rc.setX(rc.x() + 10);
|
rc.setX(rc.x() + 10);
|
||||||
rc.setY(rc.y() + 10);
|
rc.setY(rc.y() + 10);
|
||||||
|
@ -148,7 +148,7 @@ void VideoForm::showFPS(bool show)
|
||||||
m_fpsLabel->setVisible(show);
|
m_fpsLabel->setVisible(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoForm::updateRender(const AVFrame *frame)
|
void VideoForm::updateRender(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV)
|
||||||
{
|
{
|
||||||
if (m_videoWidget->isHidden()) {
|
if (m_videoWidget->isHidden()) {
|
||||||
if (m_loadingWidget) {
|
if (m_loadingWidget) {
|
||||||
|
@ -157,9 +157,9 @@ void VideoForm::updateRender(const AVFrame *frame)
|
||||||
m_videoWidget->show();
|
m_videoWidget->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateShowSize(QSize(frame->width, frame->height));
|
updateShowSize(QSize(width, height));
|
||||||
m_videoWidget->setFrameSize(QSize(frame->width, frame->height));
|
m_videoWidget->setFrameSize(QSize(width, height));
|
||||||
m_videoWidget->updateTextures(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
|
m_videoWidget->updateTextures(dataY, dataU, dataV, linesizeY, linesizeU, linesizeV);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoForm::showToolForm(bool show)
|
void VideoForm::showToolForm(bool show)
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Ui
|
||||||
class videoForm;
|
class videoForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AVFrame;
|
|
||||||
class ToolForm;
|
class ToolForm;
|
||||||
class Device;
|
class Device;
|
||||||
class FileHandler;
|
class FileHandler;
|
||||||
|
@ -24,7 +23,7 @@ public:
|
||||||
|
|
||||||
void staysOnTop(bool top = true);
|
void staysOnTop(bool top = true);
|
||||||
void updateShowSize(const QSize &newSize);
|
void updateShowSize(const QSize &newSize);
|
||||||
void updateRender(const AVFrame *frame);
|
void updateRender(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV);
|
||||||
void setDevice(Device *device);
|
void setDevice(Device *device);
|
||||||
QRect getGrabCursorRect();
|
QRect getGrabCursorRect();
|
||||||
const QSize &frameSize();
|
const QSize &frameSize();
|
||||||
|
|
|
@ -135,16 +135,6 @@ void Dialog::initUI()
|
||||||
on_useSingleModeCheck_clicked();
|
on_useSingleModeCheck_clicked();
|
||||||
|
|
||||||
on_updateDevice_clicked();
|
on_updateDevice_clicked();
|
||||||
|
|
||||||
#ifdef Q_OS_OSX
|
|
||||||
// mac need more width
|
|
||||||
setFixedWidth(550);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
// linux need more width
|
|
||||||
setFixedWidth(520);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialog::updateBootConfig(bool toView)
|
void Dialog::updateBootConfig(bool toView)
|
||||||
|
@ -603,19 +593,13 @@ void Dialog::on_updateNameBtn_clicked()
|
||||||
void Dialog::on_useSingleModeCheck_clicked()
|
void Dialog::on_useSingleModeCheck_clicked()
|
||||||
{
|
{
|
||||||
if (ui->useSingleModeCheck->isChecked()) {
|
if (ui->useSingleModeCheck->isChecked()) {
|
||||||
ui->configGroupBox->hide();
|
ui->rightWidget->hide();
|
||||||
ui->adbGroupBox->hide();
|
|
||||||
ui->wirelessGroupBox->hide();
|
|
||||||
ui->usbGroupBox->hide();
|
|
||||||
} else {
|
} else {
|
||||||
ui->configGroupBox->show();
|
ui->rightWidget->show();
|
||||||
ui->adbGroupBox->show();
|
|
||||||
ui->wirelessGroupBox->show();
|
|
||||||
ui->usbGroupBox->show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QTimer::singleShot(0, this, [this]() {
|
QTimer::singleShot(0, this, [this]() {
|
||||||
resize(width(), layout()->sizeHint().height());
|
resize(layout()->sizeHint().width(), height());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1907
QtScrcpy/dialog.ui
1907
QtScrcpy/dialog.ui
File diff suppressed because it is too large
Load diff
|
@ -135,6 +135,7 @@ void installTranslator()
|
||||||
case QLocale::English:
|
case QLocale::English:
|
||||||
default:
|
default:
|
||||||
languagePath += "en_US.qm";
|
languagePath += "en_US.qm";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
translator.load(languagePath);
|
translator.load(languagePath);
|
||||||
|
|
Binary file not shown.
|
@ -376,6 +376,10 @@ You can download it at the following address:</source>
|
||||||
<source>screen shot</source>
|
<source>screen shot</source>
|
||||||
<translation>screen shot</translation>
|
<translation>screen shot</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>open screen</source>
|
||||||
|
<translation>open screen</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VideoForm</name>
|
<name>VideoForm</name>
|
||||||
|
|
Binary file not shown.
|
@ -362,6 +362,10 @@
|
||||||
<source>screen shot</source>
|
<source>screen shot</source>
|
||||||
<translation>截图</translation>
|
<translation>截图</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>open screen</source>
|
||||||
|
<translation>打开屏幕</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VideoForm</name>
|
<name>VideoForm</name>
|
||||||
|
|
|
@ -171,7 +171,7 @@ Note: it is not necessary to keep you Android device connected via USB after you
|
||||||
- Screen recording
|
- Screen recording
|
||||||
- Screenshot to png
|
- Screenshot to png
|
||||||
- Wireless connection
|
- Wireless connection
|
||||||
- Supports up to 16 device connections (the number can be higher if your PC performance allows. You need to compile it by yourself)
|
- Supports multiple device connections
|
||||||
- Full-screen display
|
- Full-screen display
|
||||||
- Display on the top
|
- Display on the top
|
||||||
- Install apk: drag and drop apk to the video window to install
|
- Install apk: drag and drop apk to the video window to install
|
||||||
|
|
|
@ -173,7 +173,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序:
|
||||||
- 屏幕录制
|
- 屏幕录制
|
||||||
- 截图为png
|
- 截图为png
|
||||||
- 无线连接
|
- 无线连接
|
||||||
- 最多支持16台设备连接(PC性能允许的情况下可以增加,需要自己编译)
|
- 支持多台设备连接
|
||||||
- 全屏显示
|
- 全屏显示
|
||||||
- 窗口置顶
|
- 窗口置顶
|
||||||
- 安装apk:拖拽apk到视频窗口即可安装
|
- 安装apk:拖拽apk到视频窗口即可安装
|
||||||
|
|
|
@ -44,7 +44,7 @@ if "%1"=="MinSizeRel" (
|
||||||
if "%1"=="RelWithDebInfo" (
|
if "%1"=="RelWithDebInfo" (
|
||||||
goto build_mode_ok
|
goto build_mode_ok
|
||||||
)
|
)
|
||||||
echo error: unkonow build mode -- %1
|
echo error: unknown build mode -- %1
|
||||||
goto return
|
goto return
|
||||||
:build_mode_ok
|
:build_mode_ok
|
||||||
|
|
||||||
|
@ -111,4 +111,4 @@ set errno=0
|
||||||
|
|
||||||
:return
|
:return
|
||||||
cd %old_cd%
|
cd %old_cd%
|
||||||
exit /B %errno%
|
exit /B %errno%
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
最后同步scrcpy 08baaf4b575aef7ee56d14683be3f4e3a86d39aa
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
## 低优先级
|
## 低优先级
|
||||||
- text转换 https://github.com/Genymobile/scrcpy/commit/c916af0984f72a60301d13fa8ef9a85112f54202?tdsourcetag=s_pctim_aiomsg
|
- text转换 https://github.com/Genymobile/scrcpy/commit/c916af0984f72a60301d13fa8ef9a85112f54202?tdsourcetag=s_pctim_aiomsg
|
||||||
|
@ -24,6 +22,9 @@
|
||||||
## ffmpeg
|
## ffmpeg
|
||||||
[ffmpeg编译参数详解](https://www.cnblogs.com/wainiwann/p/4204230.html)
|
[ffmpeg编译参数详解](https://www.cnblogs.com/wainiwann/p/4204230.html)
|
||||||
|
|
||||||
|
## fontawesome
|
||||||
|
[fontawesome 在线搜索](http://www.fontawesome.com.cn/cheatsheet/)
|
||||||
|
|
||||||
## adb
|
## adb
|
||||||
以下是 ADB 和 Fastboot 的谷歌官方下载链接:
|
以下是 ADB 和 Fastboot 的谷歌官方下载链接:
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue