Blame SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch

7bbc9c
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
7bbc9c
From: Frediano Ziglio <fziglio@redhat.com>
7bbc9c
Date: Sat, 21 Jan 2017 19:03:19 +0000
7bbc9c
Subject: [spice-server] stream-channel: Support client
7bbc9c
 connection/disconnection
7bbc9c
7bbc9c
When a new client is connected we must restart the stream so new
7bbc9c
clients can receive correct data without having to wait for the
7bbc9c
next full screen (which on idle screen could take ages).
7bbc9c
On disconnection we should tell the guest to stop streaming
7bbc9c
not wasting resources to stream not needed data.
7bbc9c
7bbc9c
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
7bbc9c
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
7bbc9c
---
7bbc9c
 server/stream-channel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
7bbc9c
 1 file changed, 81 insertions(+)
7bbc9c
7bbc9c
diff --git a/server/stream-channel.c b/server/stream-channel.c
7bbc9c
index be6f7c7d1..2ad9ebae3 100644
7bbc9c
--- a/server/stream-channel.c
7bbc9c
+++ b/server/stream-channel.c
7bbc9c
@@ -121,8 +121,32 @@ stream_channel_client_init(StreamChannelClient *client)
7bbc9c
 }
7bbc9c
 
7bbc9c
 static void
7bbc9c
+request_new_stream(StreamChannel *channel, StreamMsgStartStop *start)
7bbc9c
+{
7bbc9c
+    if (channel->start_cb) {
7bbc9c
+        channel->start_cb(channel->start_opaque, start, channel);
7bbc9c
+    }
7bbc9c
+}
7bbc9c
+
7bbc9c
+static void
7bbc9c
 stream_channel_client_on_disconnect(RedChannelClient *rcc)
7bbc9c
 {
7bbc9c
+    RedChannel *red_channel = red_channel_client_get_channel(rcc);
7bbc9c
+
7bbc9c
+    // if there are still some client connected keep streaming
7bbc9c
+    // TODO, maybe would be worth sending new codecs if they are better
7bbc9c
+    if (red_channel_is_connected(red_channel)) {
7bbc9c
+        return;
7bbc9c
+    }
7bbc9c
+
7bbc9c
+    StreamChannel *channel = STREAM_CHANNEL(red_channel);
7bbc9c
+    channel->stream_id = -1;
7bbc9c
+    channel->width = 0;
7bbc9c
+    channel->height = 0;
7bbc9c
+
7bbc9c
+    // send stream stop to device
7bbc9c
+    StreamMsgStartStop stop = { 0, };
7bbc9c
+    request_new_stream(channel, &stop);
7bbc9c
 }
7bbc9c
 
7bbc9c
 static StreamChannelClient*
7bbc9c
@@ -258,18 +282,75 @@ stream_channel_new(RedsState *server, uint32_t id)
7bbc9c
                         NULL);
7bbc9c
 }
7bbc9c
 
7bbc9c
+#define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END
7bbc9c
+
7bbc9c
+// find common codecs supported by all clients
7bbc9c
+static uint8_t
7bbc9c
+stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
7bbc9c
+{
7bbc9c
+    RedChannelClient *rcc;
7bbc9c
+    int codec;
7bbc9c
+
7bbc9c
+    static const uint16_t codec2cap[] = {
7bbc9c
+        0, // invalid
7bbc9c
+        SPICE_DISPLAY_CAP_CODEC_MJPEG,
7bbc9c
+        SPICE_DISPLAY_CAP_CODEC_VP8,
7bbc9c
+        SPICE_DISPLAY_CAP_CODEC_H264,
7bbc9c
+        SPICE_DISPLAY_CAP_CODEC_VP9,
7bbc9c
+    };
7bbc9c
+
7bbc9c
+    bool supported[SPICE_N_ELEMENTS(codec2cap)];
7bbc9c
+
7bbc9c
+    for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
7bbc9c
+        supported[codec] = true;
7bbc9c
+    }
7bbc9c
+
7bbc9c
+    FOREACH_CLIENT(channel, rcc) {
7bbc9c
+        for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
7bbc9c
+            // if do not support codec delete from list
7bbc9c
+            if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) {
7bbc9c
+                supported[codec] = false;
7bbc9c
+            }
7bbc9c
+        }
7bbc9c
+    }
7bbc9c
+
7bbc9c
+    // surely mjpeg is supported
7bbc9c
+    supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true;
7bbc9c
+
7bbc9c
+    int num = 0;
7bbc9c
+    for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
7bbc9c
+        if (supported[codec]) {
7bbc9c
+            out_codecs[num++] = codec;
7bbc9c
+        }
7bbc9c
+    }
7bbc9c
+
7bbc9c
+    return num;
7bbc9c
+}
7bbc9c
+
7bbc9c
 static void
7bbc9c
 stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream,
7bbc9c
                        int migration, RedChannelCapabilities *caps)
7bbc9c
 {
7bbc9c
     StreamChannel *channel = STREAM_CHANNEL(red_channel);
7bbc9c
     StreamChannelClient *client;
7bbc9c
+    struct {
7bbc9c
+        StreamMsgStartStop base;
7bbc9c
+        uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
7bbc9c
+    } start_msg;
7bbc9c
+    StreamMsgStartStop *const start = &start_msg.base;
7bbc9c
 
7bbc9c
     spice_return_if_fail(stream != NULL);
7bbc9c
 
7bbc9c
     client = stream_channel_client_new(channel, red_client, stream, migration, caps);
7bbc9c
     spice_return_if_fail(client != NULL);
7bbc9c
 
7bbc9c
+    // request new stream
7bbc9c
+    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
7bbc9c
+    // send in any case, even if list is not changed
7bbc9c
+    // notify device about changes
7bbc9c
+    request_new_stream(channel, start);
7bbc9c
+
7bbc9c
+
7bbc9c
     // TODO set capabilities like  SPICE_DISPLAY_CAP_MONITORS_CONFIG
7bbc9c
     // see guest_set_client_capabilities
7bbc9c
     RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);