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 --]
next prev parent 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).