[Spice-commits] 3 commits - src/channel-display.c src/channel-display-mjpeg.c src/channel-display-priv.h

Pavel Grunt pgrunt at kemper.freedesktop.org
Mon May 9 15:03:55 UTC 2016


 src/channel-display-mjpeg.c |  135 +++++++++++++++++++++++++++++---------------
 src/channel-display-priv.h  |   55 ++++++++++++-----
 src/channel-display.c       |   71 ++++++++++-------------
 3 files changed, 161 insertions(+), 100 deletions(-)

New commits:
commit 36b9678e4655b22d027a32ba85b2e85b1f4f1685
Author: Francois Gouget <fgouget at codeweavers.com>
Date:   Wed May 4 11:44:44 2016 +0200

    streaming: Rename num_drops_on_receive to arrive_late_count
    
    The frame may not get dropped once that's left up to video decoders.
    
    Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
    Acked-by: Pavel Grunt <pgrunt at redhat.com>

diff --git a/src/channel-display-priv.h b/src/channel-display-priv.h
index 7f1c520..5256ad9 100644
--- a/src/channel-display-priv.h
+++ b/src/channel-display-priv.h
@@ -104,7 +104,7 @@ struct display_stream {
 
     /* stats */
     uint32_t             first_frame_mm_time;
-    uint32_t             num_drops_on_receive;
+    uint32_t             arrive_late_count;
     uint64_t             arrive_late_time;
     uint32_t             num_drops_on_playback;
     uint32_t             num_input_frames;
diff --git a/src/channel-display.c b/src/channel-display.c
index c681a4e..4a06193 100644
--- a/src/channel-display.c
+++ b/src/channel-display.c
@@ -1458,7 +1458,7 @@ static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in)
         CHANNEL_DEBUG(channel, "stream data too late by %u ms (ts: %u, mmtime: %u), dropping",
                       mmtime - op->multi_media_time, op->multi_media_time, mmtime);
         st->arrive_late_time += mmtime - op->multi_media_time;
-        st->num_drops_on_receive++;
+        st->arrive_late_count++;
 
         if (!st->cur_drops_seq_stats.len) {
             st->cur_drops_seq_stats.start_mm_time = op->multi_media_time;
@@ -1534,15 +1534,15 @@ static void destroy_stream(SpiceChannel *channel, int id)
     if (!st)
         return;
 
-    num_out_frames = st->num_input_frames - st->num_drops_on_receive - st->num_drops_on_playback;
+    num_out_frames = st->num_input_frames - st->arrive_late_count - st->num_drops_on_playback;
     CHANNEL_DEBUG(channel, "%s: id=%d #in-frames=%d out/in=%.2f "
         "#drops-on-receive=%d avg-late-time(ms)=%.2f "
         "#drops-on-playback=%d", __FUNCTION__,
         id,
         st->num_input_frames,
         num_out_frames / (double)st->num_input_frames,
-        st->num_drops_on_receive,
-        st->num_drops_on_receive ? st->arrive_late_time / ((double)st->num_drops_on_receive): 0,
+        st->arrive_late_count,
+        st->arrive_late_count ? st->arrive_late_time / ((double)st->arrive_late_count): 0,
         st->num_drops_on_playback);
     if (st->num_drops_seqs) {
         CHANNEL_DEBUG(channel, "%s: #drops-sequences=%u ==>", __FUNCTION__, st->num_drops_seqs);
commit c7d0892c6b4d85c3ac7802ba349ca0a3cd5e2a92
Author: Francois Gouget <fgouget at codeweavers.com>
Date:   Wed May 4 11:44:40 2016 +0200

    streaming: Optimize handling of the decoded frame buffer
    
    The MJPEG decoder does not need a zero-filled buffer.
    
    Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
    Acked-by: Pavel Grunt <pgrunt at redhat.com>

diff --git a/src/channel-display-mjpeg.c b/src/channel-display-mjpeg.c
index c7e1c6f..927827b 100644
--- a/src/channel-display-mjpeg.c
+++ b/src/channel-display-mjpeg.c
@@ -39,6 +39,7 @@ typedef struct MJpegDecoder {
     /* ---------- Output frame data ---------- */
 
     uint8_t *out_frame;
+    uint32_t out_size;
 } MJpegDecoder;
 
 
@@ -85,8 +86,12 @@ static uint8_t* mjpeg_decoder_decode_frame(VideoDecoder *video_decoder,
 
     decoder->frame_msg = frame_msg;
     stream_get_dimensions(decoder->base.stream, frame_msg, &width, &height);
-    g_free(decoder->out_frame);
-    dest = decoder->out_frame = g_malloc0(width * height * 4);
+    if (decoder->out_size < width * height * 4) {
+        g_free(decoder->out_frame);
+        decoder->out_size = width * height * 4;
+        decoder->out_frame = g_malloc(decoder->out_size);
+    }
+    dest = decoder->out_frame;
 
     jpeg_read_header(&decoder->mjpeg_cinfo, 1);
 #ifdef JCS_EXTENSIONS
commit 6f45cf90ac582a118f05fda25e1e971121181ddf
Author: Francois Gouget <fgouget at codeweavers.com>
Date:   Wed May 4 11:44:35 2016 +0200

    streaming: Enable adding alternative video decoders
    
    This replaces the original channel-display-mjpeg API with a VideoDecoder
    base class which can be reimplemented by other decoders.
    Furthermore this moves the MJPEG-specific state information from the
    display_stream struct to a derived class of VideoDecoder.
    
    Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
    Acked-by: Pavel Grunt <pgrunt at redhat.com>

diff --git a/src/channel-display-mjpeg.c b/src/channel-display-mjpeg.c
index 5ea3371..c7e1c6f 100644
--- a/src/channel-display-mjpeg.c
+++ b/src/channel-display-mjpeg.c
@@ -23,12 +23,33 @@
 
 #include "channel-display-priv.h"
 
+
+/* MJpeg decoder implementation */
+
+typedef struct MJpegDecoder {
+    VideoDecoder base;
+
+    /* ---------- The builtin mjpeg decoder ---------- */
+
+    SpiceMsgIn *frame_msg;
+    struct jpeg_source_mgr         mjpeg_src;
+    struct jpeg_decompress_struct  mjpeg_cinfo;
+    struct jpeg_error_mgr          mjpeg_jerr;
+
+    /* ---------- Output frame data ---------- */
+
+    uint8_t *out_frame;
+} MJpegDecoder;
+
+
+/* ---------- The JPEG library callbacks ---------- */
+
 static void mjpeg_src_init(struct jpeg_decompress_struct *cinfo)
 {
-    display_stream *st = SPICE_CONTAINEROF(cinfo->src, display_stream, mjpeg_src);
-    uint8_t *data;
+    MJpegDecoder *decoder = SPICE_CONTAINEROF(cinfo->src, MJpegDecoder, mjpeg_src);
 
-    cinfo->src->bytes_in_buffer = stream_get_current_frame(st, &data);
+    uint8_t *data;
+    cinfo->src->bytes_in_buffer = spice_msg_in_frame_data(decoder->frame_msg, &data);
     cinfo->src->next_input_byte = data;
 }
 
@@ -49,68 +70,57 @@ static void mjpeg_src_term(struct jpeg_decompress_struct *cinfo)
     /* nothing */
 }
 
-G_GNUC_INTERNAL
-void stream_mjpeg_init(display_stream *st)
-{
-    st->mjpeg_cinfo.err = jpeg_std_error(&st->mjpeg_jerr);
-    jpeg_create_decompress(&st->mjpeg_cinfo);
-
-    st->mjpeg_src.init_source         = mjpeg_src_init;
-    st->mjpeg_src.fill_input_buffer   = mjpeg_src_fill;
-    st->mjpeg_src.skip_input_data     = mjpeg_src_skip;
-    st->mjpeg_src.resync_to_restart   = jpeg_resync_to_restart;
-    st->mjpeg_src.term_source         = mjpeg_src_term;
-    st->mjpeg_cinfo.src               = &st->mjpeg_src;
-}
 
-G_GNUC_INTERNAL
-void stream_mjpeg_data(display_stream *st)
+/* ---------- VideoDecoder's public API ---------- */
+
+static uint8_t* mjpeg_decoder_decode_frame(VideoDecoder *video_decoder,
+                                           SpiceMsgIn *frame_msg)
 {
-    gboolean back_compat = st->channel->priv->peer_hdr.major_version == 1;
+    MJpegDecoder *decoder = (MJpegDecoder*)video_decoder;
+    gboolean back_compat = decoder->base.stream->channel->priv->peer_hdr.major_version == 1;
     int width;
     int height;
     uint8_t *dest;
     uint8_t *lines[4];
 
-    stream_get_dimensions(st, &width, &height);
-    dest = g_malloc0(width * height * 4);
+    decoder->frame_msg = frame_msg;
+    stream_get_dimensions(decoder->base.stream, frame_msg, &width, &height);
+    g_free(decoder->out_frame);
+    dest = decoder->out_frame = g_malloc0(width * height * 4);
 
-    g_free(st->out_frame);
-    st->out_frame = dest;
-
-    jpeg_read_header(&st->mjpeg_cinfo, 1);
+    jpeg_read_header(&decoder->mjpeg_cinfo, 1);
 #ifdef JCS_EXTENSIONS
     // requires jpeg-turbo
     if (back_compat)
-        st->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX;
+        decoder->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX;
     else
-        st->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX;
+        decoder->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX;
 #else
 #warning "You should consider building with libjpeg-turbo"
-    st->mjpeg_cinfo.out_color_space = JCS_RGB;
+    decoder->mjpeg_cinfo.out_color_space = JCS_RGB;
 #endif
 
 #ifndef SPICE_QUALITY
-    st->mjpeg_cinfo.dct_method = JDCT_IFAST;
-    st->mjpeg_cinfo.do_fancy_upsampling = FALSE;
-    st->mjpeg_cinfo.do_block_smoothing = FALSE;
-    st->mjpeg_cinfo.dither_mode = JDITHER_ORDERED;
+    decoder->mjpeg_cinfo.dct_method = JDCT_IFAST;
+    decoder->mjpeg_cinfo.do_fancy_upsampling = FALSE;
+    decoder->mjpeg_cinfo.do_block_smoothing = FALSE;
+    decoder->mjpeg_cinfo.dither_mode = JDITHER_ORDERED;
 #endif
     // TODO: in theory should check cinfo.output_height match with our height
-    jpeg_start_decompress(&st->mjpeg_cinfo);
+    jpeg_start_decompress(&decoder->mjpeg_cinfo);
     /* rec_outbuf_height is the recommended size of the output buffer we
      * pass to libjpeg for optimum performance
      */
-    if (st->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) {
-        jpeg_abort_decompress(&st->mjpeg_cinfo);
-        g_return_if_reached();
+    if (decoder->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) {
+        jpeg_abort_decompress(&decoder->mjpeg_cinfo);
+        g_return_val_if_reached(NULL);
     }
 
-    while (st->mjpeg_cinfo.output_scanline < st->mjpeg_cinfo.output_height) {
+    while (decoder->mjpeg_cinfo.output_scanline < decoder->mjpeg_cinfo.output_height) {
         /* only used when JCS_EXTENSIONS is undefined */
         G_GNUC_UNUSED unsigned int lines_read;
 
-        for (unsigned int j = 0; j < st->mjpeg_cinfo.rec_outbuf_height; j++) {
+        for (unsigned int j = 0; j < decoder->mjpeg_cinfo.rec_outbuf_height; j++) {
             lines[j] = dest;
 #ifdef JCS_EXTENSIONS
             dest += 4 * width;
@@ -118,8 +128,8 @@ void stream_mjpeg_data(display_stream *st)
             dest += 3 * width;
 #endif
         }
-        lines_read = jpeg_read_scanlines(&st->mjpeg_cinfo, lines,
-                                st->mjpeg_cinfo.rec_outbuf_height);
+        lines_read = jpeg_read_scanlines(&decoder->mjpeg_cinfo, lines,
+                                decoder->mjpeg_cinfo.rec_outbuf_height);
 #ifndef JCS_EXTENSIONS
         {
             uint8_t *s = lines[0];
@@ -142,14 +152,44 @@ void stream_mjpeg_data(display_stream *st)
             }
         }
 #endif
-        dest = &st->out_frame[st->mjpeg_cinfo.output_scanline * width * 4];
+        dest = &(decoder->out_frame[decoder->mjpeg_cinfo.output_scanline * width * 4]);
     }
-    jpeg_finish_decompress(&st->mjpeg_cinfo);
+    jpeg_finish_decompress(&decoder->mjpeg_cinfo);
+
+    return decoder->out_frame;
+}
+
+static void mjpeg_decoder_destroy(VideoDecoder* video_decoder)
+{
+    MJpegDecoder *decoder = (MJpegDecoder*)video_decoder;
+    jpeg_destroy_decompress(&decoder->mjpeg_cinfo);
+    g_free(decoder->out_frame);
+    free(decoder);
 }
 
 G_GNUC_INTERNAL
-void stream_mjpeg_cleanup(display_stream *st)
+VideoDecoder* create_mjpeg_decoder(int codec_type, display_stream *stream)
 {
-    jpeg_destroy_decompress(&st->mjpeg_cinfo);
-    g_clear_pointer(&st->out_frame, g_free);
+    g_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG, NULL);
+
+    MJpegDecoder *decoder = spice_new0(MJpegDecoder, 1);
+
+    decoder->base.destroy = mjpeg_decoder_destroy;
+    decoder->base.decode_frame = mjpeg_decoder_decode_frame;
+    decoder->base.codec_type = codec_type;
+    decoder->base.stream = stream;
+
+    decoder->mjpeg_cinfo.err = jpeg_std_error(&decoder->mjpeg_jerr);
+    jpeg_create_decompress(&decoder->mjpeg_cinfo);
+
+    decoder->mjpeg_src.init_source         = mjpeg_src_init;
+    decoder->mjpeg_src.fill_input_buffer   = mjpeg_src_fill;
+    decoder->mjpeg_src.skip_input_data     = mjpeg_src_skip;
+    decoder->mjpeg_src.resync_to_restart   = jpeg_resync_to_restart;
+    decoder->mjpeg_src.term_source         = mjpeg_src_term;
+    decoder->mjpeg_cinfo.src               = &decoder->mjpeg_src;
+
+    /* All the other fields are initialized to zero by spice_new0(). */
+
+    return (VideoDecoder*)decoder;
 }
diff --git a/src/channel-display-priv.h b/src/channel-display-priv.h
index f92477b..7f1c520 100644
--- a/src/channel-display-priv.h
+++ b/src/channel-display-priv.h
@@ -34,6 +34,39 @@
 
 G_BEGIN_DECLS
 
+typedef struct display_stream display_stream;
+
+typedef struct VideoDecoder VideoDecoder;
+struct VideoDecoder {
+    /* Releases the video decoder's resources */
+    void (*destroy)(VideoDecoder *decoder);
+
+    /* Decompresses the specified frame.
+     *
+     * @decoder:   The video decoder.
+     * @frame_msg: The Spice message containing the compressed frame.
+     * @return:    A pointer to the buffer holding the decoded frame. This
+     *             buffer will be invalidated by the next call to
+     *             decode_frame().
+     */
+    uint8_t* (*decode_frame)(VideoDecoder *decoder, SpiceMsgIn *frame_msg);
+
+    /* The format of the encoded video. */
+    int codec_type;
+
+    /* The associated display stream. */
+    display_stream *stream;
+};
+
+
+/* Instantiates the video decoder for the specified codec.
+ *
+ * @codec_type: The format of the video.
+ * @stream:     The associated video stream.
+ * @return:     A pointer to a structure implementing the VideoDecoder methods.
+ */
+VideoDecoder* create_mjpeg_decoder(int codec_type, display_stream *stream);
+
 
 typedef struct display_surface {
     guint32                     surface_id;
@@ -53,24 +86,18 @@ typedef struct drops_sequence_stats {
     uint32_t duration;
 } drops_sequence_stats;
 
-typedef struct display_stream {
+struct display_stream {
     SpiceMsgIn                  *msg_create;
     SpiceMsgIn                  *msg_clip;
-    SpiceMsgIn                  *msg_data;
 
     /* from messages */
     display_surface             *surface;
     SpiceClip                   *clip;
     QRegion                     region;
     int                         have_region;
-    int                         codec;
 
-    /* mjpeg decoder */
-    struct jpeg_source_mgr         mjpeg_src;
-    struct jpeg_decompress_struct  mjpeg_cinfo;
-    struct jpeg_error_mgr          mjpeg_jerr;
+    VideoDecoder                *video_decoder;
 
-    uint8_t                     *out_frame;
     GQueue                      *msgq;
     guint                       timeout;
     SpiceChannel                *channel;
@@ -97,15 +124,11 @@ typedef struct display_stream {
     uint32_t report_num_frames;
     uint32_t report_num_drops;
     uint32_t report_drops_seq_len;
-} display_stream;
+};
 
-void stream_get_dimensions(display_stream *st, int *width, int *height);
-uint32_t stream_get_current_frame(display_stream *st, uint8_t **data);
+void stream_get_dimensions(display_stream *st, SpiceMsgIn *frame_msg, int *width, int *height);
+uint32_t spice_msg_in_frame_data(SpiceMsgIn *frame_msg, uint8_t **data);
 
-/* channel-display-mjpeg.c */
-void stream_mjpeg_init(display_stream *st);
-void stream_mjpeg_data(display_stream *st);
-void stream_mjpeg_cleanup(display_stream *st);
 
 G_END_DECLS
 
diff --git a/src/channel-display.c b/src/channel-display.c
index b6c79e0..c681a4e 100644
--- a/src/channel-display.c
+++ b/src/channel-display.c
@@ -1086,7 +1086,6 @@ static void display_handle_stream_create(SpiceChannel *channel, SpiceMsgIn *in)
     st->msg_create = in;
     spice_msg_in_ref(in);
     st->clip = &op->clip;
-    st->codec = op->codec_type;
     st->surface = find_surface(c, op->surface_id);
     st->msgq = g_queue_new();
     st->channel = channel;
@@ -1095,10 +1094,15 @@ static void display_handle_stream_create(SpiceChannel *channel, SpiceMsgIn *in)
     region_init(&st->region);
     display_update_stream_region(st);
 
-    switch (st->codec) {
+    switch (op->codec_type) {
     case SPICE_VIDEO_CODEC_TYPE_MJPEG:
-        stream_mjpeg_init(st);
+        st->video_decoder = create_mjpeg_decoder(op->codec_type, st);
         break;
+    default:
+        st->video_decoder = NULL;
+    }
+    if (st->video_decoder == NULL) {
+        spice_printerr("could not create a video decoder for codec %d", op->codec_type);
     }
 }
 
@@ -1141,15 +1145,15 @@ static gboolean display_stream_schedule(display_stream *st)
     return FALSE;
 }
 
-static SpiceRect *stream_get_dest(display_stream *st)
+static SpiceRect *stream_get_dest(display_stream *st, SpiceMsgIn *frame_msg)
 {
-    if (st->msg_data == NULL ||
-        spice_msg_in_type(st->msg_data) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) {
+    if (frame_msg == NULL ||
+        spice_msg_in_type(frame_msg) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) {
         SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st->msg_create);
 
         return &info->dest;
     } else {
-        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st->msg_data);
+        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg);
 
         return &op->dest;
    }
@@ -1164,21 +1168,16 @@ static uint32_t stream_get_flags(display_stream *st)
 }
 
 G_GNUC_INTERNAL
-uint32_t stream_get_current_frame(display_stream *st, uint8_t **data)
+uint32_t spice_msg_in_frame_data(SpiceMsgIn *frame_msg, uint8_t **data)
 {
-    if (st->msg_data == NULL) {
-        *data = NULL;
-        return 0;
-    }
-
-    switch (spice_msg_in_type(st->msg_data)) {
+    switch (spice_msg_in_type(frame_msg)) {
     case SPICE_MSG_DISPLAY_STREAM_DATA: {
-        SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(st->msg_data);
+        SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(frame_msg);
         *data = op->data;
         return op->data_size;
     }
     case SPICE_MSG_DISPLAY_STREAM_DATA_SIZED: {
-        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st->msg_data);
+        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg);
         *data = op->data;
         return op->data_size;
     }
@@ -1189,19 +1188,19 @@ uint32_t stream_get_current_frame(display_stream *st, uint8_t **data)
 }
 
 G_GNUC_INTERNAL
-void stream_get_dimensions(display_stream *st, int *width, int *height)
+void stream_get_dimensions(display_stream *st, SpiceMsgIn *frame_msg, int *width, int *height)
 {
     g_return_if_fail(width != NULL);
     g_return_if_fail(height != NULL);
 
-    if (st->msg_data == NULL ||
-        spice_msg_in_type(st->msg_data) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) {
+    if (frame_msg == NULL ||
+        spice_msg_in_type(frame_msg) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) {
         SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st->msg_create);
 
         *width = info->stream_width;
         *height = info->stream_height;
     } else {
-        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st->msg_data);
+        SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg);
 
         *width = op->width;
         *height = op->height;
@@ -1219,24 +1218,21 @@ static gboolean display_stream_render(display_stream *st)
 
         g_return_val_if_fail(in != NULL, FALSE);
 
-        st->msg_data = in;
-        switch (st->codec) {
-        case SPICE_VIDEO_CODEC_TYPE_MJPEG:
-            stream_mjpeg_data(st);
-            break;
+        uint8_t *out_frame = NULL;
+        if (st->video_decoder) {
+            out_frame = st->video_decoder->decode_frame(st->video_decoder, in);
         }
-
-        if (st->out_frame) {
+        if (out_frame) {
             int width;
             int height;
             SpiceRect *dest;
             uint8_t *data;
             int stride;
 
-            stream_get_dimensions(st, &width, &height);
-            dest = stream_get_dest(st);
+            stream_get_dimensions(st, in, &width, &height);
+            dest = stream_get_dest(st, in);
 
-            data = st->out_frame;
+            data = out_frame;
             stride = width * sizeof(uint32_t);
             if (!(stream_get_flags(st) & SPICE_STREAM_FLAGS_TOP_DOWN)) {
                 data += stride * (height - 1);
@@ -1259,7 +1255,6 @@ static gboolean display_stream_render(display_stream *st)
                     dest->bottom - dest->top);
         }
 
-        st->msg_data = NULL;
         spice_msg_in_unref(in);
 
         in = g_queue_peek_head(st->msgq);
@@ -1568,10 +1563,8 @@ static void destroy_stream(SpiceChannel *channel, int id)
 
     g_array_free(st->drops_seqs_stats_arr, TRUE);
 
-    switch (st->codec) {
-    case SPICE_VIDEO_CODEC_TYPE_MJPEG:
-        stream_mjpeg_cleanup(st);
-        break;
+    if (st->video_decoder) {
+        st->video_decoder->destroy(st->video_decoder);
     }
 
     if (st->msg_clip)


More information about the Spice-commits mailing list