diff --git a/Makefile b/Makefile index a9bb47f..2d4bf77 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ -DEPENDENCIES := pixman-1 libuv +DEPENDENCIES := pixman-1 libuv libturbojpeg SOURCES := \ src/server.c \ src/util.c \ src/vec.c \ src/zrle.c \ + src/tight.c \ src/raw-encoding.c \ src/pixels.c \ src/damage.c \ diff --git a/include/rfb-proto.h b/include/rfb-proto.h index 27f89ce..20fd5d4 100644 --- a/include/rfb-proto.h +++ b/include/rfb-proto.h @@ -50,6 +50,7 @@ enum rfb_encodings { RFB_ENCODING_COPYRECT = 1, RFB_ENCODING_RRE = 2, RFB_ENCODING_HEXTILE = 5, + RFB_ENCODING_TIGHT = 7, RFB_ENCODING_TRLE = 15, RFB_ENCODING_ZRLE = 16, RFB_ENCODING_CURSOR = -239, diff --git a/include/tight.h b/include/tight.h new file mode 100644 index 0000000..82ca009 --- /dev/null +++ b/include/tight.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +struct nvnc_fb; +struct rfb_pixel_format; +struct pixman_region16; +struct vec; + +int tight_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt, + const struct nvnc_fb* src, uint32_t src_fmt, + struct pixman_region16* region); diff --git a/meson.build b/meson.build index 07a203e..6941cd2 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ libm = cc.find_library('m', required: false) pixman = dependency('pixman-1') libuv = dependency('libuv') +libturbojpeg = dependency('libturbojpeg') inc = include_directories('include', 'contrib/miniz') @@ -43,6 +44,7 @@ sources = [ 'src/util.c', 'src/vec.c', 'src/zrle.c', + 'src/tight.c', 'src/raw-encoding.c', 'src/pixels.c', 'src/damage.c', @@ -54,6 +56,7 @@ dependencies = [ libm, pixman, libuv, + libturbojpeg, ] neatvnc = shared_library( diff --git a/src/server.c b/src/server.c index a414825..5dae81f 100644 --- a/src/server.c +++ b/src/server.c @@ -17,6 +17,7 @@ #include "rfb-proto.h" #include "util.h" #include "zrle.h" +#include "tight.h" #include "raw-encoding.h" #include "vec.h" #include "type-macros.h" @@ -539,6 +540,7 @@ static int on_client_set_encodings(struct nvnc_client* client) case RFB_ENCODING_COPYRECT: case RFB_ENCODING_RRE: case RFB_ENCODING_HEXTILE: + case RFB_ENCODING_TIGHT: case RFB_ENCODING_TRLE: case RFB_ENCODING_ZRLE: case RFB_ENCODING_CURSOR: @@ -883,6 +885,9 @@ enum rfb_encodings choose_frame_encoding(struct nvnc_client* client) for (int i = 0; i < client->n_encodings; ++i) switch (client->encodings[i]) { case RFB_ENCODING_RAW: +#ifdef ENABLE_TIGHT + case RFB_ENCODING_TIGHT: +#endif case RFB_ENCODING_ZRLE: return client->encodings[i]; default: @@ -906,6 +911,12 @@ void do_client_update_fb(uv_work_t* work) raw_encode_frame(&update->frame, &client->pixfmt, fb, &update->server_fmt, &update->region); break; +#ifdef ENABLE_TIGHT + case RFB_ENCODING_TIGHT: + tight_encode_frame(&update->frame, &client->pixfmt, fb, + fb->fourcc_format, &update->region); + break; +#endif case RFB_ENCODING_ZRLE: zrle_encode_frame(&client->z_stream, &update->frame, &client->pixfmt, fb, &update->server_fmt, diff --git a/src/tight.c b/src/tight.c new file mode 100644 index 0000000..75f0e8a --- /dev/null +++ b/src/tight.c @@ -0,0 +1,125 @@ +#include "neatvnc.h" +#include "rfb-proto.h" +#include "vec.h" +#include "fb.h" + +#include +#include +#include +#include + +#define TIGHT_FILL 0x80 +#define TIGHT_JPEG 0x90 +#define TIGHT_PNG 0xA0 +#define TIGHT_BASIC 0x00 + +enum TJPF get_jpeg_pixfmt(uint32_t fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + return TJPF_XRGB; + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + return TJPF_XBGR; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + return TJPF_RGBX; + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + return TJPF_BGRX; + } + + return TJPF_UNKNOWN; +} + +static void tight_encode_size(struct vec* dst, size_t size) +{ + vec_fast_append_8(dst, (size & 0x7f) | ((size >= 128) << 7)); + if (size >= 128) + vec_fast_append_8(dst, ((size >> 7) & 0x7f) | ((size >= 16384) << 7)); + if (size >= 16384) + vec_fast_append_8(dst, (size >> 14) & 0x7f); +} + +int tight_encode_box(struct vec* dst, const struct rfb_pixel_format* dst_fmt, + const struct nvnc_fb* src, uint32_t src_fmt, + uint32_t x, uint32_t y, uint32_t stride, + uint32_t width, uint32_t height) +{ + + unsigned char* buffer = NULL; + size_t size = 0; + + int quality = 50; /* 1 - 100 */ + enum TJPF tjfmt = get_jpeg_pixfmt(src_fmt); + if (tjfmt == TJPF_UNKNOWN) + return -1; + + vec_reserve(dst, 4096); + + struct rfb_server_fb_rect rect = { + .encoding = htonl(RFB_ENCODING_TIGHT), + .x = htons(x), + .y = htons(y), + .width = htons(width), + .height = htons(height), + }; + + vec_append(dst, &rect, sizeof(rect)); + + tjhandle handle = tjInitCompress(); + + void* img = (uint32_t*)src->addr + x + y * stride; + + tjCompress2(handle, img, width, stride * 4, height, tjfmt, &buffer, + &size, TJSAMP_422, quality, TJFLAG_FASTDCT); + + vec_fast_append_8(dst, TIGHT_JPEG); + + tight_encode_size(dst, size); + + vec_append(dst, buffer, size); + + tjFree(buffer); + tjDestroy(handle); + + return 0; +} + +int tight_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt, + const struct nvnc_fb* src, uint32_t src_fmt, + struct pixman_region16* region) +{ + int rc = -1; + + int n_rects = 0; + struct pixman_box16* box = pixman_region_rectangles(region, &n_rects); + if (n_rects > UINT16_MAX) { + box = pixman_region_extents(region); + n_rects = 1; + } + + struct rfb_server_fb_update_msg head = { + .type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE, + .n_rects = htons(n_rects), + }; + + rc = vec_append(dst, &head, sizeof(head)); + if (rc < 0) + return -1; + + for (int i = 0; i < n_rects; ++i) { + int x = box[i].x1; + int y = box[i].y1; + int box_width = box[i].x2 - x; + int box_height = box[i].y2 - y; + + rc = tight_encode_box(dst, dst_fmt, src, src_fmt, x, y, + src->width, box_width, box_height); + if (rc < 0) + return -1; + } + + return 0; +}