qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] Re: [PATCH] Add JPEG encoding to VNC server


From: Alexander Graf
Subject: [Qemu-devel] Re: [PATCH] Add JPEG encoding to VNC server
Date: Fri, 31 Jul 2009 16:50:36 +0200


On 31.07.2009, at 16:11, Anthony Liguori wrote:

Alexander Graf 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.


Most clients selectively enable the jpeg part of tight by specifying a jpeg quality encoding. They usually don't do this by default.

I know you said this before and then I checked some of my clients and they all do set quality encoding.

What I would like to see, is that we respect the quality encoding and if tight is requested without a jpeg quality, we send another type of tight update (even the compressed raw tiles would be fine).

It's important for this to be useful with normal clients and for general assumptions about how servers deal with tight to be maintained.

I agree, but this is the only "safe" way I could think of. If the client disables all but tight (jpeg), take it - otherwise don't.

We could of course define thresholds as in "tight quality > 5 means no jpeg" - but IMHO that's not exactly a good solution either.

What I'd like to see is:

-- compress several times, send only once --

Go through the different encodings the client supports and figure which one is the smallest. Only send that one.

-- progressive updates --

When playing video in my guest, I want the video data to be transferred as JPEG, the rest as zlib.
  That means for regions where we sent JPEG, we'd have to remember to

  a ) send lossless before doing CopyRect
b ) send lossless after a certain amount of time the region didn't change

So far all of these are ideas, but it might help others or me too at one point to start implementing it :-).

And the good news is I had the patch around anyways and the way it's implemented now shouldn't hurt anyone. Admittedly, it probably doesn't benefit most people either. But for most workloads zlib should actually perform better than jpeg anyways, so there's little use in taking jpeg for the full desktop.


Signed-off-by: Alexander Graf <address@hidden>
---
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


just CONFIG_JPEG please.

Ok.


+vnc_jpeg="yes"

just jpeg.

##########################################
+# 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"


That's a typeo.

typeo :)


+#ifdef CONFIG_VNC_JPEG


How difficult would it be to introduce a vnc-tight.c? vnc.h has all the necessary structure definitions.

That way, we can just avoid compiling vnc-tight.c if we don't have libjpeg instead of having tons of ifdefs.

Is it that many ifdefs?

We could probably move them to vnc-tight.c and create stubs for when it's not there - the question is if it's worth it.

+/* 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


No C99 comments please.

+    }
+}
+
+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) {


Watch the formatting.

After Stefano's series, this patch needs an update because vs- >server no longer exists.

I always hit the timing when someone else is pushing something on the same codebase :-)

Alex





reply via email to

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