[Spice-devel] [PATCH 1/5] Rename dcc-encoders.[ch] to image-encoders.[ch]
Frediano Ziglio
fziglio at redhat.com
Fri Jun 17 13:58:24 UTC 2016
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
server/Makefile.am | 4 +-
server/dcc-encoders.c | 1388 -----------------------------------------------
server/dcc-encoders.h | 217 --------
server/dcc.h | 2 +-
server/image-encoders.c | 1388 +++++++++++++++++++++++++++++++++++++++++++++++
server/image-encoders.h | 217 ++++++++
6 files changed, 1608 insertions(+), 1608 deletions(-)
delete mode 100644 server/dcc-encoders.c
delete mode 100644 server/dcc-encoders.h
create mode 100644 server/image-encoders.c
create mode 100644 server/image-encoders.h
diff --git a/server/Makefile.am b/server/Makefile.am
index 0af8a1b..921b082 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -148,8 +148,8 @@ libserver_la_SOURCES = \
dcc-send.c \
dcc.h \
display-limits.h \
- dcc-encoders.c \
- dcc-encoders.h \
+ image-encoders.c \
+ image-encoders.h \
$(NULL)
if HAVE_LZ4
diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c
deleted file mode 100644
index 984f2d4..0000000
--- a/server/dcc-encoders.c
+++ /dev/null
@@ -1,1388 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2009-2015 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#include "dcc-encoders.h"
-#include "spice-bitmap-utils.h"
-#include "red-worker.h" // red_drawable_unref
-#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
-
-#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
-
-#define ENCODER_MESSAGE_SIZE 512
-
-#define MAX_GLZ_DRAWABLE_INSTANCES 2
-
-typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
-
-struct GlzSharedDictionary {
- RingItem base;
- GlzEncDictContext *dict;
- uint32_t refs;
- uint8_t id;
- pthread_rwlock_t encode_lock;
- int migrate_freeze;
- RedClient *client; // channel clients of the same client share the dict
-};
-
-/* for each qxl drawable, there may be several instances of lz drawables */
-/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
- * at the Drawable by keeping a ring, so:
- * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
- * and it should probably (but need to be sure...) be
- * Drawable -> ring of GlzDrawableInstanceItem.
- */
-struct GlzDrawableInstanceItem {
- RingItem glz_link;
- RingItem free_link;
- GlzEncDictImageContext *context;
- RedGlzDrawable *glz_drawable;
-};
-
-struct RedGlzDrawable {
- RingItem link; // ordered by the time it was encoded
- RingItem drawable_link;
- RedDrawable *red_drawable;
- GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
- Ring instances;
- uint8_t instances_count;
- gboolean has_drawable;
- ImageEncoders *encoders;
-};
-
-#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
- drawable_link)
-#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
- SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, LINK_TO_GLZ(link))
-
-static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance);
-static void encoder_data_init(EncoderData *data);
-static void encoder_data_reset(EncoderData *data);
-static void image_encoders_release_glz(ImageEncoders *enc);
-
-
-static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
-quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
-{
- EncoderData *usr_data = &(((QuicData *)usr)->data);
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
- spice_critical("%s", message_buf);
-
- longjmp(usr_data->jmp_env, 1);
-}
-
-static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
-lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
-{
- EncoderData *usr_data = &(((LzData *)usr)->data);
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
- spice_critical("%s", message_buf);
-
- longjmp(usr_data->jmp_env, 1);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
-{
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
-
- spice_critical("%s", message_buf); // if global lz fails in the middle
- // the consequences are not predictable since the window
- // can turn to be unsynchronized between the server and
- // and the client
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
-{
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
- spice_warning("%s", message_buf);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
-{
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
- spice_warning("%s", message_buf);
-}
-
-static SPICE_GNUC_PRINTF(2, 3) void
-glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
-{
- va_list ap;
- char message_buf[ENCODER_MESSAGE_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
- va_end(ap);
- spice_warning("%s", message_buf);
-}
-
-static void *quic_usr_malloc(QuicUsrContext *usr, int size)
-{
- return spice_malloc(size);
-}
-
-static void *lz_usr_malloc(LzUsrContext *usr, int size)
-{
- return spice_malloc(size);
-}
-
-static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
-{
- return spice_malloc(size);
-}
-
-static void quic_usr_free(QuicUsrContext *usr, void *ptr)
-{
- free(ptr);
-}
-
-static void lz_usr_free(LzUsrContext *usr, void *ptr)
-{
- free(ptr);
-}
-
-static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
-{
- free(ptr);
-}
-
-static void encoder_data_init(EncoderData *data)
-{
- data->bufs_tail = g_new(RedCompressBuf, 1);
- data->bufs_head = data->bufs_tail;
- data->bufs_head->send_next = NULL;
-}
-
-static void encoder_data_reset(EncoderData *data)
-{
- RedCompressBuf *buf = data->bufs_head;
- while (buf) {
- RedCompressBuf *next = buf->send_next;
- g_free(buf);
- buf = next;
- }
- data->bufs_head = data->bufs_tail = NULL;
-}
-
-/* Allocate more space for compressed buffer.
- * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
- */
-static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
-{
- RedCompressBuf *buf;
-
- buf = g_new(RedCompressBuf, 1);
- enc_data->bufs_tail->send_next = buf;
- enc_data->bufs_tail = buf;
- buf->send_next = NULL;
- *io_ptr = buf->buf.bytes;
- return sizeof(buf->buf);
-}
-
-static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
-{
- EncoderData *usr_data = &(((QuicData *)usr)->data);
- return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / sizeof(uint32_t);
-}
-
-static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
-{
- EncoderData *usr_data = &(((LzData *)usr)->data);
- return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
-{
- EncoderData *usr_data = &(((GlzData *)usr)->data);
- return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
-{
- EncoderData *usr_data = &(((JpegData *)usr)->data);
- return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-#ifdef USE_LZ4
-static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
-{
- EncoderData *usr_data = &(((Lz4Data *)usr)->data);
- return encoder_usr_more_space(usr_data, io_ptr);
-}
-#endif
-
-static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
-{
- EncoderData *usr_data = &(((ZlibData *)usr)->data);
- return encoder_usr_more_space(usr_data, io_ptr);
-}
-
-static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
-{
- struct SpiceChunk *chunk;
-
- if (enc_data->u.lines_data.reverse) {
- if (!(enc_data->u.lines_data.next >= 0)) {
- return 0;
- }
- } else {
- if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks->num_chunks)) {
- return 0;
- }
- }
-
- chunk = &enc_data->u.lines_data.chunks->chunk[enc_data->u.lines_data.next];
- if (chunk->len % enc_data->u.lines_data.stride) {
- return 0;
- }
-
- if (enc_data->u.lines_data.reverse) {
- enc_data->u.lines_data.next--;
- *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride;
- } else {
- enc_data->u.lines_data.next++;
- *lines = chunk->data;
- }
-
- return chunk->len / enc_data->u.lines_data.stride;
-}
-
-static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
-{
- EncoderData *usr_data = &(((QuicData *)usr)->data);
- return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
-{
- EncoderData *usr_data = &(((LzData *)usr)->data);
- return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
-{
- EncoderData *usr_data = &(((GlzData *)usr)->data);
- return encoder_usr_more_lines(usr_data, lines);
-}
-
-static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
-{
- EncoderData *usr_data = &(((JpegData *)usr)->data);
- return encoder_usr_more_lines(usr_data, lines);
-}
-
-#ifdef USE_LZ4
-static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
-{
- EncoderData *usr_data = &(((Lz4Data *)usr)->data);
- return encoder_usr_more_lines(usr_data, lines);
-}
-#endif
-
-static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
-{
- EncoderData *usr_data = &(((ZlibData *)usr)->data);
- int buf_size;
-
- if (!usr_data->u.compressed_data.next) {
- spice_assert(usr_data->u.compressed_data.size_left == 0);
- return 0;
- }
-
- *input = usr_data->u.compressed_data.next->buf.bytes;
- buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
- usr_data->u.compressed_data.size_left);
-
- usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
- usr_data->u.compressed_data.size_left -= buf_size;
- return buf_size;
-}
-
-static void image_encoders_init_quic(ImageEncoders *enc)
-{
- enc->quic_data.usr.error = quic_usr_error;
- enc->quic_data.usr.warn = quic_usr_warn;
- enc->quic_data.usr.info = quic_usr_warn;
- enc->quic_data.usr.malloc = quic_usr_malloc;
- enc->quic_data.usr.free = quic_usr_free;
- enc->quic_data.usr.more_space = quic_usr_more_space;
- enc->quic_data.usr.more_lines = quic_usr_more_lines;
-
- enc->quic = quic_create(&enc->quic_data.usr);
-
- if (!enc->quic) {
- spice_critical("create quic failed");
- }
-}
-
-static void image_encoders_init_lz(ImageEncoders *enc)
-{
- enc->lz_data.usr.error = lz_usr_error;
- enc->lz_data.usr.warn = lz_usr_warn;
- enc->lz_data.usr.info = lz_usr_warn;
- enc->lz_data.usr.malloc = lz_usr_malloc;
- enc->lz_data.usr.free = lz_usr_free;
- enc->lz_data.usr.more_space = lz_usr_more_space;
- enc->lz_data.usr.more_lines = lz_usr_more_lines;
-
- enc->lz = lz_create(&enc->lz_data.usr);
-
- if (!enc->lz) {
- spice_critical("create lz failed");
- }
-}
-
-static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
-{
- GlzData *lz_data = (GlzData *)usr;
- GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image;
- ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable->encoders;
- ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, glz_data);
- if (this_enc == drawable_enc) {
- glz_drawable_instance_item_free(glz_drawable_instance);
- } else {
- /* The glz dictionary is shared between all DisplayChannelClient
- * instances that belong to the same client, and glz_usr_free_image
- * can be called by the dictionary code
- * (glz_dictionary_window_remove_head). Thus this function can be
- * called from any DisplayChannelClient thread, hence the need for
- * this check.
- */
- pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
- ring_add_before(&glz_drawable_instance->free_link,
- &drawable_enc->glz_drawables_inst_to_free);
- pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
- }
-}
-
-static void image_encoders_init_glz_data(ImageEncoders *enc)
-{
- enc->glz_data.usr.error = glz_usr_error;
- enc->glz_data.usr.warn = glz_usr_warn;
- enc->glz_data.usr.info = glz_usr_warn;
- enc->glz_data.usr.malloc = glz_usr_malloc;
- enc->glz_data.usr.free = glz_usr_free;
- enc->glz_data.usr.more_space = glz_usr_more_space;
- enc->glz_data.usr.more_lines = glz_usr_more_lines;
- enc->glz_data.usr.free_image = glz_usr_free_image;
-}
-
-static void image_encoders_init_jpeg(ImageEncoders *enc)
-{
- enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
- enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
-
- enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
-
- if (!enc->jpeg) {
- spice_critical("create jpeg encoder failed");
- }
-}
-
-#ifdef USE_LZ4
-static inline void image_encoders_init_lz4(ImageEncoders *enc)
-{
- enc->lz4_data.usr.more_space = lz4_usr_more_space;
- enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
-
- enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
-
- if (!enc->lz4) {
- spice_critical("create lz4 encoder failed");
- }
-}
-#endif
-
-static void image_encoders_init_zlib(ImageEncoders *enc)
-{
- enc->zlib_data.usr.more_space = zlib_usr_more_space;
- enc->zlib_data.usr.more_input = zlib_usr_more_input;
-
- enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL);
-
- if (!enc->zlib) {
- spice_critical("create zlib encoder failed");
- }
-}
-
-void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data)
-{
- spice_assert(shared_data);
- enc->shared_data = shared_data;
-
- ring_init(&enc->glz_drawables);
- ring_init(&enc->glz_drawables_inst_to_free);
- pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
-
- image_encoders_init_glz_data(enc);
- image_encoders_init_quic(enc);
- image_encoders_init_lz(enc);
- image_encoders_init_jpeg(enc);
-#ifdef USE_LZ4
- image_encoders_init_lz4(enc);
-#endif
- image_encoders_init_zlib(enc);
-
- // todo: tune level according to bandwidth
- enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
-}
-
-void image_encoders_free(ImageEncoders *enc)
-{
- image_encoders_release_glz(enc);
- quic_destroy(enc->quic);
- enc->quic = NULL;
- lz_destroy(enc->lz);
- enc->lz = NULL;
- jpeg_encoder_destroy(enc->jpeg);
- enc->jpeg = NULL;
-#ifdef USE_LZ4
- lz4_encoder_destroy(enc->lz4);
- enc->lz4 = NULL;
-#endif
- zlib_encoder_destroy(enc->zlib);
- enc->zlib = NULL;
-}
-
-/* Remove from the to_free list and the instances_list.
- When no instance is left - the RedGlzDrawable is released too. (and the qxl drawable too, if
- it is not used by Drawable).
- NOTE - 1) can be called only by the display channel that created the drawable
- 2) it is assumed that the instance was already removed from the dictionary*/
-static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance)
-{
- RedGlzDrawable *glz_drawable;
-
- spice_assert(instance);
- spice_assert(instance->glz_drawable);
-
- glz_drawable = instance->glz_drawable;
-
- spice_assert(glz_drawable->instances_count > 0);
-
- ring_remove(&instance->glz_link);
- glz_drawable->instances_count--;
-
- // when the remove callback is performed from the channel that the
- // drawable belongs to, the instance is not added to the 'to_free' list
- if (ring_item_is_linked(&instance->free_link)) {
- ring_remove(&instance->free_link);
- }
-
- if (ring_is_empty(&glz_drawable->instances)) {
- spice_assert(glz_drawable->instances_count == 0);
-
- if (glz_drawable->has_drawable) {
- ring_remove(&glz_drawable->drawable_link);
- }
- red_drawable_unref(glz_drawable->red_drawable);
- glz_drawable->encoders->shared_data->glz_drawable_count--;
- if (ring_item_is_linked(&glz_drawable->link)) {
- ring_remove(&glz_drawable->link);
- }
- free(glz_drawable);
- }
-}
-
-/*
- * Releases all the instances of the drawable from the dictionary and the display channel client.
- * The release of the last instance will also release the drawable itself and the qxl drawable
- * if possible.
- * NOTE - the caller should prevent encoding using the dictionary during this operation
- */
-static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
-{
- ImageEncoders *enc = glz_drawable->encoders;
- RingItem *head_instance = ring_get_head(&glz_drawable->instances);
- int cont = (head_instance != NULL);
-
- while (cont) {
- if (glz_drawable->instances_count == 1) {
- /* Last instance: glz_drawable_instance_item_free will free the glz_drawable */
- cont = FALSE;
- }
- GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
- GlzDrawableInstanceItem,
- glz_link);
- if (!ring_item_is_linked(&instance->free_link)) {
- // the instance didn't get out from window yet
- glz_enc_dictionary_remove_image(enc->glz_dict->dict,
- instance->context,
- &enc->glz_data.usr);
- }
- glz_drawable_instance_item_free(instance);
-
- if (cont) {
- head_instance = ring_get_head(&glz_drawable->instances);
- }
- }
-}
-
-gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
-{
- if (enc->glz_dict) {
- pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
- return TRUE;
- }
- return FALSE;
-}
-
-void image_encoders_glz_encode_unlock(ImageEncoders *enc)
-{
- if (enc->glz_dict) {
- pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
- }
-}
-
-/*
- * Remove from the global lz dictionary some glz_drawables that have no reference to
- * Drawable (their qxl drawables are released too).
- * NOTE - the caller should prevent encoding using the dictionary during the operation
- */
-int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
-{
- RingItem *ring_link;
- int n = 0;
-
- if (!enc) {
- return 0;
- }
- ring_link = ring_get_head(&enc->glz_drawables);
- while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
- RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
- ring_link = ring_next(&enc->glz_drawables, ring_link);
- if (!glz_drawable->has_drawable) {
- red_glz_drawable_free(glz_drawable);
- n++;
- }
- }
- return n;
-}
-
-void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
-{
- RingItem *ring_link;
-
- if (!enc->glz_dict) {
- return;
- }
- pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
- while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
- GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link,
- GlzDrawableInstanceItem,
- free_link);
- glz_drawable_instance_item_free(drawable_instance);
- }
- pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
-}
-
-/* Clear all lz drawables - enforce their removal from the global dictionary.
- NOTE - prevents encoding using the dictionary during the operation*/
-void image_encoders_free_glz_drawables(ImageEncoders *enc)
-{
- RingItem *ring_link;
- GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
-
- if (!glz_dict) {
- return;
- }
-
- // assure no display channel is during global lz encoding
- pthread_rwlock_wrlock(&glz_dict->encode_lock);
- while ((ring_link = ring_get_head(&enc->glz_drawables))) {
- RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
- // no need to lock the to_free list, since we assured no other thread is encoding and
- // thus not other thread access the to_free list of the channel
- red_glz_drawable_free(drawable);
- }
- pthread_rwlock_unlock(&glz_dict->encode_lock);
-}
-
-void glz_retention_free_drawables(GlzImageRetention *ret)
-{
- RingItem *glz_item, *next_item;
- RedGlzDrawable *glz;
- SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, LINK_TO_GLZ(glz_item)) {
- red_glz_drawable_free(glz);
- }
-}
-
-void glz_retention_detach_drawables(GlzImageRetention *ret)
-{
- RingItem *item, *next;
-
- RING_FOREACH_SAFE(item, next, &ret->ring) {
- SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable = FALSE;
- ring_remove(item);
- }
-}
-
-static void image_encoders_freeze_glz(ImageEncoders *enc)
-{
- pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
- enc->glz_dict->migrate_freeze = TRUE;
- pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-}
-
-void image_encoders_glz_get_restore_data(ImageEncoders *enc,
- uint8_t *out_id, GlzEncDictRestoreData *out_data)
-{
- spice_assert(enc->glz_dict);
- image_encoders_freeze_glz(enc);
- *out_id = enc->glz_dict->id;
- glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
- &enc->glz_data.usr);
-}
-
-static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id,
- GlzEncDictContext *dict)
-{
- spice_return_val_if_fail(dict != NULL, NULL);
-
- GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
-
- shared_dict->dict = dict;
- shared_dict->id = id;
- shared_dict->refs = 1;
- shared_dict->migrate_freeze = FALSE;
- shared_dict->client = client;
- ring_item_init(&shared_dict->base);
- pthread_rwlock_init(&shared_dict->encode_lock, NULL);
-
- return shared_dict;
-}
-
-static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
-
-static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t dict_id)
-{
- RingItem *now;
- GlzSharedDictionary *ret = NULL;
-
- now = &glz_dictionary_list;
- while ((now = ring_next(&glz_dictionary_list, now))) {
- GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
- if ((dict->client == client) && (dict->id == dict_id)) {
- ret = dict;
- break;
- }
- }
-
- return ret;
-}
-
-#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
-
-static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
- RedClient *client,
- uint8_t id, int window_size)
-{
- spice_info("Lz Window %d Size=%d", id, window_size);
-
- GlzEncDictContext *glz_dict =
- glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc->glz_data.usr);
-
- return glz_shared_dictionary_new(client, id, glz_dict);
-}
-
-gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
- RedClient *client,
- uint8_t id, int window_size)
-{
- GlzSharedDictionary *shared_dict;
-
- spice_return_val_if_fail(!enc->glz_dict, FALSE);
-
- pthread_mutex_lock(&glz_dictionary_list_lock);
-
- shared_dict = find_glz_dictionary(client, id);
- if (shared_dict) {
- shared_dict->refs++;
- } else {
- shared_dict = create_glz_dictionary(enc, client, id, window_size);
- ring_add(&glz_dictionary_list, &shared_dict->base);
- }
-
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- enc->glz_dict = shared_dict;
- return shared_dict != NULL;
-}
-
-static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
- RedClient *client,
- uint8_t id,
- GlzEncDictRestoreData *restore_data)
-{
- GlzEncDictContext *glz_dict =
- glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
-
- return glz_shared_dictionary_new(client, id, glz_dict);
-}
-
-gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
- RedClient *client,
- uint8_t id,
- GlzEncDictRestoreData *restore_data)
-{
- GlzSharedDictionary *shared_dict = NULL;
-
- spice_return_val_if_fail(!enc->glz_dict, FALSE);
-
- pthread_mutex_lock(&glz_dictionary_list_lock);
-
- shared_dict = find_glz_dictionary(client, id);
-
- if (shared_dict) {
- shared_dict->refs++;
- } else {
- shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
- ring_add(&glz_dictionary_list, &shared_dict->base);
- }
-
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- enc->glz_dict = shared_dict;
- return shared_dict != NULL;
-}
-
-gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
-{
- enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc->glz_data.usr);
- return enc->glz != NULL;
-}
-
-/* destroy encoder, and dictionary if no one uses it*/
-static void image_encoders_release_glz(ImageEncoders *enc)
-{
- GlzSharedDictionary *shared_dict;
-
- image_encoders_free_glz_drawables(enc);
-
- glz_encoder_destroy(enc->glz);
- enc->glz = NULL;
-
- if (!(shared_dict = enc->glz_dict)) {
- return;
- }
-
- enc->glz_dict = NULL;
- pthread_mutex_lock(&glz_dictionary_list_lock);
- if (--shared_dict->refs != 0) {
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- return;
- }
- ring_remove(&shared_dict->base);
- pthread_mutex_unlock(&glz_dictionary_list_lock);
- glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
- free(shared_dict);
-}
-
-int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
- QuicData *quic_data = &enc->quic_data;
- QuicContext *quic = enc->quic;
- volatile QuicImageType type;
- int size, stride;
- stat_start_time_t start_time;
- stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
-
-#ifdef COMPRESS_DEBUG
- spice_info("QUIC compress");
-#endif
-
- switch (src->format) {
- case SPICE_BITMAP_FMT_32BIT:
- type = QUIC_IMAGE_TYPE_RGB32;
- break;
- case SPICE_BITMAP_FMT_RGBA:
- type = QUIC_IMAGE_TYPE_RGBA;
- break;
- case SPICE_BITMAP_FMT_16BIT:
- type = QUIC_IMAGE_TYPE_RGB16;
- break;
- case SPICE_BITMAP_FMT_24BIT:
- type = QUIC_IMAGE_TYPE_RGB24;
- break;
- default:
- return FALSE;
- }
-
- encoder_data_init(&quic_data->data);
-
- if (setjmp(quic_data->data.jmp_env)) {
- encoder_data_reset(&quic_data->data);
- return FALSE;
- }
-
- if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
- spice_chunks_linearize(src->data);
- }
-
- quic_data->data.u.lines_data.chunks = src->data;
- quic_data->data.u.lines_data.stride = src->stride;
- if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
- quic_data->data.u.lines_data.next = 0;
- quic_data->data.u.lines_data.reverse = 0;
- stride = src->stride;
- } else {
- quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
- quic_data->data.u.lines_data.reverse = 1;
- stride = -src->stride;
- }
- size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
- quic_data->data.bufs_head->buf.words,
- G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
-
- // the compressed buffer is bigger than the original data
- if ((size << 2) > (src->y * src->stride)) {
- longjmp(quic_data->data.jmp_env, 1);
- }
-
- dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
- dest->u.quic.data_size = size << 2;
-
- o_comp_data->comp_buf = quic_data->data.bufs_head;
- o_comp_data->comp_buf_size = size << 2;
-
- stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * src->y,
- o_comp_data->comp_buf_size);
- return TRUE;
-}
-
-static const LzImageType bitmap_fmt_to_lz_image_type[] = {
- LZ_IMAGE_TYPE_INVALID,
- LZ_IMAGE_TYPE_PLT1_LE,
- LZ_IMAGE_TYPE_PLT1_BE,
- LZ_IMAGE_TYPE_PLT4_LE,
- LZ_IMAGE_TYPE_PLT4_BE,
- LZ_IMAGE_TYPE_PLT8,
- LZ_IMAGE_TYPE_RGB16,
- LZ_IMAGE_TYPE_RGB24,
- LZ_IMAGE_TYPE_RGB32,
- LZ_IMAGE_TYPE_RGBA,
- LZ_IMAGE_TYPE_A8
-};
-
-int image_encoders_compress_lz(ImageEncoders *enc,
- SpiceImage *dest, SpiceBitmap *src,
- compress_send_data_t* o_comp_data)
-{
- LzData *lz_data = &enc->lz_data;
- LzContext *lz = enc->lz;
- LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
- int size; // size of the compressed data
-
- stat_start_time_t start_time;
- stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
-
-#ifdef COMPRESS_DEBUG
- spice_info("LZ LOCAL compress");
-#endif
-
- encoder_data_init(&lz_data->data);
-
- if (setjmp(lz_data->data.jmp_env)) {
- encoder_data_reset(&lz_data->data);
- return FALSE;
- }
-
- lz_data->data.u.lines_data.chunks = src->data;
- lz_data->data.u.lines_data.stride = src->stride;
- lz_data->data.u.lines_data.next = 0;
- lz_data->data.u.lines_data.reverse = 0;
-
- size = lz_encode(lz, type, src->x, src->y,
- !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
- NULL, 0, src->stride,
- lz_data->data.bufs_head->buf.bytes,
- sizeof(lz_data->data.bufs_head->buf));
-
- // the compressed buffer is bigger than the original data
- if (size > (src->y * src->stride)) {
- longjmp(lz_data->data.jmp_env, 1);
- }
-
- if (bitmap_fmt_is_rgb(src->format)) {
- dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
- dest->u.lz_rgb.data_size = size;
-
- o_comp_data->comp_buf = lz_data->data.bufs_head;
- o_comp_data->comp_buf_size = size;
- } else {
- /* masks are 1BIT bitmaps without palettes, but they are not compressed
- * (see fill_mask) */
- spice_assert(src->palette);
- dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
- dest->u.lz_plt.data_size = size;
- dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
- dest->u.lz_plt.palette = src->palette;
- dest->u.lz_plt.palette_id = src->palette->unique;
- o_comp_data->comp_buf = lz_data->data.bufs_head;
- o_comp_data->comp_buf_size = size;
-
- o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
- }
-
- stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * src->y,
- o_comp_data->comp_buf_size);
- return TRUE;
-}
-
-int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
- JpegData *jpeg_data = &enc->jpeg_data;
- LzData *lz_data = &enc->lz_data;
- JpegEncoderContext *jpeg = enc->jpeg;
- LzContext *lz = enc->lz;
- volatile JpegEncoderImageType jpeg_in_type;
- int jpeg_size = 0;
- volatile int has_alpha = FALSE;
- int alpha_lz_size = 0;
- int comp_head_filled;
- int comp_head_left;
- int stride;
- uint8_t *lz_out_start_byte;
- stat_start_time_t start_time;
- stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
-
-#ifdef COMPRESS_DEBUG
- spice_info("JPEG compress");
-#endif
-
- switch (src->format) {
- case SPICE_BITMAP_FMT_16BIT:
- jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
- break;
- case SPICE_BITMAP_FMT_24BIT:
- jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
- break;
- case SPICE_BITMAP_FMT_32BIT:
- jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
- break;
- case SPICE_BITMAP_FMT_RGBA:
- jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
- has_alpha = TRUE;
- break;
- default:
- return FALSE;
- }
-
- encoder_data_init(&jpeg_data->data);
-
- if (setjmp(jpeg_data->data.jmp_env)) {
- encoder_data_reset(&jpeg_data->data);
- return FALSE;
- }
-
- if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
- spice_chunks_linearize(src->data);
- }
-
- jpeg_data->data.u.lines_data.chunks = src->data;
- jpeg_data->data.u.lines_data.stride = src->stride;
- if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
- jpeg_data->data.u.lines_data.next = 0;
- jpeg_data->data.u.lines_data.reverse = 0;
- stride = src->stride;
- } else {
- jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
- jpeg_data->data.u.lines_data.reverse = 1;
- stride = -src->stride;
- }
- jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type,
- src->x, src->y, NULL,
- 0, stride, jpeg_data->data.bufs_head->buf.bytes,
- sizeof(jpeg_data->data.bufs_head->buf));
-
- // the compressed buffer is bigger than the original data
- if (jpeg_size > (src->y * src->stride)) {
- longjmp(jpeg_data->data.jmp_env, 1);
- }
-
- if (!has_alpha) {
- dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
- dest->u.jpeg.data_size = jpeg_size;
-
- o_comp_data->comp_buf = jpeg_data->data.bufs_head;
- o_comp_data->comp_buf_size = jpeg_size;
- o_comp_data->is_lossy = TRUE;
-
- stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src->stride * src->y,
- o_comp_data->comp_buf_size);
- return TRUE;
- }
-
- lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
- lz_data->data.bufs_tail = lz_data->data.bufs_head;
-
- comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
- comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
- lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + comp_head_filled;
-
- lz_data->data.u.lines_data.chunks = src->data;
- lz_data->data.u.lines_data.stride = src->stride;
- lz_data->data.u.lines_data.next = 0;
- lz_data->data.u.lines_data.reverse = 0;
-
- alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
- !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
- NULL, 0, src->stride,
- lz_out_start_byte,
- comp_head_left);
-
- // the compressed buffer is bigger than the original data
- if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
- longjmp(jpeg_data->data.jmp_env, 1);
- }
-
- dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
- dest->u.jpeg_alpha.flags = 0;
- if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
- dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
- }
-
- dest->u.jpeg_alpha.jpeg_size = jpeg_size;
- dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
-
- o_comp_data->comp_buf = jpeg_data->data.bufs_head;
- o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
- o_comp_data->is_lossy = TRUE;
- stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src->stride * src->y,
- o_comp_data->comp_buf_size);
- return TRUE;
-}
-
-#ifdef USE_LZ4
-int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data)
-{
- Lz4Data *lz4_data = &enc->lz4_data;
- Lz4EncoderContext *lz4 = enc->lz4;
- int lz4_size = 0;
- stat_start_time_t start_time;
- stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
-
-#ifdef COMPRESS_DEBUG
- spice_info("LZ4 compress");
-#endif
-
- encoder_data_init(&lz4_data->data);
-
- if (setjmp(lz4_data->data.jmp_env)) {
- encoder_data_reset(&lz4_data->data);
- return FALSE;
- }
-
- if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
- spice_chunks_linearize(src->data);
- }
-
- lz4_data->data.u.lines_data.chunks = src->data;
- lz4_data->data.u.lines_data.stride = src->stride;
- lz4_data->data.u.lines_data.next = 0;
- lz4_data->data.u.lines_data.reverse = 0;
-
- lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head->buf.bytes,
- sizeof(lz4_data->data.bufs_head->buf),
- src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src->format);
-
- // the compressed buffer is bigger than the original data
- if (lz4_size > (src->y * src->stride)) {
- longjmp(lz4_data->data.jmp_env, 1);
- }
-
- dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
- dest->u.lz4.data_size = lz4_size;
-
- o_comp_data->comp_buf = lz4_data->data.bufs_head;
- o_comp_data->comp_buf_size = lz4_size;
-
- stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride * src->y,
- o_comp_data->comp_buf_size);
- return TRUE;
-}
-#endif
-
-/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
- in the channel (2) to the Drawable*/
-static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable *red_drawable,
- GlzImageRetention *glz_retention)
-{
- RedGlzDrawable *ret;
- RingItem *item, *next;
-
- // TODO - I don't really understand what's going on here, so doing the technical equivalent
- // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
- // over the glz_ring (unless adding some better data structure then a ring)
- SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, LINK_TO_GLZ(item)) {
- if (ret->encoders == enc) {
- return ret;
- }
- }
-
- ret = spice_new(RedGlzDrawable, 1);
-
- ret->encoders = enc;
- ret->red_drawable = red_drawable_ref(red_drawable);
- ret->has_drawable = TRUE;
- ret->instances_count = 0;
- ring_init(&ret->instances);
-
- ring_item_init(&ret->link);
- ring_item_init(&ret->drawable_link);
- ring_add_before(&ret->link, &enc->glz_drawables);
- ring_add(&glz_retention->ring, &ret->drawable_link);
- enc->shared_data->glz_drawable_count++;
- return ret;
-}
-
-/* allocates new instance and adds it to instances in the given drawable.
- NOTE - the caller should set the glz_instance returned by the encoder by itself.*/
-static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable *glz_drawable)
-{
- spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
- // NOTE: We assume the additions are performed consecutively, without removals in the middle
- GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + glz_drawable->instances_count;
- glz_drawable->instances_count++;
-
- ring_item_init(&ret->free_link);
- ring_item_init(&ret->glz_link);
- ring_add(&glz_drawable->instances, &ret->glz_link);
- ret->context = NULL;
- ret->glz_drawable = glz_drawable;
-
- return ret;
-}
-
-#define MIN_GLZ_SIZE_FOR_ZLIB 100
-
-int image_encoders_compress_glz(ImageEncoders *enc,
- SpiceImage *dest, SpiceBitmap *src,
- RedDrawable *red_drawable,
- GlzImageRetention *glz_retention,
- compress_send_data_t* o_comp_data,
- gboolean enable_zlib_glz_wrap)
-{
- stat_start_time_t start_time;
- stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
- spice_assert(bitmap_fmt_is_rgb(src->format));
- GlzData *glz_data = &enc->glz_data;
- ZlibData *zlib_data;
- LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
- RedGlzDrawable *glz_drawable;
- GlzDrawableInstanceItem *glz_drawable_instance;
- int glz_size;
- int zlib_size;
-
-#ifdef COMPRESS_DEBUG
- spice_info("LZ global compress fmt=%d", src->format);
-#endif
-
- if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict->dict)) {
- return FALSE;
- }
-
- pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
- /* using the global dictionary only if it is not frozen */
- if (enc->glz_dict->migrate_freeze) {
- pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
- return FALSE;
- }
-
- encoder_data_init(&glz_data->data);
-
- glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
- glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
-
- glz_data->data.u.lines_data.chunks = src->data;
- glz_data->data.u.lines_data.stride = src->stride;
- glz_data->data.u.lines_data.next = 0;
- glz_data->data.u.lines_data.reverse = 0;
-
- glz_size = glz_encode(enc->glz, type, src->x, src->y,
- (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
- src->stride, glz_data->data.bufs_head->buf.bytes,
- sizeof(glz_data->data.bufs_head->buf),
- glz_drawable_instance,
- &glz_drawable_instance->context);
-
- stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * src->y, glz_size);
-
- if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
- goto glz;
- }
- stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
- zlib_data = &enc->zlib_data;
-
- encoder_data_init(&zlib_data->data);
-
- zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
- zlib_data->data.u.compressed_data.size_left = glz_size;
-
- zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
- glz_size, zlib_data->data.bufs_head->buf.bytes,
- sizeof(zlib_data->data.bufs_head->buf));
-
- // the compressed buffer is bigger than the original data
- if (zlib_size >= glz_size) {
- encoder_data_reset(&zlib_data->data);
- goto glz;
- } else {
- encoder_data_reset(&glz_data->data);
- }
-
- dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
- dest->u.zlib_glz.glz_data_size = glz_size;
- dest->u.zlib_glz.data_size = zlib_size;
-
- o_comp_data->comp_buf = zlib_data->data.bufs_head;
- o_comp_data->comp_buf_size = zlib_size;
-
- stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size, zlib_size);
- pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
- return TRUE;
-
-glz:
- pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
-
- dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
- dest->u.lz_rgb.data_size = glz_size;
-
- o_comp_data->comp_buf = glz_data->data.bufs_head;
- o_comp_data->comp_buf_size = glz_size;
-
- return TRUE;
-}
-
-void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
-{
- clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
-
- stat_compress_init(&shared_data->off_stat, "off", stat_clock);
- stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
- stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
- stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
- stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
- stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
- stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", stat_clock);
- stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
-}
-
-void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
-{
- stat_reset(&shared_data->off_stat);
- stat_reset(&shared_data->quic_stat);
- stat_reset(&shared_data->lz_stat);
- stat_reset(&shared_data->glz_stat);
- stat_reset(&shared_data->jpeg_stat);
- stat_reset(&shared_data->zlib_glz_stat);
- stat_reset(&shared_data->jpeg_alpha_stat);
- stat_reset(&shared_data->lz4_stat);
-}
-
-#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
-
-#ifdef COMPRESS_STAT
-static void stat_print_one(const char *name, const stat_info_t *stat)
-{
- spice_info(STAT_FMT, name, stat->count,
- stat_byte_to_mega(stat->orig_size),
- stat_byte_to_mega(stat->comp_size),
- stat_cpu_time_to_sec(stat->total));
-}
-
-static void stat_sum(stat_info_t *total, const stat_info_t *stat)
-{
- total->count += stat->count;
- total->orig_size += stat->orig_size;
- total->comp_size += stat->comp_size;
- total->total += stat->total;
-}
-#endif
-
-void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data)
-{
-#ifdef COMPRESS_STAT
- /* sum all statistics */
- stat_info_t total = {
- .count = 0,
- .orig_size = 0,
- .comp_size = 0,
- .total = 0
- };
- stat_sum(&total, &shared_data->off_stat);
- stat_sum(&total, &shared_data->quic_stat);
- stat_sum(&total, &shared_data->glz_stat);
- stat_sum(&total, &shared_data->lz_stat);
- stat_sum(&total, &shared_data->jpeg_stat);
- stat_sum(&total, &shared_data->jpeg_alpha_stat);
- stat_sum(&total, &shared_data->lz4_stat);
-
- /* fix for zlib glz */
- total.total += shared_data->zlib_glz_stat.total;
- if (shared_data->zlib_glz_stat.count) {
- total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
- shared_data->zlib_glz_stat.comp_size;
- }
-
- spice_info("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
- stat_print_one("OFF ", &shared_data->off_stat);
- stat_print_one("QUIC ", &shared_data->quic_stat);
- stat_print_one("GLZ ", &shared_data->glz_stat);
- stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
- stat_print_one("LZ ", &shared_data->lz_stat);
- stat_print_one("JPEG ", &shared_data->jpeg_stat);
- stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
- stat_print_one("LZ4 ", &shared_data->lz4_stat);
- spice_info("-------------------------------------------------------------------");
- stat_print_one("Total ", &total);
-#endif
-}
diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
deleted file mode 100644
index 9286970..0000000
--- a/server/dcc-encoders.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2009-2015 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#ifndef DCC_ENCODERS_H_
-#define DCC_ENCODERS_H_
-
-#include <setjmp.h>
-#include <common/quic.h>
-#include <common/lz.h>
-
-#include "stat.h"
-#include "red-parse-qxl.h"
-#include "glz-encoder.h"
-#include "jpeg-encoder.h"
-#ifdef USE_LZ4
-#include "lz4-encoder.h"
-#endif
-#include "zlib-encoder.h"
-
-struct RedClient;
-
-typedef struct RedCompressBuf RedCompressBuf;
-typedef struct RedGlzDrawable RedGlzDrawable;
-typedef struct ImageEncoders ImageEncoders;
-typedef struct ImageEncoderSharedData ImageEncoderSharedData;
-typedef struct GlzSharedDictionary GlzSharedDictionary;
-typedef struct GlzImageRetention GlzImageRetention;
-
-void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
-void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
-void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data);
-
-void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data);
-void image_encoders_free(ImageEncoders *enc);
-int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
-void image_encoders_free_glz_drawables(ImageEncoders *enc);
-void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
-gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
-void image_encoders_glz_get_restore_data(ImageEncoders *enc,
- uint8_t *out_id, GlzEncDictRestoreData *out_data);
-gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
-void image_encoders_glz_encode_unlock(ImageEncoders *enc);
-void glz_retention_free_drawables(GlzImageRetention *ret);
-void glz_retention_detach_drawables(GlzImageRetention *ret);
-
-#define RED_COMPRESS_BUF_SIZE (1024 * 64)
-struct RedCompressBuf {
- /* This buffer provide space for compression algorithms.
- * Some algorithms access the buffer as an array of 32 bit words
- * so is defined to make sure is always aligned that way.
- */
- union {
- uint8_t bytes[RED_COMPRESS_BUF_SIZE];
- uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
- } buf;
- RedCompressBuf *send_next;
-};
-
-static inline void compress_buf_free(RedCompressBuf *buf)
-{
- g_free(buf);
-}
-
-gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
- struct RedClient *client,
- uint8_t id, int window_size);
-gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
- struct RedClient *client,
- uint8_t id,
- GlzEncDictRestoreData *restore_data);
-
-typedef struct {
- RedCompressBuf *bufs_head;
- RedCompressBuf *bufs_tail;
- jmp_buf jmp_env;
- union {
- struct {
- SpiceChunks *chunks;
- int next;
- int stride;
- int reverse;
- } lines_data;
- struct {
- RedCompressBuf* next;
- int size_left;
- } compressed_data; // for encoding data that was already compressed by another method
- } u;
-} EncoderData;
-
-typedef struct {
- QuicUsrContext usr;
- EncoderData data;
-} QuicData;
-
-typedef struct {
- LzUsrContext usr;
- EncoderData data;
-} LzData;
-
-typedef struct {
- JpegEncoderUsrContext usr;
- EncoderData data;
-} JpegData;
-
-#ifdef USE_LZ4
-typedef struct {
- Lz4EncoderUsrContext usr;
- EncoderData data;
-} Lz4Data;
-#endif
-
-typedef struct {
- ZlibEncoderUsrContext usr;
- EncoderData data;
-} ZlibData;
-
-typedef struct {
- GlzEncoderUsrContext usr;
- EncoderData data;
-} GlzData;
-
-struct GlzImageRetention {
- Ring ring;
-};
-
-static inline void glz_retention_init(GlzImageRetention *ret)
-{
- ring_init(&ret->ring);
-}
-
-struct ImageEncoderSharedData {
- uint32_t glz_drawable_count;
-
- stat_info_t off_stat;
- stat_info_t lz_stat;
- stat_info_t glz_stat;
- stat_info_t quic_stat;
- stat_info_t jpeg_stat;
- stat_info_t zlib_glz_stat;
- stat_info_t jpeg_alpha_stat;
- stat_info_t lz4_stat;
-};
-
-struct ImageEncoders {
- ImageEncoderSharedData *shared_data;
-
- QuicData quic_data;
- QuicContext *quic;
-
- LzData lz_data;
- LzContext *lz;
-
- int jpeg_quality;
-
- JpegData jpeg_data;
- JpegEncoderContext *jpeg;
-
-#ifdef USE_LZ4
- Lz4Data lz4_data;
- Lz4EncoderContext *lz4;
-#endif
-
- int zlib_level;
-
- ZlibData zlib_data;
- ZlibEncoder *zlib;
-
- /* global lz encoding entities */
- GlzSharedDictionary *glz_dict;
- GlzEncoderContext *glz;
- GlzData glz_data;
-
- Ring glz_drawables; // all the living lz drawable, ordered by encoding time
- Ring glz_drawables_inst_to_free; // list of instances to be freed
- pthread_mutex_t glz_drawables_inst_to_free_lock;
-};
-
-typedef struct compress_send_data_t {
- void* comp_buf;
- uint32_t comp_buf_size;
- SpicePalette *lzplt_palette;
- int is_lossy;
-} compress_send_data_t;
-
-int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_lz(ImageEncoders *enc,
- SpiceImage *dest, SpiceBitmap *src,
- compress_send_data_t* o_comp_data);
-int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
- SpiceBitmap *src, compress_send_data_t* o_comp_data);
-int image_encoders_compress_glz(ImageEncoders *enc,
- SpiceImage *dest, SpiceBitmap *src,
- RedDrawable *red_drawable,
- GlzImageRetention *glz_retention,
- compress_send_data_t* o_comp_data,
- gboolean enable_zlib_glz_wrap);
-
-#define RED_RELEASE_BUNCH_SIZE 64
-
-#endif /* DCC_ENCODERS_H_ */
diff --git a/server/dcc.h b/server/dcc.h
index 0c19c02..5f7b16f 100644
--- a/server/dcc.h
+++ b/server/dcc.h
@@ -21,7 +21,7 @@
#include "red-worker.h"
#include "pixmap-cache.h"
#include "cache-item.h"
-#include "dcc-encoders.h"
+#include "image-encoders.h"
#include "stream.h"
#include "display-limits.h"
diff --git a/server/image-encoders.c b/server/image-encoders.c
new file mode 100644
index 0000000..b47ae35
--- /dev/null
+++ b/server/image-encoders.c
@@ -0,0 +1,1388 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009-2015 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "image-encoders.h"
+#include "spice-bitmap-utils.h"
+#include "red-worker.h" // red_drawable_unref
+#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
+
+#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
+
+#define ENCODER_MESSAGE_SIZE 512
+
+#define MAX_GLZ_DRAWABLE_INSTANCES 2
+
+typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
+
+struct GlzSharedDictionary {
+ RingItem base;
+ GlzEncDictContext *dict;
+ uint32_t refs;
+ uint8_t id;
+ pthread_rwlock_t encode_lock;
+ int migrate_freeze;
+ RedClient *client; // channel clients of the same client share the dict
+};
+
+/* for each qxl drawable, there may be several instances of lz drawables */
+/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
+ * at the Drawable by keeping a ring, so:
+ * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
+ * and it should probably (but need to be sure...) be
+ * Drawable -> ring of GlzDrawableInstanceItem.
+ */
+struct GlzDrawableInstanceItem {
+ RingItem glz_link;
+ RingItem free_link;
+ GlzEncDictImageContext *context;
+ RedGlzDrawable *glz_drawable;
+};
+
+struct RedGlzDrawable {
+ RingItem link; // ordered by the time it was encoded
+ RingItem drawable_link;
+ RedDrawable *red_drawable;
+ GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
+ Ring instances;
+ uint8_t instances_count;
+ gboolean has_drawable;
+ ImageEncoders *encoders;
+};
+
+#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
+ drawable_link)
+#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
+ SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz, LINK_TO_GLZ(link))
+
+static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance);
+static void encoder_data_init(EncoderData *data);
+static void encoder_data_reset(EncoderData *data);
+static void image_encoders_release_glz(ImageEncoders *enc);
+
+
+static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
+quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
+{
+ EncoderData *usr_data = &(((QuicData *)usr)->data);
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+ spice_critical("%s", message_buf);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
+lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
+{
+ EncoderData *usr_data = &(((LzData *)usr)->data);
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+ spice_critical("%s", message_buf);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
+{
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+
+ spice_critical("%s", message_buf); // if global lz fails in the middle
+ // the consequences are not predictable since the window
+ // can turn to be unsynchronized between the server and
+ // and the client
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
+{
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+ spice_warning("%s", message_buf);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
+{
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+ spice_warning("%s", message_buf);
+}
+
+static SPICE_GNUC_PRINTF(2, 3) void
+glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
+{
+ va_list ap;
+ char message_buf[ENCODER_MESSAGE_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
+ va_end(ap);
+ spice_warning("%s", message_buf);
+}
+
+static void *quic_usr_malloc(QuicUsrContext *usr, int size)
+{
+ return spice_malloc(size);
+}
+
+static void *lz_usr_malloc(LzUsrContext *usr, int size)
+{
+ return spice_malloc(size);
+}
+
+static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
+{
+ return spice_malloc(size);
+}
+
+static void quic_usr_free(QuicUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static void lz_usr_free(LzUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static void encoder_data_init(EncoderData *data)
+{
+ data->bufs_tail = g_new(RedCompressBuf, 1);
+ data->bufs_head = data->bufs_tail;
+ data->bufs_head->send_next = NULL;
+}
+
+static void encoder_data_reset(EncoderData *data)
+{
+ RedCompressBuf *buf = data->bufs_head;
+ while (buf) {
+ RedCompressBuf *next = buf->send_next;
+ g_free(buf);
+ buf = next;
+ }
+ data->bufs_head = data->bufs_tail = NULL;
+}
+
+/* Allocate more space for compressed buffer.
+ * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
+ */
+static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
+{
+ RedCompressBuf *buf;
+
+ buf = g_new(RedCompressBuf, 1);
+ enc_data->bufs_tail->send_next = buf;
+ enc_data->bufs_tail = buf;
+ buf->send_next = NULL;
+ *io_ptr = buf->buf.bytes;
+ return sizeof(buf->buf);
+}
+
+static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
+{
+ EncoderData *usr_data = &(((QuicData *)usr)->data);
+ return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) / sizeof(uint32_t);
+}
+
+static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((LzData *)usr)->data);
+ return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((GlzData *)usr)->data);
+ return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+#ifdef USE_LZ4
+static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+ return encoder_usr_more_space(usr_data, io_ptr);
+}
+#endif
+
+static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
+{
+ EncoderData *usr_data = &(((ZlibData *)usr)->data);
+ return encoder_usr_more_space(usr_data, io_ptr);
+}
+
+static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t **lines)
+{
+ struct SpiceChunk *chunk;
+
+ if (enc_data->u.lines_data.reverse) {
+ if (!(enc_data->u.lines_data.next >= 0)) {
+ return 0;
+ }
+ } else {
+ if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks->num_chunks)) {
+ return 0;
+ }
+ }
+
+ chunk = &enc_data->u.lines_data.chunks->chunk[enc_data->u.lines_data.next];
+ if (chunk->len % enc_data->u.lines_data.stride) {
+ return 0;
+ }
+
+ if (enc_data->u.lines_data.reverse) {
+ enc_data->u.lines_data.next--;
+ *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride;
+ } else {
+ enc_data->u.lines_data.next++;
+ *lines = chunk->data;
+ }
+
+ return chunk->len / enc_data->u.lines_data.stride;
+}
+
+static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((QuicData *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((LzData *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((GlzData *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+
+static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((JpegData *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+
+#ifdef USE_LZ4
+static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
+{
+ EncoderData *usr_data = &(((Lz4Data *)usr)->data);
+ return encoder_usr_more_lines(usr_data, lines);
+}
+#endif
+
+static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
+{
+ EncoderData *usr_data = &(((ZlibData *)usr)->data);
+ int buf_size;
+
+ if (!usr_data->u.compressed_data.next) {
+ spice_assert(usr_data->u.compressed_data.size_left == 0);
+ return 0;
+ }
+
+ *input = usr_data->u.compressed_data.next->buf.bytes;
+ buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
+ usr_data->u.compressed_data.size_left);
+
+ usr_data->u.compressed_data.next = usr_data->u.compressed_data.next->send_next;
+ usr_data->u.compressed_data.size_left -= buf_size;
+ return buf_size;
+}
+
+static void image_encoders_init_quic(ImageEncoders *enc)
+{
+ enc->quic_data.usr.error = quic_usr_error;
+ enc->quic_data.usr.warn = quic_usr_warn;
+ enc->quic_data.usr.info = quic_usr_warn;
+ enc->quic_data.usr.malloc = quic_usr_malloc;
+ enc->quic_data.usr.free = quic_usr_free;
+ enc->quic_data.usr.more_space = quic_usr_more_space;
+ enc->quic_data.usr.more_lines = quic_usr_more_lines;
+
+ enc->quic = quic_create(&enc->quic_data.usr);
+
+ if (!enc->quic) {
+ spice_critical("create quic failed");
+ }
+}
+
+static void image_encoders_init_lz(ImageEncoders *enc)
+{
+ enc->lz_data.usr.error = lz_usr_error;
+ enc->lz_data.usr.warn = lz_usr_warn;
+ enc->lz_data.usr.info = lz_usr_warn;
+ enc->lz_data.usr.malloc = lz_usr_malloc;
+ enc->lz_data.usr.free = lz_usr_free;
+ enc->lz_data.usr.more_space = lz_usr_more_space;
+ enc->lz_data.usr.more_lines = lz_usr_more_lines;
+
+ enc->lz = lz_create(&enc->lz_data.usr);
+
+ if (!enc->lz) {
+ spice_critical("create lz failed");
+ }
+}
+
+static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext *image)
+{
+ GlzData *lz_data = (GlzData *)usr;
+ GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem *)image;
+ ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable->encoders;
+ ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders, glz_data);
+ if (this_enc == drawable_enc) {
+ glz_drawable_instance_item_free(glz_drawable_instance);
+ } else {
+ /* The glz dictionary is shared between all DisplayChannelClient
+ * instances that belong to the same client, and glz_usr_free_image
+ * can be called by the dictionary code
+ * (glz_dictionary_window_remove_head). Thus this function can be
+ * called from any DisplayChannelClient thread, hence the need for
+ * this check.
+ */
+ pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
+ ring_add_before(&glz_drawable_instance->free_link,
+ &drawable_enc->glz_drawables_inst_to_free);
+ pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
+ }
+}
+
+static void image_encoders_init_glz_data(ImageEncoders *enc)
+{
+ enc->glz_data.usr.error = glz_usr_error;
+ enc->glz_data.usr.warn = glz_usr_warn;
+ enc->glz_data.usr.info = glz_usr_warn;
+ enc->glz_data.usr.malloc = glz_usr_malloc;
+ enc->glz_data.usr.free = glz_usr_free;
+ enc->glz_data.usr.more_space = glz_usr_more_space;
+ enc->glz_data.usr.more_lines = glz_usr_more_lines;
+ enc->glz_data.usr.free_image = glz_usr_free_image;
+}
+
+static void image_encoders_init_jpeg(ImageEncoders *enc)
+{
+ enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
+ enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
+
+ enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
+
+ if (!enc->jpeg) {
+ spice_critical("create jpeg encoder failed");
+ }
+}
+
+#ifdef USE_LZ4
+static inline void image_encoders_init_lz4(ImageEncoders *enc)
+{
+ enc->lz4_data.usr.more_space = lz4_usr_more_space;
+ enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
+
+ enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
+
+ if (!enc->lz4) {
+ spice_critical("create lz4 encoder failed");
+ }
+}
+#endif
+
+static void image_encoders_init_zlib(ImageEncoders *enc)
+{
+ enc->zlib_data.usr.more_space = zlib_usr_more_space;
+ enc->zlib_data.usr.more_input = zlib_usr_more_input;
+
+ enc->zlib = zlib_encoder_create(&enc->zlib_data.usr, ZLIB_DEFAULT_COMPRESSION_LEVEL);
+
+ if (!enc->zlib) {
+ spice_critical("create zlib encoder failed");
+ }
+}
+
+void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data)
+{
+ spice_assert(shared_data);
+ enc->shared_data = shared_data;
+
+ ring_init(&enc->glz_drawables);
+ ring_init(&enc->glz_drawables_inst_to_free);
+ pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
+
+ image_encoders_init_glz_data(enc);
+ image_encoders_init_quic(enc);
+ image_encoders_init_lz(enc);
+ image_encoders_init_jpeg(enc);
+#ifdef USE_LZ4
+ image_encoders_init_lz4(enc);
+#endif
+ image_encoders_init_zlib(enc);
+
+ // todo: tune level according to bandwidth
+ enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
+}
+
+void image_encoders_free(ImageEncoders *enc)
+{
+ image_encoders_release_glz(enc);
+ quic_destroy(enc->quic);
+ enc->quic = NULL;
+ lz_destroy(enc->lz);
+ enc->lz = NULL;
+ jpeg_encoder_destroy(enc->jpeg);
+ enc->jpeg = NULL;
+#ifdef USE_LZ4
+ lz4_encoder_destroy(enc->lz4);
+ enc->lz4 = NULL;
+#endif
+ zlib_encoder_destroy(enc->zlib);
+ enc->zlib = NULL;
+}
+
+/* Remove from the to_free list and the instances_list.
+ When no instance is left - the RedGlzDrawable is released too. (and the qxl drawable too, if
+ it is not used by Drawable).
+ NOTE - 1) can be called only by the display channel that created the drawable
+ 2) it is assumed that the instance was already removed from the dictionary*/
+static void glz_drawable_instance_item_free(GlzDrawableInstanceItem *instance)
+{
+ RedGlzDrawable *glz_drawable;
+
+ spice_assert(instance);
+ spice_assert(instance->glz_drawable);
+
+ glz_drawable = instance->glz_drawable;
+
+ spice_assert(glz_drawable->instances_count > 0);
+
+ ring_remove(&instance->glz_link);
+ glz_drawable->instances_count--;
+
+ // when the remove callback is performed from the channel that the
+ // drawable belongs to, the instance is not added to the 'to_free' list
+ if (ring_item_is_linked(&instance->free_link)) {
+ ring_remove(&instance->free_link);
+ }
+
+ if (ring_is_empty(&glz_drawable->instances)) {
+ spice_assert(glz_drawable->instances_count == 0);
+
+ if (glz_drawable->has_drawable) {
+ ring_remove(&glz_drawable->drawable_link);
+ }
+ red_drawable_unref(glz_drawable->red_drawable);
+ glz_drawable->encoders->shared_data->glz_drawable_count--;
+ if (ring_item_is_linked(&glz_drawable->link)) {
+ ring_remove(&glz_drawable->link);
+ }
+ free(glz_drawable);
+ }
+}
+
+/*
+ * Releases all the instances of the drawable from the dictionary and the display channel client.
+ * The release of the last instance will also release the drawable itself and the qxl drawable
+ * if possible.
+ * NOTE - the caller should prevent encoding using the dictionary during this operation
+ */
+static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
+{
+ ImageEncoders *enc = glz_drawable->encoders;
+ RingItem *head_instance = ring_get_head(&glz_drawable->instances);
+ int cont = (head_instance != NULL);
+
+ while (cont) {
+ if (glz_drawable->instances_count == 1) {
+ /* Last instance: glz_drawable_instance_item_free will free the glz_drawable */
+ cont = FALSE;
+ }
+ GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
+ GlzDrawableInstanceItem,
+ glz_link);
+ if (!ring_item_is_linked(&instance->free_link)) {
+ // the instance didn't get out from window yet
+ glz_enc_dictionary_remove_image(enc->glz_dict->dict,
+ instance->context,
+ &enc->glz_data.usr);
+ }
+ glz_drawable_instance_item_free(instance);
+
+ if (cont) {
+ head_instance = ring_get_head(&glz_drawable->instances);
+ }
+ }
+}
+
+gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
+{
+ if (enc->glz_dict) {
+ pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void image_encoders_glz_encode_unlock(ImageEncoders *enc)
+{
+ if (enc->glz_dict) {
+ pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+ }
+}
+
+/*
+ * Remove from the global lz dictionary some glz_drawables that have no reference to
+ * Drawable (their qxl drawables are released too).
+ * NOTE - the caller should prevent encoding using the dictionary during the operation
+ */
+int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
+{
+ RingItem *ring_link;
+ int n = 0;
+
+ if (!enc) {
+ return 0;
+ }
+ ring_link = ring_get_head(&enc->glz_drawables);
+ while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
+ RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
+ ring_link = ring_next(&enc->glz_drawables, ring_link);
+ if (!glz_drawable->has_drawable) {
+ red_glz_drawable_free(glz_drawable);
+ n++;
+ }
+ }
+ return n;
+}
+
+void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
+{
+ RingItem *ring_link;
+
+ if (!enc->glz_dict) {
+ return;
+ }
+ pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
+ while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
+ GlzDrawableInstanceItem *drawable_instance = SPICE_CONTAINEROF(ring_link,
+ GlzDrawableInstanceItem,
+ free_link);
+ glz_drawable_instance_item_free(drawable_instance);
+ }
+ pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
+}
+
+/* Clear all lz drawables - enforce their removal from the global dictionary.
+ NOTE - prevents encoding using the dictionary during the operation*/
+void image_encoders_free_glz_drawables(ImageEncoders *enc)
+{
+ RingItem *ring_link;
+ GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
+
+ if (!glz_dict) {
+ return;
+ }
+
+ // assure no display channel is during global lz encoding
+ pthread_rwlock_wrlock(&glz_dict->encode_lock);
+ while ((ring_link = ring_get_head(&enc->glz_drawables))) {
+ RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link, RedGlzDrawable, link);
+ // no need to lock the to_free list, since we assured no other thread is encoding and
+ // thus not other thread access the to_free list of the channel
+ red_glz_drawable_free(drawable);
+ }
+ pthread_rwlock_unlock(&glz_dict->encode_lock);
+}
+
+void glz_retention_free_drawables(GlzImageRetention *ret)
+{
+ RingItem *glz_item, *next_item;
+ RedGlzDrawable *glz;
+ SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz, LINK_TO_GLZ(glz_item)) {
+ red_glz_drawable_free(glz);
+ }
+}
+
+void glz_retention_detach_drawables(GlzImageRetention *ret)
+{
+ RingItem *item, *next;
+
+ RING_FOREACH_SAFE(item, next, &ret->ring) {
+ SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable = FALSE;
+ ring_remove(item);
+ }
+}
+
+static void image_encoders_freeze_glz(ImageEncoders *enc)
+{
+ pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
+ enc->glz_dict->migrate_freeze = TRUE;
+ pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+}
+
+void image_encoders_glz_get_restore_data(ImageEncoders *enc,
+ uint8_t *out_id, GlzEncDictRestoreData *out_data)
+{
+ spice_assert(enc->glz_dict);
+ image_encoders_freeze_glz(enc);
+ *out_id = enc->glz_dict->id;
+ glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
+ &enc->glz_data.usr);
+}
+
+static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client, uint8_t id,
+ GlzEncDictContext *dict)
+{
+ spice_return_val_if_fail(dict != NULL, NULL);
+
+ GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
+
+ shared_dict->dict = dict;
+ shared_dict->id = id;
+ shared_dict->refs = 1;
+ shared_dict->migrate_freeze = FALSE;
+ shared_dict->client = client;
+ ring_item_init(&shared_dict->base);
+ pthread_rwlock_init(&shared_dict->encode_lock, NULL);
+
+ return shared_dict;
+}
+
+static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
+
+static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t dict_id)
+{
+ RingItem *now;
+ GlzSharedDictionary *ret = NULL;
+
+ now = &glz_dictionary_list;
+ while ((now = ring_next(&glz_dictionary_list, now))) {
+ GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
+ if ((dict->client == client) && (dict->id == dict_id)) {
+ ret = dict;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
+
+static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
+ RedClient *client,
+ uint8_t id, int window_size)
+{
+ spice_info("Lz Window %d Size=%d", id, window_size);
+
+ GlzEncDictContext *glz_dict =
+ glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc->glz_data.usr);
+
+ return glz_shared_dictionary_new(client, id, glz_dict);
+}
+
+gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
+ RedClient *client,
+ uint8_t id, int window_size)
+{
+ GlzSharedDictionary *shared_dict;
+
+ spice_return_val_if_fail(!enc->glz_dict, FALSE);
+
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+
+ shared_dict = find_glz_dictionary(client, id);
+ if (shared_dict) {
+ shared_dict->refs++;
+ } else {
+ shared_dict = create_glz_dictionary(enc, client, id, window_size);
+ ring_add(&glz_dictionary_list, &shared_dict->base);
+ }
+
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ enc->glz_dict = shared_dict;
+ return shared_dict != NULL;
+}
+
+static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
+ RedClient *client,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data)
+{
+ GlzEncDictContext *glz_dict =
+ glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
+
+ return glz_shared_dictionary_new(client, id, glz_dict);
+}
+
+gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
+ RedClient *client,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data)
+{
+ GlzSharedDictionary *shared_dict = NULL;
+
+ spice_return_val_if_fail(!enc->glz_dict, FALSE);
+
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+
+ shared_dict = find_glz_dictionary(client, id);
+
+ if (shared_dict) {
+ shared_dict->refs++;
+ } else {
+ shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
+ ring_add(&glz_dictionary_list, &shared_dict->base);
+ }
+
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ enc->glz_dict = shared_dict;
+ return shared_dict != NULL;
+}
+
+gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
+{
+ enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc->glz_data.usr);
+ return enc->glz != NULL;
+}
+
+/* destroy encoder, and dictionary if no one uses it*/
+static void image_encoders_release_glz(ImageEncoders *enc)
+{
+ GlzSharedDictionary *shared_dict;
+
+ image_encoders_free_glz_drawables(enc);
+
+ glz_encoder_destroy(enc->glz);
+ enc->glz = NULL;
+
+ if (!(shared_dict = enc->glz_dict)) {
+ return;
+ }
+
+ enc->glz_dict = NULL;
+ pthread_mutex_lock(&glz_dictionary_list_lock);
+ if (--shared_dict->refs != 0) {
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ return;
+ }
+ ring_remove(&shared_dict->base);
+ pthread_mutex_unlock(&glz_dictionary_list_lock);
+ glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
+ free(shared_dict);
+}
+
+int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+ QuicData *quic_data = &enc->quic_data;
+ QuicContext *quic = enc->quic;
+ volatile QuicImageType type;
+ int size, stride;
+ stat_start_time_t start_time;
+ stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
+
+#ifdef COMPRESS_DEBUG
+ spice_info("QUIC compress");
+#endif
+
+ switch (src->format) {
+ case SPICE_BITMAP_FMT_32BIT:
+ type = QUIC_IMAGE_TYPE_RGB32;
+ break;
+ case SPICE_BITMAP_FMT_RGBA:
+ type = QUIC_IMAGE_TYPE_RGBA;
+ break;
+ case SPICE_BITMAP_FMT_16BIT:
+ type = QUIC_IMAGE_TYPE_RGB16;
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+ type = QUIC_IMAGE_TYPE_RGB24;
+ break;
+ default:
+ return FALSE;
+ }
+
+ encoder_data_init(&quic_data->data);
+
+ if (setjmp(quic_data->data.jmp_env)) {
+ encoder_data_reset(&quic_data->data);
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ quic_data->data.u.lines_data.chunks = src->data;
+ quic_data->data.u.lines_data.stride = src->stride;
+ if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ quic_data->data.u.lines_data.next = 0;
+ quic_data->data.u.lines_data.reverse = 0;
+ stride = src->stride;
+ } else {
+ quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
+ quic_data->data.u.lines_data.reverse = 1;
+ stride = -src->stride;
+ }
+ size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
+ quic_data->data.bufs_head->buf.words,
+ G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
+
+ // the compressed buffer is bigger than the original data
+ if ((size << 2) > (src->y * src->stride)) {
+ longjmp(quic_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
+ dest->u.quic.data_size = size << 2;
+
+ o_comp_data->comp_buf = quic_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size << 2;
+
+ stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+static const LzImageType bitmap_fmt_to_lz_image_type[] = {
+ LZ_IMAGE_TYPE_INVALID,
+ LZ_IMAGE_TYPE_PLT1_LE,
+ LZ_IMAGE_TYPE_PLT1_BE,
+ LZ_IMAGE_TYPE_PLT4_LE,
+ LZ_IMAGE_TYPE_PLT4_BE,
+ LZ_IMAGE_TYPE_PLT8,
+ LZ_IMAGE_TYPE_RGB16,
+ LZ_IMAGE_TYPE_RGB24,
+ LZ_IMAGE_TYPE_RGB32,
+ LZ_IMAGE_TYPE_RGBA,
+ LZ_IMAGE_TYPE_A8
+};
+
+int image_encoders_compress_lz(ImageEncoders *enc,
+ SpiceImage *dest, SpiceBitmap *src,
+ compress_send_data_t* o_comp_data)
+{
+ LzData *lz_data = &enc->lz_data;
+ LzContext *lz = enc->lz;
+ LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
+ int size; // size of the compressed data
+
+ stat_start_time_t start_time;
+ stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
+
+#ifdef COMPRESS_DEBUG
+ spice_info("LZ LOCAL compress");
+#endif
+
+ encoder_data_init(&lz_data->data);
+
+ if (setjmp(lz_data->data.jmp_env)) {
+ encoder_data_reset(&lz_data->data);
+ return FALSE;
+ }
+
+ lz_data->data.u.lines_data.chunks = src->data;
+ lz_data->data.u.lines_data.stride = src->stride;
+ lz_data->data.u.lines_data.next = 0;
+ lz_data->data.u.lines_data.reverse = 0;
+
+ size = lz_encode(lz, type, src->x, src->y,
+ !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+ NULL, 0, src->stride,
+ lz_data->data.bufs_head->buf.bytes,
+ sizeof(lz_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (size > (src->y * src->stride)) {
+ longjmp(lz_data->data.jmp_env, 1);
+ }
+
+ if (bitmap_fmt_is_rgb(src->format)) {
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
+ dest->u.lz_rgb.data_size = size;
+
+ o_comp_data->comp_buf = lz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size;
+ } else {
+ /* masks are 1BIT bitmaps without palettes, but they are not compressed
+ * (see fill_mask) */
+ spice_assert(src->palette);
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
+ dest->u.lz_plt.data_size = size;
+ dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
+ dest->u.lz_plt.palette = src->palette;
+ dest->u.lz_plt.palette_id = src->palette->unique;
+ o_comp_data->comp_buf = lz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = size;
+
+ o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
+ }
+
+ stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+ JpegData *jpeg_data = &enc->jpeg_data;
+ LzData *lz_data = &enc->lz_data;
+ JpegEncoderContext *jpeg = enc->jpeg;
+ LzContext *lz = enc->lz;
+ volatile JpegEncoderImageType jpeg_in_type;
+ int jpeg_size = 0;
+ volatile int has_alpha = FALSE;
+ int alpha_lz_size = 0;
+ int comp_head_filled;
+ int comp_head_left;
+ int stride;
+ uint8_t *lz_out_start_byte;
+ stat_start_time_t start_time;
+ stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
+
+#ifdef COMPRESS_DEBUG
+ spice_info("JPEG compress");
+#endif
+
+ switch (src->format) {
+ case SPICE_BITMAP_FMT_16BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
+ break;
+ case SPICE_BITMAP_FMT_32BIT:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+ break;
+ case SPICE_BITMAP_FMT_RGBA:
+ jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
+ has_alpha = TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+
+ encoder_data_init(&jpeg_data->data);
+
+ if (setjmp(jpeg_data->data.jmp_env)) {
+ encoder_data_reset(&jpeg_data->data);
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ jpeg_data->data.u.lines_data.chunks = src->data;
+ jpeg_data->data.u.lines_data.stride = src->stride;
+ if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ jpeg_data->data.u.lines_data.next = 0;
+ jpeg_data->data.u.lines_data.reverse = 0;
+ stride = src->stride;
+ } else {
+ jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
+ jpeg_data->data.u.lines_data.reverse = 1;
+ stride = -src->stride;
+ }
+ jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type,
+ src->x, src->y, NULL,
+ 0, stride, jpeg_data->data.bufs_head->buf.bytes,
+ sizeof(jpeg_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (jpeg_size > (src->y * src->stride)) {
+ longjmp(jpeg_data->data.jmp_env, 1);
+ }
+
+ if (!has_alpha) {
+ dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
+ dest->u.jpeg.data_size = jpeg_size;
+
+ o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+ o_comp_data->comp_buf_size = jpeg_size;
+ o_comp_data->is_lossy = TRUE;
+
+ stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+ }
+
+ lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
+ lz_data->data.bufs_tail = lz_data->data.bufs_head;
+
+ comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
+ comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
+ lz_out_start_byte = lz_data->data.bufs_head->buf.bytes + comp_head_filled;
+
+ lz_data->data.u.lines_data.chunks = src->data;
+ lz_data->data.u.lines_data.stride = src->stride;
+ lz_data->data.u.lines_data.next = 0;
+ lz_data->data.u.lines_data.reverse = 0;
+
+ alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
+ !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
+ NULL, 0, src->stride,
+ lz_out_start_byte,
+ comp_head_left);
+
+ // the compressed buffer is bigger than the original data
+ if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
+ longjmp(jpeg_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
+ dest->u.jpeg_alpha.flags = 0;
+ if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
+ dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
+ }
+
+ dest->u.jpeg_alpha.jpeg_size = jpeg_size;
+ dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
+
+ o_comp_data->comp_buf = jpeg_data->data.bufs_head;
+ o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
+ o_comp_data->is_lossy = TRUE;
+ stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+
+#ifdef USE_LZ4
+int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data)
+{
+ Lz4Data *lz4_data = &enc->lz4_data;
+ Lz4EncoderContext *lz4 = enc->lz4;
+ int lz4_size = 0;
+ stat_start_time_t start_time;
+ stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
+
+#ifdef COMPRESS_DEBUG
+ spice_info("LZ4 compress");
+#endif
+
+ encoder_data_init(&lz4_data->data);
+
+ if (setjmp(lz4_data->data.jmp_env)) {
+ encoder_data_reset(&lz4_data->data);
+ return FALSE;
+ }
+
+ if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
+ spice_chunks_linearize(src->data);
+ }
+
+ lz4_data->data.u.lines_data.chunks = src->data;
+ lz4_data->data.u.lines_data.stride = src->stride;
+ lz4_data->data.u.lines_data.next = 0;
+ lz4_data->data.u.lines_data.reverse = 0;
+
+ lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head->buf.bytes,
+ sizeof(lz4_data->data.bufs_head->buf),
+ src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src->format);
+
+ // the compressed buffer is bigger than the original data
+ if (lz4_size > (src->y * src->stride)) {
+ longjmp(lz4_data->data.jmp_env, 1);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
+ dest->u.lz4.data_size = lz4_size;
+
+ o_comp_data->comp_buf = lz4_data->data.bufs_head;
+ o_comp_data->comp_buf_size = lz4_size;
+
+ stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+#endif
+
+/* if already exists, returns it. Otherwise allocates and adds it (1) to the ring tail
+ in the channel (2) to the Drawable*/
+static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable *red_drawable,
+ GlzImageRetention *glz_retention)
+{
+ RedGlzDrawable *ret;
+ RingItem *item, *next;
+
+ // TODO - I don't really understand what's going on here, so doing the technical equivalent
+ // now that we have multiple glz_dicts, so the only way to go from dcc to drawable glz is to go
+ // over the glz_ring (unless adding some better data structure then a ring)
+ SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret, LINK_TO_GLZ(item)) {
+ if (ret->encoders == enc) {
+ return ret;
+ }
+ }
+
+ ret = spice_new(RedGlzDrawable, 1);
+
+ ret->encoders = enc;
+ ret->red_drawable = red_drawable_ref(red_drawable);
+ ret->has_drawable = TRUE;
+ ret->instances_count = 0;
+ ring_init(&ret->instances);
+
+ ring_item_init(&ret->link);
+ ring_item_init(&ret->drawable_link);
+ ring_add_before(&ret->link, &enc->glz_drawables);
+ ring_add(&glz_retention->ring, &ret->drawable_link);
+ enc->shared_data->glz_drawable_count++;
+ return ret;
+}
+
+/* allocates new instance and adds it to instances in the given drawable.
+ NOTE - the caller should set the glz_instance returned by the encoder by itself.*/
+static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable *glz_drawable)
+{
+ spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
+ // NOTE: We assume the additions are performed consecutively, without removals in the middle
+ GlzDrawableInstanceItem *ret = glz_drawable->instances_pool + glz_drawable->instances_count;
+ glz_drawable->instances_count++;
+
+ ring_item_init(&ret->free_link);
+ ring_item_init(&ret->glz_link);
+ ring_add(&glz_drawable->instances, &ret->glz_link);
+ ret->context = NULL;
+ ret->glz_drawable = glz_drawable;
+
+ return ret;
+}
+
+#define MIN_GLZ_SIZE_FOR_ZLIB 100
+
+int image_encoders_compress_glz(ImageEncoders *enc,
+ SpiceImage *dest, SpiceBitmap *src,
+ RedDrawable *red_drawable,
+ GlzImageRetention *glz_retention,
+ compress_send_data_t* o_comp_data,
+ gboolean enable_zlib_glz_wrap)
+{
+ stat_start_time_t start_time;
+ stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
+ spice_assert(bitmap_fmt_is_rgb(src->format));
+ GlzData *glz_data = &enc->glz_data;
+ ZlibData *zlib_data;
+ LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
+ RedGlzDrawable *glz_drawable;
+ GlzDrawableInstanceItem *glz_drawable_instance;
+ int glz_size;
+ int zlib_size;
+
+#ifdef COMPRESS_DEBUG
+ spice_info("LZ global compress fmt=%d", src->format);
+#endif
+
+ if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict->dict)) {
+ return FALSE;
+ }
+
+ pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
+ /* using the global dictionary only if it is not frozen */
+ if (enc->glz_dict->migrate_freeze) {
+ pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+ return FALSE;
+ }
+
+ encoder_data_init(&glz_data->data);
+
+ glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
+ glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
+
+ glz_data->data.u.lines_data.chunks = src->data;
+ glz_data->data.u.lines_data.stride = src->stride;
+ glz_data->data.u.lines_data.next = 0;
+ glz_data->data.u.lines_data.reverse = 0;
+
+ glz_size = glz_encode(enc->glz, type, src->x, src->y,
+ (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL, 0,
+ src->stride, glz_data->data.bufs_head->buf.bytes,
+ sizeof(glz_data->data.bufs_head->buf),
+ glz_drawable_instance,
+ &glz_drawable_instance->context);
+
+ stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride * src->y, glz_size);
+
+ if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
+ goto glz;
+ }
+ stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
+ zlib_data = &enc->zlib_data;
+
+ encoder_data_init(&zlib_data->data);
+
+ zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
+ zlib_data->data.u.compressed_data.size_left = glz_size;
+
+ zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
+ glz_size, zlib_data->data.bufs_head->buf.bytes,
+ sizeof(zlib_data->data.bufs_head->buf));
+
+ // the compressed buffer is bigger than the original data
+ if (zlib_size >= glz_size) {
+ encoder_data_reset(&zlib_data->data);
+ goto glz;
+ } else {
+ encoder_data_reset(&glz_data->data);
+ }
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
+ dest->u.zlib_glz.glz_data_size = glz_size;
+ dest->u.zlib_glz.data_size = zlib_size;
+
+ o_comp_data->comp_buf = zlib_data->data.bufs_head;
+ o_comp_data->comp_buf_size = zlib_size;
+
+ stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size, zlib_size);
+ pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+ return TRUE;
+
+glz:
+ pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
+
+ dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
+ dest->u.lz_rgb.data_size = glz_size;
+
+ o_comp_data->comp_buf = glz_data->data.bufs_head;
+ o_comp_data->comp_buf_size = glz_size;
+
+ return TRUE;
+}
+
+void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
+{
+ clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
+
+ stat_compress_init(&shared_data->off_stat, "off", stat_clock);
+ stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
+ stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
+ stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
+ stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
+ stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
+ stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha", stat_clock);
+ stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
+}
+
+void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
+{
+ stat_reset(&shared_data->off_stat);
+ stat_reset(&shared_data->quic_stat);
+ stat_reset(&shared_data->lz_stat);
+ stat_reset(&shared_data->glz_stat);
+ stat_reset(&shared_data->jpeg_stat);
+ stat_reset(&shared_data->zlib_glz_stat);
+ stat_reset(&shared_data->jpeg_alpha_stat);
+ stat_reset(&shared_data->lz4_stat);
+}
+
+#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
+
+#ifdef COMPRESS_STAT
+static void stat_print_one(const char *name, const stat_info_t *stat)
+{
+ spice_info(STAT_FMT, name, stat->count,
+ stat_byte_to_mega(stat->orig_size),
+ stat_byte_to_mega(stat->comp_size),
+ stat_cpu_time_to_sec(stat->total));
+}
+
+static void stat_sum(stat_info_t *total, const stat_info_t *stat)
+{
+ total->count += stat->count;
+ total->orig_size += stat->orig_size;
+ total->comp_size += stat->comp_size;
+ total->total += stat->total;
+}
+#endif
+
+void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data)
+{
+#ifdef COMPRESS_STAT
+ /* sum all statistics */
+ stat_info_t total = {
+ .count = 0,
+ .orig_size = 0,
+ .comp_size = 0,
+ .total = 0
+ };
+ stat_sum(&total, &shared_data->off_stat);
+ stat_sum(&total, &shared_data->quic_stat);
+ stat_sum(&total, &shared_data->glz_stat);
+ stat_sum(&total, &shared_data->lz_stat);
+ stat_sum(&total, &shared_data->jpeg_stat);
+ stat_sum(&total, &shared_data->jpeg_alpha_stat);
+ stat_sum(&total, &shared_data->lz4_stat);
+
+ /* fix for zlib glz */
+ total.total += shared_data->zlib_glz_stat.total;
+ if (shared_data->zlib_glz_stat.count) {
+ total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
+ shared_data->zlib_glz_stat.comp_size;
+ }
+
+ spice_info("Method \t count \torig_size(MB)\tenc_size(MB)\tenc_time(s)");
+ stat_print_one("OFF ", &shared_data->off_stat);
+ stat_print_one("QUIC ", &shared_data->quic_stat);
+ stat_print_one("GLZ ", &shared_data->glz_stat);
+ stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
+ stat_print_one("LZ ", &shared_data->lz_stat);
+ stat_print_one("JPEG ", &shared_data->jpeg_stat);
+ stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
+ stat_print_one("LZ4 ", &shared_data->lz4_stat);
+ spice_info("-------------------------------------------------------------------");
+ stat_print_one("Total ", &total);
+#endif
+}
diff --git a/server/image-encoders.h b/server/image-encoders.h
new file mode 100644
index 0000000..9286970
--- /dev/null
+++ b/server/image-encoders.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009-2015 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DCC_ENCODERS_H_
+#define DCC_ENCODERS_H_
+
+#include <setjmp.h>
+#include <common/quic.h>
+#include <common/lz.h>
+
+#include "stat.h"
+#include "red-parse-qxl.h"
+#include "glz-encoder.h"
+#include "jpeg-encoder.h"
+#ifdef USE_LZ4
+#include "lz4-encoder.h"
+#endif
+#include "zlib-encoder.h"
+
+struct RedClient;
+
+typedef struct RedCompressBuf RedCompressBuf;
+typedef struct RedGlzDrawable RedGlzDrawable;
+typedef struct ImageEncoders ImageEncoders;
+typedef struct ImageEncoderSharedData ImageEncoderSharedData;
+typedef struct GlzSharedDictionary GlzSharedDictionary;
+typedef struct GlzImageRetention GlzImageRetention;
+
+void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
+void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
+void image_encoder_shared_stat_print(const ImageEncoderSharedData *shared_data);
+
+void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData *shared_data);
+void image_encoders_free(ImageEncoders *enc);
+int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
+void image_encoders_free_glz_drawables(ImageEncoders *enc);
+void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
+gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
+void image_encoders_glz_get_restore_data(ImageEncoders *enc,
+ uint8_t *out_id, GlzEncDictRestoreData *out_data);
+gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
+void image_encoders_glz_encode_unlock(ImageEncoders *enc);
+void glz_retention_free_drawables(GlzImageRetention *ret);
+void glz_retention_detach_drawables(GlzImageRetention *ret);
+
+#define RED_COMPRESS_BUF_SIZE (1024 * 64)
+struct RedCompressBuf {
+ /* This buffer provide space for compression algorithms.
+ * Some algorithms access the buffer as an array of 32 bit words
+ * so is defined to make sure is always aligned that way.
+ */
+ union {
+ uint8_t bytes[RED_COMPRESS_BUF_SIZE];
+ uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
+ } buf;
+ RedCompressBuf *send_next;
+};
+
+static inline void compress_buf_free(RedCompressBuf *buf)
+{
+ g_free(buf);
+}
+
+gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
+ struct RedClient *client,
+ uint8_t id, int window_size);
+gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
+ struct RedClient *client,
+ uint8_t id,
+ GlzEncDictRestoreData *restore_data);
+
+typedef struct {
+ RedCompressBuf *bufs_head;
+ RedCompressBuf *bufs_tail;
+ jmp_buf jmp_env;
+ union {
+ struct {
+ SpiceChunks *chunks;
+ int next;
+ int stride;
+ int reverse;
+ } lines_data;
+ struct {
+ RedCompressBuf* next;
+ int size_left;
+ } compressed_data; // for encoding data that was already compressed by another method
+ } u;
+} EncoderData;
+
+typedef struct {
+ QuicUsrContext usr;
+ EncoderData data;
+} QuicData;
+
+typedef struct {
+ LzUsrContext usr;
+ EncoderData data;
+} LzData;
+
+typedef struct {
+ JpegEncoderUsrContext usr;
+ EncoderData data;
+} JpegData;
+
+#ifdef USE_LZ4
+typedef struct {
+ Lz4EncoderUsrContext usr;
+ EncoderData data;
+} Lz4Data;
+#endif
+
+typedef struct {
+ ZlibEncoderUsrContext usr;
+ EncoderData data;
+} ZlibData;
+
+typedef struct {
+ GlzEncoderUsrContext usr;
+ EncoderData data;
+} GlzData;
+
+struct GlzImageRetention {
+ Ring ring;
+};
+
+static inline void glz_retention_init(GlzImageRetention *ret)
+{
+ ring_init(&ret->ring);
+}
+
+struct ImageEncoderSharedData {
+ uint32_t glz_drawable_count;
+
+ stat_info_t off_stat;
+ stat_info_t lz_stat;
+ stat_info_t glz_stat;
+ stat_info_t quic_stat;
+ stat_info_t jpeg_stat;
+ stat_info_t zlib_glz_stat;
+ stat_info_t jpeg_alpha_stat;
+ stat_info_t lz4_stat;
+};
+
+struct ImageEncoders {
+ ImageEncoderSharedData *shared_data;
+
+ QuicData quic_data;
+ QuicContext *quic;
+
+ LzData lz_data;
+ LzContext *lz;
+
+ int jpeg_quality;
+
+ JpegData jpeg_data;
+ JpegEncoderContext *jpeg;
+
+#ifdef USE_LZ4
+ Lz4Data lz4_data;
+ Lz4EncoderContext *lz4;
+#endif
+
+ int zlib_level;
+
+ ZlibData zlib_data;
+ ZlibEncoder *zlib;
+
+ /* global lz encoding entities */
+ GlzSharedDictionary *glz_dict;
+ GlzEncoderContext *glz;
+ GlzData glz_data;
+
+ Ring glz_drawables; // all the living lz drawable, ordered by encoding time
+ Ring glz_drawables_inst_to_free; // list of instances to be freed
+ pthread_mutex_t glz_drawables_inst_to_free_lock;
+};
+
+typedef struct compress_send_data_t {
+ void* comp_buf;
+ uint32_t comp_buf_size;
+ SpicePalette *lzplt_palette;
+ int is_lossy;
+} compress_send_data_t;
+
+int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_lz(ImageEncoders *enc,
+ SpiceImage *dest, SpiceBitmap *src,
+ compress_send_data_t* o_comp_data);
+int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data);
+int image_encoders_compress_glz(ImageEncoders *enc,
+ SpiceImage *dest, SpiceBitmap *src,
+ RedDrawable *red_drawable,
+ GlzImageRetention *glz_retention,
+ compress_send_data_t* o_comp_data,
+ gboolean enable_zlib_glz_wrap);
+
+#define RED_RELEASE_BUNCH_SIZE 64
+
+#endif /* DCC_ENCODERS_H_ */
--
2.7.4
More information about the Spice-devel
mailing list