Create encoder abstraction
parent
66942ab913
commit
a7241658b0
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -77,6 +77,7 @@ sources = [
|
|||
'src/transform-util.c',
|
||||
'src/damage-refinery.c',
|
||||
'src/murmurhash.c',
|
||||
'src/encoder.c',
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -21,8 +21,35 @@
|
|||
#include "pixels.h"
|
||||
#include "raw-encoding.h"
|
||||
#include "enc-util.h"
|
||||
#include "encoder.h"
|
||||
#include "rcbuf.h"
|
||||
|
||||
#include <stdlib.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,
|
||||
const struct rfb_pixel_format* dst_fmt,
|
||||
|
@ -57,8 +84,8 @@ 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,
|
||||
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)
|
||||
{
|
||||
|
@ -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,
|
||||
};
|
||||
|
|
209
src/server.c
209
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -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_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;
|
||||
|
|
104
src/tight.c
104
src/tight.c
|
@ -24,6 +24,7 @@
|
|||
#include "config.h"
|
||||
#include "enc-util.h"
|
||||
#include "fb.h"
|
||||
#include "rcbuf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
@ -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,
|
||||
};
|
||||
|
|
159
src/zrle.c
159
src/zrle.c
|
@ -21,6 +21,8 @@
|
|||
#include "pixels.h"
|
||||
#include "fb.h"
|
||||
#include "enc-util.h"
|
||||
#include "encoder.h"
|
||||
#include "rcbuf.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
@ -29,11 +31,37 @@
|
|||
#include <assert.h>
|
||||
#include <pixman.h>
|
||||
#include <zlib.h>
|
||||
#include <aml.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue