qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 7/7] Add tight encoding (jpeg) to vnc.c


From: Alexander Graf
Subject: [Qemu-devel] [PATCH 7/7] Add tight encoding (jpeg) to vnc.c
Date: Thu, 29 Jan 2009 12:24:58 +0100

Because we can now speak the tight protocol, let's use it to
transmit jpeg data to the client!

This patch adds a really easy implementation of the jpeg tight
encoding. Tight in general can do a lot more, but let's take small
steps here and see how things perform.

Signed-off-by: Alexander Graf <address@hidden>
---
 Makefile.target |    4 +
 configure       |   25 ++++++++
 vnc.c           |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+), 0 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index a091ce9..7b54748 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -554,6 +554,10 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
 LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
+ifdef CONFIG_VNC_JPEG
+LIBS += $(CONFIG_VNC_JPEG_LIBS)
+endif
+
 ifdef CONFIG_BLUEZ
 LIBS += $(CONFIG_BLUEZ_LIBS)
 endif
diff --git a/configure b/configure
index c3fbbbe..44e2e0b 100755
--- a/configure
+++ b/configure
@@ -164,6 +164,7 @@ fmod_lib=""
 fmod_inc=""
 oss_lib=""
 vnc_tls="yes"
+vnc_jpeg="yes"
 bsd="no"
 linux="no"
 solaris="no"
@@ -387,6 +388,8 @@ for opt do
   ;;
   --disable-vnc-tls) vnc_tls="no"
   ;;
+  --disable-jpeg) vnc_jpeg="no"
+  ;;
   --disable-slirp) slirp="no"
   ;;
   --disable-vde) vde="no"
@@ -544,6 +547,7 @@ echo "                           Available cards: 
$audio_possible_cards"
 echo "  --enable-mixemu          enable mixer emulation"
 echo "  --disable-brlapi         disable BrlAPI"
 echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
+echo "  --disable-jpeg           disable JPEG compression for VNC server"
 echo "  --disable-curses         disable curses output"
 echo "  --disable-bluez          disable bluez stack connectivity"
 echo "  --disable-kvm            disable KVM acceleration support"
@@ -823,6 +827,21 @@ EOF
 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
   cat > $TMPC << EOF
@@ -1130,6 +1149,7 @@ if test "$vnc_tls" = "yes" ; then
     echo "    TLS CFLAGS    $vnc_tls_cflags"
     echo "    TLS LIBS      $vnc_tls_libs"
 fi
+echo "VNC JPEG support  $vnc_jpeg"
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
@@ -1371,6 +1391,11 @@ if test "$vnc_tls" = "yes" ; then
   echo "CONFIG_VNC_TLS_LIBS=$vnc_tls_libs" >> $config_mak
   echo "#define CONFIG_VNC_TLS 1" >> $config_h
 fi
+if test "$vnc_jpeg" = "yes" ; then
+  echo "CONFIG_VNC_JPEG=yes" >> $config_mak
+  echo "CONFIG_VNC_JPEG_LIBS=$vnc_jpeg_libs" >> $config_mak
+  echo "#define CONFIG_VNC_JPEG 1" >> $config_h
+fi
 qemu_version=`head $source_path/VERSION`
 echo "VERSION=$qemu_version" >>$config_mak
 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
diff --git a/vnc.c b/vnc.c
index 86e2a1b..7a7ec94 100644
--- a/vnc.c
+++ b/vnc.c
@@ -37,6 +37,10 @@
 #include "keymaps.c"
 #include "d3des.h"
 
+#ifdef CONFIG_VNC_JPEG
+#include <jpeglib.h>
+#endif /* CONFIG_VNC_JPEG */
+
 #ifdef CONFIG_VNC_TLS
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
@@ -144,6 +148,11 @@ struct VncState
     size_t read_handler_expect;
     /* input */
     uint8_t modifiers_state[256];
+
+#ifdef CONFIG_VNC_JPEG
+    Buffer jpeg_buffer;
+    struct jpeg_destination_mgr jpeg_dst_manager;
+#endif /* CONFIG_VNC_JPEG */
 };
 
 static VncState *vnc_state; /* needed for info vnc */
@@ -185,6 +194,8 @@ static void vnc_flush(VncState *vs);
 static void vnc_update_client(void *opaque);
 static void vnc_client_read(void *opaque);
 static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len);
+static void buffer_reserve(Buffer *buffer, size_t len);
+static void buffer_reset(Buffer *buffer);
 
 static void vnc_colordepth(DisplayState *ds);
 
@@ -453,9 +464,172 @@ static void send_framebuffer_update_hextile(VncState *vs, 
int x, int y, int w, i
 
 }
 
+#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 int32_t read_s32(uint8_t *data, size_t offset);
+static void jpeg_row2pixel(VncState *vs, char *in, char *out, int len)
+{
+    char *pi = in;
+    char *po = out;
+    int depth = vs->serverds.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->serverds.pf.rmask) >> vs->serverds.pf.rshift)
+            << vs->clientds.pf.rbits) >> vs->serverds.pf.rbits);
+        g = ((((v & vs->serverds.pf.gmask) >> vs->serverds.pf.gshift)
+            << vs->clientds.pf.gbits) >> vs->serverds.pf.gbits);
+        b = ((((v & vs->serverds.pf.bmask) >> vs->serverds.pf.bshift)
+            << vs->clientds.pf.bbits) >> vs->serverds.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_HEXTILE:
            send_framebuffer_update_hextile(vs, x, y, w, h);
            break;
@@ -1204,7 +1378,11 @@ static void set_encodings(VncState *vs, int32_t 
*encodings, size_t n_encodings)
             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;
+            vs->vnc_encoding = enc;
             break;
         case VNC_ENCODING_DESKTOPRESIZE:
             vs->features |= VNC_FEATURE_RESIZE_MASK;
-- 
1.6.0.2





reply via email to

[Prev in Thread] Current Thread [Next in Thread]