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@xxxxxxxxxx> --- server/stream-channel.c | 84 +++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 15 deletions(-) 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); -- 2.21.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel