From f84e039f25d49b0f5d3b2fcfe4df15a209ea6a44 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Sun, 1 Apr 2012 13:28:21 +0300 Subject: [PATCH] display: video streaming: add support for frames of different sizes rhbz #815426 When playing a youtube video on Windows guest, the driver sometimes sends images which contain a video frame, but also other parts of the screen (e.g., the you tube process bar). In order to prevent glitches, we send these images as part of the stream, using SPICE_MSG_DISPLAY_STREAM_DATA_SIZED. --- gtk/channel-display-mjpeg.c | 12 ++--- gtk/channel-display-priv.h | 3 ++ gtk/channel-display.c | 99 ++++++++++++++++++++++++++++++++----- spice-common | 2 +- 4 files changed, 98 insertions(+), 18 deletions(-) diff --git a/gtk/channel-display-mjpeg.c b/gtk/channel-display-mjpeg.c index aed3adf..627aab4 100644 --- a/gtk/channel-display-mjpeg.c +++ b/gtk/channel-display-mjpeg.c @@ -24,10 +24,10 @@ static void mjpeg_src_init(struct jpeg_decompress_struct *cinfo) { display_stream *st = SPICE_CONTAINEROF(cinfo->src, display_stream, mjpeg_src); - SpiceMsgDisplayStreamData *data = spice_msg_in_parsed(st->msg_data); + uint8_t *data; - cinfo->src->next_input_byte = data->data; - cinfo->src->bytes_in_buffer = data->data_size; + cinfo->src->bytes_in_buffer = stream_get_current_frame(st, &data); + cinfo->src->next_input_byte = data; } static boolean mjpeg_src_fill(struct jpeg_decompress_struct *cinfo) @@ -64,13 +64,13 @@ void stream_mjpeg_init(display_stream *st) G_GNUC_INTERNAL void stream_mjpeg_data(display_stream *st) { - SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st->msg_create); gboolean back_compat = st->channel->priv->peer_hdr.major_version == 1; - int width = info->stream_width; - int height = info->stream_height; + int width; + int height; uint8_t *dest; uint8_t *lines[4]; + stream_get_dimensions(st, &width, &height); dest = malloc(width * height * 4); if (st->out_frame) { diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h index 6fb624a..d327c28 100644 --- a/gtk/channel-display-priv.h +++ b/gtk/channel-display-priv.h @@ -73,6 +73,9 @@ typedef struct display_stream { SpiceChannel *channel; } 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); + /* channel-display-mjpeg.c */ void stream_mjpeg_init(display_stream *st); void stream_mjpeg_data(display_stream *st); diff --git a/gtk/channel-display.c b/gtk/channel-display.c index d3109bc..578cb03 100644 --- a/gtk/channel-display.c +++ b/gtk/channel-display.c @@ -597,6 +597,7 @@ static void spice_display_channel_init(SpiceDisplayChannel *channel) #if defined(WIN32) c->dc = create_compatible_dc(); #endif + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_SIZED_STREAM); } /* ------------------------------------------------------------------ */ @@ -962,7 +963,7 @@ static void display_handle_stream_create(SpiceChannel *channel, SpiceMsgIn *in) static gboolean display_stream_schedule(display_stream *st) { guint32 time, d; - SpiceMsgDisplayStreamData *op; + SpiceStreamDataHeader *op; SpiceMsgIn *in; if (st->timeout) @@ -989,6 +990,72 @@ static gboolean display_stream_schedule(display_stream *st) return FALSE; } +static SpiceRect *stream_get_dest(display_stream *st) +{ + if (st->msg_data == NULL || + spice_msg_in_type(st->msg_data) != 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); + + return &op->dest; + } + +} + +static uint32_t stream_get_flags(display_stream *st) +{ + SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st->msg_create); + + return info->flags; +} + +G_GNUC_INTERNAL +uint32_t stream_get_current_frame(display_stream *st, uint8_t **data) +{ + if (st->msg_data == NULL) { + *data = NULL; + return 0; + } + + if (spice_msg_in_type(st->msg_data) == SPICE_MSG_DISPLAY_STREAM_DATA) { + SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(st->msg_data); + + *data = op->data; + return op->data_size; + } else { + SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st->msg_data); + + g_return_val_if_fail(spice_msg_in_type(st->msg_data) == + SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, 0); + *data = op->data; + return op->data_size; + } + +} + +G_GNUC_INTERNAL +void stream_get_dimensions(display_stream *st, 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) { + 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); + + *width = op->width; + *height = op->height; + } +} + /* main context */ static gboolean display_stream_render(display_stream *st) { @@ -1008,14 +1075,19 @@ static gboolean display_stream_render(display_stream *st) } if (st->out_frame) { - SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st->msg_create); + int width; + int height; + SpiceRect *dest; uint8_t *data; int stride; + stream_get_dimensions(st, &width, &height); + dest = stream_get_dest(st); + data = st->out_frame; - stride = info->stream_width * sizeof(uint32_t); - if (!(info->flags & SPICE_STREAM_FLAGS_TOP_DOWN)) { - data += stride * (info->src_height - 1); + stride = width * sizeof(uint32_t); + if (!(stream_get_flags(st) & SPICE_STREAM_FLAGS_TOP_DOWN)) { + data += stride * (height - 1); stride = -stride; } @@ -1024,15 +1096,15 @@ static gboolean display_stream_render(display_stream *st) #ifdef WIN32 SPICE_DISPLAY_CHANNEL(st->channel)->priv->dc, #endif - &info->dest, data, - info->src_width, info->src_height, stride, + dest, data, + width, height, stride, st->have_region ? &st->region : NULL); if (st->surface->primary) g_signal_emit(st->channel, signals[SPICE_DISPLAY_INVALIDATE], 0, - info->dest.left, info->dest.top, - info->dest.right - info->dest.left, - info->dest.bottom - info->dest.top); + dest->left, dest->top, + dest->right - dest->left, + dest->bottom - dest->top); } st->msg_data = NULL; @@ -1053,12 +1125,16 @@ static gboolean display_stream_render(display_stream *st) static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in) { SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv; - SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(in); + SpiceStreamDataHeader *op = spice_msg_in_parsed(in); display_stream *st = c->streams[op->id]; guint32 mmtime; mmtime = spice_session_get_mm_time(spice_channel_get_session(channel)); + if (spice_msg_in_type(in) == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) { + SPICE_DEBUG("stream %d contains sized data", op->id); + } + if (op->multi_media_time == 0) { g_critical("Received frame with invalid 0 timestamp! perhaps wrong graphic driver?"); op->multi_media_time = mmtime + 100; /* workaround... */ @@ -1324,6 +1400,7 @@ static const spice_msg_handler display_handlers[] = { [ SPICE_MSG_DISPLAY_STREAM_CLIP ] = display_handle_stream_clip, [ SPICE_MSG_DISPLAY_STREAM_DESTROY ] = display_handle_stream_destroy, [ SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL ] = display_handle_stream_destroy_all, + [ SPICE_MSG_DISPLAY_STREAM_DATA_SIZED ] = display_handle_stream_data, [ SPICE_MSG_DISPLAY_DRAW_FILL ] = display_handle_draw_fill, [ SPICE_MSG_DISPLAY_DRAW_OPAQUE ] = display_handle_draw_opaque, diff --git a/spice-common b/spice-common index e96dbb4..22fc0b0 160000 --- a/spice-common +++ b/spice-common @@ -1 +1 @@ -Subproject commit e96dbb4172ec7a47a5b15d3b9e921e12623fddaa +Subproject commit 22fc0b0145876b90385c1c88923bcd72a6380812