Commit 1eb3ee74 by Balázs Ludmány

Commit Arnoldnak!

parent cb68f729
...@@ -18,11 +18,10 @@ QML_IMPORT_PATH = ...@@ -18,11 +18,10 @@ QML_IMPORT_PATH =
# Default rules for deployment. # Default rules for deployment.
include(deployment.pri) include(deployment.pri)
LIBS += -L$$PWD/../libvnc/libvncclient/.libs/ -lvncclient -lEGL -ljpeg -L$$PWD/../build-texload-Desktop_Qt_5_9_0_GCC_64bit-Release -ltexload
unix|win32: LIBS += -L$$PWD/../libvnc/libvncclient/.libs/ -lvncclient -lEGL -ljpeg INCLUDEPATH += $$PWD/../libvnc $$PWD/../texload
DEPENDPATH += $$PWD/../libvnc $$PWD/../texload
INCLUDEPATH += $$PWD/../libvnc
DEPENDPATH += $$PWD/../libvnc
HEADERS += \ HEADERS += \
vncrenderer.h \ vncrenderer.h \
...@@ -33,11 +32,12 @@ HEADERS += \ ...@@ -33,11 +32,12 @@ HEADERS += \
concurrentqueue.h concurrentqueue.h
DISTFILES += \ DISTFILES += \
draw_shader.fsh \
draw_shader.vsh \
copy_shader.vsh \ copy_shader.vsh \
copy_shader.fsh \ copy_shader.fsh \
fill_shader.vsh \ fill_shader.vsh \
fill_shader.fsh \ fill_shader.fsh \
bitmap_shader.vsh \ bitmap_shader.vsh \
bitmap_shader.fsh bitmap_shader.fsh \
ycbcr_shader.fsh
#QMAKE_CXXFLAGS_RELEASE -= -O2
uniform sampler2D texture; uniform sampler2D texture;
varying highp vec4 vartexcoord; varying highp vec2 vartexcoord;
void main(void) void main(void)
{ {
gl_FragColor = texture2D(texture, vartexcoord.st); gl_FragColor = vec4(texture2D(texture, vartexcoord).rgb, 1.0);
//gl_FragColor = vec4(vartexcoord.st, 0.0, 1.0);
} }
uniform highp mat4 ortho; uniform highp mat4 ortho; // transforms screen coordinates to [-1, 1]
uniform highp mat4 texortho; uniform highp mat4 texortho; // transforms screen coordinates to [0, 1]
attribute highp vec2 position; attribute highp vec2 position;
attribute highp vec2 texcoord;
varying highp vec4 vartexcoord; varying highp vec2 vartexcoord;
void main(void) void main(void)
{ {
gl_Position = ortho * vec4(position, 0.0, 1.0); gl_Position = ortho * vec4(position, 0.0, 1.0);
vartexcoord = texortho * vec4(texcoord, 0.0, 1.0); vartexcoord = vec4(texortho * vec4(position, 0.0, 1.0)).xy;
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <QSemaphore> #include <QSemaphore>
#include <QMutex> #include <QMutex>
#include <QTime>
template <class T, const unsigned short S> template <class T, const unsigned short S>
class ConcurrentQueue class ConcurrentQueue
...@@ -14,6 +15,7 @@ public: ...@@ -14,6 +15,7 @@ public:
bool isEmpty() const; bool isEmpty() const;
private: private:
T m_buffer[S]; T m_buffer[S];
QTime m_time[S];
unsigned short m_next; unsigned short m_next;
unsigned short m_first; unsigned short m_first;
QSemaphore m_free; QSemaphore m_free;
......
uniform sampler2D texture; uniform sampler2D texture;
varying mediump vec2 vartexcoord; varying highp vec2 vartarget;
void main(void) void main(void)
{ {
gl_FragColor = texture2D(texture, vartexcoord); gl_FragColor = texture2D(texture, vartarget);
} }
uniform mediump mat4 ortho; uniform highp mat4 srcortho;
uniform mediump mat4 texortho; uniform highp mat4 trgortho;
attribute mediump vec2 position; attribute highp vec2 source;
attribute mediump vec2 texcoord; attribute highp vec2 target;
varying mediump vec2 vartexcoord; varying highp vec2 vartarget;
void main(void) void main(void)
{ {
gl_Position = vec4((position.x - 1024.0) / 1024.0, (position.y - 1024.0) / 1024.0, 0.0, 1.0); gl_Position = trgortho * vec4(target, 0.0, 1.0);
vartexcoord = vec2(texcoord.x / 1920.0, texcoord.y / 1080.0); vartarget = vec4(srcortho * vec4(source, 0.0, 1.0)).xy;
} }
#include "dispatcher.h" #include "dispatcher.h"
#include "uploader.h"
Dispatcher::Dispatcher(Uploader *uploader, QObject *parent) : Dispatcher::Dispatcher(Uploader *uploader, QObject *parent) :
QObject(parent), m_client(NULL), m_uploader(uploader) QObject(parent)
{
m_client = rfbGetClient(8, 3, 4);
m_client->canHandleNewFBSize = FALSE;
m_client->MallocFrameBuffer = Dispatcher::MallocFrameBuffer;
m_client->GotCopyRect = Dispatcher::GotCopyRect;
m_client->GotFillRect = Dispatcher::GotFillRect;
m_client->GotBitmap = Dispatcher::GotBitmap;
m_client->GotJpeg = Dispatcher::GotJpeg;
m_client->FinishedFrameBufferUpdate = Dispatcher::FinishedFrameBufferUpdate;
m_client->GetPassword = Dispatcher::GetPassword;
rfbClientSetClientData(m_client, const_cast<char*>("dispatcher"), this);
// the pointer to the Uploader is stored in the rfbClient instead of a member of the class to avoid duplication
rfbClientSetClientData(m_client, const_cast<char*>("uploader"), uploader);
}
Dispatcher::~Dispatcher()
{ {
rfbClientCleanup(m_client);
} }
rfbBool Dispatcher::MallocFrameBuffer(rfbClient *client) rfbBool Dispatcher::MallocFrameBuffer(rfbClient *client)
{ {
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0); auto dispatcher = static_cast<Dispatcher *>(rfbClientGetClientData(client, const_cast<char*>("dispatcher")));
emit dispatcher->resizeFramebuffer(QSize(client->width, client->height)); emit dispatcher->resizeFramebuffer(QSize(client->width, client->height));
return TRUE; return TRUE;
} }
void Dispatcher::GotCopyRect(rfbClient *client, const int src_x, const int src_y, const int w, const int h, const int dest_x, const int dest_y) void Dispatcher::GotCopyRect(rfbClient *client, const int src_x, const int src_y, const int w, const int h, const int dest_x, const int dest_y)
{ {
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
dispatcher->uploader()->gotCopy(src_x, src_y, w, h, dest_x, dest_y); uploader->gotCopy(src_x, src_y, w, h, dest_x, dest_y);
} }
void Dispatcher::GotFillRect(rfbClient *client, int x, int y, int w, int h, uint32_t colour) void Dispatcher::GotFillRect(rfbClient *client, int x, int y, int w, int h, uint32_t colour)
{ {
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
int red = (colour >> client->format.redShift) & client->format.redMax; int red = (colour >> client->format.redShift) & client->format.redMax;
int green = (colour >> client->format.greenShift) & client->format.greenMax; int green = (colour >> client->format.greenShift) & client->format.greenMax;
int blue = (colour >> client->format.blueShift) & client->format.blueMax; int blue = (colour >> client->format.blueShift) & client->format.blueMax;
dispatcher->uploader()->gotFill(x, y, w, h, red, green, blue); uploader->gotFill(x, y, w, h, red, green, blue);
} }
void Dispatcher::GotBitmap(rfbClient *client, const uint8_t *buffer, int x, int y, int w, int h) void Dispatcher::GotBitmap(rfbClient *client, const uint8_t *buffer, int x, int y, int w, int h)
{ {
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
// We have to make a deep copy because libvnc might free the buffer as soon as this function returns uploader->gotBitmap(buffer, x, y, w, h);
dispatcher->uploader()->gotBitmap(buffer, x, y, w, h);
} }
rfbBool Dispatcher::GotJpeg(rfbClient *client, const uint8_t *buffer, int length, int x, int y, int w, int h) rfbBool Dispatcher::GotJpeg(rfbClient *client, const uint8_t *buffer, int length, int x, int y, int w, int h)
{ {
Dispatcher *dispatcher = static_cast<Dispatcher *>(rfbClientGetClientData(client, 0)); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
dispatcher->uploader()->gotJpeg(); uploader->gotJpeg(buffer, length, x, y, w, h);
Jpeg *jpeg = new Jpeg({(unsigned char*)buffer, length, x, y, w, h});
dispatcher->queue()->enqueue(jpeg);
return TRUE; return TRUE;
} }
void Dispatcher::FinishedFrameBufferUpdate(rfbClient *client) void Dispatcher::FinishedFrameBufferUpdate(rfbClient *client)
{ {
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
dispatcher->uploader()->finishedUpdate(); uploader->finishedUpdate();
} }
char *Dispatcher::GetPassword(rfbClient *client) char *Dispatcher::GetPassword(rfbClient *client)
{ {
char* password = new char[9]; char* password = (char*) std::malloc(sizeof(char) * 9);
strcpy(password, "asdfasdf"); strcpy(password, "asdfasdf");
return password; return password;
} }
...@@ -78,39 +96,18 @@ int Dispatcher::bitsPerPixel() ...@@ -78,39 +96,18 @@ int Dispatcher::bitsPerPixel()
return m_client->format.bitsPerPixel; return m_client->format.bitsPerPixel;
} }
ConcurrentQueue<Jpeg *, 256u> *Dispatcher::queue()
{
return &m_queue;
}
Uploader *Dispatcher::uploader() const
{
return m_uploader;
}
void Dispatcher::open(const QString host, const int port, const int width, const int height) void Dispatcher::open(const QString host, const int port, const int width, const int height)
{ {
QByteArray tmp = host.toLocal8Bit(); QByteArray tmp = host.toLocal8Bit();
m_client = rfbGetClient(8, 3, 4);
m_client->canHandleNewFBSize = FALSE;
m_client->serverHost = new char[tmp.size() + 1]; m_client->serverHost = new char[tmp.size() + 1];
strcpy(m_client->serverHost, tmp.constData()); strcpy(m_client->serverHost, tmp.constData());
m_client->serverPort = port; m_client->serverPort = port;
m_client->MallocFrameBuffer = Dispatcher::MallocFrameBuffer; //m_client->appData.useRemoteCursor = TRUE;
m_client->GotCopyRect = Dispatcher::GotCopyRect;
m_client->GotFillRect = Dispatcher::GotFillRect;
m_client->GotBitmap = Dispatcher::GotBitmap;
m_client->GotJpeg = Dispatcher::GotJpeg;
m_client->FinishedFrameBufferUpdate = Dispatcher::FinishedFrameBufferUpdate;
m_client->GetPassword = Dispatcher::GetPassword;
// Set the format requested by the user
m_client->appData.useRemoteCursor = TRUE;
m_client->width = width; m_client->width = width;
m_client->height = height; m_client->height = height;
m_client->appData.compressLevel = 9; //m_client->appData.compressLevel = 0;
m_client->appData.qualityLevel = 0; //m_client->appData.qualityLevel = 9;
#ifdef __BIG_ENDIAN__ #ifdef __BIG_ENDIAN__
m_client->format.bigEndian = TRUE; m_client->format.bigEndian = TRUE;
#else #else
...@@ -120,30 +117,11 @@ void Dispatcher::open(const QString host, const int port, const int width, const ...@@ -120,30 +117,11 @@ void Dispatcher::open(const QString host, const int port, const int width, const
m_client->format.trueColour = TRUE; m_client->format.trueColour = TRUE;
m_client->format.bitsPerPixel = 32; m_client->format.bitsPerPixel = 32;
rfbClientSetClientData(m_client, 0, this);
if(!rfbInitClient(m_client, NULL, NULL)) { if(!rfbInitClient(m_client, NULL, NULL)) {
emit error(); emit error();
return; return;
} }
// Stick to the format sent by the server if we can render it easily, set a new one otherwise
//
// Formats OpenGL ES 2.0 can (should be able to) handle:
// Internal Format External Format Type Bytes per Pixel
// --------------- --------------- ---- ---------------
// RGBA RGBA UNSIGNED_BYTE 4
// RGB RGB UNSIGNED_BYTE 3
// RGBA RGBA UNSIGNED_SHORT_4_4_4_4 2
// RGBA RGBA UNSIGNED_SHORT_5_5_5_1 2
// RGB RGB UNSIGNED_SHORT_5_6_5 2
// The Raspberry Pi supports BGRA too but only through extensions
//
// JPEG is RGB UNSIGNED_BYTE
// Supported raw formats:
// 32 bit: RGB888
// 16 bit: RGB444, RGB555, RGB565
refresh(); refresh();
decode(); decode();
} }
...@@ -166,10 +144,9 @@ void Dispatcher::refresh() ...@@ -166,10 +144,9 @@ void Dispatcher::refresh()
} }
void Dispatcher::decode() void Dispatcher::decode()
{ {
if(m_client == NULL)
return;
m_uploader->startDecoding(); auto uploader = static_cast<Uploader *>(rfbClientGetClientData(m_client, const_cast<char*>("uploader")));
uploader->startDecoding();
if(!HandleRFBServerMessage(m_client)) { if(!HandleRFBServerMessage(m_client)) {
terminate(); terminate();
......
#ifndef RFBDISPATCH_H #ifndef RFBDISPATCH_H
#define RFBDISPATCH_H #define RFBDISPATCH_H
#include <QOpenGLContext>
#include <QCoreApplication>
#include <QImage>
#include <QThreadPool>
#include <QObject> #include <QObject>
#include <QPoint>
#include <QSize>
#include <QColor>
#include <QMouseEvent>
#include <QVector3D>
#include <QTime>
#include <rfb/rfbclient.h> #include <rfb/rfbclient.h>
#include "uploader.h" class Uploader;
#include "jpegdecoder.h"
extern QTime elapsed;
class Dispatcher : public QObject class Dispatcher : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Dispatcher(Uploader *uploader, QObject *parent = Q_NULLPTR); explicit Dispatcher(Uploader *uploader, QObject *parent = Q_NULLPTR);
~Dispatcher();
// libvnc hooks // libvnc hooks
static rfbBool MallocFrameBuffer(rfbClient* client); static rfbBool MallocFrameBuffer(rfbClient* client);
...@@ -40,14 +28,8 @@ public: ...@@ -40,14 +28,8 @@ public:
// parameters of the VNC connection // parameters of the VNC connection
int bitsPerPixel(); int bitsPerPixel();
// getters
ConcurrentQueue<Jpeg *, 256u>* queue();
Uploader *uploader() const;
private: private:
rfbClient *m_client; rfbClient *m_client;
Uploader *m_uploader;
ConcurrentQueue<Jpeg*, 256u> m_queue;
signals: signals:
// libvnc events // libvnc events
void gotCopy(const int src_x, const int src_y, const int width, const int height, const int dest_x, const int dest_y); void gotCopy(const int src_x, const int src_y, const int width, const int height, const int dest_x, const int dest_y);
......
uniform sampler2D texture;
varying mediump vec2 vartexcoord;
varying lowp vec4 varcolor;
void main(void)
{
gl_FragColor = vec4(texture2D(texture, vartexcoord).rgb + varcolor.rgb, 1.0);
}
uniform mediump mat4 ortho;
attribute mediump vec2 position;
attribute lowp vec4 color;
attribute mediump vec2 texcoord;
varying lowp vec4 varcolor;
varying mediump vec2 vartexcoord;
void main(void)
{
gl_Position = ortho * vec4(position, 0.0, 1.0);
varcolor = color;
vartexcoord = vec2(texcoord.x / 2048.0, texcoord.y / 2048.0);
}
...@@ -2,5 +2,5 @@ varying highp vec4 varcolor; ...@@ -2,5 +2,5 @@ varying highp vec4 varcolor;
void main(void) void main(void)
{ {
gl_FragColor = varcolor; gl_FragColor = vec4(varcolor.rgb, 1.0);
} }
#include "jpegdecoder.h" #include "jpegdecoder.h"
int Jpeg::y_width = 0;
int Jpeg::cb_width = 0;
int Jpeg::cr_width = 0;
unsigned char *Jpeg::y_pointer = 0;
unsigned char *Jpeg::cb_pointer = 0;
unsigned char *Jpeg::cr_pointer = 0;
struct error_mgr
{
struct jpeg_error_mgr mgr;
jmp_buf setjmp_buffer;
};
typedef struct error_mgr* error_ptr;
METHODDEF(void)
error_exit (j_common_ptr cinfo)
{
error_ptr myerr = (error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
JpegDecoder::JpegDecoder(ConcurrentQueue<Jpeg *, 256u> *queue, QObject *parent) : JpegDecoder::JpegDecoder(ConcurrentQueue<Jpeg *, 1024u> *queue, QObject *parent) :
QObject(parent), m_queue(queue) QObject(parent), m_queue(queue)
{ {
} }
void JpegDecoder::operate() void JpegDecoder::operate()
{ {
//QTime time;
//time.start();
struct jpeg_decompress_struct cinfo; struct jpeg_decompress_struct cinfo;
struct error_mgr jerr; struct error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr.mgr); cinfo.err = jpeg_std_error(&jerr.mgr);
jerr.mgr.error_exit = error_exit; jerr.mgr.error_exit = &error_exit;
if (setjmp(jerr.setjmp_buffer)) { if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo); jpeg_destroy_decompress(&cinfo);
} }
jpeg_create_decompress(&cinfo); jpeg_create_decompress(&cinfo);
Jpeg *jpeg;
JSAMPLE *samples;
JSAMPROW *rows;
//int dequeue, decompress;
forever { forever {
//time.restart(); auto jpeg = m_queue->dequeue();
jpeg = m_queue->dequeue();
//dequeue = time.elapsed(); jpeg->output = new JSAMPARRAY[3];
jpeg_mem_src(&cinfo, jpeg->data, jpeg->length);
(void) jpeg_read_header(&cinfo, TRUE); int paddingHeight = (16 - (jpeg->height % 16));
cinfo.dct_method = JDCT_FASTEST; int paddingWidth = jpeg->width + (16 - jpeg->width % 16);
cinfo.do_fancy_upsampling = FALSE;
cinfo.two_pass_quantize = FALSE; jpeg->padding = new JSAMPLE[3 * paddingHeight * paddingWidth];
cinfo.dither_mode = JDITHER_ORDERED;
//cinfo.scale_num = 1; jpeg->rows = new JSAMPROW[3 * (jpeg->height + paddingHeight)];
//cinfo.scale_denom = 8; for (unsigned char component = 0; component < 3; ++component)
cinfo.out_color_space = JCS_EXT_RGBX; jpeg->output[component] = &jpeg->rows[component * (jpeg->height + paddingHeight)];
(void) jpeg_start_decompress(&cinfo);
for (int row = 0; row < jpeg->height; ++row)
samples = new JSAMPLE[cinfo.output_width * cinfo.output_height * cinfo.output_components]; {
rows = new JSAMPROW[cinfo.output_height * cinfo.output_components]; jpeg->output[0][row] = &Jpeg::y_pointer[jpeg->x + (jpeg->y + row) * Jpeg::y_width];
for(size_t i = 0; i < cinfo.output_height; i++) { jpeg->output[1][row] = &Jpeg::cb_pointer[jpeg->x + (jpeg->y + row) * Jpeg::cb_width];
rows[i] = samples + (i * cinfo.output_width * cinfo.output_components); jpeg->output[2][row] = &Jpeg::cr_pointer[jpeg->x + (jpeg->y + row) * Jpeg::cr_width];
}
for (int row = 0; row < paddingHeight; ++row)
{
jpeg->output[0][jpeg->height + row] = &jpeg->padding[row * paddingWidth];
jpeg->output[1][jpeg->height + row] = &jpeg->padding[(paddingHeight + row) * paddingWidth];
jpeg->output[2][jpeg->height + row] = &jpeg->padding[(2 * paddingHeight + row) * paddingWidth];
} }
jpeg_mem_src(&cinfo, jpeg->input, jpeg->length);
jpeg_read_header(&cinfo, TRUE);
cinfo.dct_method = JDCT_FASTEST;
cinfo.raw_data_out = TRUE;
jpeg_start_decompress(&cinfo);
JSAMPARRAY rowzero[3];
for(int component = 0; component < cinfo.output_components; component++)
rowzero[component] = &jpeg->output[component][0];
while (cinfo.output_scanline < cinfo.output_height) { while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, rows + cinfo.output_scanline, 1); for(int component = 0; component < cinfo.output_components; component++)
jpeg->output[component] = &rowzero[component][cinfo.output_scanline / (cinfo.max_v_samp_factor * cinfo.comp_info[component].v_samp_factor)];
jpeg_read_raw_data(&cinfo, jpeg->output, qMax(static_cast<int>(cinfo.output_height - cinfo.output_scanline), cinfo.max_v_samp_factor * DCTSIZE));
} }
(void) jpeg_finish_decompress(&cinfo);
//decompress = time.elapsed(); jpeg_finish_decompress(&cinfo);
//qDebug() << "dequeue" << dequeue << "decompress" << decompress; emit finishedJpeg();
emit finished(samples, jpeg->x, jpeg->y, jpeg->width, jpeg->height);
delete[] jpeg->data;
delete jpeg; delete jpeg;
delete[] rows;
} }
jpeg_destroy_decompress(&cinfo); jpeg_destroy_decompress(&cinfo);
} }
...@@ -15,12 +15,39 @@ ...@@ -15,12 +15,39 @@
struct Jpeg struct Jpeg
{ {
uint8_t *data; uint8_t *input;
int length; std::size_t length;
int x; JSAMPIMAGE output;
int y; JSAMPROW *rows;
JSAMPLE *padding;
int width; int width;
int height; int height;
int x;
int y;
static int y_width;
static int cb_width;
static int cr_width;
static unsigned char *y_pointer;
static unsigned char *cb_pointer;
static unsigned char *cr_pointer;
Jpeg(uint8_t *input,
const std::size_t length,
const int width,
const int height,
const int x,
const int y) : input(input), length(length), width(width), height(height), x(x), y(y)
{
}
~Jpeg()
{
std::free(input);
delete[] output;
delete[] rows;
delete[] padding;
}
}; };
/*! /*!
...@@ -32,28 +59,12 @@ class JpegDecoder : public QObject ...@@ -32,28 +59,12 @@ class JpegDecoder : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
JpegDecoder(ConcurrentQueue<Jpeg*, 256u> *queue, QObject *parent = 0); JpegDecoder(ConcurrentQueue<Jpeg*, 1024u> *queue, QObject *parent = 0);
ConcurrentQueue<Jpeg*, 256u> *m_queue; ConcurrentQueue<Jpeg*, 1024u> *m_queue;
signals: signals:
void finished(const unsigned char *image, const quint32 x, const quint32 y, const quint32 width, const quint32 height); void finishedJpeg();
public slots: public slots:
void operate(); void operate();
}; };
struct error_mgr
{
struct jpeg_error_mgr mgr;
jmp_buf setjmp_buffer;
};
typedef struct error_mgr* error_ptr;
METHODDEF(void)
error_exit (j_common_ptr cinfo)
{
error_ptr myerr = (error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
#endif // JPEGDECODER_H #endif // JPEGDECODER_H
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
#include <QtQml> #include <QtQml>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
QTime elapsed;
#include "qvnc.h" #include "qvnc.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
...@@ -18,8 +16,8 @@ int main(int argc, char *argv[]) ...@@ -18,8 +16,8 @@ int main(int argc, char *argv[])
format.setMajorVersion(2); format.setMajorVersion(2);
format.setMinorVersion(0); format.setMinorVersion(0);
format.setRenderableType(QSurfaceFormat::OpenGLES); format.setRenderableType(QSurfaceFormat::OpenGLES);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
format.setSwapInterval(1); format.setSwapInterval(0);
#ifdef OGL_DEBUG #ifdef OGL_DEBUG
format.setOption(QSurfaceFormat::DebugContext, true); format.setOption(QSurfaceFormat::DebugContext, true);
#endif #endif
...@@ -27,7 +25,7 @@ int main(int argc, char *argv[]) ...@@ -27,7 +25,7 @@ int main(int argc, char *argv[])
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES, true); QGuiApplication::setAttribute(Qt::AA_UseOpenGLES, true);
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
qmlRegisterType<QVnc>("thinclient", 1, 3, "QVnc"); qmlRegisterType<QVnc>("thinclient", 1, 4, "QVnc");
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
......
import QtQuick 2.6 import QtQuick 2.5
import QtQuick.Controls 2.0 import QtQuick.Controls 2.1
import thinclient 1.3 import thinclient 1.4
ApplicationWindow { ApplicationWindow {
visible: true visible: true
...@@ -27,9 +27,9 @@ ApplicationWindow { ...@@ -27,9 +27,9 @@ ApplicationWindow {
QVnc { QVnc {
id: vnc id: vnc
host: "vm.ik.bme.hu" host: "vm.ik.bme.hu"
port: 10495 port: 13070
width: 1920 width: 1920
height: 1080 height: 1080
transform: Scale {xScale: window.width / vnc.width; yScale: (window.height - tab.height) / vnc.height} //transform: Scale {xScale: window.width / vnc.width; yScale: (window.height - tab.height) / vnc.height}
} }
} }
#include "qvnc.h" #include "qvnc.h"
QVnc::QVnc(QQuickItem *parent) : QVnc::QVnc(QQuickItem *parent) :
QQuickFramebufferObject(parent), m_decoders(DECODER_COUNT), m_threads(DECODER_COUNT + 1) QQuickFramebufferObject(parent), m_host(""), m_port(0)
{ {
setAcceptedMouseButtons(Qt::AllButtons); setAcceptedMouseButtons(Qt::AllButtons);
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
// this makes sure the rendering is called only when we explicitly ask for it
setTextureFollowsItemSize(false); setTextureFollowsItemSize(false);
for(int i = 0; i < m_threads.length(); i++)
m_threads[i] = new QThread(this);
connect(this, &QVnc::windowChanged, this, &QVnc::winChanged); connect(this, &QVnc::windowChanged, this, &QVnc::winChanged);
m_uploader = new Uploader; m_uploader = new Uploader;
m_uploader->moveToThread(m_threads[0]); connect(m_uploader, &Uploader::uploadFinished, this, &QVnc::update);
connect(&m_worker, &QThread::finished, m_uploader, &QObject::deleteLater);
connect(m_threads[0], &QThread::finished, m_uploader, &QObject::deleteLater);
m_dispatcher = new Dispatcher(m_uploader); m_dispatcher = new Dispatcher(m_uploader);
m_dispatcher->moveToThread(m_threads[0]); connect(&m_worker, &QThread::finished, m_dispatcher, &QObject::deleteLater);
connect(m_threads[0], &QThread::finished, m_dispatcher, &QObject::deleteLater);
connect(this, &QVnc::open_connection, m_dispatcher, &Dispatcher::open);
connect(this, &QVnc::terminate_connection, m_dispatcher, &Dispatcher::terminate);
connect(m_dispatcher, &Dispatcher::error, this, &QVnc::dispatch_error);
connect(m_uploader, &Uploader::uploadFinished, m_dispatcher, &Dispatcher::refresh); connect(m_uploader, &Uploader::uploadFinished, m_dispatcher, &Dispatcher::refresh);
// connect(m_uploader, &Uploader::uploadFinished, this, &QVnc::update);
connect(m_uploader, &Uploader::buffersSwapped, m_dispatcher, &Dispatcher::decode);
int i = 1; m_uploader->moveToThread(&m_worker);
foreach(JpegDecoder* d, m_decoders) { m_dispatcher->moveToThread(&m_worker);
d = new JpegDecoder(m_dispatcher->queue());
d->moveToThread(m_threads[i]);
connect(m_threads[i], &QThread::finished, d, &QObject::deleteLater);
connect(d, &JpegDecoder::finished, m_uploader, &Uploader::finishedJpeg);
connect(this, &QVnc::operate, d, &JpegDecoder::operate);
i++;
}
for(int i = 0; i < m_threads.length(); i++) m_worker.start();
m_threads.at(i)->start();
emit operate(); connect(this, &QVnc::open_connection, m_dispatcher, &Dispatcher::open);
connect(this, &QVnc::terminate_connection, m_dispatcher, &Dispatcher::terminate);
connect(m_dispatcher, &Dispatcher::error, this, &QVnc::dispatch_error);
} }
QVnc::~QVnc() QVnc::~QVnc()
{ {
foreach(QThread* t, m_threads) {
t->quit();
t->wait();
}
} }
void QVnc::open() void QVnc::open()
...@@ -88,25 +67,19 @@ void QVnc::dispatch_error() ...@@ -88,25 +67,19 @@ void QVnc::dispatch_error()
void QVnc::winChanged(QQuickWindow *window) void QVnc::winChanged(QQuickWindow *window)
{ {
if(window != NULL) { if(window != NULL)
connect(window, &QQuickWindow::openglContextCreated, m_uploader, &Uploader::createContext, Qt::BlockingQueuedConnection); connect(window, &QQuickWindow::openglContextCreated, m_uploader, &Uploader::createContext);
connect(window, &QQuickWindow::frameSwapped, m_uploader, &Uploader::swapBuffers, Qt::BlockingQueuedConnection);
}
} }
QQuickFramebufferObject::Renderer *QVnc::createRenderer() const QQuickFramebufferObject::Renderer *QVnc::createRenderer() const
{ {
return new VncRenderer(window(), auto renderer = new VncRenderer(window(),
m_uploader->fillBuffer(), m_uploader->fillBuffer(),
m_uploader->copyBuffer(), m_uploader->copyBuffer(),
m_uploader->bitmapBuffer(), m_uploader->bitmapBuffer(),
m_uploader->texture(), m_uploader->ycbcrBuffer());
NULL, connect(renderer, &VncRenderer::rendered, m_dispatcher, &Dispatcher::decode);
NULL, return renderer;
NULL,
m_uploader->fillCount(),
m_uploader->copyCount(),
m_uploader->bitmapCount());
} }
QString QVnc::host() const QString QVnc::host() const
...@@ -170,9 +143,9 @@ void QVnc::wheelEvent(QWheelEvent *event) ...@@ -170,9 +143,9 @@ void QVnc::wheelEvent(QWheelEvent *event)
void QVnc::mouseMoveEvent(QMouseEvent *event) void QVnc::mouseMoveEvent(QMouseEvent *event)
{ {
// Filter mouse move events, we don't need 100+ of them in a second // Filter mouse move events, we don't need 100+ of them in a second
if((event->timestamp() - m_lastMouseEvent) > 33) { //if((event->timestamp() - m_lastMouseEvent) > 33) {
Qt::MouseButtons buttons = event->buttons(); Qt::MouseButtons buttons = event->buttons();
m_lastMouseEvent = event->timestamp(); // m_lastMouseEvent = event->timestamp();
if(m_dispatcher->mouseEvent(event->x(), if(m_dispatcher->mouseEvent(event->x(),
event->y(), event->y(),
buttons & Qt::LeftButton, buttons & Qt::LeftButton,
...@@ -183,13 +156,13 @@ void QVnc::mouseMoveEvent(QMouseEvent *event) ...@@ -183,13 +156,13 @@ void QVnc::mouseMoveEvent(QMouseEvent *event)
event->accept(); event->accept();
else else
event->ignore(); event->ignore();
} //}
} }
void QVnc::hoverMoveEvent(QHoverEvent *event) void QVnc::hoverMoveEvent(QHoverEvent *event)
{ {
// Qt bug: timestamp is zero for QHoverEvent // Qt bug: timestamp is zero for QHoverEvent
// if((event->timestamp() - m_lastMouseEvent) > 33) { //if((event->timestamp() - m_lastMouseEvent) > 33) {
// m_lastMouseEvent = event->timestamp(); // m_lastMouseEvent = event->timestamp();
QPoint position = event->pos(); QPoint position = event->pos();
if(m_dispatcher->mouseEvent(position.x(), if(m_dispatcher->mouseEvent(position.x(),
...@@ -202,7 +175,7 @@ void QVnc::hoverMoveEvent(QHoverEvent *event) ...@@ -202,7 +175,7 @@ void QVnc::hoverMoveEvent(QHoverEvent *event)
event->accept(); event->accept();
else else
event->ignore(); event->ignore();
// } //}
} }
Uploader *QVnc::uploader() const Uploader *QVnc::uploader() const
......
#ifndef VNC_H #ifndef VNC_H
#define VNC_H #define VNC_H
#define DECODER_COUNT 5 #define TEXTURE_WIDTH 1920
#define TEXTURE_HEIGHT 1080
// #define OGL_DEBUG #define VERTEX_BUFFER 16384
//#define OGL_DEBUG
#include <QQuickWindow> #include <QQuickWindow>
#include <QObject> #include <QObject>
...@@ -32,7 +33,7 @@ public: ...@@ -32,7 +33,7 @@ public:
explicit QVnc(QQuickItem *parent = Q_NULLPTR); explicit QVnc(QQuickItem *parent = Q_NULLPTR);
~QVnc(); ~QVnc();
Renderer *createRenderer() const; Renderer *createRenderer() const override;
QString host() const; QString host() const;
int port() const; int port() const;
Uploader *uploader() const; Uploader *uploader() const;
...@@ -47,9 +48,7 @@ protected: ...@@ -47,9 +48,7 @@ protected:
private: private:
Dispatcher *m_dispatcher; Dispatcher *m_dispatcher;
Uploader *m_uploader; Uploader *m_uploader;
QVector<JpegDecoder*> m_decoders; QThread m_worker;
VncRenderer *m_renderer;
QVector<QThread*> m_threads;
QString m_host; QString m_host;
int m_port; int m_port;
...@@ -64,10 +63,6 @@ signals: ...@@ -64,10 +63,6 @@ signals:
void hostChanged(QString host); void hostChanged(QString host);
void portChanged(int port); void portChanged(int port);
void operate();
void refresh();
public slots: public slots:
void open(); void open();
void terminate(); void terminate();
......
...@@ -16,12 +16,13 @@ ...@@ -16,12 +16,13 @@
#ifndef UPLOADER_H #ifndef UPLOADER_H
#define UPLOADER_H #define UPLOADER_H
#define TEXTURE_WIDTH 4096 #define TEXTURE_WIDTH 1920
#define TEXTURE_HEIGHT 4096 #define TEXTURE_HEIGHT 1080
#define VERTEX_BUFFER 1024 #define VERTEX_BUFFER 16384
// #define OGL_DEBUG //#define OGL_DEBUG
#include <utility> #include <utility>
#include <memory>
#include <QObject> #include <QObject>
#include <QDebug> #include <QDebug>
...@@ -33,15 +34,26 @@ ...@@ -33,15 +34,26 @@
#include <QtMath> #include <QtMath>
#include <QTime> #include <QTime>
#include <QOpenGLDebugLogger> #include <QOpenGLDebugLogger>
#include <QThread>
extern QTime elapsed; #include <texture.h>
#include "concurrentqueue.h"
#define DECODER_COUNT 5
struct Bitmap struct Bitmap
{ {
GLushort x; GLushort x;
GLushort y; GLushort y;
GLushort texX; };
GLushort texY;
struct Copy
{
GLushort src_x;
GLushort src_y;
GLushort dest_x;
GLushort dest_y;
}; };
struct Fill struct Fill
...@@ -54,6 +66,9 @@ struct Fill ...@@ -54,6 +66,9 @@ struct Fill
GLubyte alpha; GLubyte alpha;
}; };
class JpegDecoder;
class Jpeg;
/*! /*!
* \brief The Uploader class * \brief The Uploader class
* *
...@@ -63,17 +78,22 @@ class Uploader : public QObject ...@@ -63,17 +78,22 @@ class Uploader : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Uploader(QObject *parent = Q_NULLPTR); explicit Uploader(QObject *parent = Q_NULLPTR);
~Uploader(); ~Uploader();
GLuint **texture() const; // might as well make these public, they are shared with the renderer
QOpenGLBuffer **fillBuffer() const; std::shared_ptr<Texture> rgbTexture() const {return m_rgbTexture;}
QOpenGLBuffer **copyBuffer() const; std::shared_ptr<Texture> yTexture() const {return m_yTexture;}
QOpenGLBuffer **bitmapBuffer() const; std::shared_ptr<Texture> cbTexture() const {return m_cbTexture;}
unsigned int *fillCount() const; std::shared_ptr<Texture> crTexture() const {return m_crTexture;}
unsigned int *copyCount() const; QOpenGLBuffer fillBuffer() const {return m_fillBuffer;}
unsigned int *bitmapCount() const; QOpenGLBuffer copyBuffer() const {return m_copyBuffer;}
void swapBuffers(); QOpenGLBuffer bitmapBuffer() const {return m_bitmapBuffer;}
QOpenGLBuffer ycbcrBuffer() const {return m_ycbcrBuffer;}
std::size_t fillCount() const {return m_fillIndex;}
std::size_t copyCount() const {return m_copyIndex;}
std::size_t bitmapCount() const {return m_bitmapIndex;}
std::size_t ycbcrCount() const {return m_ycbcrIndex;}
// VNC events // VNC events
void gotFill(const GLushort x, const GLushort y, void gotFill(const GLushort x, const GLushort y,
...@@ -85,56 +105,55 @@ public: ...@@ -85,56 +105,55 @@ public:
void gotBitmap(const unsigned char *image, void gotBitmap(const unsigned char *image,
const GLushort x, const GLushort y, const GLushort x, const GLushort y,
const GLushort width, const GLushort height); const GLushort width, const GLushort height);
void gotJpeg(); void gotJpeg(const uint8_t *buffer, int length, int x, int y, int width, int height);
void finishedUpdate(); void finishedUpdate();
void startDecoding(); void startDecoding();
private: private:
QOffscreenSurface m_surface; QOffscreenSurface m_surface;
QOpenGLContext m_context; QOpenGLContext m_context;
QOpenGLFunctions *m_gl; QOpenGLFunctions *m_gl;
QOpenGLBuffer **m_uploadFill; QOpenGLBuffer m_fillBuffer;
QOpenGLBuffer **m_renderFill;
Fill *m_fillPointer; Fill *m_fillPointer;
size_t m_fillIndex; std::size_t m_fillIndex;
unsigned int *m_fillCount;
QOpenGLBuffer **m_uploadCopy; QOpenGLBuffer m_copyBuffer;
QOpenGLBuffer **m_renderCopy; Copy *m_copyPointer;
Bitmap *m_copyPointer; std::size_t m_copyIndex;
size_t m_copyIndex;
unsigned int *m_copyCount;
QOpenGLBuffer **m_uploadBitmap; QOpenGLBuffer m_bitmapBuffer;
QOpenGLBuffer **m_renderBitmap;
Bitmap *m_bitmapPointer; Bitmap *m_bitmapPointer;
size_t m_bitmapIndex; std::size_t m_bitmapIndex;
unsigned int *m_bitmapCount;
std::shared_ptr<Texture> m_rgbTexture;
unsigned char *m_rgbPointer;
QOpenGLBuffer m_ycbcrBuffer;
Bitmap *m_ycbcrPointer;
std::size_t m_ycbcrIndex;
GLuint m_texId[2]; std::shared_ptr<Texture> m_yTexture;
GLuint **m_uploadTexture; std::shared_ptr<Texture> m_cbTexture;
GLuint **m_renderTexture; std::shared_ptr<Texture> m_crTexture;
std::array<QThread, DECODER_COUNT> m_workers;
std::array<JpegDecoder*, DECODER_COUNT> m_decoders;
ConcurrentQueue<Jpeg*, 1024u> m_queue;
int m_jpegs; int m_jpegs;
bool m_finished; bool m_finished;
bool m_swap;
unsigned short m_atlasX;
unsigned short m_atlasY;
unsigned short m_atlasRowHeight;
unsigned short m_atlasLastWidth;
bool refreshAtlas(const int width, const int height); bool m_first;
void startRendering(); void startRendering();
signals: signals:
void uploadFinished(); void uploadFinished();
void buffersSwapped(); void buffersSwapped();
void operate();
public slots: public slots:
void createContext(QOpenGLContext *context); void createContext(QOpenGLContext *context);
void finishedJpeg(const unsigned char *image, void finishedJpeg();
const GLushort x, const GLushort y,
const GLushort width, const GLushort height);
private slots: private slots:
void handleDebugMessage(const QOpenGLDebugMessage &debugMessage); void handleDebugMessage(const QOpenGLDebugMessage &debugMessage);
}; };
......
#ifndef VNCRENDERER_H #ifndef VNCRENDERER_H
#define VNCRENDERER_H #define VNCRENDERER_H
#include <memory>
#include <QQuickWindow> #include <QQuickWindow>
#include <QQuickFramebufferObject> #include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObject>
...@@ -11,6 +12,8 @@ ...@@ -11,6 +12,8 @@
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QTime> #include <QTime>
#include <texture.h>
#include "qvnc.h" #include "qvnc.h"
struct Indices struct Indices
...@@ -35,54 +38,56 @@ class VncRenderer : public QObject, public QQuickFramebufferObject::Renderer ...@@ -35,54 +38,56 @@ class VncRenderer : public QObject, public QQuickFramebufferObject::Renderer
Q_OBJECT Q_OBJECT
public: public:
VncRenderer(QQuickWindow *window, VncRenderer(QQuickWindow *window,
QOpenGLBuffer **fillBuffer, QOpenGLBuffer fillBuffer,
QOpenGLBuffer **copyBuffer, QOpenGLBuffer copyBuffer,
QOpenGLBuffer **bitmapBuffer, QOpenGLBuffer bitmapBuffer,
GLuint **rgbTexture, QOpenGLBuffer ycbcrBuffer,
GLuint **yTexture,
GLuint **cbTexture,
GLuint **crTexture,
unsigned int *fillCount,
unsigned int *copyCount,
unsigned int *bitmapCount,
QObject *parent = nullptr); QObject *parent = nullptr);
protected: protected:
void render() override; void render() override;
void synchronize(QQuickFramebufferObject *fbo) override;
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override; QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override;
private: private:
QQuickWindow *m_window; QQuickWindow *m_window;
QMatrix4x4 m_copyOrtho; QMatrix4x4 m_fromTexture;
QMatrix4x4 m_copyTexortho; QMatrix4x4 m_toFramebuffer;
QMatrix4x4 m_bitmapOrtho; QMatrix4x4 m_fromFramebuffer;
QMatrix4x4 m_bitmapTexortho; QMatrix4x4 m_toTexture;
QOpenGLBuffer **m_fillBuffer; QOpenGLBuffer m_fillBuffer;
QOpenGLBuffer **m_copyBuffer; QOpenGLBuffer m_copyBuffer;
QOpenGLBuffer **m_bitmapBuffer; QOpenGLBuffer m_bitmapBuffer;
QOpenGLBuffer m_ycbcrBuffer;
GLuint **m_rgbTexture; std::shared_ptr<Texture> m_rgbTexture;
GLuint **m_yTexture; std::shared_ptr<Texture> m_yTexture;
GLuint **m_cbTexture; std::shared_ptr<Texture> m_cbTexture;
GLuint **m_crTexture; std::shared_ptr<Texture> m_crTexture;
unsigned int *m_fillCount; unsigned int m_fillCount;
unsigned int *m_copyCount; unsigned int m_copyCount;
unsigned int *m_bitmapCount; unsigned int m_bitmapCount;
unsigned int m_ycbcrCount;
QOpenGLBuffer m_indices; QOpenGLBuffer m_indices;
QOpenGLShaderProgram m_fillProgram; QOpenGLShaderProgram m_fillProgram;
QOpenGLShaderProgram m_bitmapProgram; QOpenGLShaderProgram m_bitmapProgram;
QOpenGLShaderProgram m_ycbcrProgram; QOpenGLShaderProgram m_ycbcrProgram;
QOpenGLShaderProgram m_copyProgram;
GLuint m_textureTarget; GLuint m_textureTarget;
QOpenGLFunctions *m_gl; QOpenGLFunctions *m_gl;
QOpenGLFramebufferObject *m_framebufferObject; QOpenGLFramebufferObject *m_framebufferObject;
bool m_clear;
int frameCount; int frameCount;
QTime frameTime; QTime frameTime;
private slots: private slots:
void handleDebugMessage(const QOpenGLDebugMessage &debugMessage); void handleDebugMessage(const QOpenGLDebugMessage &debugMessage);
signals:
void rendered();
}; };
#endif // VNCRENDERER_H #endif // VNCRENDERER_H
uniform sampler2D y_texture;
uniform sampler2D cb_texture;
uniform sampler2D cr_texture;
varying highp vec2 vartexcoord;
varying highp float vartexindex;
void main(void)
{
highp float Y;
highp float Cb;
highp float Cr;
highp float pos = mod((gl_FragCoord.x - 0.5), 4.0);
if(pos < 1.0) {
Y = texture2D(y_texture, vartexcoord).r;
Cb = texture2D(cb_texture, vartexcoord).r;
Cr = texture2D(cr_texture, vartexcoord).r;
} else if(pos < 2.0) {
Y = texture2D(y_texture, vartexcoord).g;
Cb = texture2D(cb_texture, vartexcoord).g;
Cr = texture2D(cr_texture, vartexcoord).g;
} else if(pos < 3.0) {
Y = texture2D(y_texture, vartexcoord).b;
Cb = texture2D(cb_texture, vartexcoord).b;
Cr = texture2D(cr_texture, vartexcoord).b;
} else {
Y = texture2D(y_texture, vartexcoord).a;
Cb = texture2D(cb_texture, vartexcoord).a;
Cr = texture2D(cr_texture, vartexcoord).a;
}
highp float R = Y + 1.402 * (Cr - 0.5);
highp float G = Y - 0.34414 * (Cb - 0.5) - 0.71414 * (Cr - 0.5);
highp float B = Y + 1.772 * (Cb - 0.5);
gl_FragColor = vec4(B, G, R, 1.0);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment