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

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