<div dir="ltr">looks good<br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, May 13, 2015 at 10:27 PM, Francois Gouget <span dir="ltr"><<a href="mailto:fgouget@codeweavers.com" target="_blank">fgouget@codeweavers.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">This patch simply replaces the mjpeg_encoder_xxx() call points with a<br>
more object oriented design.<br>
---<br>
 server/Makefile.am     |   2 +-<br>
 server/mjpeg_encoder.c | 134 +++++++++++++++++++++++++++++++++++++---<br>
 server/mjpeg_encoder.h | 114 ----------------------------------<br>
 server/red_worker.c    | 156 ++++++++++++++---------------------------------<br>
 server/video_encoder.h | 162 +++++++++++++++++++++++++++++++++++++++++++++++++<br>
 5 files changed, 331 insertions(+), 237 deletions(-)<br>
 delete mode 100644 server/mjpeg_encoder.h<br>
 create mode 100644 server/video_encoder.h<br>
<br>
diff --git a/server/Makefile.am b/server/Makefile.am<br>
index 89a375c..36b6d45 100644<br>
--- a/server/Makefile.am<br>
+++ b/server/Makefile.am<br>
@@ -81,7 +81,6 @@ libspice_server_la_SOURCES =                  \<br>
        main_channel.c                          \<br>
        main_channel.h                          \<br>
        mjpeg_encoder.c                         \<br>
-       mjpeg_encoder.h                         \<br>
        red_bitmap_utils.h                      \<br>
        red_channel.c                           \<br>
        red_channel.h                           \<br>
@@ -115,6 +114,7 @@ libspice_server_la_SOURCES =                        \<br>
        spicevmc.c                              \<br>
        spice_timer_queue.c                     \<br>
        spice_timer_queue.h                     \<br>
+       video_encoder.h                         \<br>
        zlib_encoder.c                          \<br>
        zlib_encoder.h                          \<br>
        spice_bitmap_utils.h            \<br>
diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c<br>
index 9a41ef3..d734520 100644<br>
--- a/server/mjpeg_encoder.c<br>
+++ b/server/mjpeg_encoder.c<br>
@@ -20,7 +20,11 @@<br>
 #endif<br>
<br>
 #include "red_common.h"<br>
-#include "mjpeg_encoder.h"<br>
+<br>
+typedef struct MJpegEncoder MJpegEncoder;<br>
+#define VIDEO_ENCODER_T MJpegEncoder<br>
+#include "video_encoder.h"<br>
+<br>
 #include <jerror.h><br>
 #include <jpeglib.h><br>
 #include <inttypes.h><br>
@@ -154,6 +158,7 @@ typedef struct MJpegEncoderRateControl {<br>
 } MJpegEncoderRateControl;<br>
<br>
 struct MJpegEncoder {<br>
+    VideoEncoder base;<br>
     uint8_t *row;<br>
     uint32_t row_size;<br>
     int first_frame;<br>
@@ -165,7 +170,7 @@ struct MJpegEncoder {<br>
     void (*pixel_converter)(uint8_t *src, uint8_t *dest);<br>
<br>
     MJpegEncoderRateControl rate_control;<br>
-    MJpegEncoderRateControlCbs cbs;<br>
+    VideoEncoderRateControlCbs cbs;<br>
     void *cbs_opaque;<br>
<br>
     /* stats */<br>
@@ -174,6 +179,22 @@ struct MJpegEncoder {<br>
     uint32_t num_frames;<br>
 };<br>
<br>
+static void mjpeg_encoder_destroy(MJpegEncoder *encoder);<br>
+static int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,<br>
+                const SpiceBitmap *bitmap, int width, int height,<br>
+                const SpiceRect *src, int top_down, uint32_t frame_mm_time,<br>
+                uint8_t **outbuf, size_t *outbuf_size, int *data_size);<br>
+static void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,<br>
+                                        uint32_t num_frames,<br>
+                                        uint32_t num_drops,<br>
+                                        uint32_t start_frame_mm_time,<br>
+                                        uint32_t end_frame_mm_time,<br>
+                                        int32_t end_frame_delay,<br>
+                                        uint32_t audio_delay);<br>
+static void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);<br>
+static uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);<br>
+static void mjpeg_encoder_get_stats(MJpegEncoder *encoder, VideoEncoderStats *stats);<br>
+<br>
 static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,<br>
                                                int quality_id,<br>
                                                uint32_t fps,<br>
@@ -189,8 +210,9 @@ static inline int rate_control_is_active(MJpegEncoder* encoder)<br>
     return encoder->cbs.get_roundtrip_ms != NULL;<br>
 }<br>
<br>
-MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,<br>
-                                MJpegEncoderRateControlCbs *cbs, void *opaque)<br>
+MJpegEncoder *create_mjpeg_encoder(uint64_t starting_bit_rate,<br>
+                                   VideoEncoderRateControlCbs *cbs,<br>
+                                   void *cbs_opaque)<br>
 {<br>
     MJpegEncoder *enc;<br>
<br>
@@ -198,6 +220,12 @@ MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,<br>
<br>
     enc = spice_new0(MJpegEncoder, 1);<br>
<br>
+    enc->base.destroy = &mjpeg_encoder_destroy;<br>
+    enc->base.encode_frame = &mjpeg_encoder_encode_frame;<br>
+    enc->base.client_stream_report = &mjpeg_encoder_client_stream_report;<br>
+    enc->base.notify_server_frame_drop = &mjpeg_encoder_notify_server_frame_drop;<br>
+    enc->base.get_bit_rate = &mjpeg_encoder_get_bit_rate;<br>
+    enc->base.get_stats = &mjpeg_encoder_get_stats;<br>
     enc->first_frame = TRUE;<br>
     enc->rate_control.byte_rate = starting_bit_rate / 8;<br>
     enc->starting_bit_rate = starting_bit_rate;<br>
@@ -207,7 +235,7 @@ MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,<br>
<br>
         clock_gettime(CLOCK_MONOTONIC, &time);<br>
         enc->cbs = *cbs;<br>
-        enc->cbs_opaque = opaque;<br>
+        enc->cbs_opaque = cbs_opaque;<br>
         mjpeg_encoder_reset_quality(enc, MJPEG_QUALITY_SAMPLE_NUM / 2, 5, 0);<br>
         enc->rate_control.during_quality_eval = TRUE;<br>
         enc->rate_control.quality_eval_data.type = MJPEG_QUALITY_EVAL_TYPE_SET;<br>
@@ -754,7 +782,7 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,<br>
         interval = (now - rate_control->bit_rate_info.last_frame_time);<br>
<br>
         if (interval < (1000*1000*1000) / rate_control->adjusted_fps) {<br>
-            return MJPEG_ENCODER_FRAME_DROP;<br>
+            return VIDEO_ENCODER_FRAME_DROP;<br>
         }<br>
<br>
         mjpeg_encoder_adjust_params_to_bit_rate(encoder);<br>
@@ -802,14 +830,14 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,<br>
         break;<br>
     default:<br>
         spice_debug("unsupported format %d", format);<br>
-        return MJPEG_ENCODER_FRAME_UNSUPPORTED;<br>
+        return VIDEO_ENCODER_FRAME_UNSUPPORTED;<br>
     }<br>
<br>
     if (encoder->pixel_converter != NULL) {<br>
         unsigned int stride = width * 3;<br>
         /* check for integer overflow */<br>
         if (stride < width) {<br>
-            return MJPEG_ENCODER_FRAME_UNSUPPORTED;<br>
+            return VIDEO_ENCODER_FRAME_UNSUPPORTED;<br>
         }<br>
         if (encoder->row_size < stride) {<br>
             encoder->row = spice_realloc(encoder->row, stride);<br>
@@ -829,7 +857,7 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,<br>
<br>
     encoder->num_frames++;<br>
     encoder->avg_quality += quality;<br>
-    return MJPEG_ENCODER_FRAME_ENCODE_START;<br>
+    return VIDEO_ENCODER_FRAME_ENCODE_DONE;<br>
 }<br>
<br>
 int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,<br>
@@ -888,6 +916,92 @@ size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)<br>
     return encoder->rate_control.last_enc_size;<br>
 }<br>
<br>
+static inline uint8_t *get_image_line(SpiceChunks *chunks, size_t *offset,<br>
+                                          int *chunk_nr, int stride)<br>
+{<br>
+    uint8_t *ret;<br>
+    SpiceChunk *chunk;<br>
+<br>
+    chunk = &chunks->chunk[*chunk_nr];<br>
+<br>
+    if (*offset == chunk->len) {<br>
+        if (*chunk_nr == chunks->num_chunks - 1) {<br>
+            return NULL; /* Last chunk */<br>
+        }<br>
+        *offset = 0;<br>
+        (*chunk_nr)++;<br>
+        chunk = &chunks->chunk[*chunk_nr];<br>
+    }<br>
+<br>
+    if (chunk->len - *offset < stride) {<br>
+        spice_warning("bad chunk alignment");<br>
+        return NULL;<br>
+    }<br>
+    ret = chunk->data + *offset;<br>
+    *offset += stride;<br>
+    return ret;<br>
+}<br>
+<br>
+<br>
+<br>
+static int encode_mjpeg_frame(MJpegEncoder *encoder, const SpiceRect *src,<br>
+                        const SpiceBitmap *image, int top_down)<br>
+{<br>
+    SpiceChunks *chunks;<br>
+    uint32_t image_stride;<br>
+    size_t offset;<br>
+    int i, chunk;<br>
+<br>
+    chunks = image->data;<br>
+    offset = 0;<br>
+    chunk = 0;<br>
+    image_stride = image->stride;<br>
+<br>
+    const int skip_lines = top_down ? src->top : image->y - (src->bottom - 0);<br>
+    for (i = 0; i < skip_lines; i++) {<br>
+            get_image_line(chunks, &offset, &chunk, image_stride);<br>
+    }<br>
+<br>
+    const unsigned int stream_height = src->bottom - src->top;<br>
+    const unsigned int stream_width = src->right - src->left;<br>
+<br>
+    for (i = 0; i < stream_height; i++) {<br>
+        uint8_t *src_line = (uint8_t *)get_image_line(chunks, &offset, &chunk, image_stride);<br>
+<br>
+        if (!src_line) {<br>
+            return FALSE;<br>
+        }<br>
+<br>
+        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(encoder);<br>
+        if (mjpeg_encoder_encode_scanline(encoder, src_line, stream_width) == 0)<br>
+            return FALSE;<br>
+    }<br>
+<br>
+    return TRUE;<br>
+}<br>
+<br>
+int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,<br>
+                const SpiceBitmap *bitmap, int width, int height,<br>
+                const SpiceRect *src, int top_down, uint32_t frame_mm_time,<br>
+                uint8_t **outbuf, size_t *outbuf_size, int *data_size)<br>
+{<br>
+    int ret;<br>
+<br>
+    ret = mjpeg_encoder_start_frame(encoder, bitmap->format,<br>
+                                    width, height, outbuf, outbuf_size,<br>
+                                    frame_mm_time);<br>
+    if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE)<br>
+        return ret;<br>
+<br>
+    if (!encode_mjpeg_frame(encoder, src, bitmap, top_down))<br>
+        return VIDEO_ENCODER_FRAME_UNSUPPORTED;<br>
+<br>
+    *data_size = mjpeg_encoder_end_frame(encoder);<br>
+<br>
+    return VIDEO_ENCODER_FRAME_ENCODE_DONE;<br>
+}<br>
+<br>
+<br>
 static void mjpeg_encoder_quality_eval_stop(MJpegEncoder *encoder)<br>
 {<br>
     MJpegEncoderRateControl *rate_control = &encoder->rate_control;<br>
@@ -1267,7 +1381,7 @@ uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)<br>
     return encoder->rate_control.byte_rate * 8;<br>
 }<br>
<br>
-void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats)<br>
+void mjpeg_encoder_get_stats(MJpegEncoder *encoder, VideoEncoderStats *stats)<br>
 {<br>
     spice_assert(encoder != NULL && stats != NULL);<br>
     stats->starting_bit_rate = encoder->starting_bit_rate;<br>
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h<br>
deleted file mode 100644<br>
index d584b92..0000000<br>
--- a/server/mjpeg_encoder.h<br>
+++ /dev/null<br>
@@ -1,114 +0,0 @@<br>
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */<br>
-/*<br>
-   Copyright (C) 2009 Red Hat, Inc.<br>
-<br>
-   This library is free software; you can redistribute it and/or<br>
-   modify it under the terms of the GNU Lesser General Public<br>
-   License as published by the Free Software Foundation; either<br>
-   version 2.1 of the License, or (at your option) any later version.<br>
-<br>
-   This library is distributed in the hope that it will be useful,<br>
-   but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
-   Lesser General Public License for more details.<br>
-<br>
-   You should have received a copy of the GNU Lesser General Public<br>
-   License along with this library; if not, see <<a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
-*/<br>
-<br>
-#ifndef _H_MJPEG_ENCODER<br>
-#define _H_MJPEG_ENCODER<br>
-<br>
-#include "red_common.h"<br>
-<br>
-enum {<br>
-    MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,<br>
-    MJPEG_ENCODER_FRAME_DROP,<br>
-    MJPEG_ENCODER_FRAME_ENCODE_START,<br>
-};<br>
-<br>
-typedef struct MJpegEncoder MJpegEncoder;<br>
-<br>
-/*<br>
- * Callbacks required for controling and adjusting<br>
- * the stream bit rate:<br>
- * get_roundtrip_ms: roundtrip time in milliseconds<br>
- * get_source_fps: the input frame rate (#frames per second), i.e.,<br>
- * the rate of frames arriving from the guest to spice-server,<br>
- * before any drops.<br>
- */<br>
-typedef struct MJpegEncoderRateControlCbs {<br>
-    uint32_t (*get_roundtrip_ms)(void *opaque);<br>
-    uint32_t (*get_source_fps)(void *opaque);<br>
-    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);<br>
-} MJpegEncoderRateControlCbs;<br>
-<br>
-typedef struct MJpegEncoderStats {<br>
-    uint64_t starting_bit_rate;<br>
-    uint64_t cur_bit_rate;<br>
-    double avg_quality;<br>
-} MJpegEncoderStats;<br>
-<br>
-MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,<br>
-                                MJpegEncoderRateControlCbs *cbs, void *opaque);<br>
-void mjpeg_encoder_destroy(MJpegEncoder *encoder);<br>
-<br>
-uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder);<br>
-<br>
-/*<br>
- * dest must be either NULL or allocated by malloc, since it might be freed<br>
- * during the encoding, if its size is too small.<br>
- *<br>
- * return:<br>
- *  MJPEG_ENCODER_FRAME_UNSUPPORTED : frame cannot be encoded<br>
- *  MJPEG_ENCODER_FRAME_DROP        : frame should be dropped. This value can only be returned<br>
- *                                    if mjpeg rate control is active.<br>
- *  MJPEG_ENCODER_FRAME_ENCODE_START: frame encoding started. Continue with<br>
- *                                    mjpeg_encoder_encode_scanline.<br>
- */<br>
-int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,<br>
-                              int width, int height,<br>
-                              uint8_t **dest, size_t *dest_len,<br>
-                              uint32_t frame_mm_time);<br>
-int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,<br>
-                                  size_t image_width);<br>
-size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder);<br>
-<br>
-/*<br>
- * bit rate control<br>
- */<br>
-<br>
-/*<br>
- * Data that should be periodically obtained from the client. The report contains:<br>
- * num_frames         : the number of frames that reached the client during the time<br>
- *                      the report is referring to.<br>
- * num_drops          : the part of the above frames that was dropped by the client due to<br>
- *                      late arrival time.<br>
- * start_frame_mm_time: the mm_time of the first frame included in the report<br>
- * end_frame_mm_time  : the mm_time of the last_frame included in the report<br>
- * end_frame_delay    : (end_frame_mm_time - client_mm_time)<br>
- * audio delay        : the latency of the audio playback.<br>
- *                      If there is no audio playback, set it to MAX_UINT.<br>
- *<br>
- */<br>
-void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,<br>
-                                        uint32_t num_frames,<br>
-                                        uint32_t num_drops,<br>
-                                        uint32_t start_frame_mm_time,<br>
-                                        uint32_t end_frame_mm_time,<br>
-                                        int32_t end_frame_delay,<br>
-                                        uint32_t audio_delay);<br>
-<br>
-/*<br>
- * Notify the encoder each time a frame is dropped due to pipe<br>
- * congestion.<br>
- * We can deduce the client state by the frame dropping rate in the server.<br>
- * Monitoring the frame drops can help in fine tuning the playback parameters<br>
- * when the client reports are delayed.<br>
- */<br>
-void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);<br>
-<br>
-uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);<br>
-void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats);<br>
-<br>
-#endif<br>
diff --git a/server/red_worker.c b/server/red_worker.c<br>
index e0ff8e9..c6b39cb 100644<br>
--- a/server/red_worker.c<br>
+++ b/server/red_worker.c<br>
@@ -70,7 +70,7 @@<br>
 #include "glz_encoder.h"<br>
 #include "stat.h"<br>
 #include "reds.h"<br>
-#include "mjpeg_encoder.h"<br>
+#include "video_encoder.h"<br>
 #include "red_memslots.h"<br>
 #include "red_parse_qxl.h"<br>
 #include "jpeg_encoder.h"<br>
@@ -484,7 +484,7 @@ typedef struct StreamAgent {<br>
     PipeItem destroy_item;<br>
     Stream *stream;<br>
     uint64_t last_send_time;<br>
-    MJpegEncoder *mjpeg_encoder;<br>
+    VideoEncoder *video_encoder;<br>
     DisplayChannelClient *dcc;<br>
<br>
     int frames;<br>
@@ -723,7 +723,7 @@ struct DisplayChannelClient {<br>
     QRegion surface_client_lossy_region[NUM_SURFACES];<br>
<br>
     StreamAgent stream_agents[NUM_STREAMS];<br>
-    int use_mjpeg_encoder_rate_control;<br>
+    int use_video_encoder_rate_control;<br>
     uint32_t streams_max_latency;<br>
     uint64_t streams_max_bit_rate;<br>
 };<br>
@@ -2642,10 +2642,10 @@ static void red_print_stream_stats(DisplayChannelClient *dcc, StreamAgent *agent<br>
 #ifdef STREAM_STATS<br>
     StreamStats *stats = &agent->stats;<br>
     double passed_mm_time = (stats->end - stats->start) / 1000.0;<br>
-    MJpegEncoderStats encoder_stats = {0};<br>
+    VideoEncoderStats encoder_stats = {0};<br>
<br>
-    if (agent->mjpeg_encoder) {<br>
-        mjpeg_encoder_get_stats(agent->mjpeg_encoder, &encoder_stats);<br>
+    if (agent->video_encoder) {<br>
+        agent->video_encoder->get_stats(agent->video_encoder, &encoder_stats);<br>
     }<br>
<br>
     spice_debug("stream=%"PRIdPTR" dim=(%dx%d) #in-frames=%"PRIu64" #in-avg-fps=%.2f #out-frames=%"PRIu64" "<br>
@@ -2688,8 +2688,8 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)<br>
         region_clear(&stream_agent->vis_region);<br>
         region_clear(&stream_agent->clip);<br>
         spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));<br>
-        if (stream_agent->mjpeg_encoder && dcc->use_mjpeg_encoder_rate_control) {<br>
-            uint64_t stream_bit_rate = mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder);<br>
+        if (stream_agent->video_encoder && dcc->use_video_encoder_rate_control) {<br>
+            uint64_t stream_bit_rate = stream_agent->video_encoder->get_bit_rate(stream_agent->video_encoder);<br>
<br>
             if (stream_bit_rate > dcc->streams_max_bit_rate) {<br>
                 spice_debug("old max-bit-rate=%.2f new=%.2f",<br>
@@ -2996,7 +2996,7 @@ static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc,<br>
            stream->width * stream->height) / dcc->common.worker->streams_size_total;<br>
 }<br>
<br>
-static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque)<br>
+static uint32_t red_stream_video_encoder_get_roundtrip(void *opaque)<br>
 {<br>
     StreamAgent *agent = opaque;<br>
     int roundtrip;<br>
@@ -3017,7 +3017,7 @@ static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque)<br>
     return roundtrip;<br>
 }<br>
<br>
-static uint32_t red_stream_mjpeg_encoder_get_source_fps(void *opaque)<br>
+static uint32_t red_stream_video_encoder_get_source_fps(void *opaque)<br>
 {<br>
     StreamAgent *agent = opaque;<br>
<br>
@@ -3040,7 +3040,7 @@ static void red_display_update_streams_max_latency(DisplayChannelClient *dcc, St<br>
     }<br>
     for (i = 0; i < NUM_STREAMS; i++) {<br>
         StreamAgent *other_agent = &dcc->stream_agents[i];<br>
-        if (other_agent == remove_agent || !other_agent->mjpeg_encoder) {<br>
+        if (other_agent == remove_agent || !other_agent->video_encoder) {<br>
             continue;<br>
         }<br>
         if (other_agent->client_required_latency > new_max_latency) {<br>
@@ -3053,9 +3053,9 @@ static void red_display_update_streams_max_latency(DisplayChannelClient *dcc, St<br>
 static void red_display_stream_agent_stop(DisplayChannelClient *dcc, StreamAgent *agent)<br>
 {<br>
     red_display_update_streams_max_latency(dcc, agent);<br>
-    if (agent->mjpeg_encoder) {<br>
-        mjpeg_encoder_destroy(agent->mjpeg_encoder);<br>
-        agent->mjpeg_encoder = NULL;<br>
+    if (agent->video_encoder) {<br>
+        agent->video_encoder->destroy(agent->video_encoder);<br>
+        agent->video_encoder = NULL;<br>
     }<br>
 }<br>
<br>
@@ -3091,18 +3091,18 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)<br>
     agent->fps = MAX_FPS;<br>
     agent->dcc = dcc;<br>
<br>
-    if (dcc->use_mjpeg_encoder_rate_control) {<br>
-        MJpegEncoderRateControlCbs mjpeg_cbs;<br>
+    if (dcc->use_video_encoder_rate_control) {<br>
+        VideoEncoderRateControlCbs video_cbs;<br>
         uint64_t initial_bit_rate;<br>
<br>
-        mjpeg_cbs.get_roundtrip_ms = red_stream_mjpeg_encoder_get_roundtrip;<br>
-        mjpeg_cbs.get_source_fps = red_stream_mjpeg_encoder_get_source_fps;<br>
-        mjpeg_cbs.update_client_playback_delay = red_stream_update_client_playback_latency;<br>
+        video_cbs.get_roundtrip_ms = red_stream_video_encoder_get_roundtrip;<br>
+        video_cbs.get_source_fps = red_stream_video_encoder_get_source_fps;<br>
+        video_cbs.update_client_playback_delay = red_stream_update_client_playback_latency;<br>
<br>
         initial_bit_rate = red_stream_get_initial_bit_rate(dcc, stream);<br>
-        agent->mjpeg_encoder = mjpeg_encoder_new(initial_bit_rate, &mjpeg_cbs, agent);<br>
+        agent->video_encoder = create_mjpeg_encoder(initial_bit_rate, &video_cbs, agent);<br>
     } else {<br>
-        agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL, NULL);<br>
+        agent->video_encoder = create_mjpeg_encoder(0, NULL, NULL);<br>
     }<br>
     red_channel_client_pipe_add(&dcc->common.base, &agent->create_item);<br>
<br>
@@ -3209,7 +3209,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)<br>
         red_channel_pipe_item_init(channel, &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);<br>
         red_channel_pipe_item_init(channel, &agent->destroy_item, PIPE_ITEM_TYPE_STREAM_DESTROY);<br>
     }<br>
-    dcc->use_mjpeg_encoder_rate_control =<br>
+    dcc->use_video_encoder_rate_control =<br>
         red_channel_client_test_remote_cap(&dcc->common.base, SPICE_DISPLAY_CAP_STREAM_REPORT);<br>
 }<br>
<br>
@@ -3221,9 +3221,9 @@ static void red_display_destroy_streams_agents(DisplayChannelClient *dcc)<br>
         StreamAgent *agent = &dcc->stream_agents[i];<br>
         region_destroy(&agent->vis_region);<br>
         region_destroy(&agent->clip);<br>
-        if (agent->mjpeg_encoder) {<br>
-            mjpeg_encoder_destroy(agent->mjpeg_encoder);<br>
-            agent->mjpeg_encoder = NULL;<br>
+        if (agent->video_encoder) {<br>
+            agent->video_encoder->destroy(agent->video_encoder);<br>
+            agent->video_encoder = NULL;<br>
         }<br>
     }<br>
 }<br>
@@ -3350,7 +3350,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa<br>
         dcc = dpi->dcc;<br>
         agent = &dcc->stream_agents[index];<br>
<br>
-        if (!dcc->use_mjpeg_encoder_rate_control &&<br>
+        if (!dcc->use_video_encoder_rate_control &&<br>
             !dcc->common.is_low_bandwidth) {<br>
             continue;<br>
         }<br>
@@ -3359,8 +3359,8 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa<br>
 #ifdef STREAM_STATS<br>
             agent->stats.num_drops_pipe++;<br>
 #endif<br>
-            if (dcc->use_mjpeg_encoder_rate_control) {<br>
-                mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder);<br>
+            if (dcc->use_video_encoder_rate_control) {<br>
+                agent->video_encoder->notify_server_frame_drop(agent->video_encoder);<br>
             } else {<br>
                 ++agent->drops;<br>
             }<br>
@@ -3373,7 +3373,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa<br>
<br>
         agent = &dcc->stream_agents[index];<br>
<br>
-        if (dcc->use_mjpeg_encoder_rate_control) {<br>
+        if (dcc->use_video_encoder_rate_control) {<br>
             continue;<br>
         }<br>
         if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {<br>
@@ -8481,70 +8481,6 @@ static inline void display_begin_send_message(RedChannelClient *rcc)<br>
     red_channel_client_begin_send_message(rcc);<br>
 }<br>
<br>
-static inline uint8_t *red_get_image_line(SpiceChunks *chunks, size_t *offset,<br>
-                                          int *chunk_nr, int stride)<br>
-{<br>
-    uint8_t *ret;<br>
-    SpiceChunk *chunk;<br>
-<br>
-    chunk = &chunks->chunk[*chunk_nr];<br>
-<br>
-    if (*offset == chunk->len) {<br>
-        if (*chunk_nr == chunks->num_chunks - 1) {<br>
-            return NULL; /* Last chunk */<br>
-        }<br>
-        *offset = 0;<br>
-        (*chunk_nr)++;<br>
-        chunk = &chunks->chunk[*chunk_nr];<br>
-    }<br>
-<br>
-    if (chunk->len - *offset < stride) {<br>
-        spice_warning("bad chunk alignment");<br>
-        return NULL;<br>
-    }<br>
-    ret = chunk->data + *offset;<br>
-    *offset += stride;<br>
-    return ret;<br>
-}<br>
-<br>
-static int encode_frame(DisplayChannelClient *dcc, const SpiceRect *src,<br>
-                        const SpiceBitmap *image, Stream *stream)<br>
-{<br>
-    SpiceChunks *chunks;<br>
-    uint32_t image_stride;<br>
-    size_t offset;<br>
-    int i, chunk;<br>
-    StreamAgent *agent = &dcc->stream_agents[stream - dcc->common.worker->streams_buf];<br>
-<br>
-    chunks = image->data;<br>
-    offset = 0;<br>
-    chunk = 0;<br>
-    image_stride = image->stride;<br>
-<br>
-    const int skip_lines = stream->top_down ? src->top : image->y - (src->bottom - 0);<br>
-    for (i = 0; i < skip_lines; i++) {<br>
-        red_get_image_line(chunks, &offset, &chunk, image_stride);<br>
-    }<br>
-<br>
-    const unsigned int stream_height = src->bottom - src->top;<br>
-    const unsigned int stream_width = src->right - src->left;<br>
-<br>
-    for (i = 0; i < stream_height; i++) {<br>
-        uint8_t *src_line =<br>
-            (uint8_t *)red_get_image_line(chunks, &offset, &chunk, image_stride);<br>
-<br>
-        if (!src_line) {<br>
-            return FALSE;<br>
-        }<br>
-<br>
-        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(agent->mjpeg_encoder);<br>
-        if (mjpeg_encoder_encode_scanline(agent->mjpeg_encoder, src_line, stream_width) == 0)<br>
-            return FALSE;<br>
-    }<br>
-<br>
-    return TRUE;<br>
-}<br>
-<br>
 static inline int red_marshall_stream_data(RedChannelClient *rcc,<br>
                   SpiceMarshaller *base_marshaller, Drawable *drawable)<br>
 {<br>
@@ -8589,7 +8525,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,<br>
     uint64_t time_now = red_now();<br>
     size_t outbuf_size;<br>
<br>
-    if (!dcc->use_mjpeg_encoder_rate_control) {<br>
+    if (!dcc->use_video_encoder_rate_control) {<br>
         if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {<br>
             agent->frames--;<br>
 #ifdef STREAM_STATS<br>
@@ -8603,33 +8539,29 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,<br>
     frame_mm_time =  drawable->red_drawable->mm_time ?<br>
                         drawable->red_drawable->mm_time :<br>
                         reds_get_mm_time();<br>
+<br>
     outbuf_size = dcc->send_data.stream_outbuf_size;<br>
-    ret = mjpeg_encoder_start_frame(agent->mjpeg_encoder, image->u.bitmap.format,<br>
-                                    width, height,<br>
-                                    &dcc->send_data.stream_outbuf,<br>
-                                    &outbuf_size,<br>
-                                    frame_mm_time);<br>
+    ret = agent->video_encoder->encode_frame(agent->video_encoder,<br>
+            &image->u.bitmap, width, height,<br>
+            &drawable->red_drawable->u.copy.src_area,<br>
+            stream->top_down, frame_mm_time,<br>
+            &dcc->send_data.stream_outbuf, &outbuf_size, &n);<br>
+<br>
     switch (ret) {<br>
-    case MJPEG_ENCODER_FRAME_DROP:<br>
-        spice_assert(dcc->use_mjpeg_encoder_rate_control);<br>
+    case VIDEO_ENCODER_FRAME_DROP:<br>
+        spice_assert(dcc->use_video_encoder_rate_control);<br>
 #ifdef STREAM_STATS<br>
         agent->stats.num_drops_fps++;<br>
 #endif<br>
         return TRUE;<br>
-    case MJPEG_ENCODER_FRAME_UNSUPPORTED:<br>
+    case VIDEO_ENCODER_FRAME_UNSUPPORTED:<br>
         return FALSE;<br>
-    case MJPEG_ENCODER_FRAME_ENCODE_START:<br>
+    case VIDEO_ENCODER_FRAME_ENCODE_DONE:<br>
         break;<br>
     default:<br>
-        spice_error("bad return value (%d) from mjpeg_encoder_start_frame", ret);<br>
-        return FALSE;<br>
-    }<br>
-<br>
-    if (!encode_frame(dcc, &drawable->red_drawable->u.copy.src_area,<br>
-                      &image->u.bitmap, stream)) {<br>
+        spice_error("bad return value (%d) from VideoEncoder::encode_frame", ret);<br>
         return FALSE;<br>
     }<br>
-    n = mjpeg_encoder_end_frame(agent->mjpeg_encoder);<br>
     dcc->send_data.stream_outbuf_size = outbuf_size;<br>
<br>
     if (!drawable->sized_stream) {<br>
@@ -10255,7 +10187,7 @@ static int display_channel_handle_stream_report(DisplayChannelClient *dcc,<br>
         return FALSE;<br>
     }<br>
     stream_agent = &dcc->stream_agents[stream_report->stream_id];<br>
-    if (!stream_agent->mjpeg_encoder) {<br>
+    if (!stream_agent->video_encoder) {<br>
         spice_info("stream_report: no encoder for stream id %u."<br>
                     "Probably the stream has been destroyed", stream_report->stream_id);<br>
         return TRUE;<br>
@@ -10266,7 +10198,7 @@ static int display_channel_handle_stream_report(DisplayChannelClient *dcc,<br>
                       stream_agent->report_id, stream_report->unique_id);<br>
         return TRUE;<br>
     }<br>
-    mjpeg_encoder_client_stream_report(stream_agent->mjpeg_encoder,<br>
+    stream_agent->video_encoder->client_stream_report(stream_agent->video_encoder,<br>
                                        stream_report->num_frames,<br>
                                        stream_report->num_drops,<br>
                                        stream_report->start_frame_mm_time,<br>
diff --git a/server/video_encoder.h b/server/video_encoder.h<br>
new file mode 100644<br>
index 0000000..8a9f9c6<br>
--- /dev/null<br>
+++ b/server/video_encoder.h<br>
@@ -0,0 +1,162 @@<br>
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */<br>
+/*<br>
+   Copyright (C) 2009 Red Hat, Inc.<br>
+   Copyright (C) 2015 Jeremy White<br>
+   Copyright (C) 2015 Francois Gouget<br>
+<br>
+   This library is free software; you can redistribute it and/or<br>
+   modify it under the terms of the GNU Lesser General Public<br>
+   License as published by the Free Software Foundation; either<br>
+   version 2.1 of the License, or (at your option) any later version.<br>
+<br>
+   This library is distributed in the hope that it will be useful,<br>
+   but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
+   Lesser General Public License for more details.<br>
+<br>
+   You should have received a copy of the GNU Lesser General Public<br>
+   License along with this library; if not, see <<A HREF="<a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a>"><a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a></A>>.<br>
+*/<br>
+<br>
+#ifndef _H_video_encoder<br>
+#define _H_video_encoder<br>
+<br>
+#include "red_common.h"<br>
+<br>
+enum {<br>
+    VIDEO_ENCODER_FRAME_UNSUPPORTED = -1,<br>
+    VIDEO_ENCODER_FRAME_DROP,<br>
+    VIDEO_ENCODER_FRAME_ENCODE_DONE,<br>
+};<br>
+<br>
+typedef struct VideoEncoderStats {<br>
+    uint64_t starting_bit_rate;<br>
+    uint64_t cur_bit_rate;<br>
+    double avg_quality;<br>
+} VideoEncoderStats;<br>
+<br>
+typedef struct VideoEncoder VideoEncoder;<br>
+#ifndef VIDEO_ENCODER_T<br>
+# define VIDEO_ENCODER_T VideoEncoder<br>
+#endif<br>
+<br>
+struct VideoEncoder {<br>
+    /* Releases the video encoder's resources */<br>
+    void (*destroy)(VIDEO_ENCODER_T *encoder);<br>
+<br>
+    /* Compresses the specified src image area into the outbuf buffer.<br>
+     *<br>
+     * @encoder:   The video encoder.<br>
+     * @bitmap:    The Spice screen.<br>
+     * @width:     The width of the Spice screen.<br>
+     * @height:    The heigth of the Spice screen.<br></blockquote><div><br></div><div>It's not the screen, it's the video area. It's not clear to me what would happen if the "src" size doesn't match the width&height.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+     * @src:       A rectangle specifying the area occupied by the video.<br>
+     * @top_down:  If true the first video line is specified by src.top.<br>
+     * @outbuf:    The buffer for the compressed frame. This must either be<br>
+     *             NULL or point to a buffer allocated by malloc since it may be<br>
+     *             reallocated, if its size is too small.<br>
+     * @outbuf_size: The size of the outbuf buffer.<br>
+     * @data_size: The size of the compressed frame.<br>
+     * @return:<br>
+     *     VIDEO_ENCODER_FRAME_ENCODE_DONE if successful.<br>
+     *     VIDEO_ENCODER_FRAME_UNSUPPORTED if the frame cannot be encoded.<br>
+     *     VIDEO_ENCODER_FRAME_DROP if the frame was dropped. This value can<br>
+     *                              only hapen if rate control is active.<br>
+     */<br>
+    int (*encode_frame)(VIDEO_ENCODER_T *encoder, const SpiceBitmap *bitmap,<br>
+                        int width, int height, const SpiceRect *src,<br>
+                        int top_down, uint32_t frame_mm_time,uint8_t **outbuf,<br>
+                        size_t *outbuf_size, int *data_size);<br>
+<br>
+    /*<br>
+     * Bit rate control methods.<br>
+     */<br>
+<br>
+    /* When rate control is active statistics are periodically obtained from<br>
+     * the client and sent to the video encoder through this method.<br>
+     *<br>
+     * @encoder:    The video encoder.<br>
+     * @num_frames: The number of frames that reached the client during the<br>
+     *              time period the report is referring to.<br>
+     * @num_drops:  The part of the above frames that was dropped by the client<br>
+     *              due to late arrival time.<br>
+     * @start_frame_mm_time: The mm_time of the first frame included in the<br>
+     *              report.<br>
+     * @end_frame_mm_time: The mm_time of the last frame included in the report.<br>
+     * @end_frame_delay: (end_frame_mm_time - client_mm_time)<br>
+     * @audio delay: The latency of the audio playback. If there is no audio<br>
+     *              playback this is MAX_UINT.<br>
+     *<br>
+     */<br>
+    void (*client_stream_report)(VIDEO_ENCODER_T *encoder,<br>
+                                 uint32_t num_frames, uint32_t num_drops,<br>
+                                 uint32_t start_frame_mm_time,<br>
+                                 uint32_t end_frame_mm_time,<br>
+                                 int32_t end_frame_delay, uint32_t audio_delay);<br>
+<br>
+    /* This notifies the video encoder each time a frame is dropped due to pipe<br>
+     * congestion.<br>
+     *<br>
+     * We can deduce the client state by the frame dropping rate in the server.<br>
+     * Monitoring the frame drops can help in fine tuning the playback<br>
+     * parameters when the client reports are delayed.<br>
+     *<br>
+     * @encoder:    The video encoder.<br>
+     */<br>
+    void (*notify_server_frame_drop)(VIDEO_ENCODER_T *encoder);<br>
+<br>
+    /* This queries the video encoder's current bit rate.<br>
+     *<br>
+     * @encoder:    The video encoder.<br>
+     * @return:     The current bit rate in bits per second.<br>
+     */<br>
+    uint64_t (*get_bit_rate)(VIDEO_ENCODER_T *encoder);<br>
+<br>
+    /* Collects video statistics.<br>
+     *<br>
+     * @encoder:    The video encoder.<br>
+     * @stats:      A VideoEncoderStats structure to fill with the collected<br>
+     *              statistics.<br>
+     */<br>
+    void (*get_stats)(VIDEO_ENCODER_T *encoder, VideoEncoderStats *stats);<br>
+};<br>
+<br>
+<br>
+/* When rate control is active the video encoder can use these callbacks to<br>
+ * figure out how to adjust the stream bit rate and adjust some stream<br>
+ * parameters.<br>
+ */<br>
+typedef struct VideoEncoderRateControlCbs {<br>
+    /* Returns the stream's estimated roundtrip time in milliseconds. */<br>
+    uint32_t (*get_roundtrip_ms)(void *opaque);<br>
+<br>
+    /* Returns the estimated input frame rate.<br>
+     *<br>
+     * This is the number of frames per second arriving from the guest to<br>
+     * spice-server, before any drops.<br>
+     */<br>
+    uint32_t (*get_source_fps)(void *opaque);<br>
+<br>
+    /* Informs the client of the minimum playback delay.<br>
+     *<br>
+     * @delay_ms:   The minimum number of milliseconds required for the frames<br>
+     *              to reach the client.<br>
+     */<br>
+    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);<br>
+} VideoEncoderRateControlCbs;<br>
+<br>
+<br>
+/* Instantiates the builtin MJPEG video encoder.<br>
+ *<br>
+ * @starting_bit_rate: An initial estimate of the available stream bit rate .<br>
+ * @bit_rate_control:  True if the client supports rate control.<br>
+ * @cbs:               A set of callback methods to be used for rate control.<br>
+ * @cbs_opaque:        A pointer to be passed to the rate control callbacks.<br>
+ * @return:            A pointer to a structure implementing the VideoEncoder<br>
+ *                     methods.<br>
+ */<br>
+VIDEO_ENCODER_T* create_mjpeg_encoder(uint64_t starting_bit_rate,<br>
+                                      VideoEncoderRateControlCbs *cbs,<br>
+                                      void *cbs_opaque);<br>
+<br>
+#endif<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.1.4<br>
<br>
_______________________________________________<br>
Spice-devel mailing list<br>
<a href="mailto:Spice-devel@lists.freedesktop.org">Spice-devel@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/spice-devel" target="_blank">http://lists.freedesktop.org/mailman/listinfo/spice-devel</a><br>
</font></span></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature">Marc-André Lureau</div>
</div></div>