Create encoder abstraction

abstract-encoders
Andri Yngvason 2021-12-09 22:48:31 +00:00
parent 66942ab913
commit a7241658b0
11 changed files with 590 additions and 228 deletions

View File

@ -24,7 +24,6 @@
#include "sys/queue.h" #include "sys/queue.h"
#include "neatvnc.h" #include "neatvnc.h"
#include "tight.h"
#include "config.h" #include "config.h"
#ifdef ENABLE_TLS #ifdef ENABLE_TLS
@ -82,8 +81,6 @@ struct nvnc_client {
bool is_updating; bool is_updating;
struct nvnc_fb* current_fb; struct nvnc_fb* current_fb;
nvnc_client_fn cleanup_fn; nvnc_client_fn cleanup_fn;
z_stream z_stream;
struct tight_encoder tight_encoder;
size_t buffer_index; size_t buffer_index;
size_t buffer_len; size_t buffer_len;
uint8_t msg_buffer[MSG_BUFFER_SIZE]; uint8_t msg_buffer[MSG_BUFFER_SIZE];
@ -91,6 +88,7 @@ struct nvnc_client {
uint32_t known_height; uint32_t known_height;
struct cut_text cut_text; struct cut_text cut_text;
bool is_qemu_key_ext_notified; bool is_qemu_key_ext_notified;
struct encoder* encoder;
}; };
LIST_HEAD(nvnc_client_list, nvnc_client); LIST_HEAD(nvnc_client_list, nvnc_client);

62
include/encoder.h 100644
View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2021 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "rfb-proto.h"
#include <stdint.h>
#include <unistd.h>
struct encoder;
struct nvnc_fb;
struct pixman_region16;
struct rcbuf;
struct encoder_impl {
int (*init)(struct encoder*);
void (*destroy)(struct encoder*);
void (*set_output_format)(struct encoder*,
const struct rfb_pixel_format*);
void (*set_tight_quality)(struct encoder*, int quality);
int (*resize)(struct encoder*, uint16_t width, uint16_t height);
int (*encode)(struct encoder*, struct nvnc_fb* fb,
struct pixman_region16* damage);
};
struct encoder {
struct encoder_impl* impl;
void (*on_done)(struct encoder*, struct rcbuf* result);
void* userdata;
};
struct encoder* encoder_new(enum rfb_encodings type, uint16_t width,
uint16_t height);
void encoder_destroy(struct encoder* self);
enum rfb_encodings encoder_get_type(const struct encoder* self);
void encoder_set_output_format(struct encoder* self,
const struct rfb_pixel_format*);
void encoder_set_tight_quality(struct encoder* self, int value);
int encoder_resize(struct encoder* self, uint16_t width, uint16_t height);
int encoder_encode(struct encoder* self, struct nvnc_fb* fb,
struct pixman_region16* damage);

View File

@ -20,8 +20,3 @@ struct nvnc_fb;
struct rfb_pixel_format; struct rfb_pixel_format;
struct pixman_region16; struct pixman_region16;
struct vec; struct vec;
int raw_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt,
struct nvnc_fb* src,
const struct rfb_pixel_format* src_fmt,
struct pixman_region16* region);

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include "encoder.h"
#include "rfb-proto.h" #include "rfb-proto.h"
#include "vec.h" #include "vec.h"
@ -38,6 +39,8 @@ enum tight_quality {
}; };
struct tight_encoder { struct tight_encoder {
struct encoder encoder;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t grid_width; uint32_t grid_width;
@ -61,18 +64,3 @@ struct tight_encoder {
tight_done_fn on_frame_done; tight_done_fn on_frame_done;
void* userdata; void* userdata;
}; };
int tight_encoder_init(struct tight_encoder* self, uint32_t width,
uint32_t height);
void tight_encoder_destroy(struct tight_encoder* self);
int tight_encoder_resize(struct tight_encoder* self, uint32_t width,
uint32_t height);
int tight_encode_frame(struct tight_encoder* self,
const struct rfb_pixel_format* dfmt,
struct nvnc_fb* src,
const struct rfb_pixel_format* sfmt,
struct pixman_region16* damage,
enum tight_quality quality,
tight_done_fn on_done, void* userdata);

View File

@ -24,9 +24,3 @@ struct nvnc_fb;
struct rfb_pixel_format; struct rfb_pixel_format;
struct pixman_region16; struct pixman_region16;
struct vec; struct vec;
int zrle_encode_frame(z_stream* zs, struct vec* dst,
const struct rfb_pixel_format* dst_fmt,
struct nvnc_fb* src,
const struct rfb_pixel_format* src_fmt,
struct pixman_region16* region);

View File

@ -77,6 +77,7 @@ sources = [
'src/transform-util.c', 'src/transform-util.c',
'src/damage-refinery.c', 'src/damage-refinery.c',
'src/murmurhash.c', 'src/murmurhash.c',
'src/encoder.c',
] ]
dependencies = [ dependencies = [

92
src/encoder.c 100644
View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "encoder.h"
#include <stdlib.h>
struct encoder* raw_encoder_new(void);
struct encoder* zrle_encoder_new(void);
struct encoder* tight_encoder_new(uint16_t width, uint16_t height);
extern struct encoder_impl encoder_impl_raw;
extern struct encoder_impl encoder_impl_zrle;
extern struct encoder_impl encoder_impl_tight;
struct encoder* encoder_new(enum rfb_encodings type, uint16_t width,
uint16_t height)
{
switch (type) {
case RFB_ENCODING_RAW: return raw_encoder_new();
case RFB_ENCODING_ZRLE: return zrle_encoder_new();
case RFB_ENCODING_TIGHT: return tight_encoder_new(width, height);
default: break;
}
return NULL;
}
enum rfb_encodings encoder_get_type(const struct encoder* self)
{
if (self->impl == &encoder_impl_raw)
return RFB_ENCODING_RAW;
if (self->impl == &encoder_impl_zrle)
return RFB_ENCODING_ZRLE;
if (self->impl == &encoder_impl_tight)
return RFB_ENCODING_TIGHT;
abort();
return 0;
}
void encoder_destroy(struct encoder* self)
{
if (!self)
return;
if (self->impl->destroy)
self->impl->destroy(self);
}
void encoder_set_output_format(struct encoder* self,
const struct rfb_pixel_format* pixfmt)
{
if (self->impl->set_output_format)
self->impl->set_output_format(self, pixfmt);
}
void encoder_set_tight_quality(struct encoder* self, int value)
{
if (self->impl->set_tight_quality)
self->impl->set_tight_quality(self, value);
}
int encoder_resize(struct encoder* self, uint16_t width, uint16_t height)
{
if (self->impl->resize)
return self->impl->resize(self, width, height);
return 0;
}
int encoder_encode(struct encoder* self, struct nvnc_fb* fb,
struct pixman_region16* damage)
{
if (self->impl->encode)
return self->impl->encode(self, fb, damage);
abort();
return -1;
}

View File

@ -21,8 +21,35 @@
#include "pixels.h" #include "pixels.h"
#include "raw-encoding.h" #include "raw-encoding.h"
#include "enc-util.h" #include "enc-util.h"
#include "encoder.h"
#include "rcbuf.h"
#include <stdlib.h>
#include <pixman.h> #include <pixman.h>
#include <aml.h>
struct encoder* raw_encoder_new(void);
struct raw_encoder {
struct encoder encoder;
struct rfb_pixel_format output_format;
struct nvnc_fb* current_fb;
struct pixman_region16 current_damage;
struct rcbuf *current_result;
struct aml_work* work;
};
struct encoder_impl encoder_impl_raw;
static inline struct raw_encoder* raw_encoder(struct encoder* encoder)
{
assert(encoder->impl == &encoder_impl_raw);
return (struct raw_encoder*)encoder;
}
static int raw_encode_box(struct vec* dst, static int raw_encode_box(struct vec* dst,
const struct rfb_pixel_format* dst_fmt, const struct rfb_pixel_format* dst_fmt,
@ -57,8 +84,8 @@ static int raw_encode_box(struct vec* dst,
return 0; return 0;
} }
int raw_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt, static int raw_encode_frame(struct vec* dst,
struct nvnc_fb* src, const struct rfb_pixel_format* dst_fmt, struct nvnc_fb* src,
const struct rfb_pixel_format* src_fmt, const struct rfb_pixel_format* src_fmt,
struct pixman_region16* region) struct pixman_region16* region)
{ {
@ -97,3 +124,118 @@ int raw_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt,
return 0; return 0;
} }
static void raw_encoder_do_work(void* obj)
{
struct raw_encoder* self = aml_get_userdata(obj);
int rc;
struct nvnc_fb* fb = self->current_fb;
assert(fb);
// TODO: Calculate the ideal buffer size based on the size of the
// damaged area.
size_t buffer_size = nvnc_fb_get_stride(fb) * nvnc_fb_get_height(fb) *
nvnc_fb_get_pixel_size(fb);
struct vec dst;
rc = vec_init(&dst, buffer_size);
assert(rc == 0);
struct rfb_pixel_format src_fmt;
rc = rfb_pixfmt_from_fourcc(&src_fmt, nvnc_fb_get_fourcc_format(fb));
assert(rc == 0);
rc = raw_encode_frame(&dst, &self->output_format, fb, &src_fmt,
&self->current_damage);
assert(rc == 0);
self->current_result = rcbuf_new(dst.data, dst.len);
assert(self->current_result);
}
static void raw_encoder_on_done(void* obj)
{
struct raw_encoder* self = aml_get_userdata(obj);
assert(self->current_result);
nvnc_fb_unref(self->current_fb);
self->current_fb = NULL;
pixman_region_clear(&self->current_damage);
struct rcbuf* result = self->current_result;
self->current_result = NULL;
aml_unref(self->work);
self->work = NULL;
if (self->encoder.on_done)
self->encoder.on_done(&self->encoder, result);
rcbuf_unref(result);
}
struct encoder* raw_encoder_new(void)
{
struct raw_encoder* self = calloc(1, sizeof(*self));
if (!self)
return NULL;
self->encoder.impl = &encoder_impl_raw;
pixman_region_init(&self->current_damage);
return (struct encoder*)self;
}
static void raw_encoder_destroy(struct encoder* encoder)
{
struct raw_encoder* self = raw_encoder(encoder);
pixman_region_fini(&self->current_damage);
if (self->work)
aml_unref(self->work);
free(self);
}
static void raw_encoder_set_output_format(struct encoder* encoder,
const struct rfb_pixel_format* pixfmt)
{
struct raw_encoder* self = raw_encoder(encoder);
memcpy(&self->output_format, pixfmt, sizeof(self->output_format));
}
static int raw_encoder_encode(struct encoder* encoder, struct nvnc_fb* fb,
struct pixman_region16* damage)
{
struct raw_encoder* self = raw_encoder(encoder);
assert(!self->current_fb);
self->work = aml_work_new(raw_encoder_do_work, raw_encoder_on_done,
self, NULL);
if (!self->work)
return -1;
self->current_fb = fb;
nvnc_fb_ref(self->current_fb);
pixman_region_copy(&self->current_damage, damage);
int rc = aml_start(aml_get_default(), self->work);
if (rc < 0) {
aml_unref(self->work);
self->work = NULL;
pixman_region_clear(&self->current_damage);
nvnc_fb_unref(self->current_fb);
self->current_fb = NULL;
}
return rc;
}
struct encoder_impl encoder_impl_raw = {
.destroy = raw_encoder_destroy,
.set_output_format = raw_encoder_set_output_format,
.encode = raw_encoder_encode,
};

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019 - 2020 Andri Yngvason * Copyright (c) 2019 - 2021 Andri Yngvason
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -15,9 +15,6 @@
*/ */
#include "rfb-proto.h" #include "rfb-proto.h"
#include "zrle.h"
#include "tight.h"
#include "raw-encoding.h"
#include "vec.h" #include "vec.h"
#include "type-macros.h" #include "type-macros.h"
#include "fb.h" #include "fb.h"
@ -29,6 +26,8 @@
#include "config.h" #include "config.h"
#include "logging.h" #include "logging.h"
#include "usdt.h" #include "usdt.h"
#include "encoder.h"
#include "tight.h"
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -61,27 +60,16 @@
#define EXPORT __attribute__((visibility("default"))) #define EXPORT __attribute__((visibility("default")))
struct fb_update_work {
struct aml_work* work;
struct nvnc_client* client;
struct pixman_region16 region;
struct rfb_pixel_format server_fmt;
struct vec frame;
struct nvnc_fb* fb;
};
enum addrtype { enum addrtype {
ADDRTYPE_TCP, ADDRTYPE_TCP,
ADDRTYPE_UNIX, ADDRTYPE_UNIX,
}; };
int schedule_client_update_fb(struct nvnc_client* client,
struct pixman_region16* damage);
static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb); static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb);
static int send_qemu_key_ext_frame(struct nvnc_client* client); static int send_qemu_key_ext_frame(struct nvnc_client* client);
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client); static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client);
static enum tight_quality client_get_tight_quality(struct nvnc_client* client); static enum tight_quality client_get_tight_quality(struct nvnc_client* client);
static void on_tight_encode_frame_done(struct vec* frame, void* userdata); static void on_encode_frame_done(struct encoder*, struct rcbuf*);
static bool client_has_encoding(const struct nvnc_client* client, static bool client_has_encoding(const struct nvnc_client* client,
enum rfb_encodings encoding); enum rfb_encodings encoding);
@ -114,8 +102,7 @@ static void client_close(struct nvnc_client* client)
LIST_REMOVE(client, link); LIST_REMOVE(client, link);
stream_destroy(client->net_stream); stream_destroy(client->net_stream);
tight_encoder_destroy(&client->tight_encoder); encoder_destroy(client->encoder);
deflateEnd(&client->z_stream);
pixman_region_fini(&client->damage); pixman_region_fini(&client->damage);
free(client->cut_text.buffer); free(client->cut_text.buffer);
free(client); free(client);
@ -566,43 +553,42 @@ static void process_fb_update_requests(struct nvnc_client* client)
nvnc_fb_hold(fb); nvnc_fb_hold(fb);
nvnc_fb_ref(fb); nvnc_fb_ref(fb);
int rc;
enum rfb_encodings encoding = choose_frame_encoding(client); enum rfb_encodings encoding = choose_frame_encoding(client);
if (!client->encoder || encoding != encoder_get_type(client->encoder)) {
// TODO: Check the return value int width = server->display->buffer->width;
struct rfb_pixel_format server_fmt; int height = server->display->buffer->height;
rfb_pixfmt_from_fourcc(&server_fmt, fb->fourcc_format); encoder_destroy(client->encoder);
client->encoder = encoder_new(encoding, width, height);
switch (encoding) { if (!client->encoder) {
case RFB_ENCODING_RAW: log_error("Failed to allocate new encoder");
case RFB_ENCODING_ZRLE: goto failure;
rc = schedule_client_update_fb(client, &damage); }
break;
case RFB_ENCODING_TIGHT:
client_ref(client);
enum tight_quality quality = client_get_tight_quality(client);
rc = tight_encode_frame(&client->tight_encoder, &client->pixfmt,
fb, &server_fmt, &damage, quality,
on_tight_encode_frame_done, client);
if (rc < 0)
client_unref(client);
pixman_region_fini(&damage);
break;
default:
rc = -1;
break;
} }
if (rc < 0) { client_ref(client);
int q = client_get_tight_quality(client);
encoder_set_tight_quality(client->encoder, q);
encoder_set_output_format(client->encoder, &client->pixfmt);
client->encoder->on_done = on_encode_frame_done;
client->encoder->userdata = client;
if (encoder_encode(client->encoder, fb, &damage) < 0) {
log_error("Failed to encode current frame");
client_unref(client);
goto failure;
}
pixman_region_fini(&damage);
return;
failure:
client->is_updating = false; client->is_updating = false;
assert(client->current_fb); assert(client->current_fb);
nvnc_fb_release(client->current_fb); nvnc_fb_release(client->current_fb);
nvnc_fb_unref(client->current_fb); nvnc_fb_unref(client->current_fb);
client->current_fb = NULL; client->current_fb = NULL;
}
} }
static int on_client_fb_update_request(struct nvnc_client* client) static int on_client_fb_update_request(struct nvnc_client* client)
@ -978,30 +964,11 @@ static void on_connection(void* obj)
goto stream_failure; goto stream_failure;
} }
int rc = deflateInit2(&client->z_stream,
/* compression level: */ 1,
/* method: */ Z_DEFLATED,
/* window bits: */ 15,
/* mem level: */ 9,
/* strategy: */ Z_DEFAULT_STRATEGY);
if (rc != Z_OK) {
log_debug("OOM\n");
goto deflate_failure;
}
if (!server->display->buffer) { if (!server->display->buffer) {
log_debug("No display buffer has been set\n"); log_debug("No display buffer has been set\n");
goto buffer_failure; goto buffer_failure;
} }
int width = server->display->buffer->width;
int height = server->display->buffer->height;
if (tight_encoder_init(&client->tight_encoder, width, height) < 0) {
log_debug("OOM\n");
goto tight_failure;
}
pixman_region_init(&client->damage); pixman_region_init(&client->damage);
struct rcbuf* payload = rcbuf_from_string(RFB_VERSION_MESSAGE); struct rcbuf* payload = rcbuf_from_string(RFB_VERSION_MESSAGE);
@ -1021,12 +988,8 @@ static void on_connection(void* obj)
return; return;
payload_failure: payload_failure:
tight_encoder_destroy(&client->tight_encoder);
pixman_region_fini(&client->damage); pixman_region_fini(&client->damage);
tight_failure:
buffer_failure: buffer_failure:
deflateEnd(&client->z_stream);
deflate_failure:
stream_destroy(client->net_stream); stream_destroy(client->net_stream);
stream_failure: stream_failure:
close(fd); close(fd);
@ -1260,39 +1223,13 @@ static bool client_has_encoding(const struct nvnc_client* client,
return false; return false;
} }
static void do_client_update_fb(void* work) static void finish_fb_update(struct nvnc_client* client, struct rcbuf* payload)
{
struct fb_update_work* update = aml_get_userdata(work);
struct nvnc_client* client = update->client;
struct nvnc_fb* fb = update->fb;
enum rfb_encodings encoding = choose_frame_encoding(client);
switch (encoding) {
case RFB_ENCODING_RAW:
raw_encode_frame(&update->frame, &client->pixfmt, fb,
&update->server_fmt, &update->region);
break;
case RFB_ENCODING_TIGHT:
abort();
break;
case RFB_ENCODING_ZRLE:
zrle_encode_frame(&client->z_stream, &update->frame,
&client->pixfmt, fb, &update->server_fmt,
&update->region);
break;
default:
break;
}
}
static void finish_fb_update(struct nvnc_client* client, struct vec* frame)
{ {
client_ref(client); client_ref(client);
if (client->net_stream->state != STREAM_STATE_CLOSED) { if (client->net_stream->state != STREAM_STATE_CLOSED) {
struct rcbuf* payload = rcbuf_new(frame->data, frame->len);
DTRACE_PROBE1(neatvnc, send_fb_start, client); DTRACE_PROBE1(neatvnc, send_fb_start, client);
rcbuf_ref(payload);
stream_send(client->net_stream, payload, on_write_frame_done, stream_send(client->net_stream, payload, on_write_frame_done,
client); client);
DTRACE_PROBE1(neatvnc, send_fb_done, client); DTRACE_PROBE1(neatvnc, send_fb_done, client);
@ -1302,7 +1239,6 @@ static void finish_fb_update(struct nvnc_client* client, struct vec* frame)
nvnc_fb_release(client->current_fb); nvnc_fb_release(client->current_fb);
nvnc_fb_unref(client->current_fb); nvnc_fb_unref(client->current_fb);
client->current_fb = NULL; client->current_fb = NULL;
vec_destroy(frame);
process_fb_update_requests(client); process_fb_update_requests(client);
client_unref(client); client_unref(client);
} }
@ -1312,25 +1248,10 @@ static void finish_fb_update(struct nvnc_client* client, struct vec* frame)
DTRACE_PROBE1(neatvnc, update_fb_done, client); DTRACE_PROBE1(neatvnc, update_fb_done, client);
} }
static void on_tight_encode_frame_done(struct vec* frame, void* userdata) static void on_encode_frame_done(struct encoder* encoder, struct rcbuf* result)
{ {
struct nvnc_client* client = userdata; struct nvnc_client* client = encoder->userdata;
finish_fb_update(client, frame); finish_fb_update(client, result);
client_unref(client);
}
static void on_client_update_fb_done(void* work)
{
struct fb_update_work* update = aml_get_userdata(work);
struct nvnc_client* client = update->client;
struct vec* frame = &update->frame;
nvnc_fb_unref(update->fb);
finish_fb_update(client, frame);
pixman_region_fini(&update->region);
client_unref(client); client_unref(client);
} }
@ -1346,7 +1267,7 @@ static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb)
client->known_width = fb->width; client->known_width = fb->width;
client->known_height = fb->height; client->known_height = fb->height;
tight_encoder_resize(&client->tight_encoder, fb->width, fb->height); encoder_resize(client->encoder, fb->width, fb->height);
pixman_region_union_rect(&client->damage, &client->damage, 0, 0, pixman_region_union_rect(&client->damage, &client->damage, 0, 0,
fb->width, fb->height); fb->width, fb->height);
@ -1383,58 +1304,6 @@ static int send_qemu_key_ext_frame(struct nvnc_client* client)
return 0; return 0;
} }
int schedule_client_update_fb(struct nvnc_client* client,
struct pixman_region16* damage)
{
struct nvnc_fb* fb = client->server->display->buffer;
assert(fb);
struct fb_update_work* work = calloc(1, sizeof(*work));
if (!work)
return -1;
if (rfb_pixfmt_from_fourcc(&work->server_fmt, fb->fourcc_format) < 0)
goto pixfmt_failure;
work->client = client;
work->fb = fb;
work->region = *damage;
int rc = vec_init(&work->frame, fb->width * fb->height * 3 / 2);
if (rc < 0)
goto vec_failure;
client_ref(client);
nvnc_fb_ref(fb);
struct aml_work* obj =
aml_work_new(do_client_update_fb, on_client_update_fb_done,
work, free);
if (!obj) {
goto oom_failure;
}
rc = aml_start(aml_get_default(), obj);
aml_unref(obj);
if (rc < 0)
goto start_failure;
work->work = obj;
return 0;
start_failure:
work = NULL; /* handled in unref */
oom_failure:
nvnc_fb_unref(fb);
client_unref(client);
vec_destroy(&work->frame);
vec_failure:
pixfmt_failure:
free(work);
return -1;
}
void nvnc__damage_region(struct nvnc* self, const struct pixman_region16* damage) void nvnc__damage_region(struct nvnc* self, const struct pixman_region16* damage)
{ {
struct nvnc_client* client; struct nvnc_client* client;

View File

@ -24,6 +24,7 @@
#include "config.h" #include "config.h"
#include "enc-util.h" #include "enc-util.h"
#include "fb.h" #include "fb.h"
#include "rcbuf.h"
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -54,6 +55,8 @@
#define MAX_TILE_SIZE (2 * TSL * TSL * 4) #define MAX_TILE_SIZE (2 * TSL * TSL * 4)
struct encoder* tight_encoder_new(uint16_t width, uint16_t height);
enum tight_tile_state { enum tight_tile_state {
TIGHT_TILE_READY = 0, TIGHT_TILE_READY = 0,
TIGHT_TILE_DAMAGED, TIGHT_TILE_DAMAGED,
@ -72,10 +75,18 @@ struct tight_zs_worker_ctx {
int index; int index;
}; };
struct encoder_impl encoder_impl_tight;
static void do_tight_zs_work(void*); static void do_tight_zs_work(void*);
static void on_tight_zs_work_done(void*); static void on_tight_zs_work_done(void*);
static int schedule_tight_finish(struct tight_encoder* self); static int schedule_tight_finish(struct tight_encoder* self);
static inline struct tight_encoder* tight_encoder(struct encoder* encoder)
{
assert(encoder->impl == &encoder_impl_tight);
return (struct tight_encoder*)encoder;
}
static int tight_encoder_init_stream(z_stream* zs) static int tight_encoder_init_stream(z_stream* zs)
{ {
int rc = deflateInit2(zs, int rc = deflateInit2(zs,
@ -126,7 +137,7 @@ failure:
return -1; return -1;
} }
int tight_encoder_resize(struct tight_encoder* self, uint32_t width, static int tight_encoder_resize(struct tight_encoder* self, uint32_t width,
uint32_t height) uint32_t height)
{ {
self->width = width; self->width = width;
@ -143,7 +154,7 @@ int tight_encoder_resize(struct tight_encoder* self, uint32_t width,
return self->grid ? 0 : -1; return self->grid ? 0 : -1;
} }
int tight_encoder_init(struct tight_encoder* self, uint32_t width, static int tight_encoder_init(struct tight_encoder* self, uint32_t width,
uint32_t height) uint32_t height)
{ {
memset(self, 0, sizeof(*self)); memset(self, 0, sizeof(*self));
@ -165,7 +176,7 @@ int tight_encoder_init(struct tight_encoder* self, uint32_t width,
return 0; return 0;
} }
void tight_encoder_destroy(struct tight_encoder* self) static void tight_encoder_destroy(struct tight_encoder* self)
{ {
aml_unref(self->zs_worker[3]); aml_unref(self->zs_worker[3]);
aml_unref(self->zs_worker[2]); aml_unref(self->zs_worker[2]);
@ -465,7 +476,14 @@ static void do_tight_finish(void* obj)
static void on_tight_finished(void* obj) static void on_tight_finished(void* obj)
{ {
struct tight_encoder* self = aml_get_userdata(obj); struct tight_encoder* self = aml_get_userdata(obj);
self->on_frame_done(&self->dst, self->userdata);
struct rcbuf* result = rcbuf_new(self->dst.data, self->dst.len);
assert(result);
if (self->encoder.on_done)
self->encoder.on_done(&self->encoder, result);
rcbuf_unref(result);
} }
static int schedule_tight_finish(struct tight_encoder* self) static int schedule_tight_finish(struct tight_encoder* self)
@ -480,27 +498,65 @@ static int schedule_tight_finish(struct tight_encoder* self)
return rc; return rc;
} }
int tight_encode_frame(struct tight_encoder* self, struct encoder* tight_encoder_new(uint16_t width, uint16_t height)
const struct rfb_pixel_format* dfmt,
struct nvnc_fb* src,
const struct rfb_pixel_format* sfmt,
struct pixman_region16* damage,
enum tight_quality quality,
tight_done_fn on_done, void* userdata)
{ {
memcpy(&self->dfmt, dfmt, sizeof(self->dfmt)); struct tight_encoder* self = calloc(1, sizeof(*self));
memcpy(&self->sfmt, sfmt, sizeof(self->sfmt)); if (!self)
self->fb = src; return NULL;
self->quality = quality;
self->on_frame_done = on_done;
self->userdata = userdata;
int rc = nvnc_fb_map(self->fb); if (tight_encoder_init(self, width, height) < 0) {
free(self);
return NULL;
}
self->encoder.impl = &encoder_impl_tight;
return (struct encoder*)self;
}
static void tight_encoder_destroy_wrapper(struct encoder* encoder)
{
tight_encoder_destroy(tight_encoder(encoder));
free(encoder);
}
static void tight_encoder_set_output_format(struct encoder* encoder,
const struct rfb_pixel_format* pixfmt)
{
struct tight_encoder* self = tight_encoder(encoder);
memcpy(&self->dfmt, pixfmt, sizeof(self->dfmt));
}
static void tight_encoder_set_quality(struct encoder* encoder, int value)
{
struct tight_encoder* self = tight_encoder(encoder);
self->quality = value;
}
static int tight_encoder_resize_wrapper(struct encoder* encoder, uint16_t width,
uint16_t height)
{
struct tight_encoder* self = tight_encoder(encoder);
return tight_encoder_resize(self, width, height);
}
static int tight_encoder_encode(struct encoder* encoder, struct nvnc_fb* fb,
struct pixman_region16* damage)
{
struct tight_encoder* self = tight_encoder(encoder);
int rc;
rc = rfb_pixfmt_from_fourcc(&self->sfmt, nvnc_fb_get_fourcc_format(fb));
assert(rc == 0);
self->fb = fb;
rc = nvnc_fb_map(self->fb);
if (rc < 0) if (rc < 0)
return -1; return -1;
uint32_t width = nvnc_fb_get_width(src); uint32_t width = nvnc_fb_get_width(fb);
uint32_t height = nvnc_fb_get_height(src); uint32_t height = nvnc_fb_get_height(fb);
rc = vec_init(&self->dst, width * height * 4); rc = vec_init(&self->dst, width * height * 4);
if (rc < 0) if (rc < 0)
return -1; return -1;
@ -520,3 +576,11 @@ int tight_encode_frame(struct tight_encoder* self,
return 0; return 0;
} }
struct encoder_impl encoder_impl_tight = {
.destroy = tight_encoder_destroy_wrapper,
.set_output_format = tight_encoder_set_output_format,
.set_tight_quality = tight_encoder_set_quality,
.resize = tight_encoder_resize_wrapper,
.encode = tight_encoder_encode,
};

View File

@ -21,6 +21,8 @@
#include "pixels.h" #include "pixels.h"
#include "fb.h" #include "fb.h"
#include "enc-util.h" #include "enc-util.h"
#include "encoder.h"
#include "rcbuf.h"
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
@ -29,11 +31,37 @@
#include <assert.h> #include <assert.h>
#include <pixman.h> #include <pixman.h>
#include <zlib.h> #include <zlib.h>
#include <aml.h>
#define TILE_LENGTH 64 #define TILE_LENGTH 64
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) #define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
struct encoder* zrle_encoder_new(void);
struct zrle_encoder {
struct encoder encoder;
struct rfb_pixel_format output_format;
struct nvnc_fb* current_fb;
struct pixman_region16 current_damage;
struct rcbuf *current_result;
z_stream zs;
struct aml_work* work;
};
struct encoder_impl encoder_impl_zrle;
static inline struct zrle_encoder* zrle_encoder(struct encoder* encoder)
{
assert(encoder->impl == &encoder_impl_zrle);
return (struct zrle_encoder*)encoder;
}
static inline int find_colour_in_palette(uint32_t* palette, int len, static inline int find_colour_in_palette(uint32_t* palette, int len,
uint32_t colour) uint32_t colour)
{ {
@ -261,7 +289,7 @@ failure:
#undef CHUNK #undef CHUNK
} }
int zrle_encode_frame(z_stream* zs, struct vec* dst, static int zrle_encode_frame(z_stream* zs, struct vec* dst,
const struct rfb_pixel_format* dst_fmt, const struct rfb_pixel_format* dst_fmt,
struct nvnc_fb* src, struct nvnc_fb* src,
const struct rfb_pixel_format* src_fmt, const struct rfb_pixel_format* src_fmt,
@ -298,3 +326,132 @@ int zrle_encode_frame(z_stream* zs, struct vec* dst,
return 0; return 0;
} }
static void zrle_encoder_do_work(void* obj)
{
struct zrle_encoder* self = aml_get_userdata(obj);
int rc;
struct nvnc_fb* fb = self->current_fb;
assert(fb);
// TODO: Calculate the ideal buffer size based on the size of the
// damaged area.
size_t buffer_size = nvnc_fb_get_stride(fb) * nvnc_fb_get_height(fb) *
nvnc_fb_get_pixel_size(fb);
struct vec dst;
rc = vec_init(&dst, buffer_size);
assert(rc == 0);
struct rfb_pixel_format src_fmt;
rc = rfb_pixfmt_from_fourcc(&src_fmt, nvnc_fb_get_fourcc_format(fb));
assert(rc == 0);
rc = zrle_encode_frame(&self->zs, &dst, &self->output_format, fb,
&src_fmt, &self->current_damage);
assert(rc == 0);
self->current_result = rcbuf_new(dst.data, dst.len);
assert(self->current_result);
}
static void zrle_encoder_on_done(void* obj)
{
struct zrle_encoder* self = aml_get_userdata(obj);
assert(self->current_result);
nvnc_fb_unref(self->current_fb);
self->current_fb = NULL;
pixman_region_clear(&self->current_damage);
struct rcbuf* result = self->current_result;
self->current_result = NULL;
aml_unref(self->work);
self->work = NULL;
if (self->encoder.on_done)
self->encoder.on_done(&self->encoder, result);
rcbuf_unref(result);
}
struct encoder* zrle_encoder_new(void)
{
struct zrle_encoder* self = calloc(1, sizeof(*self));
if (!self)
return NULL;
self->encoder.impl = &encoder_impl_zrle;
int rc = deflateInit2(&self->zs,
/* compression level: */ 1,
/* method: */ Z_DEFLATED,
/* window bits: */ 15,
/* mem level: */ 9,
/* strategy: */ Z_DEFAULT_STRATEGY);
if (rc != Z_OK)
goto deflate_failure;
pixman_region_init(&self->current_damage);
return (struct encoder*)self;
deflate_failure:
free(self);
return NULL;
}
static void zrle_encoder_destroy(struct encoder* encoder)
{
struct zrle_encoder* self = zrle_encoder(encoder);
pixman_region_fini(&self->current_damage);
deflateEnd(&self->zs);
if (self->work)
aml_unref(self->work);
free(self);
}
static void zrle_encoder_set_output_format(struct encoder* encoder,
const struct rfb_pixel_format* pixfmt)
{
struct zrle_encoder* self = zrle_encoder(encoder);
memcpy(&self->output_format, pixfmt, sizeof(self->output_format));
}
static int zrle_encoder_encode(struct encoder* encoder, struct nvnc_fb* fb,
struct pixman_region16* damage)
{
struct zrle_encoder* self = zrle_encoder(encoder);
assert(!self->current_fb);
self->work = aml_work_new(zrle_encoder_do_work, zrle_encoder_on_done,
self, NULL);
if (!self->work)
return -1;
self->current_fb = fb;
nvnc_fb_ref(self->current_fb);
pixman_region_copy(&self->current_damage, damage);
int rc = aml_start(aml_get_default(), self->work);
if (rc < 0) {
aml_unref(self->work);
self->work = NULL;
pixman_region_clear(&self->current_damage);
nvnc_fb_unref(self->current_fb);
self->current_fb = NULL;
}
return rc;
}
struct encoder_impl encoder_impl_zrle = {
.destroy = zrle_encoder_destroy,
.set_output_format = zrle_encoder_set_output_format,
.encode = zrle_encoder_encode,
};