[Spice-devel] [spice v9] dcc: handle preferred video codec message
Frediano Ziglio
fziglio at redhat.com
Mon Feb 27 13:06:30 UTC 2017
>
> From: Victor Toso <me at victortoso.com>
>
> [0] SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE
>
> This message provides a list of video codecs based on client's order
> of preference.
>
> We duplicate the video codecs array from reds.c and sort it using the
> order of codecs as reference.
>
> This message will not change an ongoing streaming but it could change
> newly created streams depending the rank value of each video codec
> that can be set by spice_server_set_video_codecs()
>
> Signed-off-by: Victor Toso <victortoso at redhat.com>
> ---
> server/dcc-private.h | 5 ++
> server/dcc.c | 132
> +++++++++++++++++++++++++++++++++++++++++++++++
> server/dcc.h | 1 +
> server/display-channel.c | 2 +
> server/stream.c | 3 +-
> 5 files changed, 141 insertions(+), 2 deletions(-)
>
> diff --git a/server/dcc-private.h b/server/dcc-private.h
> index 64b32a7..bf69fbb 100644
> --- a/server/dcc-private.h
> +++ b/server/dcc-private.h
> @@ -51,6 +51,11 @@ struct DisplayChannelClientPrivate
> int num_pixmap_cache_items;
> } send_data;
>
> + /* Host preferred video-codec order sorted with client preferred */
> + GArray *preferred_video_codecs;
> + /* Last client preferred video-codec order */
> + GArray *client_preferred_video_codecs;
> +
> uint8_t surface_client_created[NUM_SURFACES];
> QRegion surface_client_lossy_region[NUM_SURFACES];
>
> diff --git a/server/dcc.c b/server/dcc.c
> index cf9431a..476f43d 100644
> --- a/server/dcc.c
> +++ b/server/dcc.c
> @@ -39,6 +39,8 @@ enum
> PROP_ZLIB_GLZ_STATE
> };
>
> +static void on_display_video_codecs_update(GObject *gobject, GParamSpec
> *pspec, gpointer user_data);
> +
> static void
> display_channel_client_get_property(GObject *object,
> guint property_id,
> @@ -99,12 +101,19 @@ display_channel_client_constructed(GObject *object)
> dcc_init_stream_agents(self);
>
> image_encoders_init(&self->priv->encoders,
> &DCC_TO_DC(self)->priv->encoder_shared_data);
> +
> + g_signal_connect(DCC_TO_DC(self), "notify::video-codecs",
> + G_CALLBACK(on_display_video_codecs_update), self);
> }
>
> static void
> display_channel_client_finalize(GObject *object)
> {
> DisplayChannelClient *self = DISPLAY_CHANNEL_CLIENT(object);
> +
> + g_signal_handlers_disconnect_by_func(DCC_TO_DC(self),
> on_display_video_codecs_update, self);
> + g_clear_pointer(&self->priv->preferred_video_codecs, g_array_unref);
> + g_clear_pointer(&self->priv->client_preferred_video_codecs,
> g_array_unref);
> g_free(self->priv);
>
> G_OBJECT_CLASS(display_channel_client_parent_class)->finalize(object);
> @@ -1105,6 +1114,126 @@ static int
> dcc_handle_preferred_compression(DisplayChannelClient *dcc,
> return TRUE;
> }
>
> +
> +/* TODO: Client preference should only be considered when host has
> video-codecs
> + * with the same priority value. At the moment, the video-codec GArray will
> be
> + * sorted following only the client's preference (@user_data)
> + *
> + * example:
> + * host encoding preference: gstreamer:mjpeg;gstreamer:vp8;gstreamer:h264
> + * client decoding preference: h264, vp9, mjpeg
> + * result: gstreamer:h264;gstreamer:mjpeg;gstreamer:vp8
> + */
> +static gint sort_video_codecs_by_client_preference(gconstpointer a_pointer,
> + gconstpointer b_pointer,
> + gpointer user_data)
> +{
> + const RedVideoCodec *a = a_pointer;
> + const RedVideoCodec *b = b_pointer;
> +
> + if (a->type != b->type) {
> + guint i;
> + GArray *client_pref = user_data;
> +
> + for (i = 0; i < client_pref->len; i++) {
> + if (a->type == g_array_index(client_pref, gint, i)) {
> + return -1;
> + }
> + if (b->type == g_array_index(client_pref, gint, i)) {
> + return 1;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void dcc_update_preferred_video_codecs(DisplayChannelClient *dcc)
> +{
> + guint i;
> + GArray *video_codecs, *server_codecs;
> + GString *msg;
> +
> + server_codecs = display_channel_get_video_codecs(DCC_TO_DC(dcc));
> + spice_return_if_fail(server_codecs != NULL);
> +
> + /* Copy current host preference */
> + video_codecs = g_array_sized_new(FALSE, FALSE, sizeof(RedVideoCodec),
> server_codecs->len);
> + g_array_append_vals(video_codecs, server_codecs->data,
> server_codecs->len);
> +
> + /* Sort the copy of current host preference based on client's preference
> */
> + g_array_sort_with_data(video_codecs,
> sort_video_codecs_by_client_preference,
> + dcc->priv->client_preferred_video_codecs);
> + g_clear_pointer(&dcc->priv->preferred_video_codecs, g_array_unref);
> + dcc->priv->preferred_video_codecs = video_codecs;
> +
> + msg = g_string_new("Preferred video-codecs:");
> + for (i = 0; i < video_codecs->len; i++) {
> + RedVideoCodec codec = g_array_index(video_codecs, RedVideoCodec, i);
> + g_string_append_printf(msg, " %d", codec.type);
> + }
> + spice_info("%s", msg->str);
> + g_string_free(msg, TRUE);
> +}
> +
> +static void on_display_video_codecs_update(GObject *gobject,
> + GParamSpec *pspec, gpointer user_data)
> +{
> + DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(user_data);
> +
> + /* Only worry about video-codecs update if client has sent
> + * SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE */
> + if (dcc->priv->client_preferred_video_codecs == NULL) {
> + return;
> + }
> +
> + /* New host preference */
> + dcc_update_preferred_video_codecs(dcc);
> +}
> +
> +static int dcc_handle_preferred_video_codec_type(DisplayChannelClient *dcc,
> + SpiceMsgcDisplayPreferredVideoCodecType *msg)
> +{
> + gint i;
> + GArray *client_codecs;
> + gint has_codec[SPICE_VIDEO_CODEC_TYPE_ENUM_END] = { 0 };
> +
> + g_return_val_if_fail(msg->num_of_codecs > 0, TRUE);
> +
> + client_codecs = g_array_new(FALSE, FALSE, sizeof(gint));
> + for (i = 0; i < msg->num_of_codecs; i++) {
> + gint video_codec = msg->codecs[i];
> +
> + if (video_codec < SPICE_VIDEO_CODEC_TYPE_MJPEG ||
> + video_codec >= SPICE_VIDEO_CODEC_TYPE_ENUM_END) {
> + spice_debug("Client has sent unknow video-codec (value %d at
> index %d). "
> + "Ignoring as server can't handle it",
> + video_codec, i);
> + continue;
Is this actually logically possible?
I mean, there should be an exchange of capabilities between client and
server and client should not attempt so send something the server
cannot understand.
> + }
> +
> + if (has_codec[video_codec] != 0)
> + continue;
> +
> + has_codec[video_codec] = 1;
This array made me think about having an hash of the index
(in this case the hash function would be f(n) = n so basically
a similar array).
If you initialize the array to a maximum value you could have a
static gint sort_video_codecs_by_client_preference(gconstpointer a_pointer,
gconstpointer b_pointer,
gpointer user_data)
{
const RedVideoCodec *a = a_pointer;
const RedVideoCodec *b = b_pointer;
const int *indexes = user_data;
return indexes[b->type] - indexes[a->type];
}
> + g_array_append_val(client_codecs, video_codec);
> + }
> + g_clear_pointer(&dcc->priv->client_preferred_video_codecs,
> g_array_unref);
> + dcc->priv->client_preferred_video_codecs = client_codecs;
> +
> + /* New client preference */
> + dcc_update_preferred_video_codecs(dcc);
> + return TRUE;
> +}
> +
> +GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient
> *dcc)
> +{
> + if (dcc->priv->preferred_video_codecs != NULL) {
> + return dcc->priv->preferred_video_codecs;
> + }
> + return display_channel_get_video_codecs(DCC_TO_DC(dcc));
> +}
> +
> static int dcc_handle_gl_draw_done(DisplayChannelClient *dcc)
> {
> DisplayChannel *display = DCC_TO_DC(dcc);
> @@ -1135,6 +1264,9 @@ int dcc_handle_message(RedChannelClient *rcc, uint16_t
> type, uint32_t size, void
> (SpiceMsgcDisplayPreferredCompression *)msg);
> case SPICE_MSGC_DISPLAY_GL_DRAW_DONE:
> return dcc_handle_gl_draw_done(dcc);
> + case SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE:
> + return dcc_handle_preferred_video_codec_type(dcc,
> + (SpiceMsgcDisplayPreferredVideoCodecType *)msg);
> default:
> return red_channel_client_handle_message(rcc, type, size, msg);
> }
> diff --git a/server/dcc.h b/server/dcc.h
> index ebdbb8d..37bd3e9 100644
> --- a/server/dcc.h
> +++ b/server/dcc.h
> @@ -204,6 +204,7 @@ uint64_t dcc_get_max_stream_bit_rate(DisplayChannelClient
> *dcc);
> void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate);
> int dcc_config_socket(RedChannelClient *rcc);
> gboolean dcc_is_low_bandwidth(DisplayChannelClient *dcc);
> +GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient
> *dcc);
>
> G_END_DECLS
>
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 288969c..85df19d 100644
> --- a/server/display-channel.c
> +++ b/server/display-channel.c
> @@ -214,6 +214,7 @@ void display_channel_set_video_codecs(DisplayChannel
> *display, GArray *video_cod
>
> g_clear_pointer(&display->priv->video_codecs, g_array_unref);
> display->priv->video_codecs = g_array_ref(video_codecs);
> + g_object_notify(G_OBJECT(display), "video-codecs");
> }
>
> GArray *display_channel_get_video_codecs(DisplayChannel *display)
> @@ -2057,6 +2058,7 @@ display_channel_constructed(GObject *object)
>
> red_channel_set_cap(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
> red_channel_set_cap(channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION);
> + red_channel_set_cap(channel, SPICE_DISPLAY_CAP_PREF_VIDEO_CODEC_TYPE);
> red_channel_set_cap(channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
> }
>
> diff --git a/server/stream.c b/server/stream.c
> index e2cd66e..f1ec560 100644
> --- a/server/stream.c
> +++ b/server/stream.c
> @@ -687,13 +687,12 @@ static VideoEncoder*
> dcc_create_video_encoder(DisplayChannelClient *dcc,
> uint64_t starting_bit_rate,
> VideoEncoderRateControlCbs
> *cbs)
> {
> - DisplayChannel *display = DCC_TO_DC(dcc);
> RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc);
> int client_has_multi_codec = red_channel_client_test_remote_cap(rcc,
> SPICE_DISPLAY_CAP_MULTI_CODEC);
> int i;
> GArray *video_codecs;
>
> - video_codecs = display_channel_get_video_codecs(display);
> + video_codecs = dcc_get_preferred_video_codecs_for_encoding(dcc);
> for (i = 0; i < video_codecs->len; i++) {
> RedVideoCodec* video_codec = &g_array_index (video_codecs,
> RedVideoCodec, i);
>
<OT>
looking at all these flags looks like the filtering/ordering of these encoding
is getting complicated. Basically we excluded codecs based on:
- server settings;
- client settings;
- encoders support.
The code is spread quite in different places.
I have the sensation it's spread in too many places.
</OT>
Frediano
More information about the Spice-devel
mailing list