tight: Re-implement with threads
parent
9b54f6d936
commit
497f9adb29
|
@ -16,13 +16,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <zlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct vec;
|
||||
struct nvnc_client;
|
||||
struct nvnc_fb;
|
||||
struct tight_tile;
|
||||
struct pixman_region16;
|
||||
struct rfb_pixel_format;
|
||||
struct aml_work;
|
||||
|
||||
enum tight_quality {
|
||||
TIGHT_QUALITY_UNSPEC = 0,
|
||||
|
@ -32,14 +33,37 @@ enum tight_quality {
|
|||
};
|
||||
|
||||
struct tight_encoder {
|
||||
z_stream zs[4];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t grid_width;
|
||||
uint32_t grid_height;
|
||||
enum tight_quality quality;
|
||||
|
||||
struct tight_tile* grid;
|
||||
|
||||
z_stream zs[4];
|
||||
struct aml_work* zs_worker[4];
|
||||
|
||||
const struct rfb_pixel_format* dfmt;
|
||||
const struct rfb_pixel_format* sfmt;
|
||||
const struct nvnc_fb* fb;
|
||||
|
||||
uint32_t n_rects;
|
||||
uint32_t n_jobs;
|
||||
|
||||
struct vec* dst;
|
||||
|
||||
pthread_mutex_t wait_mutex;
|
||||
pthread_cond_t wait_cond;
|
||||
};
|
||||
|
||||
int tight_encoder_init(struct tight_encoder*);
|
||||
void tight_encoder_destroy(struct tight_encoder*);
|
||||
int tight_encoder_init(struct tight_encoder* self, uint32_t width,
|
||||
uint32_t height);
|
||||
void tight_encoder_destroy(struct tight_encoder* self);
|
||||
|
||||
int tight_encode_frame(struct tight_encoder* self, struct vec* dst,
|
||||
const struct nvnc_fb* fb,
|
||||
const struct rfb_pixel_format* src_fmt,
|
||||
struct pixman_region16* region);
|
||||
const struct rfb_pixel_format* dfmt,
|
||||
const struct nvnc_fb* src,
|
||||
const struct rfb_pixel_format* sfmt,
|
||||
struct pixman_region16* damage,
|
||||
enum tight_quality quality);
|
||||
|
|
28
src/server.c
28
src/server.c
|
@ -731,7 +731,9 @@ static void on_connection(void* obj)
|
|||
goto deflate_failure;
|
||||
|
||||
#ifdef ENABLE_TIGHT
|
||||
if (tight_encoder_init(&client->tight_encoder) < 0)
|
||||
int width = server->display->buffer->width;
|
||||
int height = server->display->buffer->height;
|
||||
if (tight_encoder_init(&client->tight_encoder, width, height) < 0)
|
||||
goto tight_failure;
|
||||
#endif
|
||||
|
||||
|
@ -941,6 +943,22 @@ static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client)
|
|||
return RFB_ENCODING_RAW;
|
||||
}
|
||||
|
||||
static enum tight_quality client_get_tight_quality(struct nvnc_client* client)
|
||||
{
|
||||
if (client->pixfmt.bits_per_pixel != 16 &&
|
||||
client->pixfmt.bits_per_pixel != 32)
|
||||
return TIGHT_QUALITY_LOSSLESS;
|
||||
|
||||
for (size_t i = 0; i < client->n_encodings; ++i)
|
||||
switch (client->encodings[i]) {
|
||||
case RFB_ENCODING_JPEG_HIGHQ: return TIGHT_QUALITY_HIGH;
|
||||
case RFB_ENCODING_JPEG_LOWQ: return TIGHT_QUALITY_LOW;
|
||||
default:;
|
||||
}
|
||||
|
||||
return TIGHT_QUALITY_LOSSLESS;
|
||||
}
|
||||
|
||||
static void do_client_update_fb(void* work)
|
||||
{
|
||||
struct fb_update_work* update = aml_get_userdata(work);
|
||||
|
@ -960,9 +978,11 @@ static void do_client_update_fb(void* work)
|
|||
&update->server_fmt, &update->region);
|
||||
break;
|
||||
#ifdef ENABLE_TIGHT
|
||||
case RFB_ENCODING_TIGHT:
|
||||
tight_encode_frame(&client->tight_encoder, &update->frame, fb,
|
||||
&update->server_fmt, &update->region);
|
||||
case RFB_ENCODING_TIGHT:;
|
||||
enum tight_quality quality = client_get_tight_quality(client);
|
||||
tight_encode_frame(&client->tight_encoder, &update->frame,
|
||||
&client->pixfmt, fb, &update->server_fmt,
|
||||
&update->region, quality);
|
||||
break;
|
||||
#endif
|
||||
case RFB_ENCODING_ZRLE:
|
||||
|
|
607
src/tight.c
607
src/tight.c
|
@ -16,50 +16,63 @@
|
|||
|
||||
#include "neatvnc.h"
|
||||
#include "rfb-proto.h"
|
||||
#include "vec.h"
|
||||
#include "fb.h"
|
||||
#include "tight.h"
|
||||
#include "common.h"
|
||||
#include "pixels.h"
|
||||
#include "vec.h"
|
||||
#include "logging.h"
|
||||
#include "type-macros.h"
|
||||
#include "tight.h"
|
||||
|
||||
#include <pixman.h>
|
||||
#include <turbojpeg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <zlib.h>
|
||||
#include <pixels.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <aml.h>
|
||||
#include <turbojpeg.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
|
||||
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||
|
||||
#define TIGHT_FILL 0x80
|
||||
#define TIGHT_JPEG 0x90
|
||||
#define TIGHT_PNG 0xA0
|
||||
#define TIGHT_BASIC 0x00
|
||||
|
||||
#define TIGHT_MAX_WIDTH 2048
|
||||
#define TIGHT_STREAM(n) ((n) << 4)
|
||||
#define TIGHT_RESET(n) (1 << (n))
|
||||
|
||||
static enum tight_quality tight_get_quality(struct tight_encoder* self)
|
||||
#define TSL 64 /* Tile Side Length */
|
||||
|
||||
#define MAX_TILE_SIZE (2 * TSL * TSL * 4)
|
||||
|
||||
enum tight_tile_state {
|
||||
TIGHT_TILE_READY = 0,
|
||||
TIGHT_TILE_DAMAGED,
|
||||
TIGHT_TILE_ENCODED,
|
||||
};
|
||||
|
||||
struct tight_tile {
|
||||
enum tight_tile_state state;
|
||||
size_t size;
|
||||
uint8_t type;
|
||||
char buffer[MAX_TILE_SIZE];
|
||||
};
|
||||
|
||||
struct tight_zs_worker_ctx {
|
||||
struct tight_encoder* encoder;
|
||||
int index;
|
||||
};
|
||||
|
||||
static void do_tight_zs_work(void*);
|
||||
static void on_tight_zs_work_done(void*);
|
||||
|
||||
static int tight_encoder_init_stream(z_stream* zs)
|
||||
{
|
||||
struct nvnc_client* client =
|
||||
container_of(self, struct nvnc_client, tight_encoder);
|
||||
|
||||
if (client->pixfmt.bits_per_pixel != 16 &&
|
||||
client->pixfmt.bits_per_pixel != 32)
|
||||
return TIGHT_QUALITY_LOSSLESS;
|
||||
|
||||
for (size_t i = 0; i < client->n_encodings; ++i)
|
||||
switch (client->encodings[i]) {
|
||||
case RFB_ENCODING_JPEG_HIGHQ: return TIGHT_QUALITY_HIGH;
|
||||
case RFB_ENCODING_JPEG_LOWQ: return TIGHT_QUALITY_LOW;
|
||||
default:;
|
||||
}
|
||||
|
||||
return TIGHT_QUALITY_LOSSLESS;
|
||||
}
|
||||
|
||||
static int tight_init_zstream(z_stream* zx)
|
||||
{
|
||||
int rc = deflateInit2(zx,
|
||||
int rc = deflateInit2(zs,
|
||||
/* compression level: */ 1,
|
||||
/* method: */ Z_DEFLATED,
|
||||
/* window bits: */ 15,
|
||||
|
@ -68,15 +81,135 @@ static int tight_init_zstream(z_stream* zx)
|
|||
return rc == Z_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int tight_encoder_init(struct tight_encoder* self)
|
||||
static inline struct tight_tile* tight_tile(struct tight_encoder* self,
|
||||
uint32_t x, uint32_t y)
|
||||
{
|
||||
// TODO: Implement more stream channels
|
||||
return tight_init_zstream(&self->zs[0]);
|
||||
return &self->grid[x + y * self->grid_width];
|
||||
}
|
||||
|
||||
static inline uint32_t tight_tile_width(struct tight_encoder* self,
|
||||
uint32_t x)
|
||||
{
|
||||
return x + TSL > self->width ? self->width - x : TSL;
|
||||
}
|
||||
|
||||
static inline uint32_t tight_tile_height(struct tight_encoder* self,
|
||||
uint32_t y)
|
||||
{
|
||||
return y + TSL > self->height ? self->height - y : TSL;
|
||||
}
|
||||
|
||||
static int tight_init_zs_worker(struct tight_encoder* self, int index)
|
||||
{
|
||||
struct tight_zs_worker_ctx* ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx)
|
||||
return -1;
|
||||
|
||||
ctx->encoder = self;
|
||||
ctx->index = index;
|
||||
|
||||
self->zs_worker[index] =
|
||||
aml_work_new(do_tight_zs_work, on_tight_zs_work_done, ctx, free);
|
||||
if (!self->zs_worker[index])
|
||||
goto failure;
|
||||
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tight_encoder_init(struct tight_encoder* self, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
memset(self, 0, sizeof(*self));
|
||||
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
self->grid_width = UDIV_UP(width, 64);
|
||||
self->grid_height = UDIV_UP(height, 64);
|
||||
|
||||
self->grid = calloc(self->grid_width * self->grid_height,
|
||||
sizeof(*self->grid));
|
||||
if (!self->grid)
|
||||
return -1;
|
||||
|
||||
tight_encoder_init_stream(&self->zs[0]);
|
||||
tight_encoder_init_stream(&self->zs[1]);
|
||||
tight_encoder_init_stream(&self->zs[2]);
|
||||
tight_encoder_init_stream(&self->zs[3]);
|
||||
|
||||
tight_init_zs_worker(self, 0);
|
||||
tight_init_zs_worker(self, 1);
|
||||
tight_init_zs_worker(self, 2);
|
||||
tight_init_zs_worker(self, 3);
|
||||
|
||||
pthread_mutex_init(&self->wait_mutex, NULL);
|
||||
pthread_cond_init(&self->wait_cond, NULL);
|
||||
|
||||
// One worker is blocked while other workers are encoding, so at least
|
||||
// 2 are required.
|
||||
aml_require_workers(aml_get_default(), 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tight_encoder_destroy(struct tight_encoder* self)
|
||||
{
|
||||
pthread_cond_destroy(&self->wait_cond);
|
||||
pthread_mutex_destroy(&self->wait_mutex);
|
||||
|
||||
aml_unref(self->zs_worker[3]);
|
||||
aml_unref(self->zs_worker[2]);
|
||||
aml_unref(self->zs_worker[1]);
|
||||
aml_unref(self->zs_worker[0]);
|
||||
|
||||
deflateEnd(&self->zs[3]);
|
||||
deflateEnd(&self->zs[2]);
|
||||
deflateEnd(&self->zs[1]);
|
||||
deflateEnd(&self->zs[0]);
|
||||
|
||||
free(self->grid);
|
||||
}
|
||||
|
||||
static int tight_apply_damage(struct tight_encoder* self,
|
||||
struct pixman_region16* damage)
|
||||
{
|
||||
int n_damaged = 0;
|
||||
|
||||
/* Align damage to tile grid */
|
||||
for (uint32_t y = 0; y < self->grid_height; ++y)
|
||||
for (uint32_t x = 0; x < self->grid_width; ++x) {
|
||||
struct pixman_box16 box = {
|
||||
.x1 = x * TSL,
|
||||
.y1 = y * TSL,
|
||||
.x2 = ((x + 1) * TSL) - 1,
|
||||
.y2 = ((y + 1) * TSL) - 1,
|
||||
};
|
||||
|
||||
pixman_region_overlap_t overlap
|
||||
= pixman_region_contains_rectangle(damage, &box);
|
||||
|
||||
if (overlap != PIXMAN_REGION_OUT) {
|
||||
++n_damaged;
|
||||
tight_tile(self, x, y)->state = TIGHT_TILE_DAMAGED;
|
||||
} else {
|
||||
tight_tile(self, x, y)->state = TIGHT_TILE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
return n_damaged;
|
||||
}
|
||||
|
||||
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) & 0xff);
|
||||
}
|
||||
|
||||
static int calc_bytes_per_cpixel(const struct rfb_pixel_format* fmt)
|
||||
|
@ -85,7 +218,66 @@ static int calc_bytes_per_cpixel(const struct rfb_pixel_format* fmt)
|
|||
: fmt->bits_per_pixel / 8;
|
||||
}
|
||||
|
||||
static enum TJPF get_jpeg_pixfmt(uint32_t fourcc)
|
||||
static int tight_deflate(struct tight_tile* tile, void* src,
|
||||
size_t len, z_stream* zs, bool flush)
|
||||
{
|
||||
zs->next_in = src;
|
||||
zs->avail_in = len;
|
||||
|
||||
do {
|
||||
if (tile->size >= MAX_TILE_SIZE)
|
||||
return -1;
|
||||
|
||||
zs->next_out = ((Bytef*)tile->buffer) + tile->size;
|
||||
zs->avail_out = MAX_TILE_SIZE - tile->size;
|
||||
|
||||
int r = deflate(zs, flush ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
||||
if (r == Z_STREAM_ERROR)
|
||||
return -1;
|
||||
|
||||
tile->size = zs->next_out - (Bytef*)tile->buffer;
|
||||
} while (zs->avail_out == 0);
|
||||
|
||||
assert(zs->avail_in == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tight_encode_tile_basic(struct tight_encoder* self,
|
||||
struct tight_tile* tile, uint32_t x, uint32_t y_start,
|
||||
uint32_t width, uint32_t height, int zs_index)
|
||||
{
|
||||
z_stream* zs = &self->zs[zs_index];
|
||||
tile->type = TIGHT_BASIC | TIGHT_STREAM(zs_index);
|
||||
|
||||
int bytes_per_cpixel = calc_bytes_per_cpixel(self->dfmt);
|
||||
assert(bytes_per_cpixel <= 4);
|
||||
uint8_t row[TSL * 4];
|
||||
|
||||
struct rfb_pixel_format cfmt = { 0 };
|
||||
if (bytes_per_cpixel == 3)
|
||||
rfb_pixfmt_from_fourcc(&cfmt, DRM_FORMAT_XBGR8888);
|
||||
else
|
||||
memcpy(&cfmt, self->dfmt, sizeof(cfmt));
|
||||
|
||||
uint32_t* addr = nvnc_fb_get_addr(self->fb);
|
||||
uint32_t stride = nvnc_fb_get_width(self->fb);
|
||||
|
||||
// TODO: Limit width and hight to the sides
|
||||
for (uint32_t y = y_start; y < y_start + height; ++y) {
|
||||
void* img = addr + x + y * stride;
|
||||
pixel32_to_cpixel(row, &cfmt, img, self->sfmt, bytes_per_cpixel,
|
||||
width);
|
||||
|
||||
// TODO What to do if the buffer fills up?
|
||||
if (tight_deflate(tile, row, bytes_per_cpixel * width,
|
||||
zs, y == y_start + height - 1) < 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static enum TJPF tight_get_jpeg_pixfmt(uint32_t fourcc)
|
||||
{
|
||||
switch (fourcc) {
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
|
@ -105,22 +297,14 @@ static enum TJPF get_jpeg_pixfmt(uint32_t fourcc)
|
|||
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) & 0xff);
|
||||
}
|
||||
|
||||
static int tight_encode_box_jpeg(struct tight_encoder* self, struct vec* dst,
|
||||
const struct nvnc_fb* fb, uint32_t x, uint32_t y,
|
||||
uint32_t stride, uint32_t width, uint32_t height)
|
||||
static int tight_encode_tile_jpeg(struct tight_encoder* self,
|
||||
struct tight_tile* tile, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
tile->type = TIGHT_JPEG;
|
||||
|
||||
unsigned char* buffer = NULL;
|
||||
size_t size = 0;
|
||||
unsigned long size = 0;
|
||||
|
||||
int quality; /* 1 - 100 */
|
||||
|
||||
|
@ -130,11 +314,140 @@ static int tight_encode_box_jpeg(struct tight_encoder* self, struct vec* dst,
|
|||
default: abort();
|
||||
}
|
||||
|
||||
enum TJPF tjfmt = get_jpeg_pixfmt(fb->fourcc_format);
|
||||
uint32_t fourcc = nvnc_fb_get_fourcc_format(self->fb);
|
||||
enum TJPF tjfmt = tight_get_jpeg_pixfmt(fourcc);
|
||||
if (tjfmt == TJPF_UNKNOWN)
|
||||
return -1;
|
||||
|
||||
vec_reserve(dst, 4096);
|
||||
tjhandle handle = tjInitCompress();
|
||||
if (!handle)
|
||||
return -1;
|
||||
|
||||
uint32_t* addr = nvnc_fb_get_addr(self->fb);
|
||||
uint32_t stride = nvnc_fb_get_width(self->fb);
|
||||
void* img = (uint32_t*)addr + x + y * stride;
|
||||
|
||||
int rc = -1;
|
||||
rc = tjCompress2(handle, img, width, stride * 4, height, tjfmt, &buffer,
|
||||
&size, TJSAMP_422, quality, TJFLAG_FASTDCT);
|
||||
if (rc < 0) {
|
||||
log_error("Failed to encode tight JPEG box: %s\n", tjGetErrorStr());
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (size > MAX_TILE_SIZE) {
|
||||
log_error("Whoops, encoded JPEG was too big for the buffer\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
memcpy(tile->buffer, buffer, size);
|
||||
tile->size = size;
|
||||
|
||||
rc = 0;
|
||||
tjFree(buffer);
|
||||
failure:
|
||||
tjDestroy(handle);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tight_encode_tile(struct tight_encoder* self,
|
||||
uint32_t gx, uint32_t gy)
|
||||
{
|
||||
struct tight_tile* tile = tight_tile(self, gx, gy);
|
||||
|
||||
uint32_t x = gx * TSL;
|
||||
uint32_t y = gy * TSL;
|
||||
|
||||
uint32_t width = tight_tile_width(self, x);
|
||||
uint32_t height = tight_tile_height(self, y);
|
||||
|
||||
tile->size = 0;
|
||||
|
||||
switch (self->quality) {
|
||||
case TIGHT_QUALITY_LOSSLESS:
|
||||
tight_encode_tile_basic(self, tile, x, y, width, height, gx % 4);
|
||||
break;
|
||||
case TIGHT_QUALITY_HIGH:
|
||||
case TIGHT_QUALITY_LOW:
|
||||
// TODO: Use more workers for jpeg
|
||||
tight_encode_tile_jpeg(self, tile, x, y, width, height);
|
||||
break;
|
||||
case TIGHT_QUALITY_UNSPEC:
|
||||
abort();
|
||||
}
|
||||
//TODO Jpeg
|
||||
|
||||
tile->state = TIGHT_TILE_ENCODED;
|
||||
}
|
||||
|
||||
static int tight_encode_rect_count(struct tight_encoder* self)
|
||||
{
|
||||
struct rfb_server_fb_update_msg msg = {
|
||||
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
||||
.n_rects = htons(self->n_rects),
|
||||
};
|
||||
|
||||
return vec_append(self->dst, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
static void do_tight_zs_work(void* obj)
|
||||
{
|
||||
struct tight_zs_worker_ctx* ctx = aml_get_userdata(obj);
|
||||
struct tight_encoder* self = ctx->encoder;
|
||||
int index = ctx->index;
|
||||
|
||||
for (uint32_t y = 0; y < self->grid_height; ++y)
|
||||
for (uint32_t x = index; x < self->grid_width; x += 4)
|
||||
if (tight_tile(self, x, y)->state == TIGHT_TILE_DAMAGED)
|
||||
tight_encode_tile(self, x, y);
|
||||
}
|
||||
|
||||
static void on_tight_zs_work_done(void* obj)
|
||||
{
|
||||
struct tight_zs_worker_ctx* ctx = aml_get_userdata(obj);
|
||||
struct tight_encoder* self = ctx->encoder;
|
||||
|
||||
pthread_mutex_lock(&self->wait_mutex);
|
||||
if (--self->n_jobs == 0)
|
||||
pthread_cond_signal(&self->wait_cond);
|
||||
pthread_mutex_unlock(&self->wait_mutex);
|
||||
}
|
||||
|
||||
static int tight_schedule_zs_work(struct tight_encoder* self, int index)
|
||||
{
|
||||
int rc = aml_start(aml_get_default(), self->zs_worker[index]);
|
||||
if (rc >= 0)
|
||||
++self->n_jobs;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tight_schedule_encoding_jobs(struct tight_encoder* self)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
pthread_mutex_lock(&self->wait_mutex);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (tight_schedule_zs_work(self, i) < 0)
|
||||
goto failure;
|
||||
|
||||
rc = 0;
|
||||
failure:
|
||||
pthread_mutex_unlock(&self->wait_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tight_finish_tile(struct tight_encoder* self,
|
||||
uint32_t gx, uint32_t gy)
|
||||
{
|
||||
struct tight_tile* tile = tight_tile(self, gx, gy);
|
||||
|
||||
uint32_t x = gx * TSL;
|
||||
uint32_t y = gy * TSL;
|
||||
|
||||
uint32_t width = tight_tile_width(self, x);
|
||||
uint32_t height = tight_tile_height(self, y);
|
||||
|
||||
struct rfb_server_fb_rect rect = {
|
||||
.encoding = htonl(RFB_ENCODING_TIGHT),
|
||||
|
@ -144,186 +457,52 @@ static int tight_encode_box_jpeg(struct tight_encoder* self, struct vec* dst,
|
|||
.height = htons(height),
|
||||
};
|
||||
|
||||
vec_append(dst, &rect, sizeof(rect));
|
||||
vec_append(self->dst, &rect, sizeof(rect));
|
||||
vec_append(self->dst, &tile->type, sizeof(tile->type));
|
||||
tight_encode_size(self->dst, tile->size);
|
||||
vec_append(self->dst, tile->buffer, tile->size);
|
||||
|
||||
tjhandle handle = tjInitCompress();
|
||||
if (!handle)
|
||||
return -1;
|
||||
|
||||
void* img = (uint32_t*)fb->addr + x + y * stride;
|
||||
|
||||
int rc = -1;
|
||||
rc = tjCompress2(handle, img, width, stride * 4, height, tjfmt, &buffer,
|
||||
&size, TJSAMP_422, quality, TJFLAG_FASTDCT);
|
||||
if (rc < 0) {
|
||||
log_error("Failed to encode tight JPEG box: %s\n", tjGetErrorStr());
|
||||
goto compress_failure;
|
||||
}
|
||||
|
||||
vec_fast_append_8(dst, TIGHT_JPEG);
|
||||
|
||||
tight_encode_size(dst, size);
|
||||
|
||||
vec_append(dst, buffer, size);
|
||||
|
||||
rc = 0;
|
||||
tjFree(buffer);
|
||||
compress_failure:
|
||||
tjDestroy(handle);
|
||||
|
||||
return rc;
|
||||
tile->state = TIGHT_TILE_READY;
|
||||
}
|
||||
|
||||
static int tight_deflate(struct vec* dst, void* src, size_t len, z_stream* zs,
|
||||
bool flush)
|
||||
static void tight_finish(struct tight_encoder* self)
|
||||
{
|
||||
zs->next_in = src;
|
||||
zs->avail_in = len;
|
||||
|
||||
do {
|
||||
if (dst->len == dst->cap && vec_reserve(dst, dst->cap * 2) < 0)
|
||||
return -1;
|
||||
|
||||
zs->next_out = ((Bytef*)dst->data) + dst->len;
|
||||
zs->avail_out = dst->cap - dst->len;
|
||||
|
||||
int r = deflate(zs, flush ? Z_SYNC_FLUSH : Z_NO_FLUSH);
|
||||
if (r == Z_STREAM_ERROR)
|
||||
return -1;
|
||||
|
||||
dst->len = zs->next_out - (Bytef*)dst->data;
|
||||
} while (zs->avail_out == 0);
|
||||
|
||||
assert(zs->avail_in == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tight_encode_box_basic(struct tight_encoder* self, struct vec* dst,
|
||||
const struct nvnc_fb* fb,
|
||||
const struct rfb_pixel_format* src_fmt,
|
||||
uint32_t x, uint32_t y_start, uint32_t stride,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
struct nvnc_client* client =
|
||||
container_of(self, struct nvnc_client, tight_encoder);
|
||||
|
||||
vec_reserve(dst, 4096);
|
||||
|
||||
int bytes_per_cpixel = calc_bytes_per_cpixel(&client->pixfmt);
|
||||
uint8_t* row = malloc(bytes_per_cpixel * width);
|
||||
if (!row)
|
||||
return -1;
|
||||
|
||||
struct vec buffer;
|
||||
if (vec_init(&buffer, 4096) < 0)
|
||||
goto buffer_failure;
|
||||
|
||||
struct rfb_server_fb_rect rect = {
|
||||
.encoding = htonl(RFB_ENCODING_TIGHT),
|
||||
.x = htons(x),
|
||||
.y = htons(y_start),
|
||||
.width = htons(width),
|
||||
.height = htons(height),
|
||||
};
|
||||
|
||||
vec_append(dst, &rect, sizeof(rect));
|
||||
|
||||
vec_fast_append_8(dst, TIGHT_BASIC);
|
||||
|
||||
struct rfb_pixel_format cfmt = { 0 };
|
||||
if (bytes_per_cpixel == 3)
|
||||
rfb_pixfmt_from_fourcc(&cfmt, DRM_FORMAT_RGBX8888 | DRM_FORMAT_BIG_ENDIAN);
|
||||
else
|
||||
memcpy(&cfmt, &client->pixfmt, sizeof(cfmt));
|
||||
|
||||
if (width * height * bytes_per_cpixel < 12) {
|
||||
for (uint32_t y = y_start; y < y_start + height; ++y) {
|
||||
void* img = (uint32_t*)fb->addr + x + y * stride;
|
||||
pixel32_to_cpixel(row, &cfmt, img, src_fmt,
|
||||
bytes_per_cpixel, width);
|
||||
vec_append(&buffer, row, width * bytes_per_cpixel);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t y = y_start; y < y_start + height; ++y) {
|
||||
void* img = (uint32_t*)fb->addr + x + y * stride;
|
||||
pixel32_to_cpixel(row, &cfmt, img, src_fmt,
|
||||
bytes_per_cpixel, width);
|
||||
tight_deflate(&buffer, row, bytes_per_cpixel * width,
|
||||
&self->zs[0], y == y_start + height - 1);
|
||||
}
|
||||
|
||||
tight_encode_size(dst, buffer.len);
|
||||
}
|
||||
|
||||
vec_append(dst, buffer.data, buffer.len);
|
||||
|
||||
vec_destroy(&buffer);
|
||||
free(row);
|
||||
return 0;
|
||||
|
||||
buffer_failure:
|
||||
free(row);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int tight_encode_box(struct tight_encoder* self, struct vec* dst,
|
||||
const struct nvnc_fb* fb,
|
||||
const struct rfb_pixel_format* src_fmt,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t stride, uint32_t width, uint32_t height)
|
||||
{
|
||||
switch (self->quality) {
|
||||
case TIGHT_QUALITY_LOSSLESS:
|
||||
return tight_encode_box_basic(self, dst, fb, src_fmt, x, y,
|
||||
stride, width, height);
|
||||
case TIGHT_QUALITY_HIGH:
|
||||
case TIGHT_QUALITY_LOW:
|
||||
return tight_encode_box_jpeg(self, dst, fb, x, y, stride,
|
||||
width, height);
|
||||
case TIGHT_QUALITY_UNSPEC:;
|
||||
}
|
||||
|
||||
abort();
|
||||
return -1;
|
||||
for (uint32_t y = 0; y < self->grid_height; ++y)
|
||||
for (uint32_t x = 0; x < self->grid_width; ++x)
|
||||
if (tight_tile(self, x, y)->state == TIGHT_TILE_ENCODED)
|
||||
tight_finish_tile(self, x, y);
|
||||
}
|
||||
|
||||
int tight_encode_frame(struct tight_encoder* self, struct vec* dst,
|
||||
const struct nvnc_fb* fb,
|
||||
const struct rfb_pixel_format* src_fmt,
|
||||
struct pixman_region16* region)
|
||||
const struct rfb_pixel_format* dfmt,
|
||||
const struct nvnc_fb* src,
|
||||
const struct rfb_pixel_format* sfmt,
|
||||
struct pixman_region16* damage,
|
||||
enum tight_quality quality)
|
||||
{
|
||||
self->quality = tight_get_quality(self);
|
||||
self->dfmt = dfmt;
|
||||
self->sfmt = sfmt;
|
||||
self->fb = src;
|
||||
self->dst = dst;
|
||||
self->quality = quality;
|
||||
|
||||
int rc = -1;
|
||||
vec_clear(dst);
|
||||
|
||||
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;
|
||||
}
|
||||
self->n_rects = tight_apply_damage(self, damage);
|
||||
assert(self->n_rects > 0);
|
||||
|
||||
struct rfb_server_fb_update_msg head = {
|
||||
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
|
||||
.n_rects = htons(n_rects),
|
||||
};
|
||||
tight_encode_rect_count(self);
|
||||
|
||||
rc = vec_append(dst, &head, sizeof(head));
|
||||
if (rc < 0)
|
||||
if (tight_schedule_encoding_jobs(self) < 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;
|
||||
// TODO Change architecture so we don't have to wait here
|
||||
pthread_mutex_lock(&self->wait_mutex);
|
||||
while (self->n_jobs != 0)
|
||||
pthread_cond_wait(&self->wait_cond, &self->wait_mutex);
|
||||
pthread_mutex_unlock(&self->wait_mutex);
|
||||
|
||||
rc = tight_encode_box(self, dst, fb, src_fmt, x, y,
|
||||
fb->width, box_width, box_height);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
}
|
||||
tight_finish(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue