diff --git a/include/common.h b/include/common.h index a3a1dbf..b13dae9 100644 --- a/include/common.h +++ b/include/common.h @@ -24,7 +24,6 @@ #include "sys/queue.h" #include "neatvnc.h" -#include "tight.h" #include "config.h" #ifdef ENABLE_TLS @@ -82,8 +81,6 @@ struct nvnc_client { bool is_updating; struct nvnc_fb* current_fb; nvnc_client_fn cleanup_fn; - z_stream z_stream; - struct tight_encoder tight_encoder; size_t buffer_index; size_t buffer_len; uint8_t msg_buffer[MSG_BUFFER_SIZE]; @@ -91,6 +88,7 @@ struct nvnc_client { uint32_t known_height; struct cut_text cut_text; bool is_qemu_key_ext_notified; + struct encoder* encoder; }; LIST_HEAD(nvnc_client_list, nvnc_client); diff --git a/include/encoder.h b/include/encoder.h new file mode 100644 index 0000000..5456630 --- /dev/null +++ b/include/encoder.h @@ -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 +#include + +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); diff --git a/include/raw-encoding.h b/include/raw-encoding.h index 44610ae..7e906ea 100644 --- a/include/raw-encoding.h +++ b/include/raw-encoding.h @@ -20,8 +20,3 @@ struct nvnc_fb; struct rfb_pixel_format; struct pixman_region16; 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); diff --git a/include/tight.h b/include/tight.h index 31a0ed4..0499500 100644 --- a/include/tight.h +++ b/include/tight.h @@ -16,6 +16,7 @@ #pragma once +#include "encoder.h" #include "rfb-proto.h" #include "vec.h" @@ -38,6 +39,8 @@ enum tight_quality { }; struct tight_encoder { + struct encoder encoder; + uint32_t width; uint32_t height; uint32_t grid_width; @@ -61,18 +64,3 @@ struct tight_encoder { tight_done_fn on_frame_done; 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); diff --git a/include/zrle.h b/include/zrle.h index d15ca33..662b467 100644 --- a/include/zrle.h +++ b/include/zrle.h @@ -24,9 +24,3 @@ struct nvnc_fb; struct rfb_pixel_format; struct pixman_region16; 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); diff --git a/meson.build b/meson.build index e134d9f..e951fb5 100644 --- a/meson.build +++ b/meson.build @@ -77,6 +77,7 @@ sources = [ 'src/transform-util.c', 'src/damage-refinery.c', 'src/murmurhash.c', + 'src/encoder.c', ] dependencies = [ diff --git a/src/encoder.c b/src/encoder.c new file mode 100644 index 0000000..fab68d5 --- /dev/null +++ b/src/encoder.c @@ -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 + +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; +} diff --git a/src/raw-encoding.c b/src/raw-encoding.c index 967ad8a..93380b2 100644 --- a/src/raw-encoding.c +++ b/src/raw-encoding.c @@ -21,8 +21,35 @@ #include "pixels.h" #include "raw-encoding.h" #include "enc-util.h" +#include "encoder.h" +#include "rcbuf.h" +#include #include +#include + +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, const struct rfb_pixel_format* dst_fmt, @@ -57,10 +84,10 @@ static int raw_encode_box(struct vec* dst, return 0; } -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) +static 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) { int rc = -1; @@ -97,3 +124,118 @@ int raw_encode_frame(struct vec* dst, const struct rfb_pixel_format* dst_fmt, 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, +}; diff --git a/src/server.c b/src/server.c index 1eaefb1..07b15e7 100644 --- a/src/server.c +++ b/src/server.c @@ -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 * purpose with or without fee is hereby granted, provided that the above @@ -15,9 +15,6 @@ */ #include "rfb-proto.h" -#include "zrle.h" -#include "tight.h" -#include "raw-encoding.h" #include "vec.h" #include "type-macros.h" #include "fb.h" @@ -29,6 +26,8 @@ #include "config.h" #include "logging.h" #include "usdt.h" +#include "encoder.h" +#include "tight.h" #include #include @@ -61,27 +60,16 @@ #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 { ADDRTYPE_TCP, 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_qemu_key_ext_frame(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 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, enum rfb_encodings encoding); @@ -114,8 +102,7 @@ static void client_close(struct nvnc_client* client) LIST_REMOVE(client, link); stream_destroy(client->net_stream); - tight_encoder_destroy(&client->tight_encoder); - deflateEnd(&client->z_stream); + encoder_destroy(client->encoder); pixman_region_fini(&client->damage); free(client->cut_text.buffer); free(client); @@ -566,43 +553,42 @@ static void process_fb_update_requests(struct nvnc_client* client) nvnc_fb_hold(fb); nvnc_fb_ref(fb); - int rc; enum rfb_encodings encoding = choose_frame_encoding(client); - - // TODO: Check the return value - struct rfb_pixel_format server_fmt; - rfb_pixfmt_from_fourcc(&server_fmt, fb->fourcc_format); - - switch (encoding) { - case RFB_ENCODING_RAW: - case RFB_ENCODING_ZRLE: - 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 (!client->encoder || encoding != encoder_get_type(client->encoder)) { + int width = server->display->buffer->width; + int height = server->display->buffer->height; + encoder_destroy(client->encoder); + client->encoder = encoder_new(encoding, width, height); + if (!client->encoder) { + log_error("Failed to allocate new encoder"); + goto failure; + } } - if (rc < 0) { - client->is_updating = false; - assert(client->current_fb); - nvnc_fb_release(client->current_fb); - nvnc_fb_unref(client->current_fb); - client->current_fb = NULL; + 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; + assert(client->current_fb); + nvnc_fb_release(client->current_fb); + nvnc_fb_unref(client->current_fb); + client->current_fb = NULL; } static int on_client_fb_update_request(struct nvnc_client* client) @@ -978,30 +964,11 @@ static void on_connection(void* obj) 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) { log_debug("No display buffer has been set\n"); 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); struct rcbuf* payload = rcbuf_from_string(RFB_VERSION_MESSAGE); @@ -1021,12 +988,8 @@ static void on_connection(void* obj) return; payload_failure: - tight_encoder_destroy(&client->tight_encoder); pixman_region_fini(&client->damage); -tight_failure: buffer_failure: - deflateEnd(&client->z_stream); -deflate_failure: stream_destroy(client->net_stream); stream_failure: close(fd); @@ -1260,39 +1223,13 @@ static bool client_has_encoding(const struct nvnc_client* client, return false; } -static void do_client_update_fb(void* work) -{ - 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) +static void finish_fb_update(struct nvnc_client* client, struct rcbuf* payload) { client_ref(client); if (client->net_stream->state != STREAM_STATE_CLOSED) { - struct rcbuf* payload = rcbuf_new(frame->data, frame->len); DTRACE_PROBE1(neatvnc, send_fb_start, client); + rcbuf_ref(payload); stream_send(client->net_stream, payload, on_write_frame_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_unref(client->current_fb); client->current_fb = NULL; - vec_destroy(frame); process_fb_update_requests(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); } -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; - finish_fb_update(client, frame); - 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); - + struct nvnc_client* client = encoder->userdata; + finish_fb_update(client, result); 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_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, fb->width, fb->height); @@ -1383,58 +1304,6 @@ static int send_qemu_key_ext_frame(struct nvnc_client* client) 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) { struct nvnc_client* client; diff --git a/src/tight.c b/src/tight.c index 962bf4e..c670282 100644 --- a/src/tight.c +++ b/src/tight.c @@ -24,6 +24,7 @@ #include "config.h" #include "enc-util.h" #include "fb.h" +#include "rcbuf.h" #include #include @@ -54,6 +55,8 @@ #define MAX_TILE_SIZE (2 * TSL * TSL * 4) +struct encoder* tight_encoder_new(uint16_t width, uint16_t height); + enum tight_tile_state { TIGHT_TILE_READY = 0, TIGHT_TILE_DAMAGED, @@ -72,10 +75,18 @@ struct tight_zs_worker_ctx { int index; }; +struct encoder_impl encoder_impl_tight; + static void do_tight_zs_work(void*); static void on_tight_zs_work_done(void*); 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) { int rc = deflateInit2(zs, @@ -126,7 +137,7 @@ failure: 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) { self->width = width; @@ -143,7 +154,7 @@ int tight_encoder_resize(struct tight_encoder* self, uint32_t width, 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) { memset(self, 0, sizeof(*self)); @@ -165,7 +176,7 @@ int tight_encoder_init(struct tight_encoder* self, uint32_t width, 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[2]); @@ -465,7 +476,14 @@ static void do_tight_finish(void* obj) static void on_tight_finished(void* 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) @@ -480,27 +498,65 @@ static int schedule_tight_finish(struct tight_encoder* self) return rc; } -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) +struct encoder* tight_encoder_new(uint16_t width, uint16_t height) { - memcpy(&self->dfmt, dfmt, sizeof(self->dfmt)); - memcpy(&self->sfmt, sfmt, sizeof(self->sfmt)); - self->fb = src; - self->quality = quality; - self->on_frame_done = on_done; - self->userdata = userdata; + struct tight_encoder* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; - 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) return -1; - uint32_t width = nvnc_fb_get_width(src); - uint32_t height = nvnc_fb_get_height(src); + uint32_t width = nvnc_fb_get_width(fb); + uint32_t height = nvnc_fb_get_height(fb); rc = vec_init(&self->dst, width * height * 4); if (rc < 0) return -1; @@ -520,3 +576,11 @@ int tight_encode_frame(struct tight_encoder* self, 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, +}; diff --git a/src/zrle.c b/src/zrle.c index 720dc78..cde6450 100644 --- a/src/zrle.c +++ b/src/zrle.c @@ -21,6 +21,8 @@ #include "pixels.h" #include "fb.h" #include "enc-util.h" +#include "encoder.h" +#include "rcbuf.h" #include #include @@ -29,11 +31,37 @@ #include #include #include +#include #define TILE_LENGTH 64 #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, uint32_t colour) { @@ -261,7 +289,7 @@ failure: #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, struct nvnc_fb* src, const struct rfb_pixel_format* src_fmt, @@ -298,3 +326,132 @@ int zrle_encode_frame(z_stream* zs, struct vec* dst, 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, +};