Re: Patch to support LZ4 compression algorithm

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello

Marc-André, I added the changes you asked for. A new compression type 
SPICE_IMAGE_COMPRESS_LZ4 has been added, so that either LZ or LZ4 can be 
selected. Then, when LZ4 is selected, it will gracely fallback to LZ if:
- LZ4 support was not enabled in the server during compilation.
- The client does not support a new capablity 
SPICE_DISPLAY_CAP_LZ4_COMPRESSION.

The new capability is added to spice-protocol, so I send another patch for 
that repository.

El Miércoles, 5 de noviembre de 2014 15:11:01 Javier Celaya escribió:
> Hello
> 
> El Miércoles, 5 de noviembre de 2014 08:58:25 Marc-André Lureau escribió:
> > Hi
> > 
> > ----- Original Message -----
> > 
> > > Hello
> > > 
> > > My name is Javier Celaya and I work for Flexible Solutions [1]. We
> > > develop
> > > a virtualization product that includes a VDI module with Spice. As part
> > > of this module, we have included support for the LZ4 compression
> > > algorithm [2], and we
> > > would like to contribute it to the main project.
> > 
> > Great!
> > 
> > > The LZ4 algorithm focuses on speed sacrificing compression ratio. In our
> > > tests,
> > > we obtained a compression ratio of x1.75~x2, while QUIC obtained ~x4.
> > > However,
> > > LZ4 was around 4~5 times faster than QUIC or GLZ. The objective was to
> > > implement a Spice client for devices with limited CPU performance, like
> > > smartphones or ARM set-top-boxes.
> > 
> > Sounds reasonable
> > 
> > > We have tried to keep things simple. Our approach consists in replacing
> > > the
> > > LZ
> > > algorithm with LZ4 when Spice is built with the --enable-lz4 switch.
> > > Adding
> > > it
> > > as a new option would also require changes in the software that uses the
> > > server, like QEMU. Then, each image is compressed independently. Since
> > > the
> > > LZ4
> > > dictionary size is quite small (64KB), there is no improvement in
> > > compressing all the images as a stream.
> > > 
> > > I send you three patches, for spice-common, spice and spice-gtk. The
> > > compression code is in spice, the decompression is in spice-common, and
> > > spice-gtk only contains the macros needed to find liblz4 during
> > > configure.
> > 
> > The patches look nice. However, I am not sure I understand why you chose
> > not to modify the protocol to declare the new image type. We also need a
> > new display capability before applying this patch, as the server needs to
> > remain compatible with older clients no matter before accepting this.
> > 
> > Would you be willing to do those changes to get your patch accepted?
> 
> Yes, I will do those changes and send the patches again.
> 
> Thanks
> 
> > thanks
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@xxxxxxxxxxxxxxxxxxxxx
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 2753fae..a1bfc27 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -26,6 +26,10 @@
 #include <stdio.h>
 #include <math.h>
 
+#ifdef USE_LZ4
+#include <arpa/inet.h>
+#include <lz4.h>
+#endif
 #include <spice/macros.h>
 #include "log.h"
 #include "quic.h"
@@ -543,6 +547,64 @@ static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceImage *image, in
     return surface;
 }
 
+#ifdef USE_LZ4
+static pixman_image_t *canvas_get_lz4(CanvasBase *canvas, SpiceImage *image, int invers)
+{
+    pixman_image_t *surface = NULL;
+    int dec_size, enc_size;
+    int stride;
+    int stride_abs;
+    uint8_t *dest, *data, *data_end;
+    int width, height, direction;
+    LZ4_streamDecode_t *stream;
+
+    spice_chunks_linearize(image->u.lz4.data);
+    data = image->u.lz4.data->chunk[0].data;
+    data_end = data + image->u.lz4.data->chunk[0].len;
+    width = image->descriptor.width;
+    height = image->descriptor.height;
+    direction = *(data++);
+
+    surface = surface_create(
+#ifdef WIN32
+                             canvas->dc,
+#endif
+                             PIXMAN_a8r8g8b8,
+                             width, height, direction == 0);
+    if (surface == NULL) {
+        spice_warning("create surface failed");
+        return NULL;
+    }
+
+    stream = LZ4_createStreamDecode();
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    stride = pixman_image_get_stride(surface);
+    stride_abs = abs(stride);
+    if (direction == 1) {
+        dest -= (stride_abs * (height - 1));
+    }
+
+    do {
+        // Read next compressed block
+        enc_size = ntohl(*((uint32_t *)data));
+        data += 4;
+        dec_size = LZ4_decompress_safe_continue(stream, (const char *) data,
+                                                (char *) dest, enc_size, height * stride_abs);
+        if (dec_size <= 0) {
+            spice_warning("Error decoding LZ4 block\n");
+            pixman_image_unref(surface);
+            surface = NULL;
+            break;
+        }
+        dest += dec_size;
+        data += enc_size;
+    } while (data < data_end);
+
+    LZ4_freeStreamDecode(stream);
+    return surface;
+}
+#endif
+
 static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
                                              SpiceImage *image, int invers)
 {
@@ -1119,6 +1181,15 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage
         surface = canvas_get_jpeg_alpha(canvas, image, 0);
         break;
     }
+    case SPICE_IMAGE_TYPE_LZ4: {
+#ifdef USE_LZ4
+        surface = canvas_get_lz4(canvas, image, 0);
+#else
+        spice_warning("Lz4 compression algorithm not supported.\n");
+        surface = NULL;
+#endif
+        break;
+    }
 #if defined(SW_CANVAS_CACHE)
     case SPICE_IMAGE_TYPE_GLZ_RGB: {
         surface = canvas_get_glz(canvas, image, want_original);
diff --git a/common/draw.h b/common/draw.h
index 3704358..1155dc5 100644
--- a/common/draw.h
+++ b/common/draw.h
@@ -120,7 +120,7 @@ typedef struct SpiceSurface {
 typedef struct SpiceQUICData {
     uint32_t data_size;
     SpiceChunks *data;
-} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData;
+} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData, SpiceLZ4Data;
 
 typedef struct SpiceLZPLTData {
     uint8_t flags;
@@ -153,6 +153,7 @@ typedef struct SpiceImage {
         SpiceLZRGBData      lz_rgb;
         SpiceLZPLTData      lz_plt;
         SpiceJPEGData       jpeg;
+        SpiceLZ4Data        lz4;
         SpiceZlibGlzRGBData zlib_glz;
         SpiceJPEGAlphaData  jpeg_alpha;
     } u;
diff --git a/spice.proto b/spice.proto
index 316d287..01493c9 100644
--- a/spice.proto
+++ b/spice.proto
@@ -358,6 +358,7 @@ enum8 image_type {
     FROM_CACHE_LOSSLESS,
     ZLIB_GLZ_RGB,
     JPEG_ALPHA,
+    LZ4,
 };
 
 flags8 image_flags {
@@ -568,6 +569,8 @@ struct Image {
         BinaryData lz_rgb;
     case JPEG:
         BinaryData jpeg;
+    case LZ4:
+        BinaryData lz4;
     case LZ_PLT:
         LZPLTData lz_plt;
     case ZLIB_GLZ_RGB:
diff --git a/configure.ac b/configure.ac
index cedeb40..89f36cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -124,6 +124,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"])
@@ -268,6 +274,17 @@ AC_SUBST(GL_CFLAGS)
 AC_SUBST(GL_LIBS)
 SPICE_NONPKGCONFIG_LIBS+=" $GL_LIBS"
 
+if test "x$enable_lz4" = "xyes"; then
+    AC_CHECK_LIB(lz4, LZ4_compress_limitedOutput, LZ4_LIBS='-llz4', enable_lz4=no)
+    AC_CHECK_HEADER([lz4.h], [], [enable_lz4=no])
+    AC_DEFINE([USE_LZ4], [1], [Define to build with Lz4 support])
+
+    if test "x$enable_lz4" = "xno"; then
+        AC_MSG_ERROR([lz4 not found])
+    fi
+fi
+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)
@@ -535,6 +552,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 34219c8..acff1a7 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -35,6 +35,7 @@ libspice_server_la_LIBADD =						\
 	$(GL_LIBS)							\
 	$(GLIB2_LIBS)							\
 	$(JPEG_LIBS)							\
+	$(LZ4_LIBS)							\
 	$(LIBRT)							\
 	$(PIXMAN_LIBS)							\
 	$(SASL_LIBS)							\
@@ -60,6 +61,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 0000000..cbcd5df
--- /dev/null
+++ b/server/lz4_encoder.c
@@ -0,0 +1,123 @@
+/*
+   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 0000000..41e41c5
--- /dev/null
+++ b/server/lz4_encoder.h
@@ -0,0 +1,51 @@
+/*
+   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 e177b68..f3580a3 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)
                );
 }
@@ -5723,6 +5750,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);
@@ -5783,6 +5818,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);
@@ -5884,6 +5927,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;
@@ -6353,6 +6410,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)
@@ -6469,6 +6600,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 {
@@ -6520,7 +6652,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);
@@ -6541,8 +6674,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
@@ -8773,9 +8914,16 @@ static void red_marshall_image(RedChannelClient *rcc, SpiceMarshaller *m, ImageI
                                                      &comp_send_data,
                                                      worker->mem_slots.internal_groupslot_id);
         } else {
+            spice_warning("Do we ever get here??");
+#ifdef USE_LZ4
+            comp_succeeded = red_lz4_compress_image(dcc, &red_image, &bitmap,
+                                                    &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);
+#endif
         }
     }
 
@@ -10565,6 +10713,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)
@@ -11589,6 +11738,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;
@@ -11610,6 +11764,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
 }
@@ -12013,6 +12168,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.h b/server/spice.h
index 58700d1..b38ca62 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -479,6 +479,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/configure.ac b/configure.ac
index f47ee20..d295c5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -694,6 +694,24 @@ if test "x$enable_dbus" != "xno"; then
   fi
 fi
 
+AC_ARG_ENABLE([lz4],
+  AS_HELP_STRING([--enable-lz4=@<:@yes/no@:>@],
+                 [Enable lz4 compression algorithm @<:@default=no@:>@]),
+  [],
+  [enable_lz4="no"])
+
+if test "x$enable_lz4" = "xyes"; then
+    AC_CHECK_LIB(lz4, LZ4_compress_limitedOutput, LZ4_LIBS='-llz4', enable_lz4=no)
+    AC_CHECK_HEADER([lz4.h], [], [enable_lz4=no])
+    AC_DEFINE([USE_LZ4], [1], [Define to build with Lz4 support])
+
+    if test "x$enable_lz4" = "xno"; then
+        AC_MSG_ERROR([lz4 not found])
+    fi
+fi
+AC_SUBST(LZ4_LIBS)
+
+
 dnl ===========================================================================
 dnl check compiler flags
 
@@ -765,6 +783,7 @@ AC_MSG_NOTICE([
         USB redirection support:  ${have_usbredir} ${with_usbredir_hotplug}
         DBus:                     ${have_dbus}
         WebDAV support:           ${have_phodav}
+        LZ4 support:              ${enable_lz4}
 
         Now type 'make' to build $PACKAGE
 
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 3d87958..f799543 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -194,6 +194,7 @@ libspice_client_glib_2_0_la_LIBADD =					\
 	$(OPUS_LIBS)							\
 	$(JPEG_LIBS)							\
 	$(Z_LIBS)							\
+	$(LZ4_LIBS)							\
 	$(PIXMAN_LIBS)							\
 	$(SSL_LIBS)							\
 	$(PULSE_LIBS)							\
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index d9f8d45..dfebbb2 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -590,6 +590,9 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel)
     spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_MONITORS_CONFIG);
     spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_COMPOSITE);
     spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_A8_SURFACE);
+#ifdef USE_LZ4
+    spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_LZ4_COMPRESSION);
+#endif
     if (SPICE_DISPLAY_CHANNEL(channel)->priv->enable_adaptive_streaming) {
         spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_STREAM_REPORT);
     }
diff --git a/spice/protocol.h b/spice/protocol.h
index 961a303..bea376c 100644
--- a/spice/protocol.h
+++ b/spice/protocol.h
@@ -133,6 +133,7 @@ enum {
     SPICE_DISPLAY_CAP_COMPOSITE,
     SPICE_DISPLAY_CAP_A8_SURFACE,
     SPICE_DISPLAY_CAP_STREAM_REPORT,
+    SPICE_DISPLAY_CAP_LZ4_COMPRESSION,
 };
 
 enum {
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]