[RFC,spice-server,2/3] stream-channel: Use the preferred codec list instead of supported

Submitted by Kevin Pouget on Aug. 6, 2019, 3:34 p.m.

Details

Message ID 20190806153453.20616-3-kpouget@redhat.com
State New
Headers show
Series "Series without cover letter" ( rev: 3 2 1 ) in Spice

Not browsing as part of any series.

Commit Message

Kevin Pouget Aug. 6, 2019, 3:34 p.m.
This patch computes and sends the list of the video codecs preferred
by all the clients when requesting to start or reset a video stream.

It used to be the list of the supported codecs.

The MJPEG codec is used as a fallback if there is no codec preferred
by all the clients.

Note: at the moment, the server's list of allowed video-stream codecs
is used to disable the usage of some codecs. This may not be the best
option. To be further discussed ...

Signed-off-by: Kevin Pouget <kpouget@redhat.com>
---
 server/stream-channel.c | 84 +++++++++++++++++++++++++++++++++--------
 1 file changed, 69 insertions(+), 15 deletions(-)

--
2.21.0

Patch hide | download patch | download mbox

diff --git a/server/stream-channel.c b/server/stream-channel.c
index fa4804f1..e0e41895 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -356,12 +356,17 @@  stream_channel_new(RedsState *server, uint32_t id)

 #define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END

-// find common codecs supported by all clients
+// find common codecs preferred by all clients
 static uint8_t
-stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
+stream_channel_get_preferred_codecs(StreamChannel *channel, uint8_t *out_codecs)
 {
+    RedChannel *red_channel = RED_CHANNEL(channel);
+    RedsState *reds = red_channel_get_server(red_channel);
+    GArray *allowed_codecs = reds_get_video_codecs(reds);
+
     RedChannelClient *rcc;
     int codec;
+    int i;

     static const uint16_t codec2cap[] = {
         0, // invalid
@@ -372,29 +377,78 @@  stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
         SPICE_DISPLAY_CAP_CODEC_H265,
     };

-    bool supported[SPICE_N_ELEMENTS(codec2cap)];
+    unsigned int weight[SPICE_N_ELEMENTS(codec2cap)];
+
+    /* fill the weight array with a preference weight: 0 is not
+     * supported, otherwise: weight := sum(position in preference
+     * array, for each client). */

-    for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
-        supported[codec] = true;
+    // disable codecs not allowed by the server configuration
+    for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
+        weight[codec] = 0;
+        for (i = 0; i < allowed_codecs->len; i++) {
+            RedVideoCodec pref_codec = g_array_index(allowed_codecs, RedVideoCodec, i);
+
+            if (pref_codec.type == codec) {
+                weight[codec] = 1;
+                break;
+            }
+        }
     }

     FOREACH_CLIENT(channel, rcc) {
+        StreamChannelClient *scc = STREAM_CHANNEL_CLIENT(rcc);
+        GArray *preferred_codecs = scc->client_preferred_video_codecs;
+
+        g_assert(preferred_codecs == NULL ||
+                 preferred_codecs->len == SPICE_VIDEO_CODEC_TYPE_ENUM_END);
+
         for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
-            // if do not support codec delete from list
+            // skip the codec if it is already not supported
+            if (!weight[codec]) {
+                continue;
+            }
+
+            // delete from the list if it's not supported by this client
             if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) {
-                supported[codec] = false;
+                weight[codec] = 0;
+                continue;
             }
+
+            // the client didn't provide a preferred codecs list
+            if (preferred_codecs == NULL) {
+                continue;
+            }
+
+            // delete the codec from the list if it's not in the preferred list
+            weight[codec] += g_array_index(preferred_codecs, gint, codec);
         }
     }

-    // surely mjpeg is supported
-    supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true;
-
+    /* sort the codec by the preferrence weight, lower is better */
     int num = 0;
-    for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
-        if (supported[codec]) {
-            out_codecs[num++] = codec;
+
+    while (true) {
+        unsigned int min_codec, min_weight = UINT_MAX;
+
+        for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
+
+            if (weight[codec] && weight[codec] < min_weight) {
+                min_codec = codec;
+                min_weight = weight[codec];
+            }
+        }
+        if (min_weight == UINT_MAX) {
+            break;
         }
+
+        out_codecs[num++] = min_codec;
+        weight[min_codec] = 0;
+    }
+
+    if (num == 0) {
+        // fallback, surely mjpeg is supported
+        out_codecs[num++] = SPICE_VIDEO_CODEC_TYPE_MJPEG;
     }

     return num;
@@ -432,7 +486,7 @@  stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedStream
     spice_return_if_fail(client != NULL);

     // request new stream
-    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
+    start->num_codecs = stream_channel_get_preferred_codecs(channel, start->codecs);
     // send in any case, even if list is not changed
     // notify device about changes
     request_new_stream(channel, start);
@@ -647,7 +701,7 @@  stream_channel_reset(StreamChannel *channel)

     // try to request a new stream, this should start a new stream
     // if the guest is connected to the device and a client is already connected
-    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
+    start->num_codecs = stream_channel_get_preferred_codecs(channel, start->codecs);
     // send in any case, even if list is not changed
     // notify device about changes
     request_new_stream(channel, start);