Hello I resend the patch for the spice repository. I just realized I forgot to remove a debug message from red_worker.c, sorry. El Jueves, 6 de noviembre de 2014 18:21:21 Javier Celaya escribió: > Hello > > El Jueves, 6 de noviembre de 2014 17:20:42 Christophe Fergeau escribió: > > On Thu, Nov 06, 2014 at 10:33:19AM +0100, Javi wrote: > > > 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 > > > > You should be able to use PKG_CHECK_MODULES instead of manually checking > > for lib and header as lz4 comes with a .pc file. > > > > Christophe > > Yes, that's true, thanks. I resend the patches for the spice and spice-gtk > repositories with that change.
diff --git a/configure.ac b/configure.ac index cedeb40..ec20b89 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,13 @@ AC_SUBST(GL_CFLAGS) AC_SUBST(GL_LIBS) SPICE_NONPKGCONFIG_LIBS+=" $GL_LIBS" +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) @@ -535,6 +548,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..c5fc164 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) \ @@ -60,6 +62,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..0ee10df 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,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); } } @@ -10565,6 +10715,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 +11740,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 +11766,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 +12170,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,
_______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel