[Spice-devel] [PATCH 1/2] server: Don't send stream frames that are identical to the last one

alexl at redhat.com alexl at redhat.com
Wed Aug 25 01:49:24 PDT 2010


From: Alexander Larsson <alexl at redhat.com>

This happens e.g. with the vlc player when paused, as it still draws
a continuous stream of frames (bug #28817). It may also help slightly
if a video contains identical frames in a row for other reasons.

We detect frame equality using the adler32 checksum. There is a small
chance of checksum collisions, but that will just cause one frame to
be skipped, or if it is the last frame it will be fixed when the stream
ends and we get the lossless data for the region.
---
 server/red_worker.c |   49 ++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 2ce15f5..89f8d42 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -57,6 +57,7 @@
 #include "demarshallers.h"
 #include "generated_marshallers.h"
 #include "zlib_encoder.h"
+#include <zlib.h>
 
 //#define COMPRESS_STAT
 //#define DUMP_BITMAP
@@ -441,6 +442,7 @@ typedef struct StreamAgent {
     int frames;
     int drops;
     int fps;
+    uint32_t last_checksum;
 } StreamAgent;
 
 typedef struct StreamClipItem {
@@ -7509,10 +7511,26 @@ static void red_display_unshare_stream_buf(DisplayChannel *display_channel)
 {
 }
 
+/* Adler32 is a 32bit checksum method from zlib. It is similar to crc32 but faster
+   and a bit less reliable. We use it to detect that a new stream frame is the
+   same as the last sent one. There is a very small chance that we'll get a
+   checksum collision and not send a frame, but that will fix itself the next frame
+   or when the stream ends and we send a lossless version of the region.
+*/
+static uint32_t init_checksum(void)
+{
+    return adler32(0, NULL, 0);
+}
+
+static uint32_t update_checksum(uint32_t checksum, uint8_t *data, size_t len)
+{
+    return adler32(checksum, data, len);
+}
+
 static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               int id, Stream *stream)
+                               int id, uint32_t *checksum, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
@@ -7542,6 +7560,8 @@ static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 
         src_line += src->left;
 
+        *checksum = update_checksum(*checksum, (uint8_t *)src_line, image_width * 4);
+
         frame_row = frame;
         for (x = 0; x < image_width; x++) {
             uint32_t pixel = *src_line++;
@@ -7559,7 +7579,7 @@ static int red_rgb32bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               int id, Stream *stream)
+                               int id, uint32_t *checksum, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
@@ -7589,6 +7609,8 @@ static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 
         src_line += src->left * 3;
 
+        *checksum = update_checksum(*checksum, (uint8_t *)src_line, image_width * 3);
+
         frame_row = frame;
         for (x = 0; x < image_width; x++) {
             /* libjpegs stores rgb, spice/win32 stores bgr */
@@ -7606,7 +7628,7 @@ static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 static int red_rgb16bpp_to_24 (RedWorker *worker, const SpiceRect *src,
                                const SpiceBitmap *image,
                                uint8_t *frame, size_t frame_stride,
-                               int id, Stream *stream)
+                               int id, uint32_t *checksum, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
@@ -7636,6 +7658,8 @@ static int red_rgb16bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 
         src_line += src->left;
 
+        *checksum = update_checksum(*checksum, (uint8_t *)src_line, image_width * 2);
+
         frame_row = frame;
         for (x = 0; x < image_width; x++) {
             uint16_t pixel = *src_line++;
@@ -7658,6 +7682,7 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
     RedWorker* worker;
     uint8_t *frame;
     size_t frame_stride;
+    uint32_t checksum;
     int n;
 
     ASSERT(stream);
@@ -7681,25 +7706,26 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
     frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
     frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
 
+    checksum = init_checksum();
     switch (image->u.bitmap.format) {
     case SPICE_BITMAP_FMT_32BIT:
         if (!red_rgb32bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
                                 &image->u.bitmap, frame, frame_stride,
-                                stream - worker->streams_buf, stream)) {
+                                stream - worker->streams_buf, &checksum, stream)) {
             return FALSE;
         }
         break;
     case SPICE_BITMAP_FMT_16BIT:
         if (!red_rgb16bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
                                 &image->u.bitmap, frame, frame_stride,
-                                stream - worker->streams_buf, stream)) {
+                                stream - worker->streams_buf, &checksum, stream)) {
             return FALSE;
         }
         break;
     case SPICE_BITMAP_FMT_24BIT:
         if (!red_rgb24bpp_to_24(worker, &drawable->red_drawable->u.copy.src_area,
                                 &image->u.bitmap, frame, frame_stride,
-                                stream - worker->streams_buf, stream)) {
+                                stream - worker->streams_buf, &checksum, stream)) {
             return FALSE;
         }
         break;
@@ -7708,6 +7734,15 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
         return FALSE;
     }
 
+    agent->lats_send_time = time_now;
+    /* Don't actually send the frame if its the same, but otherwise
+       act like we sent it. This is merely a bandwidth saver, so no change
+       in behaviour. */
+    if (checksum == agent->last_checksum) {
+        return TRUE;
+    }
+    agent->last_checksum = checksum;
+
     while ((n = mjpeg_encoder_encode_frame(stream->mjpeg_encoder,
                                            display_channel->send_data.stream_outbuf,
                                            display_channel->send_data.stream_outbuf_size)) == 0) {
@@ -7736,7 +7771,6 @@ static inline int red_send_stream_data(DisplayChannel *display_channel, Drawable
                              display_channel->send_data.stream_outbuf, n);
 
     display_begin_send_message(display_channel, NULL);
-    agent->lats_send_time = time_now;
     return TRUE;
 }
 
@@ -8062,6 +8096,7 @@ static void red_display_send_stream_start(DisplayChannel *display_channel, Strea
     Stream *stream = agent->stream;
 
     agent->lats_send_time = 0;
+    agent->last_checksum = 0;
     ASSERT(stream);
     channel->send_data.header->type = SPICE_MSG_DISPLAY_STREAM_CREATE;
     SpiceMsgDisplayStreamCreate stream_create;
-- 
1.7.2.1



More information about the Spice-devel mailing list