qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: David Turner <digit@google.com>
To: Alexander Graf <agraf@suse.de>
Cc: stefano.stabellini@eu.citrix.com, qemu-devel@nongnu.org,
	kraxel@redhat.com
Subject: Re: [Qemu-devel] [PATCH] Add JPEG encoding to VNC server
Date: Fri, 31 Jul 2009 14:52:39 +0200	[thread overview]
Message-ID: <60cad3f0907310552q6b75422kad75f47381d8cd09@mail.gmail.com> (raw)
In-Reply-To: <1249024897-11100-1-git-send-email-agraf@suse.de>

[-- Attachment #1: Type: text/plain, Size: 12429 bytes --]

the feature check seems to use "vnc_tls=no" if -ljpeg linking doesn't work.
shouldn't this be "vnc_jpeg=no" instead ?

On Fri, Jul 31, 2009 at 9:21 AM, Alexander Graf <agraf@suse.de> wrote:

> We know most of the tight protocol already, so implementing JPEG is rather
> easy, especially considering that I had this implementation lying around
> still anyways.
>
> The big concern Anthony raised about JPEG compression is that as soon as
> you
> use JPEG, CopyRect looks ugly for most use cases and most users probably
> don't
> want it anyways.
>
> So the road I went for this patch was to only enable JPEG encoding when
> it's
> the only available choice. Allow any other protocols? You don't get JPEG
> then.
>
> While this might sound like it renders the whole implementation useless, it
> does make sense to implement it nevertheless. I have some ideas to
> implement
> progressive encodings for video.
>
> So when we'd detect that one region is updated a lot in a short about of
> time
> with content that zlib can't really handle well, we'd just send a really
> low
> quality JPEG first and then send the update after a timer if the region
> wasn't
> updated within that timeframe.
>
> But this is all ideas so far. For now the JPEG implementation is stand
> alone,
> but would enable either me or someone else who'd like to do it worlds of
> encoding fun :-).
>
> Also, if you're daring, you can always see if JPEG performs good for your
> specific VNC workload and at least have the chance to use it.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  Makefile.target |    4 +
>  configure       |   24 ++++++++
>  vnc.c           |  178
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  vnc.h           |    9 +++
>  4 files changed, 214 insertions(+), 1 deletions(-)
>
> diff --git a/Makefile.target b/Makefile.target
> index 49ba08d..f1dd54d 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -303,6 +303,10 @@ CPPFLAGS += $(VNC_SASL_CFLAGS)
>  LIBS += $(VNC_SASL_LIBS)
>  endif
>
> +ifdef CONFIG_VNC_JPEG
> +LIBS += $(CONFIG_VNC_JPEG_LIBS)
> +endif
> +
>  ifdef CONFIG_BLUEZ
>  LIBS += $(BLUEZ_LIBS)
>  endif
> diff --git a/configure b/configure
> index 8160bed..67fe51a 100755
> --- a/configure
> +++ b/configure
> @@ -175,6 +175,7 @@ fmod_inc=""
>  oss_lib=""
>  vnc_tls="yes"
>  vnc_sasl="yes"
> +vnc_jpeg="yes"
>  bsd="no"
>  linux="no"
>  solaris="no"
> @@ -428,6 +429,8 @@ for opt do
>   ;;
>   --disable-vnc-sasl) vnc_sasl="no"
>   ;;
> +  --disable-jpeg) vnc_jpeg="no"
> +  ;;
>   --disable-slirp) slirp="no"
>   ;;
>   --disable-vde) vde="no"
> @@ -649,6 +652,7 @@ echo "  --disable-xen            disable xen backend
> driver support"
>  echo "  --disable-brlapi         disable BrlAPI"
>  echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
>  echo "  --disable-vnc-sasl       disable SASL encryption for VNC server"
> +echo "  --disable-jpeg           disable JPEG compression for VNC server"
>  echo "  --disable-curses         disable curses output"
>  echo "  --disable-curl           disable curl connectivity"
>  echo "  --disable-bluez          disable bluez stack connectivity"
> @@ -994,6 +998,21 @@ if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2>
> /dev/null ; then
>  fi
>
>  ##########################################
> +# VNC JPEG detection
> +if test "$vnc_jpeg" = "yes" ; then
> +cat > $TMPC <<EOF
> +#include <jpeglib.h>
> +int main(void) { jpeg_compress_struct s; jpeg_create_compress(&s); return
> 0; }
> +EOF
> +    vnc_jpeg_libs="-ljpeg"
> +    if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $TMPC $vnc_jpeg_libs >
> /dev/null 2> /dev/null ; then
> +       :
> +    else
> +       vnc_tls="no"
> +    fi
> +fi
> +
> +##########################################
>  # vde libraries probe
>  if test "$vde" = "yes" ; then
>   vde=no
> @@ -1452,6 +1471,7 @@ if test "$vnc_sasl" = "yes" ; then
>     echo "    SASL CFLAGS    $vnc_sasl_cflags"
>     echo "    SASL LIBS      $vnc_sasl_libs"
>  fi
> +echo "VNC JPEG support  $vnc_jpeg"
>  if test -n "$sparc_cpu"; then
>     echo "Target Sparc Arch $sparc_cpu"
>  fi
> @@ -1604,6 +1624,10 @@ fi
>  if test "$fnmatch" = "yes" ; then
>   echo "CONFIG_FNMATCH=y" >> $config_host_mak
>  fi
> +if test "$vnc_jpeg" = "yes" ; then
> +  echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
> +  echo "CONFIG_VNC_JPEG_LIBS=$vnc_jpeg_libs" >> $config_host_mak
> +fi
>  qemu_version=`head $source_path/VERSION`
>  echo "VERSION=$qemu_version" >>$config_host_mak
>  echo "PKGVERSION=$pkgversion" >>$config_host_mak
> diff --git a/vnc.c b/vnc.c
> index 903dd95..4e0c967 100644
> --- a/vnc.c
> +++ b/vnc.c
> @@ -635,9 +635,171 @@ static void send_framebuffer_update_zlib(VncState
> *vs, int x, int y, int w, int
>     vs->output.offset = new_offset;
>  }
>
> +#ifdef CONFIG_VNC_JPEG
> +/* This is called once per encoding */
> +static void jpeg_init_destination(j_compress_ptr cinfo)
> +{
> +    VncState *vs = (VncState*)cinfo->client_data;
> +    Buffer *buffer = &vs->jpeg_buffer;
> +
> +    cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer +
> buffer->offset;
> +    cinfo->dest->free_in_buffer = (size_t)(buffer->capacity -
> buffer->offset);
> +}
> +
> +/* This is called when we ran out of buffer (shouldn't happen!) */
> +static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
> +{
> +    VncState *vs = (VncState*)cinfo->client_data;
> +    Buffer *buffer = &vs->jpeg_buffer;
> +
> +    buffer->offset = buffer->capacity;
> +    buffer_reserve(buffer, 2048);
> +    jpeg_init_destination(cinfo);
> +    return TRUE;
> +}
> +
> +/* This is called when we are done processing data */
> +static void jpeg_term_destination(j_compress_ptr cinfo)
> +{
> +    VncState *vs = (VncState*)cinfo->client_data;
> +    Buffer *buffer = &vs->jpeg_buffer;
> +
> +    buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
> +}
> +
> +
> +static void vnc_send_compact_size(VncState *vs, int len)
> +{
> +    char buf[3];
> +    int lpc = 0;
> +    int bytes = 0;
> +
> +    /* Adapted from SendCompressedData() in
> Xvnc/programs/Xserver/hw/vnc/tight.c */
> +    buf[bytes++] = len & 0x7F;
> +    if (len > 0x7F) {
> +       buf[bytes-1] |= 0x80;
> +       buf[bytes++] = len >> 7 & 0x7F;
> +       if (len > 0x3FFF) {
> +           buf[bytes-1] |= 0x80;
> +           buf[bytes++] = len >> 14 & 0xFF;
> +       }
> +    }
> +
> +    for(lpc = 0; lpc < bytes; lpc++) {
> +       vnc_write_u8(vs, buf[lpc]);
> +    }
> +}
> +
> +static void jpeg_row2pixel(VncState *vs, char *in, char *out, int len)
> +{
> +    char *pi = in;
> +    char *po = out;
> +    int depth = vs->server.ds->pf.bytes_per_pixel;
> +    int i;
> +
> +    for (i = 0; i < len; i++) {
> +        uint32_t v;
> +        uint8_t r, g, b;
> +        switch (depth) {
> +            case 1:
> +                po[0] = pi[0];
> +                po[1] = pi[0];
> +                po[2] = pi[0];
> +                continue;
> +                break;
> +            case 2:
> +                v = *((uint16_t*)pi);
> +                break;
> +            case 4:
> +                v = *((uint32_t*)pi);
> +                break;
> +        }
> +        r = ((((v & vs->server.ds->pf.rmask) >> vs->server.ds->pf.rshift)
> +            << vs->clientds.pf.rbits) >> vs->server.ds->pf.rbits);
> +        g = ((((v & vs->server.ds->pf.gmask) >> vs->server.ds->pf.gshift)
> +            << vs->clientds.pf.gbits) >> vs->server.ds->pf.gbits);
> +        b = ((((v & vs->server.ds->pf.bmask) >> vs->server.ds->pf.bshift)
> +            << vs->clientds.pf.bbits) >> vs->server.ds->pf.bbits);
> +
> +        po[0] = r;
> +        po[1] = g;
> +        po[2] = b;
> +
> +        pi += depth;
> +        po += 3; // RGB
> +    }
> +}
> +
> +static void send_framebuffer_update_tight(VncState *vs, int x, int y, int
> w, int h)
> +{
> +    struct jpeg_compress_struct cinfo;
> +    struct jpeg_error_mgr jerr;
> +    uint8_t *row = ds_get_data(vs->ds) +
> +                   y * ds_get_linesize(vs->ds) +
> +                   x * ds_get_bytes_per_pixel(vs->ds);
> +    int dy;
> +    JSAMPROW row_pointer[1];
> +
> +    if(vnc_has_feature(vs, VNC_FEATURE_HEXTILE) && (w * h) < 300) {
> +       /* Below a certain size its actually more efficient to send
> hextiles
> +        * Take a rough stab in the dark at 300 for text-based displays */
> +        send_framebuffer_update_hextile(vs, x, y, w, h);
> +        return;
> +    }
> +
> +    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
> +
> +    // XXX For now let's be stupid and always send JPEG data. Tight can do
> a lot more!
> +
> +    // Indicate its a Jpeg data stream
> +    vnc_write_u8(vs, VNC_TIGHT_CCB_TYPE_JPEG);
> +
> +    // Compress data
> +    cinfo.client_data = vs;
> +    cinfo.err = jpeg_std_error(&jerr);
> +    jpeg_create_compress(&cinfo);
> +    cinfo.image_width = w;
> +    cinfo.image_height = h;
> +    cinfo.input_components = 3;
> +    cinfo.in_color_space = JCS_RGB;
> +
> +    jpeg_set_defaults(&cinfo);
> +    jpeg_set_quality(&cinfo, (vs->tight_quality+1) * 10, TRUE);
> +
> +    buffer_reserve(&vs->jpeg_buffer, 1024);
> +    vs->jpeg_dst_manager.init_destination = jpeg_init_destination;
> +    vs->jpeg_dst_manager.empty_output_buffer = jpeg_empty_output_buffer;
> +    vs->jpeg_dst_manager.term_destination = jpeg_term_destination;
> +    cinfo.dest = &vs->jpeg_dst_manager;
> +
> +    jpeg_start_compress(&cinfo, TRUE);
> +
> +    row_pointer[0] = qemu_malloc(3 * w);
> +    for (dy = 0; dy < h; dy++) {
> +        jpeg_row2pixel(vs, (char*)row, (char*)row_pointer[0], w);
> +        jpeg_write_scanlines(&cinfo, row_pointer, 1);
> +        row += ds_get_linesize(vs->ds);
> +    }
> +    qemu_free(row_pointer[0]);
> +
> +    jpeg_finish_compress(&cinfo);
> +    jpeg_destroy_compress(&cinfo);
> +
> +    VNC_DEBUG("JPEG: Sending %d bytes of jpeg data\n",
> (int)vs->jpeg_buffer.offset);
> +    vnc_send_compact_size(vs, vs->jpeg_buffer.offset);
> +    vnc_write(vs, vs->jpeg_buffer.buffer, vs->jpeg_buffer.offset);
> +    buffer_reset(&vs->jpeg_buffer);
> +}
> +#endif /* CONFIG_VNC_JPEG */
> +
>  static void send_framebuffer_update(VncState *vs, int x, int y, int w, int
> h)
>  {
>     switch(vs->vnc_encoding) {
> +#ifdef CONFIG_VNC_JPEG
> +        case VNC_ENCODING_TIGHT:
> +           send_framebuffer_update_tight(vs, x, y, w, h);
> +           break;
> +#endif /* CONFIG_VNC_JPEG */
>         case VNC_ENCODING_ZLIB:
>             send_framebuffer_update_zlib(vs, x, y, w, h);
>             break;
> @@ -1552,7 +1714,7 @@ static void set_encodings(VncState *vs, int32_t
> *encodings, size_t n_encodings)
>
>     vnc_zlib_init(vs);
>     vs->features = 0;
> -    vs->vnc_encoding = 0;
> +    vs->vnc_encoding = -1;
>     vs->tight_compression = 9;
>     vs->tight_quality = 9;
>     vs->absolute = -1;
> @@ -1574,6 +1736,17 @@ static void set_encodings(VncState *vs, int32_t
> *encodings, size_t n_encodings)
>             vs->features |= VNC_FEATURE_ZLIB_MASK;
>             vs->vnc_encoding = enc;
>             break;
> +        case VNC_ENCODING_TIGHT:
> +#ifdef CONFIG_VNC_JPEG
> +            buffer_reset(&vs->jpeg_buffer);
> +#endif
> +            vs->features |= VNC_FEATURE_TIGHT_MASK;
> +
> +            /* We don't want to do JPEG encoding by accident, so only
> +             * enable it when it's the only choice. */
> +            if (vs->vnc_encoding == -1)
> +                vs->vnc_encoding = enc;
> +            break;
>         case VNC_ENCODING_DESKTOPRESIZE:
>             vs->features |= VNC_FEATURE_RESIZE_MASK;
>             break;
> @@ -1601,6 +1774,9 @@ static void set_encodings(VncState *vs, int32_t
> *encodings, size_t n_encodings)
>         }
>     }
>
> +    if (vs->vnc_encoding == -1)
> +        vs->vnc_encoding = VNC_ENCODING_RAW;
> +
>     check_pointer_type_change(vs, kbd_mouse_is_absolute());
>  }
>
> diff --git a/vnc.h b/vnc.h
> index 3ae95f3..b57081f 100644
> --- a/vnc.h
> +++ b/vnc.h
> @@ -33,6 +33,10 @@
>  #include "audio/audio.h"
>  #include <zlib.h>
>
> +#ifdef CONFIG_VNC_JPEG
> +#include <jpeglib.h>
> +#endif /* CONFIG_VNC_JPEG */
> +
>  #include "keymaps.h"
>
>  // #define _VNC_DEBUG 1
> @@ -161,6 +165,11 @@ struct VncState
>     Buffer zlib_tmp;
>     z_stream zlib_stream[4];
>
> +#ifdef CONFIG_VNC_JPEG
> +    Buffer jpeg_buffer;
> +    struct jpeg_destination_mgr jpeg_dst_manager;
> +#endif /* CONFIG_VNC_JPEG */
> +
>     VncState *next;
>  };
>
> --
> 1.6.0.2
>
>
>
>

[-- Attachment #2: Type: text/html, Size: 14854 bytes --]

  reply	other threads:[~2009-07-31 12:52 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-07-31  7:21 [Qemu-devel] [PATCH] Add JPEG encoding to VNC server Alexander Graf
2009-07-31 12:52 ` David Turner [this message]
2009-07-31 13:03   ` Alexander Graf
2009-07-31 14:11 ` [Qemu-devel] " Anthony Liguori
2009-07-31 14:50   ` Alexander Graf
2009-07-31 15:27 ` [Qemu-devel] " Jamie Lokier
2009-07-31 15:35   ` Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=60cad3f0907310552q6b75422kad75f47381d8cd09@mail.gmail.com \
    --to=digit@google.com \
    --cc=agraf@suse.de \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefano.stabellini@eu.citrix.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).