[Spice-commits] 10 commits - server/mjpeg_encoder.c server/mjpeg_encoder.h server/red_worker.c

Christophe Fergau teuf at kemper.freedesktop.org
Fri Jul 22 07:55:50 PDT 2011


 server/mjpeg_encoder.c |  241 ++++++++++++++++++++++++++++++++++++++++---------
 server/mjpeg_encoder.h |   10 +-
 server/red_worker.c    |  166 +++------------------------------
 3 files changed, 222 insertions(+), 195 deletions(-)

New commits:
commit 47684d2885caf43fcc2aa6ac51c07e6876b12db3
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Wed Jul 13 11:05:02 2011 +0200

    mjpeg: rename jpeg_mem_dest
    
    jpeg_mem_dest is a public symbol in libjpeg8 so using it with
    no prefix will cause symbol clashes. Rename it to spice_jpeg_mem_dest.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index ed242db..ea2e15f 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -163,8 +163,8 @@ static void term_mem_destination(j_compress_ptr cinfo)
  */
 
 static void
-jpeg_mem_dest (j_compress_ptr cinfo,
-	       unsigned char ** outbuffer, unsigned long * outsize)
+spice_jpeg_mem_dest(j_compress_ptr cinfo,
+                    unsigned char ** outbuffer, unsigned long * outsize)
 {
   mem_destination_mgr *dest;
 #define OUTPUT_BUF_SIZE  4096	/* choose an efficiently fwrite'able size */
@@ -241,7 +241,7 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
         encoder->row = spice_malloc(stride);
     }
 
-    jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
+    spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
 
     encoder->cinfo.image_width      = encoder->width;
     encoder->cinfo.image_height     = encoder->height;
commit c12bafbc5326babf3cbab96d6d1537075c49c927
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Jun 28 14:28:22 2011 +0200

    mjpeg_encoder: allocate "row" on demand
    
    It's not used when we use jpeg-turbo colorspaces, so it's better
    to allocate it when we know we'll need it rather than always
    allocating it even if it won't be used.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index 8b323ed..ed242db 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -27,7 +27,6 @@
 struct MJpegEncoder {
     int width;
     int height;
-    int stride;
     uint8_t *row;
     int first_frame;
     int quality;
@@ -48,15 +47,8 @@ MJpegEncoder *mjpeg_encoder_new(int width, int height)
     enc->first_frame = TRUE;
     enc->width = width;
     enc->height = height;
-    enc->stride = width * 3;
     enc->quality = 70;
-    if (enc->stride < width) {
-        abort();
-    }
-    enc->row = spice_malloc(enc->stride);
-
     enc->cinfo.err = jpeg_std_error(&enc->jerr);
-
     jpeg_create_compress(&enc->cinfo);
 
     return enc;
@@ -240,6 +232,15 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
         return FALSE;
     }
 
+    if ((encoder->pixel_converter != NULL) && (encoder->row == NULL)) {
+        unsigned int stride = encoder->width * 3;
+        /* check for integer overflow */
+        if (stride < encoder->width) {
+            return FALSE;
+        }
+        encoder->row = spice_malloc(stride);
+    }
+
     jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
 
     encoder->cinfo.image_width      = encoder->width;
commit 3a433912e91727c5cc733f5c0305b3453f2b2f8c
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Jun 28 12:59:02 2011 +0200

    mjpeg_encoder: remove unused functions
    
    After the refactoring to optionally use libjpeg-turbo, some
    of the functions that mjpeg-encoder used to provide are now no
    longer used. This commit removes them.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index d3ddf3f..8b323ed 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -28,7 +28,6 @@ struct MJpegEncoder {
     int width;
     int height;
     int stride;
-    uint8_t *frame;
     uint8_t *row;
     int first_frame;
     int quality;
@@ -54,7 +53,6 @@ MJpegEncoder *mjpeg_encoder_new(int width, int height)
     if (enc->stride < width) {
         abort();
     }
-    enc->frame = spice_malloc_n(enc->stride, height);
     enc->row = spice_malloc(enc->stride);
 
     enc->cinfo.err = jpeg_std_error(&enc->jerr);
@@ -67,20 +65,10 @@ MJpegEncoder *mjpeg_encoder_new(int width, int height)
 void mjpeg_encoder_destroy(MJpegEncoder *encoder)
 {
     jpeg_destroy_compress(&encoder->cinfo);
-    free(encoder->frame);
     free(encoder->row);
     free(encoder);
 }
 
-uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder)
-{
-    return encoder->frame;
-}
-size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
-{
-    return encoder->stride;
-}
-
 uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
 {
     return encoder->bytes_per_pixel;
@@ -299,37 +287,3 @@ size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
     encoder->first_frame = FALSE;
     return dest->pub.next_output_byte - dest->buffer;
 }
-
-int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               uint8_t **buffer, size_t *buffer_len)
-{
-    uint8_t *frame;
-    int n;
-
-    jpeg_mem_dest(&encoder->cinfo, buffer, buffer_len);
-
-    encoder->cinfo.image_width      = encoder->width;
-    encoder->cinfo.image_height     = encoder->height;
-    encoder->cinfo.input_components = 3;
-    encoder->cinfo.in_color_space   = JCS_RGB;
-
-    jpeg_set_defaults(&encoder->cinfo);
-    encoder->cinfo.dct_method       = JDCT_IFAST;
-    jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
-    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
-
-    frame = encoder->frame;
-    while (encoder->cinfo.next_scanline < encoder->cinfo.image_height) {
-        n = jpeg_write_scanlines(&encoder->cinfo, &frame, 1);
-        if (n == 0) { /* Not enough space */
-            jpeg_abort_compress(&encoder->cinfo);
-            return 0;
-        }
-        frame += encoder->stride;
-    }
-
-    jpeg_finish_compress(&encoder->cinfo);
-
-    encoder->first_frame = FALSE;
-    return encoder->cinfo.dest->next_output_byte - *buffer;
-}
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
index 0773a14..c43827f 100644
--- a/server/mjpeg_encoder.h
+++ b/server/mjpeg_encoder.h
@@ -26,11 +26,7 @@ typedef struct MJpegEncoder MJpegEncoder;
 MJpegEncoder *mjpeg_encoder_new(int width, int height);
 void mjpeg_encoder_destroy(MJpegEncoder *encoder);
 
-uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder);
-size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder);
 uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder);
-int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               uint8_t **buffer, size_t *buffer_len);
 int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
                               uint8_t **dest, size_t *dest_len);
 int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,
commit ad55230a2d970edb859d646f10c0f6baf18aa41c
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jun 27 18:38:51 2011 +0200

    mjpeg_encoder: use libjpeg-turbo extra colorspaces
    
    When libjpeg-turbo is available, we can use the BGR and BGRX
    colorspaces that it provides to avoid extra conversions of the
    data we want to compress to mjpeg

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index bfe8ca7..d3ddf3f 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -86,7 +86,7 @@ uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
     return encoder->bytes_per_pixel;
 }
 
-
+#ifndef JCS_EXTENSIONS
 /* Pixel conversion routines */
 static void pixel_rgb24bpp_to_24(uint8_t *src, uint8_t *dest)
 {
@@ -103,6 +103,7 @@ static void pixel_rgb32bpp_to_24(uint8_t *src, uint8_t *dest)
     *dest++ = (pixel >>  8) & 0xff;
     *dest++ = (pixel >>  0) & 0xff;
 }
+#endif
 
 static void pixel_rgb16bpp_to_24(uint8_t *src, uint8_t *dest)
 {
@@ -222,10 +223,17 @@ jpeg_mem_dest (j_compress_ptr cinfo,
 int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
                               uint8_t **dest, size_t *dest_len)
 {
+    encoder->cinfo.in_color_space   = JCS_RGB;
+    encoder->cinfo.input_components = 3;
     switch (format) {
     case SPICE_BITMAP_FMT_32BIT:
         encoder->bytes_per_pixel = 4;
+#ifdef JCS_EXTENSIONS
+        encoder->cinfo.in_color_space   = JCS_EXT_BGRX;
+        encoder->cinfo.input_components = 4;
+#else
         encoder->pixel_converter = pixel_rgb32bpp_to_24;
+#endif
         break;
     case SPICE_BITMAP_FMT_16BIT:
         encoder->bytes_per_pixel = 2;
@@ -233,7 +241,11 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
         break;
     case SPICE_BITMAP_FMT_24BIT:
         encoder->bytes_per_pixel = 3;
+#ifdef JCS_EXTENSIONS
+        encoder->cinfo.in_color_space = JCS_EXT_BGR;
+#else
         encoder->pixel_converter = pixel_rgb24bpp_to_24;
+#endif
         break;
     default:
         red_printf_some(1000, "unsupported format %d", format);
@@ -244,9 +256,6 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
 
     encoder->cinfo.image_width      = encoder->width;
     encoder->cinfo.image_height     = encoder->height;
-    encoder->cinfo.input_components = 3;
-    encoder->cinfo.in_color_space   = JCS_RGB;
-
     jpeg_set_defaults(&encoder->cinfo);
     encoder->cinfo.dct_method       = JDCT_IFAST;
     jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
@@ -269,8 +278,10 @@ int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,
             row += 3;
             src_pixels += encoder->bytes_per_pixel;
         }
+        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
+    } else {
+        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &src_pixels, 1);
     }
-    scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
     if (scanlines_written == 0) { /* Not enough space */
         jpeg_abort_compress(&encoder->cinfo);
         return 0;
commit b0cd4f4883b86327bb86fce5422ab2474128aac6
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Jun 24 15:00:45 2011 +0200

    red_worker: use new mjpeg_encoder_encode_scanline API
    
    The main point is to move the pixel conversion code into
    the MjpegEncoder class to be able to make use libjpeg-turbo
    additional pixel formats without the reds_worker code noticing.

diff --git a/server/red_worker.c b/server/red_worker.c
index c17194e..6737940 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -7311,47 +7311,18 @@ static inline uint8_t *red_get_image_line(RedWorker *worker, SpiceChunks *chunks
     return ret;
 }
 
-static void pixel_rgb24bpp_to_24(uint8_t *src, uint8_t *dest)
-{
-    /* libjpegs stores rgb, spice/win32 stores bgr */
-    *dest++ = src[2]; /* red */
-    *dest++ = src[1]; /* green */
-    *dest++ = src[0]; /* blue */
-}
-
-static void pixel_rgb32bpp_to_24(uint8_t *src, uint8_t *dest)
-{
-    uint32_t pixel = *(uint32_t *)src;
-    *dest++ = (pixel >> 16) & 0xff;
-    *dest++ = (pixel >>  8) & 0xff;
-    *dest++ = (pixel >>  0) & 0xff;
-}
-
-static void pixel_rgb16bpp_to_24(uint8_t *src, uint8_t *dest)
-{
-    uint16_t pixel = *(uint16_t *)src;
-    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
-    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
-    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
-}
-
-static int red_rgb_to_24bpp (RedWorker *worker, const SpiceRect *src,
-                             const SpiceBitmap *image, Stream *stream)
+static int encode_frame (RedWorker *worker, const SpiceRect *src,
+                         const SpiceBitmap *image, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
-    uint8_t *frame_row;
     size_t offset;
-    int i, x, chunk;
-    uint8_t *frame;
-    size_t frame_stride;
+    int i, chunk;
 
     chunks = image->data;
     offset = 0;
     chunk = 0;
     image_stride = image->stride;
-    frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
-    frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
 
     const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
     for (i = 0; i < skip_lines; i++) {
@@ -7360,27 +7331,7 @@ static int red_rgb_to_24bpp (RedWorker *worker, const SpiceRect *src,
 
     const int image_height = src->bottom - src->top;
     const int image_width = src->right - src->left;
-    unsigned int bytes_per_pixel;
-    void (*pixel_converter)(uint8_t *src, uint8_t *dest);
-
 
-    switch (image->format) {
-    case SPICE_BITMAP_FMT_32BIT:
-        bytes_per_pixel = 4;
-        pixel_converter = pixel_rgb32bpp_to_24;
-        break;
-    case SPICE_BITMAP_FMT_16BIT:
-        bytes_per_pixel = 2;
-        pixel_converter = pixel_rgb16bpp_to_24;
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        bytes_per_pixel = 3;
-        pixel_converter = pixel_rgb24bpp_to_24;
-        break;
-    default:
-        red_printf_some(1000, "unsupported format %d", image->format);
-        return FALSE;
-    }
     for (i = 0; i < image_height; i++) {
         uint8_t *src_line =
             (uint8_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
@@ -7389,16 +7340,9 @@ static int red_rgb_to_24bpp (RedWorker *worker, const SpiceRect *src,
             return FALSE;
         }
 
-        src_line += src->left * bytes_per_pixel;
-
-        frame_row = frame;
-        for (x = 0; x < image_width; x++) {
-            pixel_converter(src_line, frame_row);
-            frame_row += 3;
-            src_line += bytes_per_pixel;
-        }
-
-        frame += frame_stride;
+        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(stream->mjpeg_encoder);
+        if (mjpeg_encoder_encode_scanline(stream->mjpeg_encoder, src_line, image_width) == 0)
+            return FALSE;
     }
 
     return TRUE;
@@ -7432,20 +7376,17 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
         return TRUE;
     }
 
-    if (!red_rgb_to_24bpp(worker, &drawable->red_drawable->u.copy.src_area,
-                          &image->u.bitmap, stream)) {
-        return FALSE;
-    }
-
-
     outbuf_size = display_channel->send_data.stream_outbuf_size;
-    n = mjpeg_encoder_encode_frame(stream->mjpeg_encoder,
+    if (!mjpeg_encoder_start_frame(stream->mjpeg_encoder, image->u.bitmap.format,
                                    &display_channel->send_data.stream_outbuf,
-                                   &outbuf_size);
-    if (n == 0) {
-        red_printf("failed to encode frame, out of memory?");
+                                   &outbuf_size)) {
+        return FALSE;
+    }
+    if (!encode_frame(worker, &drawable->red_drawable->u.copy.src_area,
+                      &image->u.bitmap, stream)) {
         return FALSE;
     }
+    n = mjpeg_encoder_end_frame(stream->mjpeg_encoder);
     display_channel->send_data.stream_outbuf_size = outbuf_size;
 
     red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
commit d4305e16adce395bb105d8d4210c286c93780f30
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Jun 24 16:00:12 2011 +0200

    mjpeg_encoder: add mjpeg_encoder_get_bytes_per_pixel
    
    Returns the number of bytes per pixel corresponding to the input
    data format.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index 8e74e0a..bfe8ca7 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -81,6 +81,11 @@ size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
     return encoder->stride;
 }
 
+uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
+{
+    return encoder->bytes_per_pixel;
+}
+
 
 /* Pixel conversion routines */
 static void pixel_rgb24bpp_to_24(uint8_t *src, uint8_t *dest)
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
index 03b25cc..0773a14 100644
--- a/server/mjpeg_encoder.h
+++ b/server/mjpeg_encoder.h
@@ -28,6 +28,7 @@ void mjpeg_encoder_destroy(MJpegEncoder *encoder);
 
 uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder);
 size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder);
+uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder);
 int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
                                uint8_t **buffer, size_t *buffer_len);
 int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
commit 4275095ff956068027af10d26b966d3e23887619
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Jun 24 14:48:24 2011 +0200

    mjpeg_encoder: add mjpeg_encoder_encode_scanline
    
    This API is meant to allow us to move the pixel format conversion
    into MjpegEncoder. This will allow us to be able to use the
    additional pixel formats from libjpeg-turbo when available.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index ae48da5..8e74e0a 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -29,11 +29,15 @@ struct MJpegEncoder {
     int height;
     int stride;
     uint8_t *frame;
+    uint8_t *row;
     int first_frame;
     int quality;
 
     struct jpeg_compress_struct cinfo;
     struct jpeg_error_mgr jerr;
+
+    unsigned int bytes_per_pixel; /* bytes per pixel of the input buffer */
+    void (*pixel_converter)(uint8_t *src, uint8_t *dest);
 };
 
 MJpegEncoder *mjpeg_encoder_new(int width, int height)
@@ -51,6 +55,7 @@ MJpegEncoder *mjpeg_encoder_new(int width, int height)
         abort();
     }
     enc->frame = spice_malloc_n(enc->stride, height);
+    enc->row = spice_malloc(enc->stride);
 
     enc->cinfo.err = jpeg_std_error(&enc->jerr);
 
@@ -63,6 +68,7 @@ void mjpeg_encoder_destroy(MJpegEncoder *encoder)
 {
     jpeg_destroy_compress(&encoder->cinfo);
     free(encoder->frame);
+    free(encoder->row);
     free(encoder);
 }
 
@@ -76,6 +82,32 @@ size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
 }
 
 
+/* Pixel conversion routines */
+static void pixel_rgb24bpp_to_24(uint8_t *src, uint8_t *dest)
+{
+    /* libjpegs stores rgb, spice/win32 stores bgr */
+    *dest++ = src[2]; /* red */
+    *dest++ = src[1]; /* green */
+    *dest++ = src[0]; /* blue */
+}
+
+static void pixel_rgb32bpp_to_24(uint8_t *src, uint8_t *dest)
+{
+    uint32_t pixel = *(uint32_t *)src;
+    *dest++ = (pixel >> 16) & 0xff;
+    *dest++ = (pixel >>  8) & 0xff;
+    *dest++ = (pixel >>  0) & 0xff;
+}
+
+static void pixel_rgb16bpp_to_24(uint8_t *src, uint8_t *dest)
+{
+    uint16_t pixel = *(uint16_t *)src;
+    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
+    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
+    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
+}
+
+
 /* code from libjpeg 8 to handle compression to a memory buffer
  *
  * Copyright (C) 1994-1996, Thomas G. Lane.
@@ -182,6 +214,76 @@ jpeg_mem_dest (j_compress_ptr cinfo,
 }
 /* end of code from libjpeg */
 
+int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
+                              uint8_t **dest, size_t *dest_len)
+{
+    switch (format) {
+    case SPICE_BITMAP_FMT_32BIT:
+        encoder->bytes_per_pixel = 4;
+        encoder->pixel_converter = pixel_rgb32bpp_to_24;
+        break;
+    case SPICE_BITMAP_FMT_16BIT:
+        encoder->bytes_per_pixel = 2;
+        encoder->pixel_converter = pixel_rgb16bpp_to_24;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        encoder->bytes_per_pixel = 3;
+        encoder->pixel_converter = pixel_rgb24bpp_to_24;
+        break;
+    default:
+        red_printf_some(1000, "unsupported format %d", format);
+        return FALSE;
+    }
+
+    jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
+
+    encoder->cinfo.image_width      = encoder->width;
+    encoder->cinfo.image_height     = encoder->height;
+    encoder->cinfo.input_components = 3;
+    encoder->cinfo.in_color_space   = JCS_RGB;
+
+    jpeg_set_defaults(&encoder->cinfo);
+    encoder->cinfo.dct_method       = JDCT_IFAST;
+    jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
+    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
+
+    return TRUE;
+}
+
+int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,
+                                  size_t image_width)
+{
+    unsigned int scanlines_written;
+    uint8_t *row;
+
+    row = encoder->row;
+    if (encoder->pixel_converter) {
+        unsigned int x;
+        for (x = 0; x < image_width; x++) {
+            encoder->pixel_converter(src_pixels, row);
+            row += 3;
+            src_pixels += encoder->bytes_per_pixel;
+        }
+    }
+    scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
+    if (scanlines_written == 0) { /* Not enough space */
+        jpeg_abort_compress(&encoder->cinfo);
+        return 0;
+    }
+
+    return scanlines_written;
+}
+
+size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
+{
+    mem_destination_mgr *dest = (mem_destination_mgr *) encoder->cinfo.dest;
+
+    jpeg_finish_compress(&encoder->cinfo);
+
+    encoder->first_frame = FALSE;
+    return dest->pub.next_output_byte - dest->buffer;
+}
+
 int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
                                uint8_t **buffer, size_t *buffer_len)
 {
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
index cd8f6af..03b25cc 100644
--- a/server/mjpeg_encoder.h
+++ b/server/mjpeg_encoder.h
@@ -30,6 +30,11 @@ uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder);
 size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder);
 int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
                                uint8_t **buffer, size_t *buffer_len);
+int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
+                              uint8_t **dest, size_t *dest_len);
+int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,
+                                  size_t image_width);
+size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder);
 
 
 #endif
commit cacca4dce6be474a2e564eba544af8222228f492
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Jun 24 14:20:32 2011 +0200

    red_worker: simplify red_rgb_to_24bpp prototype
    
    It takes a lot of arguments, "id" is unused, "frame" and
    "frame_size" can be obtained from the "stream" argument, so
    can get rid of 3 arguments to make things more readable.

diff --git a/server/red_worker.c b/server/red_worker.c
index 817fc41..c17194e 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -7336,20 +7336,22 @@ static void pixel_rgb16bpp_to_24(uint8_t *src, uint8_t *dest)
 }
 
 static int red_rgb_to_24bpp (RedWorker *worker, const SpiceRect *src,
-                             const SpiceBitmap *image,
-                             uint8_t *frame, size_t frame_stride,
-                             int id, Stream *stream)
+                             const SpiceBitmap *image, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
     uint8_t *frame_row;
     size_t offset;
     int i, x, chunk;
+    uint8_t *frame;
+    size_t frame_stride;
 
     chunks = image->data;
     offset = 0;
     chunk = 0;
     image_stride = image->stride;
+    frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
+    frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
 
     const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
     for (i = 0; i < skip_lines; i++) {
@@ -7409,8 +7411,6 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
     SpiceImage *image;
     RedChannel *channel;
     RedWorker* worker;
-    uint8_t *frame;
-    size_t frame_stride;
     int n;
 
     ASSERT(stream);
@@ -7432,12 +7432,8 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
         return TRUE;
     }
 
-    frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
-    frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
-
     if (!red_rgb_to_24bpp(worker, &drawable->red_drawable->u.copy.src_area,
-                          &image->u.bitmap, frame, frame_stride,
-                          stream - worker->streams_buf, stream)) {
+                          &image->u.bitmap, stream)) {
         return FALSE;
     }
 
commit 1d808d31f42925ff3dcbc3504bc2c9c49ebd2ef7
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Jun 24 12:13:03 2011 +0200

    mjpeg_encoder: rework output buffer allocation
    
    When encoding a frame, red_worker passes an allocated buffer to
    libjpeg where it should encode the frame. When it fails, a new
    bigger buffer is allocated and the encoding is restarted from
    scratch. However, it's possible to use libjpeg to realloc this
    buffer if it gets too small during the encoding process. Make use
    of this feature, especially since it will make it easier to encore
    one line at a time instead of a full frame in subsequent commits.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index 30f15ef..ae48da5 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -21,6 +21,7 @@
 
 #include "red_common.h"
 #include "mjpeg_encoder.h"
+#include <jerror.h>
 #include <jpeglib.h>
 
 struct MJpegEncoder {
@@ -74,33 +75,120 @@ size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder)
     return encoder->stride;
 }
 
-static void init_destination(j_compress_ptr cinfo)
+
+/* code from libjpeg 8 to handle compression to a memory buffer
+ *
+ * Copyright (C) 1994-1996, Thomas G. Lane.
+ * Modified 2009 by Guido Vollbeding.
+ * This file is part of the Independent JPEG Group's software.
+ */
+typedef struct {
+  struct jpeg_destination_mgr pub; /* public fields */
+
+  unsigned char ** outbuffer;	/* target buffer */
+  unsigned long * outsize;
+  unsigned char * newbuffer;	/* newly allocated buffer */
+  uint8_t * buffer;		/* start of buffer */
+  size_t bufsize;
+} mem_destination_mgr;
+
+static void init_mem_destination(j_compress_ptr cinfo)
 {
 }
 
-static boolean empty_output_buffer(j_compress_ptr cinfo)
+static boolean empty_mem_output_buffer(j_compress_ptr cinfo)
 {
-    return FALSE;
+  size_t nextsize;
+  uint8_t * nextbuffer;
+  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
+
+  /* Try to allocate new buffer with double size */
+  nextsize = dest->bufsize * 2;
+  nextbuffer = malloc(nextsize);
+
+  if (nextbuffer == NULL)
+    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
+
+  memcpy(nextbuffer, dest->buffer, dest->bufsize);
+
+  if (dest->newbuffer != NULL)
+    free(dest->newbuffer);
+
+  dest->newbuffer = nextbuffer;
+
+  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
+  dest->pub.free_in_buffer = dest->bufsize;
+
+  dest->buffer = nextbuffer;
+  dest->bufsize = nextsize;
+
+  return TRUE;
+}
+
+static void term_mem_destination(j_compress_ptr cinfo)
+{
+  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
+
+  *dest->outbuffer = dest->buffer;
+  *dest->outsize = dest->bufsize - dest->pub.free_in_buffer;
 }
 
-static void term_destination(j_compress_ptr cinfo)
+/*
+ * Prepare for output to a memory buffer.
+ * The caller may supply an own initial buffer with appropriate size.
+ * Otherwise, or when the actual data output exceeds the given size,
+ * the library adapts the buffer size as necessary.
+ * The standard library functions malloc/free are used for allocating
+ * larger memory, so the buffer is available to the application after
+ * finishing compression, and then the application is responsible for
+ * freeing the requested memory.
+ */
+
+static void
+jpeg_mem_dest (j_compress_ptr cinfo,
+	       unsigned char ** outbuffer, unsigned long * outsize)
 {
+  mem_destination_mgr *dest;
+#define OUTPUT_BUF_SIZE  4096	/* choose an efficiently fwrite'able size */
+
+  if (outbuffer == NULL || outsize == NULL)	/* sanity check */
+    ERREXIT(cinfo, JERR_BUFFER_SIZE);
+
+  /* The destination object is made permanent so that multiple JPEG images
+   * can be written to the same buffer without re-executing jpeg_mem_dest.
+   */
+  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
+    cinfo->dest = spice_malloc(sizeof(mem_destination_mgr));
+  }
+
+  dest = (mem_destination_mgr *) cinfo->dest;
+  dest->pub.init_destination = init_mem_destination;
+  dest->pub.empty_output_buffer = empty_mem_output_buffer;
+  dest->pub.term_destination = term_mem_destination;
+  dest->outbuffer = outbuffer;
+  dest->outsize = outsize;
+  dest->newbuffer = NULL;
+
+  if (*outbuffer == NULL || *outsize == 0) {
+    /* Allocate initial buffer */
+    dest->newbuffer = *outbuffer = malloc(OUTPUT_BUF_SIZE);
+    if (dest->newbuffer == NULL)
+      ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
+    *outsize = OUTPUT_BUF_SIZE;
+  }
+
+  dest->pub.next_output_byte = dest->buffer = *outbuffer;
+  dest->pub.free_in_buffer = dest->bufsize = *outsize;
 }
+/* end of code from libjpeg */
 
 int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               uint8_t *buffer, size_t buffer_len)
+                               uint8_t **buffer, size_t *buffer_len)
 {
-    struct jpeg_destination_mgr destmgr;
     uint8_t *frame;
     int n;
 
-    destmgr.next_output_byte = buffer;
-    destmgr.free_in_buffer = buffer_len;
-    destmgr.init_destination = init_destination;
-    destmgr.empty_output_buffer = empty_output_buffer;
-    destmgr.term_destination = term_destination;
-
-    encoder->cinfo.dest = &destmgr;
+    jpeg_mem_dest(&encoder->cinfo, buffer, buffer_len);
 
     encoder->cinfo.image_width      = encoder->width;
     encoder->cinfo.image_height     = encoder->height;
@@ -125,5 +213,5 @@ int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
     jpeg_finish_compress(&encoder->cinfo);
 
     encoder->first_frame = FALSE;
-    return destmgr.next_output_byte - buffer;
+    return encoder->cinfo.dest->next_output_byte - *buffer;
 }
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
index da1966d..cd8f6af 100644
--- a/server/mjpeg_encoder.h
+++ b/server/mjpeg_encoder.h
@@ -29,7 +29,7 @@ void mjpeg_encoder_destroy(MJpegEncoder *encoder);
 uint8_t *mjpeg_encoder_get_frame(MJpegEncoder *encoder);
 size_t mjpeg_encoder_get_frame_stride(MJpegEncoder *encoder);
 int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               uint8_t *buffer, size_t buffer_len);
+                               uint8_t **buffer, size_t *buffer_len);
 
 
 #endif
diff --git a/server/red_worker.c b/server/red_worker.c
index 07d1d9f..817fc41 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -7426,6 +7426,7 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
 
     StreamAgent *agent = &display_channel->stream_agents[stream - worker->streams_buf];
     uint64_t time_now = red_now();
+    size_t outbuf_size;
     if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
         agent->frames--;
         return TRUE;
@@ -7440,19 +7441,16 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
         return FALSE;
     }
 
-    while ((n = mjpeg_encoder_encode_frame(stream->mjpeg_encoder,
-                                           display_channel->send_data.stream_outbuf,
-                                           display_channel->send_data.stream_outbuf_size)) == 0) {
-        uint8_t *new_buf;
-        size_t new_size;
 
-        new_size = display_channel->send_data.stream_outbuf_size * 2;
-        new_buf = spice_malloc(new_size);
-
-        free(display_channel->send_data.stream_outbuf);
-        display_channel->send_data.stream_outbuf = new_buf;
-        display_channel->send_data.stream_outbuf_size = new_size;
+    outbuf_size = display_channel->send_data.stream_outbuf_size;
+    n = mjpeg_encoder_encode_frame(stream->mjpeg_encoder,
+                                   &display_channel->send_data.stream_outbuf,
+                                   &outbuf_size);
+    if (n == 0) {
+        red_printf("failed to encode frame, out of memory?");
+        return FALSE;
     }
+    display_channel->send_data.stream_outbuf_size = outbuf_size;
 
     red_channel_init_send_data(channel, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
 
commit 22743bee9f8041dd0003ae25c530df268d57abd6
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Thu Jun 23 18:22:34 2011 +0200

    red_worker: factor pixel conversion code
    
    When encoding to mjpeg, the on screen data have to be converted
    to 24bpp RGB since that's the format that libjpeg expects. Factor
    as much code as possible for the 3 formats we handle.

diff --git a/server/red_worker.c b/server/red_worker.c
index 433af2c..07d1d9f 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -7311,57 +7311,34 @@ static inline uint8_t *red_get_image_line(RedWorker *worker, SpiceChunks *chunks
     return ret;
 }
 
-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)
+static void pixel_rgb24bpp_to_24(uint8_t *src, uint8_t *dest)
 {
-    SpiceChunks *chunks;
-    uint32_t image_stride;
-    uint8_t *frame_row;
-    size_t offset;
-    int i, x, chunk;
-
-    chunks = image->data;
-    offset = 0;
-    chunk = 0;
-    image_stride = image->stride;
-
-    const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
-    for (i = 0; i < skip_lines; i++) {
-        red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
-    }
-
-    const int image_height = src->bottom - src->top;
-    const int image_width = src->right - src->left;
-    for (i = 0; i < image_height; i++) {
-        uint32_t *src_line =
-            (uint32_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
-
-        if (!src_line) {
-            return FALSE;
-        }
-
-        src_line += src->left;
-
-        frame_row = frame;
-        for (x = 0; x < image_width; x++) {
-            uint32_t pixel = *src_line++;
-            *frame_row++ = (pixel >> 16) & 0xff;
-            *frame_row++ = (pixel >>  8) & 0xff;
-            *frame_row++ = (pixel >>  0) & 0xff;
-        }
+    /* libjpegs stores rgb, spice/win32 stores bgr */
+    *dest++ = src[2]; /* red */
+    *dest++ = src[1]; /* green */
+    *dest++ = src[0]; /* blue */
+}
 
-        frame += frame_stride;
-    }
+static void pixel_rgb32bpp_to_24(uint8_t *src, uint8_t *dest)
+{
+    uint32_t pixel = *(uint32_t *)src;
+    *dest++ = (pixel >> 16) & 0xff;
+    *dest++ = (pixel >>  8) & 0xff;
+    *dest++ = (pixel >>  0) & 0xff;
+}
 
-    return TRUE;
+static void pixel_rgb16bpp_to_24(uint8_t *src, uint8_t *dest)
+{
+    uint16_t pixel = *(uint16_t *)src;
+    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
+    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
+    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
 }
 
-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)
+static int red_rgb_to_24bpp (RedWorker *worker, const SpiceRect *src,
+                             const SpiceBitmap *image,
+                             uint8_t *frame, size_t frame_stride,
+                             int id, Stream *stream)
 {
     SpiceChunks *chunks;
     uint32_t image_stride;
@@ -7381,69 +7358,42 @@ static int red_rgb24bpp_to_24 (RedWorker *worker, const SpiceRect *src,
 
     const int image_height = src->bottom - src->top;
     const int image_width = src->right - src->left;
-    for (i = 0; i < image_height; i++) {
-        uint8_t *src_line =
-            (uint8_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
-
-        if (!src_line) {
-            return FALSE;
-        }
-
-        src_line += src->left * 3;
-
-        frame_row = frame;
-        for (x = 0; x < image_width; x++) {
-            /* libjpegs stores rgb, spice/win32 stores bgr */
-            *frame_row++ = src_line[2]; /* red */
-            *frame_row++ = src_line[1]; /* green */
-            *frame_row++ = src_line[0]; /* blue */
-            src_line += 3;
-        }
-        frame += frame_stride;
-    }
-
-    return TRUE;
-}
+    unsigned int bytes_per_pixel;
+    void (*pixel_converter)(uint8_t *src, uint8_t *dest);
 
-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)
-{
-    SpiceChunks *chunks;
-    uint32_t image_stride;
-    uint8_t *frame_row;
-    size_t offset;
-    int i, x, chunk;
 
-    chunks = image->data;
-    offset = 0;
-    chunk = 0;
-    image_stride = image->stride;
-
-    const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);
-    for (i = 0; i < skip_lines; i++) {
-        red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
+    switch (image->format) {
+    case SPICE_BITMAP_FMT_32BIT:
+        bytes_per_pixel = 4;
+        pixel_converter = pixel_rgb32bpp_to_24;
+        break;
+    case SPICE_BITMAP_FMT_16BIT:
+        bytes_per_pixel = 2;
+        pixel_converter = pixel_rgb16bpp_to_24;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        bytes_per_pixel = 3;
+        pixel_converter = pixel_rgb24bpp_to_24;
+        break;
+    default:
+        red_printf_some(1000, "unsupported format %d", image->format);
+        return FALSE;
     }
-
-    const int image_height = src->bottom - src->top;
-    const int image_width = src->right - src->left;
     for (i = 0; i < image_height; i++) {
-        uint16_t *src_line =
-            (uint16_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
+        uint8_t *src_line =
+            (uint8_t *)red_get_image_line(worker, chunks, &offset, &chunk, image_stride);
 
         if (!src_line) {
             return FALSE;
         }
 
-        src_line += src->left;
+        src_line += src->left * bytes_per_pixel;
 
         frame_row = frame;
         for (x = 0; x < image_width; x++) {
-            uint16_t pixel = *src_line++;
-            *frame_row++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
-            *frame_row++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
-            *frame_row++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
+            pixel_converter(src_line, frame_row);
+            frame_row += 3;
+            src_line += bytes_per_pixel;
         }
 
         frame += frame_stride;
@@ -7484,30 +7434,9 @@ static inline int red_send_stream_data(DisplayChannel *display_channel,
     frame = mjpeg_encoder_get_frame(stream->mjpeg_encoder);
     frame_stride = mjpeg_encoder_get_frame_stride(stream->mjpeg_encoder);
 
-    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)) {
-            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)) {
-            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)) {
-            return FALSE;
-        }
-        break;
-    default:
-        red_printf_some(1000, "unsupported format %d", image->u.bitmap.format);
+    if (!red_rgb_to_24bpp(worker, &drawable->red_drawable->u.copy.src_area,
+                          &image->u.bitmap, frame, frame_stride,
+                          stream - worker->streams_buf, stream)) {
         return FALSE;
     }
 


More information about the Spice-commits mailing list