summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac15
-rw-r--r--server/Makefile.am4
-rw-r--r--server/lz4_encoder.c122
-rw-r--r--server/lz4_encoder.h50
-rw-r--r--server/red_worker.c172
-rw-r--r--server/spice-server.h1
m---------spice-common0
7 files changed, 358 insertions, 6 deletions
diff --git a/configure.ac b/configure.ac
index c9d83e1a..7545ba05 100644
--- a/configure.ac
+++ b/configure.ac
@@ -138,6 +138,12 @@ AC_ARG_ENABLE(opengl,
AS_IF([test x"$enable_opengl" != "xno"], [enable_opengl="yes"])
AM_CONDITIONAL(SUPPORT_GL, test "x$enable_opengl" = "xyes")
+AC_ARG_ENABLE(lz4,
+[ --enable-lz4 Enable lz4 compression algorithm],,
+[enable_lz4="no"])
+AS_IF([test x"$enable_lz4" != "xno"], [enable_lz4="yes"])
+AM_CONDITIONAL(SUPPORT_LZ4, test "x$enable_lz4" = "xyes")
+
AC_ARG_ENABLE(smartcard,
[ --enable-smartcard Enable network redirection],,
[enable_smartcard="no"])
@@ -287,6 +293,13 @@ AC_ARG_ENABLE([xinerama],
[AS_HELP_STRING([--disable-xinerama],
[disable Xinerama library @<:@default=no@:>@])])
+if test "x$enable_lz4" = "xyes"; then
+ PKG_CHECK_MODULES(LZ4, liblz4)
+ AC_DEFINE([USE_LZ4], [1], [Define to build with Lz4 support])
+fi
+AC_SUBST(LZ4_CFLAGS)
+AC_SUBST(LZ4_LIBS)
+
if test "x$red_target" = "xx11" && test "x$enable_client" = "xyes" ; then
if test "$os_linux" = yes; then
PKG_CHECK_MODULES(ALSA, alsa)
@@ -549,6 +562,8 @@ echo "
GUI: ${enable_gui}
" ; fi ; echo "\
+ LZ4 support: ${enable_lz4}
+
Smartcard: ${enable_smartcard}
SASL support: ${enable_sasl}
diff --git a/server/Makefile.am b/server/Makefile.am
index f162a184..3cef2430 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
-DRED_STATISTICS \
$(COMMON_CFLAGS) \
$(GLIB2_CFLAGS) \
+ $(LZ4_CFLAGS) \
$(PIXMAN_CFLAGS) \
$(SASL_CFLAGS) \
$(SLIRP_CFLAGS) \
@@ -35,6 +36,7 @@ libspice_server_la_LIBADD = \
$(GL_LIBS) \
$(GLIB2_LIBS) \
$(JPEG_LIBS) \
+ $(LZ4_LIBS) \
$(LIBRT) \
$(PIXMAN_LIBS) \
$(SASL_LIBS) \
@@ -75,6 +77,8 @@ libspice_server_la_SOURCES = \
inputs_channel.h \
jpeg_encoder.c \
jpeg_encoder.h \
+ lz4_encoder.c \
+ lz4_encoder.h \
main_channel.c \
main_channel.h \
mjpeg_encoder.c \
diff --git a/server/lz4_encoder.c b/server/lz4_encoder.c
new file mode 100644
index 00000000..531ab4bc
--- /dev/null
+++ b/server/lz4_encoder.c
@@ -0,0 +1,122 @@
+/*
+ Copyright (C) 2014 Flexible Software Solutions S.L.
+
+ 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
+
+#ifdef USE_LZ4
+
+#define SPICE_LOG_DOMAIN "SpiceLz4Encoder"
+
+#include <arpa/inet.h>
+#include <lz4.h>
+#include "red_common.h"
+#include "lz4_encoder.h"
+
+typedef struct Lz4Encoder {
+ Lz4EncoderUsrContext *usr;
+} Lz4Encoder;
+
+Lz4EncoderContext* lz4_encoder_create(Lz4EncoderUsrContext *usr)
+{
+ Lz4Encoder *enc;
+ if (!usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ enc = spice_new0(Lz4Encoder, 1);
+ enc->usr = usr;
+
+ return (Lz4EncoderContext*)enc;
+}
+
+void lz4_encoder_destroy(Lz4EncoderContext* encoder)
+{
+ free(encoder);
+}
+
+int lz4_encode(Lz4EncoderContext *lz4, int height, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+ Lz4Encoder *enc = (Lz4Encoder *)lz4;
+ uint8_t *lines;
+ int num_lines = 0;
+ int total_lines = 0;
+ int in_size, enc_size, out_size = 0, already_copied;
+ int stride_abs = abs(stride);
+ uint8_t *in_buf, *compressed_lines;
+ uint8_t *out_buf = io_ptr;
+ LZ4_stream_t *stream = LZ4_createStream();
+
+ // Encode direction
+ *(out_buf++) = stride < 0 ? 1 : 0;
+ num_io_bytes--;
+
+ do {
+ num_lines = enc->usr->more_lines(enc->usr, &lines);
+ if (num_lines <= 0) {
+ spice_error("more lines failed");
+ LZ4_freeStream(stream);
+ return 0;
+ }
+ in_buf = stride < 0 ? lines - (stride_abs * (num_lines - 1)) : lines;
+ lines += stride * num_lines;
+ in_size = stride_abs * num_lines;
+ compressed_lines = (uint8_t *) malloc(LZ4_compressBound(in_size) + 4);
+ enc_size = LZ4_compress_continue(stream, (const char *) in_buf,
+ (char *) compressed_lines + 4, in_size);
+ if (enc_size <= 0) {
+ spice_error("compress failed!");
+ free(compressed_lines);
+ LZ4_freeStream(stream);
+ return 0;
+ }
+ *((uint32_t *)compressed_lines) = htonl(enc_size);
+
+ out_size += enc_size += 4;
+ already_copied = 0;
+ while (num_io_bytes < enc_size) {
+ memcpy(out_buf, compressed_lines + already_copied, num_io_bytes);
+ already_copied += num_io_bytes;
+ enc_size -= num_io_bytes;
+ num_io_bytes = enc->usr->more_space(enc->usr, &io_ptr);
+ if (num_io_bytes <= 0) {
+ spice_error("more space failed");
+ free(compressed_lines);
+ LZ4_freeStream(stream);
+ return 0;
+ }
+ out_buf = io_ptr;
+ }
+ memcpy(out_buf, compressed_lines + already_copied, enc_size);
+ out_buf += enc_size;
+ num_io_bytes -= enc_size;
+
+ free(compressed_lines);
+ total_lines += num_lines;
+ } while (total_lines < height);
+
+ LZ4_freeStream(stream);
+ if (total_lines != height) {
+ spice_error("too many lines\n");
+ out_size = 0;
+ }
+
+ return out_size;
+}
+
+#endif // USE_LZ4
diff --git a/server/lz4_encoder.h b/server/lz4_encoder.h
new file mode 100644
index 00000000..f3359c09
--- /dev/null
+++ b/server/lz4_encoder.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2014 Flexible Software Solutions S.L.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _H_LZ4_ENCODER
+#define _H_LZ4_ENCODER
+
+#include <spice/types.h>
+
+typedef void* Lz4EncoderContext;
+typedef struct Lz4EncoderUsrContext Lz4EncoderUsrContext;
+
+struct Lz4EncoderUsrContext {
+ int (*more_space)(Lz4EncoderUsrContext *usr, uint8_t **io_ptr);
+ int (*more_lines)(Lz4EncoderUsrContext *usr, uint8_t **lines);
+};
+
+Lz4EncoderContext* lz4_encoder_create(Lz4EncoderUsrContext *usr);
+void lz4_encoder_destroy(Lz4EncoderContext *encoder);
+
+/* returns the total size of the encoded data. Images must be supplied from the
+ top line to the bottom */
+int lz4_encode(Lz4EncoderContext *lz4, int height, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
diff --git a/server/red_worker.c b/server/red_worker.c
index cbb78a23..a18987a6 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -74,6 +74,9 @@
#include "red_memslots.h"
#include "red_parse_qxl.h"
#include "jpeg_encoder.h"
+#ifdef USE_LZ4
+#include "lz4_encoder.h"
+#endif
#include "demarshallers.h"
#include "zlib_encoder.h"
#include "red_channel.h"
@@ -237,6 +240,7 @@ static const char *quic_stat_name = "quic";
static const char *jpeg_stat_name = "jpeg";
static const char *zlib_stat_name = "zlib_glz";
static const char *jpeg_alpha_stat_name = "jpeg_alpha";
+static const char *lz4_stat_name = "lz4";
static inline void stat_compress_init(stat_info_t *info, const char *name)
{
@@ -599,6 +603,13 @@ typedef struct {
EncoderData data;
} JpegData;
+#ifdef USE_LZ4
+typedef struct {
+ Lz4EncoderUsrContext usr;
+ EncoderData data;
+} Lz4Data;
+#endif
+
typedef struct {
ZlibEncoderUsrContext usr;
EncoderData data;
@@ -740,6 +751,7 @@ struct DisplayChannel {
stat_info_t jpeg_stat;
stat_info_t zlib_glz_stat;
stat_info_t jpeg_alpha_stat;
+ stat_info_t lz4_stat;
#endif
};
@@ -998,6 +1010,11 @@ typedef struct RedWorker {
JpegData jpeg_data;
JpegEncoderContext *jpeg;
+#ifdef USE_LZ4
+ Lz4Data lz4_data;
+ Lz4EncoderContext *lz4;
+#endif
+
ZlibData zlib_data;
ZlibEncoder *zlib;
@@ -1190,27 +1207,37 @@ static void print_compress_stats(DisplayChannel *display_channel)
stat_byte_to_mega(display_channel->jpeg_alpha_stat.comp_size),
stat_cpu_time_to_sec(display_channel->jpeg_alpha_stat.total)
);
+ spice_info("LZ4 \t%8d\t%13.2f\t%12.2f\t%12.2f",
+ display_channel->lz4_stat.count,
+ stat_byte_to_mega(display_channel->lz4_stat.orig_size),
+ stat_byte_to_mega(display_channel->lz4_stat.comp_size),
+ stat_cpu_time_to_sec(display_channel->lz4_stat.total)
+ );
spice_info("-------------------------------------------------------------------");
spice_info("Total \t%8d\t%13.2f\t%12.2f\t%12.2f",
display_channel->lz_stat.count + display_channel->glz_stat.count +
display_channel->quic_stat.count +
display_channel->jpeg_stat.count +
+ display_channel->lz4_stat.count +
display_channel->jpeg_alpha_stat.count,
stat_byte_to_mega(display_channel->lz_stat.orig_size +
display_channel->glz_stat.orig_size +
display_channel->quic_stat.orig_size +
display_channel->jpeg_stat.orig_size +
+ display_channel->lz4_stat.orig_size +
display_channel->jpeg_alpha_stat.orig_size),
stat_byte_to_mega(display_channel->lz_stat.comp_size +
glz_enc_size +
display_channel->quic_stat.comp_size +
display_channel->jpeg_stat.comp_size +
+ display_channel->lz4_stat.comp_size +
display_channel->jpeg_alpha_stat.comp_size),
stat_cpu_time_to_sec(display_channel->lz_stat.total +
display_channel->glz_stat.total +
display_channel->zlib_glz_stat.total +
display_channel->quic_stat.total +
display_channel->jpeg_stat.total +
+ display_channel->lz4_stat.total +
display_channel->jpeg_alpha_stat.total)
);
}
@@ -5725,6 +5752,14 @@ static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
return (encoder_usr_more_space(usr_data, (uint32_t **)io_ptr) << 2);
}
+#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, (uint32_t **)io_ptr) << 2);
+}
+#endif
+
static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
{
EncoderData *usr_data = &(((ZlibData *)usr)->data);
@@ -5785,6 +5820,14 @@ static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
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);
@@ -5886,6 +5929,20 @@ static inline void red_init_jpeg(RedWorker *worker)
}
}
+#ifdef USE_LZ4
+static inline void red_init_lz4(RedWorker *worker)
+{
+ worker->lz4_data.usr.more_space = lz4_usr_more_space;
+ worker->lz4_data.usr.more_lines = lz4_usr_more_lines;
+
+ worker->lz4 = lz4_encoder_create(&worker->lz4_data.usr);
+
+ if (!worker->lz4) {
+ spice_critical("create lz4 encoder failed");
+ }
+}
+#endif
+
static inline void red_init_zlib(RedWorker *worker)
{
worker->zlib_data.usr.more_space = zlib_usr_more_space;
@@ -6355,6 +6412,80 @@ static int red_jpeg_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
return TRUE;
}
+#ifdef USE_LZ4
+static int red_lz4_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
+ SpiceBitmap *src, compress_send_data_t* o_comp_data,
+ uint32_t group_id)
+{
+ DisplayChannel *display_channel = DCC_TO_DC(dcc);
+ RedWorker *worker = display_channel->common.worker;
+ Lz4Data *lz4_data = &worker->lz4_data;
+ Lz4EncoderContext *lz4 = worker->lz4;
+ int lz4_size = 0;
+ int stride;
+
+#ifdef COMPRESS_STAT
+ stat_time_t start_time = stat_now();
+#endif
+
+ lz4_data->data.bufs_tail = red_display_alloc_compress_buf(dcc);
+ lz4_data->data.bufs_head = lz4_data->data.bufs_tail;
+
+ if (!lz4_data->data.bufs_head) {
+ spice_warning("failed to allocate compress buffer");
+ return FALSE;
+ }
+
+ lz4_data->data.bufs_head->send_next = NULL;
+ lz4_data->data.dcc = dcc;
+
+ if (setjmp(lz4_data->data.jmp_env)) {
+ while (lz4_data->data.bufs_head) {
+ RedCompressBuf *buf = lz4_data->data.bufs_head;
+ lz4_data->data.bufs_head = buf->send_next;
+ red_display_free_compress_buf(dcc, buf);
+ }
+ 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->usr.more_lines = lz4_usr_more_lines;
+
+ if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ lz4_data->data.u.lines_data.next = 0;
+ lz4_data->data.u.lines_data.reverse = 0;
+ stride = src->stride;
+ } else {
+ lz4_data->data.u.lines_data.next = src->data->num_chunks - 1;
+ lz4_data->data.u.lines_data.reverse = 1;
+ stride = -src->stride;
+ }
+
+ lz4_size = lz4_encode(lz4, src->y, stride, (uint8_t*)lz4_data->data.bufs_head->buf,
+ sizeof(lz4_data->data.bufs_head->buf));
+
+ // 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(&display_channel->lz4_stat, start_time, src->stride * src->y,
+ o_comp_data->comp_buf_size);
+ return TRUE;
+}
+#endif
+
static inline int red_quic_compress_image(DisplayChannelClient *dcc, SpiceImage *dest,
SpiceBitmap *src, compress_send_data_t* o_comp_data,
uint32_t group_id)
@@ -6471,6 +6602,7 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
if (_stride_is_extra(src) || (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
if ((image_compression == SPICE_IMAGE_COMPRESS_LZ) ||
(image_compression == SPICE_IMAGE_COMPRESS_GLZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESS_LZ4) ||
BITMAP_FMT_IS_PLT[src->format]) {
return FALSE;
} else {
@@ -6522,7 +6654,8 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
(src->x * src->y) < glz_enc_dictionary_get_size(
dcc->glz_dict->dict));
} else if ((image_compression == SPICE_IMAGE_COMPRESS_AUTO_LZ) ||
- (image_compression == SPICE_IMAGE_COMPRESS_LZ)) {
+ (image_compression == SPICE_IMAGE_COMPRESS_LZ) ||
+ (image_compression == SPICE_IMAGE_COMPRESS_LZ4)) {
glz = FALSE;
} else {
spice_error("invalid image compression type %u", image_compression);
@@ -6543,8 +6676,16 @@ static inline int red_compress_image(DisplayChannelClient *dcc,
}
if (!glz) {
- ret = red_lz_compress_image(dcc, dest, src, o_comp_data,
- drawable->group_id);
+#ifdef USE_LZ4
+ if (image_compression == SPICE_IMAGE_COMPRESS_LZ4 &&
+ red_channel_client_test_remote_cap(&dcc->common.base,
+ SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
+ ret = red_lz4_compress_image(dcc, dest, src, o_comp_data,
+ drawable->group_id);
+ } else
+#endif
+ ret = red_lz_compress_image(dcc, dest, src, o_comp_data,
+ drawable->group_id);
#ifdef COMPRESS_DEBUG
spice_info("LZ LOCAL compress");
#endif
@@ -8775,9 +8916,18 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
&comp_send_data,
worker->mem_slots.internal_groupslot_id);
} else {
- comp_succeeded = red_lz_compress_image(dcc, &red_image, &bitmap,
- &comp_send_data,
- worker->mem_slots.internal_groupslot_id);
+#ifdef USE_LZ4
+ if (comp_mode == SPICE_IMAGE_COMPRESS_LZ4 &&
+ red_channel_client_test_remote_cap(&dcc->common.base,
+ SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
+ comp_succeeded = red_lz4_compress_image(dcc, &red_image, &bitmap,
+ &comp_send_data,
+ worker->mem_slots.internal_groupslot_id);
+ } else
+#endif
+ comp_succeeded = red_lz_compress_image(dcc, &red_image, &bitmap,
+ &comp_send_data,
+ worker->mem_slots.internal_groupslot_id);
}
}
@@ -10567,6 +10717,7 @@ static void display_channel_create(RedWorker *worker, int migrate)
stat_compress_init(&display_channel->jpeg_stat, jpeg_stat_name);
stat_compress_init(&display_channel->zlib_glz_stat, zlib_stat_name);
stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
+ stat_compress_init(&display_channel->lz4_stat, lz4_stat_name);
}
static void guest_set_client_capabilities(RedWorker *worker)
@@ -11591,6 +11742,11 @@ void handle_dev_set_compression(void *opaque, void *payload)
case SPICE_IMAGE_COMPRESS_QUIC:
spice_info("ic quic");
break;
+#ifdef USE_LZ4
+ case SPICE_IMAGE_COMPRESS_LZ4:
+ spice_info("ic lz4");
+ break;
+#endif
case SPICE_IMAGE_COMPRESS_LZ:
spice_info("ic lz");
break;
@@ -11612,6 +11768,7 @@ void handle_dev_set_compression(void *opaque, void *payload)
stat_reset(&worker->display_channel->jpeg_stat);
stat_reset(&worker->display_channel->zlib_glz_stat);
stat_reset(&worker->display_channel->jpeg_alpha_stat);
+ stat_reset(&worker->display_channel->lz4_stat);
}
#endif
}
@@ -12015,6 +12172,9 @@ SPICE_GNUC_NORETURN void *red_worker_main(void *arg)
red_init_quic(worker);
red_init_lz(worker);
red_init_jpeg(worker);
+#ifdef USE_LZ4
+ red_init_lz4(worker);
+#endif
red_init_zlib(worker);
worker->event_timeout = INF_EVENT_WAIT;
for (;;) {
diff --git a/server/spice-server.h b/server/spice-server.h
index 79daac5f..c97b221a 100644
--- a/server/spice-server.h
+++ b/server/spice-server.h
@@ -74,6 +74,7 @@ typedef enum {
SPICE_IMAGE_COMPRESS_QUIC = 4,
SPICE_IMAGE_COMPRESS_GLZ = 5,
SPICE_IMAGE_COMPRESS_LZ = 6,
+ SPICE_IMAGE_COMPRESS_LZ4 = 7,
} spice_image_compression_t;
int spice_server_set_image_compression(SpiceServer *s,
diff --git a/spice-common b/spice-common
-Subproject b3a00f4411962e0c06c7ad89a9936df388aa002
+Subproject 5b3cdad921d32c9294377efac61243a09f849d0